[BACK]Return to zsig.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / signify

Annotation of src/usr.bin/signify/zsig.c, Revision 1.13

1.13    ! tedu        1: /* $OpenBSD: zsig.c,v 1.12 2016/09/10 12:23:16 deraadt Exp $ */
1.1       espie       2: /*
                      3:  * Copyright (c) 2016 Marc Espie <espie@openbsd.org>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17:
                     18: #ifndef VERIFYONLY
                     19: #include <stdint.h>
                     20: #include <err.h>
                     21: #include <stdio.h>
                     22: #include <stdlib.h>
                     23: #include <unistd.h>
                     24: #include <sha2.h>
                     25: #include <string.h>
                     26: #include <sys/stat.h>
1.6       espie      27: #include <time.h>
1.1       espie      28: #include <fcntl.h>
                     29: #include "signify.h"
                     30:
                     31: struct gzheader {
                     32:        uint8_t flg;
                     33:        uint32_t mtime;
                     34:        uint8_t xflg;
                     35:        uint8_t os;
                     36:        uint8_t *name;
                     37:        uint8_t *comment;
                     38:        uint8_t *endcomment;
                     39:        unsigned long long headerlength;
1.5       espie      40:        uint8_t *buffer;
1.1       espie      41: };
                     42:
                     43: #define FTEXT_FLAG 1
                     44: #define FHCRC_FLAG 2
                     45: #define FEXTRA_FLAG 4
                     46: #define FNAME_FLAG 8
                     47: #define FCOMMENT_FLAG 16
                     48:
1.5       espie      49: #define GZHEADERLENGTH 10
1.1       espie      50: #define MYBUFSIZE 65536LU
                     51:
                     52:
1.6       espie      53: static uint8_t fake[10] = { 0x1f, 0x8b, 8, FCOMMENT_FLAG, 0, 0, 0, 0, 0, 3 };
1.1       espie      54:
1.13    ! tedu       55: static uint8_t *
1.1       espie      56: readgz_header(struct gzheader *h, int fd)
                     57: {
                     58:        size_t sz = 1024;
                     59:        uint8_t *p;
                     60:        size_t pos = 0;
                     61:        size_t len = 0;
                     62:        int state = 0;
                     63:        ssize_t n;
1.4       tedu       64:        uint8_t *buf;
1.1       espie      65:
1.4       tedu       66:        buf = xmalloc(sz);
1.1       espie      67:
                     68:        while (1) {
                     69:                if (len == sz) {
                     70:                        sz *= 2;
                     71:                        buf = realloc(buf, sz);
                     72:                        if (!buf)
1.3       tedu       73:                                err(1, "realloc");
1.1       espie      74:                }
                     75:                n = read(fd, buf+len, sz-len);
                     76:                if (n == -1)
1.3       tedu       77:                        err(1, "read");
1.1       espie      78:                /* incomplete info */
                     79:                if (n == 0)
1.3       tedu       80:                        errx(1, "gzheader truncated");
1.1       espie      81:                len += n;
                     82:                h->comment = NULL;
                     83:                h->name = NULL;
                     84:
                     85:                switch(state) {
                     86:                case 0: /* check header proper */
                     87:                        /* need ten bytes */
1.5       espie      88:                        if (len < GZHEADERLENGTH)
1.1       espie      89:                                continue;
                     90:                        h->flg = buf[3];
1.4       tedu       91:                        h->mtime = buf[4] | (buf[5] << 8U) | (buf[6] << 16U) |
1.1       espie      92:                            (buf[7] << 24U);
                     93:                        h->xflg = buf[8];
                     94:                        h->os = buf[9];
                     95:                        /* magic gzip header */
                     96:                        if (buf[0] != 0x1f || buf[1] != 0x8b || buf[2] != 8)
1.3       tedu       97:                                err(1, "invalud magic in gzheader");
1.1       espie      98:                        /* XXX special code that only caters to our needs */
                     99:                        if (h->flg & ~ (FCOMMENT_FLAG | FNAME_FLAG))
1.3       tedu      100:                                err(1, "invalid flags in gzheader");
1.5       espie     101:                        pos = GZHEADERLENGTH;
1.1       espie     102:                        state++;
                    103:                        /*FALLTHRU*/
                    104:                case 1:
                    105:                        if (h->flg & FNAME_FLAG) {
                    106:                                p = memchr(buf+pos, 0, len - pos);
                    107:                                if (!p)
                    108:                                        continue;
                    109:                                pos = (p - buf) + 1;
                    110:                        }
                    111:                        state++;
                    112:                        /*FALLTHRU*/
                    113:                case 2:
                    114:                        if (h->flg & FCOMMENT_FLAG) {
                    115:                                p = memchr(buf+pos, 0, len - pos);
                    116:                                if (!p)
                    117:                                        continue;
                    118:                                h->comment = buf + pos;
                    119:                                h->endcomment = p;
                    120:                                pos = (p - buf) + 1;
                    121:                        }
1.5       espie     122:                        if (h->flg & FNAME_FLAG)
                    123:                                h->name = buf + GZHEADERLENGTH;
1.1       espie     124:                        h->headerlength = pos;
1.5       espie     125:                        h->buffer = buf;
1.1       espie     126:                        return buf + len;
                    127:                }
                    128:
                    129:        }
                    130: }
                    131:
