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

1.8     ! tedu        1: /* $OpenBSD: zsig.c,v 1.7 2016/09/03 12:59:33 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:
                     55: /* XXX no static there, confuses the hell out of gcc which displays
                     56:  * non-existent warnings.
                     57:  */
                     58: uint8_t *
                     59: readgz_header(struct gzheader *h, int fd)
                     60: {
                     61:        size_t sz = 1024;
                     62:        uint8_t *p;
                     63:        size_t pos = 0;
                     64:        size_t len = 0;
                     65:        int state = 0;
                     66:        ssize_t n;
1.4       tedu       67:        uint8_t *buf;
1.1       espie      68:
1.4       tedu       69:        buf = xmalloc(sz);
1.1       espie      70:
                     71:        while (1) {
                     72:                if (len == sz) {
                     73:                        sz *= 2;
                     74:                        buf = realloc(buf, sz);
                     75:                        if (!buf)
1.3       tedu       76:                                err(1, "realloc");
1.1       espie      77:                }
                     78:                n = read(fd, buf+len, sz-len);
                     79:                if (n == -1)
1.3       tedu       80:                        err(1, "read");
1.1       espie      81:                /* incomplete info */
                     82:                if (n == 0)
1.3       tedu       83:                        errx(1, "gzheader truncated");
1.1       espie      84:                len += n;
                     85:                h->comment = NULL;
                     86:                h->name = NULL;
                     87:
                     88:                switch(state) {
                     89:                case 0: /* check header proper */
                     90:                        /* need ten bytes */
1.5       espie      91:                        if (len < GZHEADERLENGTH)
1.1       espie      92:                                continue;
                     93:                        h->flg = buf[3];
1.4       tedu       94:                        h->mtime = buf[4] | (buf[5] << 8U) | (buf[6] << 16U) |
1.1       espie      95:                            (buf[7] << 24U);
                     96:                        h->xflg = buf[8];
                     97:                        h->os = buf[9];
                     98:                        /* magic gzip header */
                     99:                        if (buf[0] != 0x1f || buf[1] != 0x8b || buf[2] != 8)
1.3       tedu      100:                                err(1, "invalud magic in gzheader");
1.1       espie     101:                        /* XXX special code that only caters to our needs */
                    102:                        if (h->flg & ~ (FCOMMENT_FLAG | FNAME_FLAG))
1.3       tedu      103:                                err(1, "invalid flags in gzheader");
1.5       espie     104:                        pos = GZHEADERLENGTH;
1.1       espie     105:                        state++;
                    106:                        /*FALLTHRU*/
                    107:                case 1:
                    108:                        if (h->flg & FNAME_FLAG) {
                    109:                                p = memchr(buf+pos, 0, len - pos);
                    110:                                if (!p)
                    111:                                        continue;
                    112:                                pos = (p - buf) + 1;
                    113:                        }
                    114:                        state++;
                    115:                        /*FALLTHRU*/
                    116:                case 2:
                    117:                        if (h->flg & FCOMMENT_FLAG) {
                    118:                                p = memchr(buf+pos, 0, len - pos);
                    119:                                if (!p)
                    120:                                        continue;
                    121:                                h->comment = buf + pos;
                    122:                                h->endcomment = p;
                    123:                                pos = (p - buf) + 1;
                    124:                        }
1.5       espie     125:                        if (h->flg & FNAME_FLAG)
                    126:                                h->name = buf + GZHEADERLENGTH;
1.1       espie     127:                        h->headerlength = pos;
1.5       espie     128:                        h->buffer = buf;
1.1       espie     129:                        return buf + len;
                    130:                }
                    131:
                    132:        }
                    133: }
                    134:
