[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.19

1.19    ! espie       1: /* $OpenBSD: zsig.c,v 1.18 2019/12/22 06:37:25 espie 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: {
1.15      tedu       58:        size_t sz = 1023;
1.1       espie      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.14      espie      97:                                err(1, "invalid 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.19    ! espie     163:                if (n == 0)
        !           164:                        break;
1.8       tedu      165:                SHA512_256Data(buffer, n, output);
                    166:                if (endsha - sha < SHA512_256_DIGEST_STRING_LENGTH-1)
1.1       espie     167:                        errx(4, "signature truncated");
1.8       tedu      168:                if (memcmp(output, sha, SHA512_256_DIGEST_STRING_LENGTH-1) != 0)
1.1       espie     169:                        errx(4, "signature mismatch");
1.8       tedu      170:                if (sha[SHA512_256_DIGEST_STRING_LENGTH-1] != '\n')
1.1       espie     171:                        errx(4, "signature mismatch");
1.8       tedu      172:                sha += SHA512_256_DIGEST_STRING_LENGTH;
1.1       espie     173:                writeall(fdout, buffer, n, "stdout");
                    174:                if (n != bufsize)
                    175:                        break;
1.4       tedu      176:        }
1.19    ! espie     177:        if (endsha != sha)
        !           178:                errx(4, "file truncated");
1.1       espie     179:        free(buffer);
                    180: }
                    181:
                    182: void
                    183: zverify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
                    184:     const char *keytype)
                    185: {
                    186:        struct gzheader h;
1.17      espie     187:        size_t bufsize, len;
1.18      espie     188:        char *p;
1.1       espie     189:        uint8_t *bufend;
                    190:        int fdin, fdout;
1.4       tedu      191:
1.1       espie     192:        /* by default, verification will love pipes */
                    193:        if (!sigfile)
                    194:                sigfile = "-";
                    195:        if (!msgfile)
                    196:                msgfile = "-";
                    197:
                    198:        fdin = xopen(sigfile, O_RDONLY | O_NOFOLLOW, 0);
                    199:
                    200:        bufend = readgz_header(&h, fdin);
                    201:        if (!(h.flg & FCOMMENT_FLAG))
1.10      espie     202:                errx(1, "unsigned gzip archive");
1.1       espie     203:        fake[8] = h.xflg;
1.17      espie     204:        len = h.endcomment-h.comment;
1.1       espie     205:
1.17      espie     206:        p = verifyzdata(h.comment, len, sigfile,
1.1       espie     207:            pubkeyfile, keytype);
                    208:
                    209:        bufsize = MYBUFSIZE;
                    210:
1.6       espie     211: #define BEGINS_WITH(x, y) memcmp((x), (y), sizeof(y)-1) == 0
                    212:
1.8       tedu      213:        while (BEGINS_WITH(p, "algorithm=SHA512/256") ||
1.6       espie     214:            BEGINS_WITH(p, "date=") ||
1.9       espie     215:            BEGINS_WITH(p, "key=") ||
1.6       espie     216:            sscanf(p, "blocksize=%zu\n", &bufsize) > 0) {
1.1       espie     217:                while (*(p++) != '\n')
                    218:                        continue;
                    219:        }
1.6       espie     220:
                    221:        if (*p != '\n')
                    222:                errx(1, "invalid signature");
                    223:
1.1       espie     224:        fdout = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
                    225:        writeall(fdout, fake, sizeof fake, msgfile);
1.18      espie     226:        writeall(fdout, h.comment, len+1, msgfile);
                    227:        *(p++) = 0;
1.1       espie     228:        copy_blocks(fdout, fdin, p, h.endcomment, bufsize, bufend);
1.5       espie     229:        free(h.buffer);
1.1       espie     230:        close(fdout);
                    231:        close(fdin);
                    232: }
                    233:
                    234: void
1.16      tedu      235: zsign(const char *seckeyfile, const char *msgfile, const char *sigfile,
                    236:     int skipdate)
1.1       espie     237: {
                    238:        size_t bufsize = MYBUFSIZE;
                    239:        int fdin, fdout;
                    240:        struct gzheader h;
                    241:        struct stat sb;
                    242:        size_t space;
                    243:        char *msg;
                    244:        char *p;
                    245:        uint8_t *buffer;
                    246:        uint8_t *sighdr;
1.6       espie     247:        char date[80];
                    248:        time_t clock;
1.1       espie     249:
                    250:        fdin = xopen(msgfile, O_RDONLY, 0);
                    251:        if (fstat(fdin, &sb) == -1 || !S_ISREG(sb.st_mode))
                    252:                errx(1, "Sorry can only sign regular files");
                    253:
                    254:        readgz_header(&h, fdin);
1.5       espie     255:        /* we don't care about the header, actually */
                    256:        free(h.buffer);
1.1       espie     257:
                    258:        if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
                    259:                err(1, "seek in %s", msgfile);
                    260:
1.8       tedu      261:        space = (sb.st_size / MYBUFSIZE+1) * SHA512_256_DIGEST_STRING_LENGTH +
1.6       espie     262:                1024; /* long enough for extra header information */
1.1       espie     263:
                    264:        msg = xmalloc(space);
                    265:        buffer = xmalloc(bufsize);
1.16      tedu      266:        if (skipdate) {
                    267:                clock = 0;
                    268:        } else {
                    269:                time(&clock);
                    270:        }
1.6       espie     271:        strftime(date, sizeof date, "%Y-%m-%dT%H:%M:%SZ", gmtime(&clock));
1.12      deraadt   272:        snprintf(msg, space,
1.6       espie     273:            "date=%s\n"
1.9       espie     274:            "key=%s\n"
1.8       tedu      275:            "algorithm=SHA512/256\n"
1.6       espie     276:            "blocksize=%zu\n\n",
1.9       espie     277:            date, seckeyfile, bufsize);
1.1       espie     278:        p = strchr(msg, 0);
                    279:
                    280:        while (1) {
                    281:                size_t n = read(fdin, buffer, bufsize);
                    282:                if (n == -1)
                    283:                        err(1, "read from %s", msgfile);
                    284:                if (n == 0)
                    285:                        break;
1.8       tedu      286:                SHA512_256Data(buffer, n, p);
                    287:                p += SHA512_256_DIGEST_STRING_LENGTH;
1.1       espie     288:                p[-1] = '\n';
                    289:                if (msg + space < p)
                    290:                        errx(1, "file too long %s", msgfile);
                    291:        }
                    292:        *p = 0;
                    293:
                    294:        fdout = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
                    295:        sighdr = createsig(seckeyfile, msgfile, msg, p-msg);
                    296:        fake[8] = h.xflg;
                    297:
                    298:        writeall(fdout, fake, sizeof fake, sigfile);
                    299:        writeall(fdout, sighdr, strlen(sighdr), sigfile);
                    300:        free(sighdr);
                    301:        /* need the 0 ! */
                    302:        writeall(fdout, msg, p - msg + 1, sigfile);
                    303:        free(msg);
                    304:
                    305:        if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
                    306:                err(1, "seek in %s", msgfile);
                    307:
                    308:        while (1) {
                    309:                size_t n = read(fdin, buffer, bufsize);
                    310:                if (n == -1)
                    311:                        err(1, "read from %s", msgfile);
                    312:                if (n == 0)
                    313:                        break;
                    314:                writeall(fdout, buffer, n, sigfile);
                    315:        }
                    316:        free(buffer);
                    317:        close(fdout);
                    318: }
                    319: #endif