diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/liboutils/include/sha2.h | 22 | ||||
-rw-r--r-- | src/liboutils/outils.h | 6 | ||||
-rw-r--r-- | src/liboutils/sha2.c | 96 | ||||
-rw-r--r-- | src/usr.bin/signify/signify.h | 33 | ||||
-rw-r--r-- | src/usr.bin/signify/zsig.c | 322 |
5 files changed, 466 insertions, 13 deletions
diff --git a/src/liboutils/include/sha2.h b/src/liboutils/include/sha2.h index 065c30d..52ddb3f 100644 --- a/src/liboutils/include/sha2.h +++ b/src/liboutils/include/sha2.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sha2.h,v 1.9 2013/04/15 15:54:17 millert Exp $ */ +/* $OpenBSD: sha2.h,v 1.10 2016/09/03 17:00:29 tedu Exp $ */ /* * FILE: sha2.h @@ -51,6 +51,9 @@ #define SHA512_BLOCK_LENGTH 128 #define SHA512_DIGEST_LENGTH 64 #define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) +#define SHA512_256_BLOCK_LENGTH 128 +#define SHA512_256_DIGEST_LENGTH 32 +#define SHA512_256_DIGEST_STRING_LENGTH (SHA512_256_DIGEST_LENGTH * 2 + 1) /*** SHA-224/256/384/512 Context Structure *******************************/ @@ -131,6 +134,23 @@ char *SHA512FileChunk(const char *, char *, off_t, off_t) char *SHA512Data(const u_int8_t *, size_t, char *) __attribute__((__bounded__(__string__,1,2))) __attribute__((__bounded__(__minbytes__,3,SHA512_DIGEST_STRING_LENGTH))); + +void SHA512_256Init(SHA2_CTX *); +void SHA512_256Transform(u_int64_t state[8], const u_int8_t [SHA512_256_BLOCK_LENGTH]); +void SHA512_256Update(SHA2_CTX *, const u_int8_t *, size_t) + __attribute__((__bounded__(__string__,2,3))); +void SHA512_256Pad(SHA2_CTX *); +void SHA512_256Final(u_int8_t [SHA512_256_DIGEST_LENGTH], SHA2_CTX *) + __attribute__((__bounded__(__minbytes__,1,SHA512_256_DIGEST_LENGTH))); +char *SHA512_256End(SHA2_CTX *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA512_256_DIGEST_STRING_LENGTH))); +char *SHA512_256File(const char *, char *) + __attribute__((__bounded__(__minbytes__,2,SHA512_256_DIGEST_STRING_LENGTH))); +char *SHA512_256FileChunk(const char *, char *, off_t, off_t) + __attribute__((__bounded__(__minbytes__,2,SHA512_256_DIGEST_STRING_LENGTH))); +char *SHA512_256Data(const u_int8_t *, size_t, char *) + __attribute__((__bounded__(__string__,1,2))) + __attribute__((__bounded__(__minbytes__,3,SHA512_256_DIGEST_STRING_LENGTH))); __END_DECLS #endif /* _SHA2_H */ diff --git a/src/liboutils/outils.h b/src/liboutils/outils.h index 6e516b7..7686d60 100644 --- a/src/liboutils/outils.h +++ b/src/liboutils/outils.h @@ -1,5 +1,9 @@ +#define DEF_WEAK(x) + #define __weak_alias(new, old) \ extern __typeof(old) new __attribute__((weak, alias(#old))) +#define MAKE_CLONE(new, old) \ + extern __typeof(old) new __attribute__((weak, alias(#old))) #define __dead __attribute__((__noreturn__)) #define __BEGIN_DECLS #define __END_DECLS @@ -33,4 +37,4 @@ int timingsafe_bcmp(const void *, const void *, size_t); void *reallocarray(void *, size_t, size_t); int pledge(const char *, const char **); void explicit_bzero(void *buf, size_t len); - +int getentropy(void *buf, size_t len); diff --git a/src/liboutils/sha2.c b/src/liboutils/sha2.c index e8216df..ec13e44 100644 --- a/src/liboutils/sha2.c +++ b/src/liboutils/sha2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sha2.c,v 1.18 2014/07/20 04:22:34 guenther Exp $ */ +/* $OpenBSD: sha2.c,v 1.25 2016/09/03 16:25:03 tedu Exp $ */ /* * FILE: sha2.c @@ -52,6 +52,11 @@ * #define SHA2_UNROLL_TRANSFORM * */ +#ifndef SHA2_SMALL +#if defined(__amd64__) || defined(__i386__) +#define SHA2_UNROLL_TRANSFORM +#endif +#endif /*** SHA-224/256/384/512 Machine Architecture Definitions *****************/ /* @@ -283,6 +288,18 @@ static const u_int64_t sha384_initial_hash_value[8] = { 0x47b5481dbefa4fa4ULL }; +/* Initial hash value H for SHA-512-256 */ +static const u_int64_t sha512_256_initial_hash_value[8] = { + 0x22312194fc2bf72cULL, + 0x9f555fa3c84c64c2ULL, + 0x2393b86b6f53b151ULL, + 0x963877195940eabdULL, + 0x96283ee2a88effe3ULL, + 0xbe5e1e2553863992ULL, + 0x2b0199fc2c85b8aaULL, + 0x0eb72ddc81c52ca2ULL +}; + /*** SHA-224: *********************************************************/ void SHA224Init(SHA2_CTX *context) @@ -292,10 +309,14 @@ SHA224Init(SHA2_CTX *context) memset(context->buffer, 0, sizeof(context->buffer)); context->bitcount[0] = 0; } +DEF_WEAK(SHA224Init); -__weak_alias(SHA224Transform, SHA256Transform); -__weak_alias(SHA224Update, SHA256Update); -__weak_alias(SHA224Pad, SHA256Pad); +MAKE_CLONE(SHA224Transform, SHA256Transform); +MAKE_CLONE(SHA224Update, SHA256Update); +MAKE_CLONE(SHA224Pad, SHA256Pad); +DEF_WEAK(SHA224Transform); +DEF_WEAK(SHA224Update); +DEF_WEAK(SHA224Pad); void SHA224Final(u_int8_t digest[SHA224_DIGEST_LENGTH], SHA2_CTX *context) @@ -311,8 +332,9 @@ SHA224Final(u_int8_t digest[SHA224_DIGEST_LENGTH], SHA2_CTX *context) #else memcpy(digest, context->state.st32, SHA224_DIGEST_LENGTH); #endif - memset(context, 0, sizeof(*context)); + explicit_bzero(context, sizeof(*context)); } +DEF_WEAK(SHA224Final); #endif /* !defined(SHA2_SMALL) */ /*** SHA-256: *********************************************************/ @@ -324,6 +346,7 @@ SHA256Init(SHA2_CTX *context) memset(context->buffer, 0, sizeof(context->buffer)); context->bitcount[0] = 0; } +DEF_WEAK(SHA256Init); #ifdef SHA2_UNROLL_TRANSFORM @@ -482,6 +505,7 @@ SHA256Transform(u_int32_t state[8], const u_int8_t data[SHA256_BLOCK_LENGTH]) } #endif /* SHA2_UNROLL_TRANSFORM */ +DEF_WEAK(SHA256Transform); void SHA256Update(SHA2_CTX *context, const u_int8_t *data, size_t len) @@ -528,6 +552,7 @@ SHA256Update(SHA2_CTX *context, const u_int8_t *data, size_t len) /* Clean up: */ usedspace = freespace = 0; } +DEF_WEAK(SHA256Update); void SHA256Pad(SHA2_CTX *context) @@ -571,6 +596,7 @@ SHA256Pad(SHA2_CTX *context) /* Clean up: */ usedspace = 0; } +DEF_WEAK(SHA256Pad); void SHA256Final(u_int8_t digest[SHA256_DIGEST_LENGTH], SHA2_CTX *context) @@ -586,8 +612,9 @@ SHA256Final(u_int8_t digest[SHA256_DIGEST_LENGTH], SHA2_CTX *context) #else memcpy(digest, context->state.st32, SHA256_DIGEST_LENGTH); #endif - memset(context, 0, sizeof(*context)); + explicit_bzero(context, sizeof(*context)); } +DEF_WEAK(SHA256Final); /*** SHA-512: *********************************************************/ @@ -599,6 +626,7 @@ SHA512Init(SHA2_CTX *context) memset(context->buffer, 0, sizeof(context->buffer)); context->bitcount[0] = context->bitcount[1] = 0; } +DEF_WEAK(SHA512Init); #ifdef SHA2_UNROLL_TRANSFORM @@ -758,6 +786,7 @@ SHA512Transform(u_int64_t state[8], const u_int8_t data[SHA512_BLOCK_LENGTH]) } #endif /* SHA2_UNROLL_TRANSFORM */ +DEF_WEAK(SHA512Transform); void SHA512Update(SHA2_CTX *context, const u_int8_t *data, size_t len) @@ -804,6 +833,7 @@ SHA512Update(SHA2_CTX *context, const u_int8_t *data, size_t len) /* Clean up: */ usedspace = freespace = 0; } +DEF_WEAK(SHA512Update); void SHA512Pad(SHA2_CTX *context) @@ -847,6 +877,7 @@ SHA512Pad(SHA2_CTX *context) /* Clean up: */ usedspace = 0; } +DEF_WEAK(SHA512Pad); void SHA512Final(u_int8_t digest[SHA512_DIGEST_LENGTH], SHA2_CTX *context) @@ -862,8 +893,9 @@ SHA512Final(u_int8_t digest[SHA512_DIGEST_LENGTH], SHA2_CTX *context) #else memcpy(digest, context->state.st64, SHA512_DIGEST_LENGTH); #endif - memset(context, 0, sizeof(*context)); + explicit_bzero(context, sizeof(*context)); } +DEF_WEAK(SHA512Final); #if !defined(SHA2_SMALL) @@ -876,10 +908,14 @@ SHA384Init(SHA2_CTX *context) memset(context->buffer, 0, sizeof(context->buffer)); context->bitcount[0] = context->bitcount[1] = 0; } +DEF_WEAK(SHA384Init); -__weak_alias(SHA384Transform, SHA512Transform); -__weak_alias(SHA384Update, SHA512Update); -__weak_alias(SHA384Pad, SHA512Pad); +MAKE_CLONE(SHA384Transform, SHA512Transform); +MAKE_CLONE(SHA384Update, SHA512Update); +MAKE_CLONE(SHA384Pad, SHA512Pad); +DEF_WEAK(SHA384Transform); +DEF_WEAK(SHA384Update); +DEF_WEAK(SHA384Pad); void SHA384Final(u_int8_t digest[SHA384_DIGEST_LENGTH], SHA2_CTX *context) @@ -896,6 +932,44 @@ SHA384Final(u_int8_t digest[SHA384_DIGEST_LENGTH], SHA2_CTX *context) memcpy(digest, context->state.st64, SHA384_DIGEST_LENGTH); #endif /* Zero out state data */ - memset(context, 0, sizeof(*context)); + explicit_bzero(context, sizeof(*context)); +} +DEF_WEAK(SHA384Final); + +/*** SHA-512/256: *********************************************************/ +void +SHA512_256Init(SHA2_CTX *context) +{ + memcpy(context->state.st64, sha512_256_initial_hash_value, + sizeof(sha512_256_initial_hash_value)); + memset(context->buffer, 0, sizeof(context->buffer)); + context->bitcount[0] = context->bitcount[1] = 0; +} +DEF_WEAK(SHA512_256Init); + +MAKE_CLONE(SHA512_256Transform, SHA512Transform); +MAKE_CLONE(SHA512_256Update, SHA512Update); +MAKE_CLONE(SHA512_256Pad, SHA512Pad); +DEF_WEAK(SHA512_256Transform); +DEF_WEAK(SHA512_256Update); +DEF_WEAK(SHA512_256Pad); + +void +SHA512_256Final(u_int8_t digest[SHA512_256_DIGEST_LENGTH], SHA2_CTX *context) +{ + SHA512_256Pad(context); + +#if BYTE_ORDER == LITTLE_ENDIAN + int i; + + /* Convert TO host byte order */ + for (i = 0; i < 4; i++) + BE_64_TO_8(digest + i * 8, context->state.st64[i]); +#else + memcpy(digest, context->state.st64, SHA512_256_DIGEST_LENGTH); +#endif + /* Zero out state data */ + explicit_bzero(context, sizeof(*context)); } +DEF_WEAK(SHA512_256Final); #endif /* !defined(SHA2_SMALL) */ diff --git a/src/usr.bin/signify/signify.h b/src/usr.bin/signify/signify.h new file mode 100644 index 0000000..6edb6a4 --- /dev/null +++ b/src/usr.bin/signify/signify.h @@ -0,0 +1,33 @@ +/* $OpenBSD: signify.h,v 1.1 2016/09/02 16:10:56 espie Exp $ */ +/* + * Copyright (c) 2016 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* common interface to signify.c/zsig.c */ +#ifndef signify_h +#define signify_h +extern void zverify(const char *, const char *, const char *, const char *); +extern void zsign(const char *, const char *, const char *); + +extern void *xmalloc(size_t); +extern void writeall(int, const void *, size_t, const char *); +extern int xopen(const char *, int, mode_t); +extern void *verifyzdata(uint8_t *, unsigned long long, + const char *, const char *, const char *); +extern uint8_t *createsig(const char *, const char *, uint8_t *, + unsigned long long); + + +#endif diff --git a/src/usr.bin/signify/zsig.c b/src/usr.bin/signify/zsig.c new file mode 100644 index 0000000..3611f28 --- /dev/null +++ b/src/usr.bin/signify/zsig.c @@ -0,0 +1,322 @@ +/* $OpenBSD: zsig.c,v 1.10 2016/09/04 17:00:22 espie Exp $ */ +/* + * Copyright (c) 2016 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef VERIFYONLY +#include <stdint.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sha2.h> +#include <string.h> +#include <sys/stat.h> +#include <time.h> +#include <fcntl.h> +#include "signify.h" + +struct gzheader { + uint8_t flg; + uint32_t mtime; + uint8_t xflg; + uint8_t os; + uint8_t *name; + uint8_t *comment; + uint8_t *endcomment; + unsigned long long headerlength; + uint8_t *buffer; +}; + +#define FTEXT_FLAG 1 +#define FHCRC_FLAG 2 +#define FEXTRA_FLAG 4 +#define FNAME_FLAG 8 +#define FCOMMENT_FLAG 16 + +#define GZHEADERLENGTH 10 +#define MYBUFSIZE 65536LU + + +static uint8_t fake[10] = { 0x1f, 0x8b, 8, FCOMMENT_FLAG, 0, 0, 0, 0, 0, 3 }; + +/* XXX no static there, confuses the hell out of gcc which displays + * non-existent warnings. + */ +uint8_t * +readgz_header(struct gzheader *h, int fd) +{ + size_t sz = 1024; + uint8_t *p; + size_t pos = 0; + size_t len = 0; + int state = 0; + ssize_t n; + uint8_t *buf; + + buf = xmalloc(sz); + + while (1) { + if (len == sz) { + sz *= 2; + buf = realloc(buf, sz); + if (!buf) + err(1, "realloc"); + } + n = read(fd, buf+len, sz-len); + if (n == -1) + err(1, "read"); + /* incomplete info */ + if (n == 0) + errx(1, "gzheader truncated"); + len += n; + h->comment = NULL; + h->name = NULL; + + switch(state) { + case 0: /* check header proper */ + /* need ten bytes */ + if (len < GZHEADERLENGTH) + continue; + h->flg = buf[3]; + h->mtime = buf[4] | (buf[5] << 8U) | (buf[6] << 16U) | + (buf[7] << 24U); + h->xflg = buf[8]; + h->os = buf[9]; + /* magic gzip header */ + if (buf[0] != 0x1f || buf[1] != 0x8b || buf[2] != 8) + err(1, "invalud magic in gzheader"); + /* XXX special code that only caters to our needs */ + if (h->flg & ~ (FCOMMENT_FLAG | FNAME_FLAG)) + err(1, "invalid flags in gzheader"); + pos = GZHEADERLENGTH; + state++; + /*FALLTHRU*/ + case 1: + if (h->flg & FNAME_FLAG) { + p = memchr(buf+pos, 0, len - pos); + if (!p) + continue; + pos = (p - buf) + 1; + } + state++; + /*FALLTHRU*/ + case 2: + if (h->flg & FCOMMENT_FLAG) { + p = memchr(buf+pos, 0, len - pos); + if (!p) + continue; + h->comment = buf + pos; + h->endcomment = p; + pos = (p - buf) + 1; + } + if (h->flg & FNAME_FLAG) + h->name = buf + GZHEADERLENGTH; + h->headerlength = pos; + h->buffer = buf; + return buf + len; + } + + } +} + +static void +copy_blocks(int fdout, int fdin, const char *sha, const char *endsha, + size_t bufsize, uint8_t *bufend) +{ + uint8_t *buffer; + uint8_t *residual; + uint8_t output[SHA512_256_DIGEST_STRING_LENGTH]; + + buffer = xmalloc(bufsize); + residual = (uint8_t *)endsha + 1; + + while (1) { + /* get the next block */ + size_t n = 0; + /* if we have residual data, we use it */ + if (residual != bufend) { + /* how much can we copy */ + size_t len = bufend - residual; + if (len >= bufsize) { + memcpy(buffer, residual, bufsize); + n = bufsize; + residual += bufsize; + } else { + memcpy(buffer, residual, len); + residual += len; + n = len; + } + } + /* if we're not done yet, try to obtain more until EOF */ + while (n != bufsize) { + ssize_t more = read(fdin, buffer+n, bufsize-n); + if (more == -1) + err(1, "read"); + n += more; + if (more == 0) + break; + } + SHA512_256Data(buffer, n, output); + if (endsha - sha < SHA512_256_DIGEST_STRING_LENGTH-1) + errx(4, "signature truncated"); + if (memcmp(output, sha, SHA512_256_DIGEST_STRING_LENGTH-1) != 0) + errx(4, "signature mismatch"); + if (sha[SHA512_256_DIGEST_STRING_LENGTH-1] != '\n') + errx(4, "signature mismatch"); + sha += SHA512_256_DIGEST_STRING_LENGTH; + writeall(fdout, buffer, n, "stdout"); + if (n != bufsize) + break; + } + free(buffer); +} + +void +zverify(const char *pubkeyfile, const char *msgfile, const char *sigfile, + const char *keytype) +{ + struct gzheader h; + size_t bufsize; + char *p, *meta; + uint8_t *bufend; + int fdin, fdout; + + /* by default, verification will love pipes */ + if (!sigfile) + sigfile = "-"; + if (!msgfile) + msgfile = "-"; + + fdin = xopen(sigfile, O_RDONLY | O_NOFOLLOW, 0); + + bufend = readgz_header(&h, fdin); + if (!(h.flg & FCOMMENT_FLAG)) + errx(1, "unsigned gzip archive"); + fake[8] = h.xflg; + + p = verifyzdata(h.comment, h.endcomment-h.comment, sigfile, + pubkeyfile, keytype); + + bufsize = MYBUFSIZE; + + meta = p; +#define BEGINS_WITH(x, y) memcmp((x), (y), sizeof(y)-1) == 0 + + while (BEGINS_WITH(p, "algorithm=SHA512/256") || + BEGINS_WITH(p, "date=") || + BEGINS_WITH(p, "key=") || + sscanf(p, "blocksize=%zu\n", &bufsize) > 0) { + while (*(p++) != '\n') + continue; + } + + if (*p != '\n') + errx(1, "invalid signature"); + *(p++) = 0; + + fdout = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666); + /* we don't actually copy the header, but put in a fake one with about + * zero useful information. + */ + writeall(fdout, fake, sizeof fake, msgfile); + writeall(fdout, meta, p - meta, msgfile); + copy_blocks(fdout, fdin, p, h.endcomment, bufsize, bufend); + free(h.buffer); + close(fdout); + close(fdin); +} + +void +zsign(const char *seckeyfile, const char *msgfile, const char *sigfile) +{ + size_t bufsize = MYBUFSIZE; + int fdin, fdout; + struct gzheader h; + struct stat sb; + size_t space; + char *msg; + char *p; + uint8_t *buffer; + uint8_t *sighdr; + char date[80]; + time_t clock; + + fdin = xopen(msgfile, O_RDONLY, 0); + if (fstat(fdin, &sb) == -1 || !S_ISREG(sb.st_mode)) + errx(1, "Sorry can only sign regular files"); + + readgz_header(&h, fdin); + /* we don't care about the header, actually */ + free(h.buffer); + + if (lseek(fdin, h.headerlength, SEEK_SET) == -1) + err(1, "seek in %s", msgfile); + + space = (sb.st_size / MYBUFSIZE+1) * SHA512_256_DIGEST_STRING_LENGTH + + 1024; /* long enough for extra header information */ + + msg = xmalloc(space); + buffer = xmalloc(bufsize); + time(&clock); + strftime(date, sizeof date, "%Y-%m-%dT%H:%M:%SZ", gmtime(&clock)); + snprintf(msg, space, + "date=%s\n" + "key=%s\n" + "algorithm=SHA512/256\n" + "blocksize=%zu\n\n", + date, seckeyfile, bufsize); + p = strchr(msg, 0); + + while (1) { + size_t n = read(fdin, buffer, bufsize); + if (n == -1) + err(1, "read from %s", msgfile); + if (n == 0) + break; + SHA512_256Data(buffer, n, p); + p += SHA512_256_DIGEST_STRING_LENGTH; + p[-1] = '\n'; + if (msg + space < p) + errx(1, "file too long %s", msgfile); + } + *p = 0; + + fdout = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666); + sighdr = createsig(seckeyfile, msgfile, msg, p-msg); + fake[8] = h.xflg; + + writeall(fdout, fake, sizeof fake, sigfile); + writeall(fdout, sighdr, strlen(sighdr), sigfile); + free(sighdr); + /* need the 0 ! */ + writeall(fdout, msg, p - msg + 1, sigfile); + free(msg); + + if (lseek(fdin, h.headerlength, SEEK_SET) == -1) + err(1, "seek in %s", msgfile); + + while (1) { + size_t n = read(fdin, buffer, bufsize); + if (n == -1) + err(1, "read from %s", msgfile); + if (n == 0) + break; + writeall(fdout, buffer, n, sigfile); + } + free(buffer); + close(fdout); +} +#endif |