1.4       tedu      135: static void
1.1       espie     136: copy_blocks(int fdout, int fdin, const char *sha, const char *endsha,
1.4       tedu      137:     size_t bufsize, uint8_t *bufend)
1.1       espie     138: {
1.4       tedu      139:        uint8_t *buffer;
                    140:        uint8_t *residual;
1.8     ! tedu      141:        uint8_t output[SHA512_256_DIGEST_STRING_LENGTH];
1.4       tedu      142:
                    143:        buffer = xmalloc(bufsize);
                    144:        residual = (uint8_t *)endsha + 1;
1.1       espie     145:
                    146:        while (1) {
                    147:                /* get the next block */
                    148:                size_t n = 0;
                    149:                /* if we have residual data, we use it */
                    150:                if (residual != bufend) {
                    151:                        /* how much can we copy */
                    152:                        size_t len = bufend - residual;
                    153:                        if (len >= bufsize) {
                    154:                                memcpy(buffer, residual, bufsize);
                    155:                                n = bufsize;
                    156:                                residual += bufsize;
                    157:                        } else {
                    158:                                memcpy(buffer, residual, len);
                    159:                                residual += len;
                    160:                                n = len;
                    161:                        }
1.4       tedu      162:                }
1.1       espie     163:                /* if we're not done yet, try to obtain more until EOF */
                    164:                while (n != bufsize) {
                    165:                        ssize_t more = read(fdin, buffer+n, bufsize-n);
                    166:                        if (more == -1)
1.3       tedu      167:                                err(1, "read");
1.1       espie     168:                        n += more;
                    169:                        if (more == 0)
                    170:                                break;
                    171:                }
1.8     ! tedu      172:                SHA512_256Data(buffer, n, output);
        !           173:                if (endsha - sha < SHA512_256_DIGEST_STRING_LENGTH-1)
1.1       espie     174:                        errx(4, "signature truncated");
1.8     ! tedu      175:                if (memcmp(output, sha, SHA512_256_DIGEST_STRING_LENGTH-1) != 0)
1.1       espie     176:                        errx(4, "signature mismatch");
1.8     ! tedu      177:                if (sha[SHA512_256_DIGEST_STRING_LENGTH-1] != '\n')
1.1       espie     178:                        errx(4, "signature mismatch");
1.8     ! tedu      179:                sha += SHA512_256_DIGEST_STRING_LENGTH;
1.1       espie     180:                writeall(fdout, buffer, n, "stdout");
                    181:                if (n != bufsize)
                    182:                        break;
1.4       tedu      183:        }
1.1       espie     184:        free(buffer);
                    185: }
                    186:
                    187: void
                    188: zverify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
                    189:     const char *keytype)
                    190: {
                    191:        struct gzheader h;
                    192:        size_t bufsize;
1.6       espie     193:        char *p, *meta;
1.1       espie     194:        uint8_t *bufend;
                    195:        int fdin, fdout;
1.4       tedu      196:
1.1       espie     197:        /* by default, verification will love pipes */
                    198:        if (!sigfile)
                    199:                sigfile = "-";
                    200:        if (!msgfile)
                    201:                msgfile = "-";
                    202:
                    203:        fdin = xopen(sigfile, O_RDONLY | O_NOFOLLOW, 0);
                    204:
                    205:        bufend = readgz_header(&h, fdin);
                    206:        if (!(h.flg & FCOMMENT_FLAG))
                    207:                errx(1, "%s is an unsigned archive", sigfile);
                    208:        fake[8] = h.xflg;
                    209:
1.4       tedu      210:        p = verifyzdata(h.comment, h.endcomment-h.comment, sigfile,
1.1       espie     211:            pubkeyfile, keytype);
                    212:
                    213:        bufsize = MYBUFSIZE;
                    214:
1.6       espie     215:        meta = p;
                    216: #define BEGINS_WITH(x, y) memcmp((x), (y), sizeof(y)-1) == 0
                    217:
1.8     ! tedu      218:        while (BEGINS_WITH(p, "algorithm=SHA512/256") ||
1.6       espie     219:            BEGINS_WITH(p, "date=") ||
                    220:            sscanf(p, "blocksize=%zu\n", &bufsize) > 0) {
1.1       espie     221:                while (*(p++) != '\n')
                    222:                        continue;
                    223:        }
1.6       espie     224:
                    225:        if (*p != '\n')
                    226:                errx(1, "invalid signature");
                    227:        *(p++) = 0;
                    228:
1.1       espie     229:        fdout = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
                    230:        /* we don't actually copy the header, but put in a fake one with about
                    231:         * zero useful information.
                    232:         */
                    233:        writeall(fdout, fake, sizeof fake, msgfile);
1.6       espie     234:        writeall(fdout, meta, p - meta, msgfile);
1.1       espie     235:        copy_blocks(fdout, fdin, p, h.endcomment, bufsize, bufend);
1.5       espie     236:        free(h.buffer);
1.1       espie     237:        close(fdout);
                    238:        close(fdin);
                    239: }
                    240:
                    241: void
                    242: zsign(const char *seckeyfile, const char *msgfile, const char *sigfile)
                    243: {
                    244:        size_t bufsize = MYBUFSIZE;
                    245:        int fdin, fdout;
                    246:        struct gzheader h;
                    247:        struct stat sb;
                    248:        size_t space;
                    249:        char *msg;
                    250:        char *p;
                    251:        uint8_t *buffer;
                    252:        uint8_t *sighdr;
1.6       espie     253:        char date[80];
                    254:        time_t clock;
1.1       espie     255:
                    256:        fdin = xopen(msgfile, O_RDONLY, 0);
                    257:        if (fstat(fdin, &sb) == -1 || !S_ISREG(sb.st_mode))
                    258:                errx(1, "Sorry can only sign regular files");
                    259:
                    260:        readgz_header(&h, fdin);
1.5       espie     261:        /* we don't care about the header, actually */
                    262:        free(h.buffer);
1.1       espie     263:
                    264:        if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
                    265:                err(1, "seek in %s", msgfile);
                    266:
1.8     ! tedu      267:        space = (sb.st_size / MYBUFSIZE+1) * SHA512_256_DIGEST_STRING_LENGTH +
1.6       espie     268:                1024; /* long enough for extra header information */
1.1       espie     269:
                    270:        msg = xmalloc(space);
                    271:        buffer = xmalloc(bufsize);
1.6       espie     272:        time(&clock);
                    273:        strftime(date, sizeof date, "%Y-%m-%dT%H:%M:%SZ", gmtime(&clock));
                    274:        snprintf(msg, space,
                    275:            "date=%s\n"
1.8     ! tedu      276:            "algorithm=SHA512/256\n"
1.6       espie     277:            "blocksize=%zu\n\n",
                    278:            date, bufsize);
1.1       espie     279:        p = strchr(msg, 0);
                    280:
                    281:        while (1) {
                    282:                size_t n = read(fdin, buffer, bufsize);
                    283:                if (n == -1)
                    284:                        err(1, "read from %s", msgfile);
                    285:                if (n == 0)
                    286:                        break;
1.8     ! tedu      287:                SHA512_256Data(buffer, n, p);
        !           288:                p += SHA512_256_DIGEST_STRING_LENGTH;
1.1       espie     289:                p[-1] = '\n';
                    290:                if (msg + space < p)
                    291:                        errx(1, "file too long %s", msgfile);
                    292:        }
                    293:        *p = 0;
                    294:
                    295:        fdout = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
                    296:        sighdr = createsig(seckeyfile, msgfile, msg, p-msg);
                    297:        fake[8] = h.xflg;
                    298:
                    299:        writeall(fdout, fake, sizeof fake, sigfile);
                    300:        writeall(fdout, sighdr, strlen(sighdr), sigfile);
                    301:        free(sighdr);
                    302:        /* need the 0 ! */
                    303:        writeall(fdout, msg, p - msg + 1, sigfile);
                    304:        free(msg);
                    305:
                    306:        if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
                    307:                err(1, "seek in %s", msgfile);
                    308:
                    309:        while (1) {
                    310:                size_t n = read(fdin, buffer, bufsize);
                    311:                if (n == -1)
                    312:                        err(1, "read from %s", msgfile);
                    313:                if (n == 0)
                    314:                        break;
                    315:                writeall(fdout, buffer, n, sigfile);
                    316:        }
                    317:        free(buffer);
                    318:        close(fdout);
                    319: }
                    320: #endif