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

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