1.4       tedu      132: static void
1.1       espie     133: copy_blocks(int fdout, int fdin, const char *sha, const char *endsha,
1.4       tedu      134:     size_t bufsize, uint8_t *bufend)
1.1       espie     135: {
1.4       tedu      136:        uint8_t *buffer;
                    137:        uint8_t *residual;
1.8       tedu      138:        uint8_t output[SHA512_256_DIGEST_STRING_LENGTH];
1.4       tedu      139:
                    140:        buffer = xmalloc(bufsize);
                    141:        residual = (uint8_t *)endsha + 1;
1.1       espie     142:
                    143:        while (1) {
                    144:                /* get the next block */
                    145:                size_t n = 0;
                    146:                /* if we have residual data, we use it */
                    147:                if (residual != bufend) {
                    148:                        /* how much can we copy */
                    149:                        size_t len = bufend - residual;
1.11      espie     150:                        n = len >= bufsize ? bufsize : len;
                    151:                        memcpy(buffer, residual, n);
                    152:                        residual += n;
1.4       tedu      153:                }
1.1       espie     154:                /* if we're not done yet, try to obtain more until EOF */
                    155:                while (n != bufsize) {
                    156:                        ssize_t more = read(fdin, buffer+n, bufsize-n);
                    157:                        if (more == -1)
1.3       tedu      158:                                err(1, "read");
1.1       espie     159:                        n += more;
                    160:                        if (more == 0)
                    161:                                break;
                    162:                }
1.8       tedu      163:                SHA512_256Data(buffer, n, output);
                    164:                if (endsha - sha < SHA512_256_DIGEST_STRING_LENGTH-1)
1.1       espie     165:                        errx(4, "signature truncated");
1.8       tedu      166:                if (memcmp(output, sha, SHA512_256_DIGEST_STRING_LENGTH-1) != 0)
1.1       espie     167:                        errx(4, "signature mismatch");
1.8       tedu      168:                if (sha[SHA512_256_DIGEST_STRING_LENGTH-1] != '\n')
1.1       espie     169:                        errx(4, "signature mismatch");
1.8       tedu      170:                sha += SHA512_256_DIGEST_STRING_LENGTH;
1.1       espie     171:                writeall(fdout, buffer, n, "stdout");
                    172:                if (n != bufsize)
                    173:                        break;
1.4       tedu      174:        }
1.1       espie     175:        free(buffer);
                    176: }
                    177:
                    178: void
                    179: zverify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
                    180:     const char *keytype)
                    181: {
                    182:        struct gzheader h;
                    183:        size_t bufsize;
1.6       espie     184:        char *p, *meta;
1.1       espie     185:        uint8_t *bufend;
                    186:        int fdin, fdout;
1.4       tedu      187:
1.1       espie     188:        /* by default, verification will love pipes */
                    189:        if (!sigfile)
                    190:                sigfile = "-";
                    191:        if (!msgfile)
                    192:                msgfile = "-";
                    193:
                    194:        fdin = xopen(sigfile, O_RDONLY | O_NOFOLLOW, 0);
                    195:
                    196:        bufend = readgz_header(&h, fdin);
                    197:        if (!(h.flg & FCOMMENT_FLAG))
1.10      espie     198:                errx(1, "unsigned gzip archive");
1.1       espie     199:        fake[8] = h.xflg;
                    200:
1.4       tedu      201:        p = verifyzdata(h.comment, h.endcomment-h.comment, sigfile,
1.1       espie     202:            pubkeyfile, keytype);
                    203:
                    204:        bufsize = MYBUFSIZE;
                    205:
1.6       espie     206:        meta = p;
                    207: #define BEGINS_WITH(x, y) memcmp((x), (y), sizeof(y)-1) == 0
                    208:
1.8       tedu      209:        while (BEGINS_WITH(p, "algorithm=SHA512/256") ||
1.6       espie     210:            BEGINS_WITH(p, "date=") ||
1.9       espie     211:            BEGINS_WITH(p, "key=") ||
1.6       espie     212:            sscanf(p, "blocksize=%zu\n", &bufsize) > 0) {
1.1       espie     213:                while (*(p++) != '\n')
                    214:                        continue;
                    215:        }
1.6       espie     216:
                    217:        if (*p != '\n')
                    218:                errx(1, "invalid signature");
                    219:        *(p++) = 0;
                    220:
1.1       espie     221:        fdout = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
                    222:        /* we don't actually copy the header, but put in a fake one with about
                    223:         * zero useful information.
                    224:         */
                    225:        writeall(fdout, fake, sizeof fake, msgfile);
1.6       espie     226:        writeall(fdout, meta, p - meta, msgfile);
1.1       espie     227:        copy_blocks(fdout, fdin, p, h.endcomment, bufsize, bufend);
1.5       espie     228:        free(h.buffer);
1.1       espie     229:        close(fdout);
                    230:        close(fdin);
                    231: }
                    232:
                    233: void
                    234: zsign(const char *seckeyfile, const char *msgfile, const char *sigfile)
                    235: {
                    236:        size_t bufsize = MYBUFSIZE;
                    237:        int fdin, fdout;
                    238:        struct gzheader h;
                    239:        struct stat sb;
                    240:        size_t space;
                    241:        char *msg;
                    242:        char *p;
                    243:        uint8_t *buffer;
                    244:        uint8_t *sighdr;
1.6       espie     245:        char date[80];
                    246:        time_t clock;
1.1       espie     247:
                    248:        fdin = xopen(msgfile, O_RDONLY, 0);
                    249:        if (fstat(fdin, &sb) == -1 || !S_ISREG(sb.st_mode))
                    250:                errx(1, "Sorry can only sign regular files");
                    251:
                    252:        readgz_header(&h, fdin);
1.5       espie     253:        /* we don't care about the header, actually */
                    254:        free(h.buffer);
1.1       espie     255:
                    256:        if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
                    257:                err(1, "seek in %s", msgfile);
                    258:
1.8       tedu      259:        space = (sb.st_size / MYBUFSIZE+1) * SHA512_256_DIGEST_STRING_LENGTH +
1.6       espie     260:                1024; /* long enough for extra header information */
1.1       espie     261:
                    262:        msg = xmalloc(space);
                    263:        buffer = xmalloc(bufsize);
1.6       espie     264:        time(&clock);
                    265:        strftime(date, sizeof date, "%Y-%m-%dT%H:%M:%SZ", gmtime(&clock));
1.12      deraadt   266:        snprintf(msg, space,
1.6       espie     267:            "date=%s\n"
1.9       espie     268:            "key=%s\n"
1.8       tedu      269:            "algorithm=SHA512/256\n"
1.6       espie     270:            "blocksize=%zu\n\n",
1.9       espie     271:            date, seckeyfile, bufsize);
1.1       espie     272:        p = strchr(msg, 0);
                    273:
                    274:        while (1) {
                    275:                size_t n = read(fdin, buffer, bufsize);
                    276:                if (n == -1)
                    277:                        err(1, "read from %s", msgfile);
                    278:                if (n == 0)
                    279:                        break;
1.8       tedu      280:                SHA512_256Data(buffer, n, p);
                    281:                p += SHA512_256_DIGEST_STRING_LENGTH;
1.1       espie     282:                p[-1] = '\n';
                    283:                if (msg + space < p)
                    284:                        errx(1, "file too long %s", msgfile);
                    285:        }
                    286:        *p = 0;
                    287:
                    288:        fdout = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
                    289:        sighdr = createsig(seckeyfile, msgfile, msg, p-msg);
                    290:        fake[8] = h.xflg;
                    291:
                    292:        writeall(fdout, fake, sizeof fake, sigfile);
                    293:        writeall(fdout, sighdr, strlen(sighdr), sigfile);
                    294:        free(sighdr);
                    295:        /* need the 0 ! */
                    296:        writeall(fdout, msg, p - msg + 1, sigfile);
                    297:        free(msg);
                    298:
                    299:        if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
                    300:                err(1, "seek in %s", msgfile);
                    301:
                    302:        while (1) {
                    303:                size_t n = read(fdin, buffer, bufsize);
                    304:                if (n == -1)
                    305:                        err(1, "read from %s", msgfile);
                    306:                if (n == 0)
                    307:                        break;
                    308:                writeall(fdout, buffer, n, sigfile);
                    309:        }
                    310:        free(buffer);
                    311:        close(fdout);
                    312: }
                    313: #endif