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

1.4     ! tedu        1: /* $OpenBSD: zsig.c,v 1.3 2016/09/02 21:48:03 tedu 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;
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 */
                     88:                        if (len < 10)
                     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.1       espie     101:                        pos = 10;
                    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:                                h->name = buf + pos;
                    110:                                pos = (p - buf) + 1;
                    111:                        }
                    112:                        state++;
                    113:                        /*FALLTHRU*/
                    114:                case 2:
                    115:                        if (h->flg & FCOMMENT_FLAG) {
                    116:                                p = memchr(buf+pos, 0, len - pos);
                    117:                                if (!p)
                    118:                                        continue;
                    119:                                h->comment = buf + pos;
                    120:                                h->endcomment = p;
                    121:                                pos = (p - buf) + 1;
                    122:                        }
                    123:                        h->headerlength = pos;
                    124:                        return buf + len;
                    125:                }
                    126:
                    127:        }
                    128: }
                    129:
1.4     ! tedu      130: static void
1.1       espie     131: copy_blocks(int fdout, int fdin, const char *sha, const char *endsha,
1.4     ! tedu      132:     size_t bufsize, uint8_t *bufend)
1.1       espie     133: {
1.4     ! tedu      134:        uint8_t *buffer;
        !           135:        uint8_t *residual;
        !           136:        uint8_t output[SHA256_DIGEST_STRING_LENGTH];
        !           137:
        !           138:        buffer = xmalloc(bufsize);
        !           139:        residual = (uint8_t *)endsha + 1;
1.1       espie     140:
                    141:        while (1) {
                    142:                /* get the next block */
                    143:                size_t n = 0;
                    144:                /* if we have residual data, we use it */
                    145:                if (residual != bufend) {
                    146:                        /* how much can we copy */
                    147:                        size_t len = bufend - residual;
                    148:                        if (len >= bufsize) {
                    149:                                memcpy(buffer, residual, bufsize);
                    150:                                n = bufsize;
                    151:                                residual += bufsize;
                    152:                        } else {
                    153:                                memcpy(buffer, residual, len);
                    154:                                residual += len;
                    155:                                n = len;
                    156:                        }
1.4     ! tedu      157:                }
1.1       espie     158:                /* if we're not done yet, try to obtain more until EOF */
                    159:                while (n != bufsize) {
                    160:                        ssize_t more = read(fdin, buffer+n, bufsize-n);
                    161:                        if (more == -1)
1.3       tedu      162:                                err(1, "read");
1.1       espie     163:                        n += more;
                    164:                        if (more == 0)
                    165:                                break;
                    166:                }
                    167:                SHA256Data(buffer, n, output);
                    168:                if (endsha - sha < SHA256_DIGEST_STRING_LENGTH-1)
                    169:                        errx(4, "signature truncated");
                    170:                if (memcmp(output, sha, SHA256_DIGEST_STRING_LENGTH-1) != 0)
                    171:                        errx(4, "signature mismatch");
                    172:                if (sha[SHA256_DIGEST_STRING_LENGTH-1] != '\n')
                    173:                        errx(4, "signature mismatch");
                    174:                sha += SHA256_DIGEST_STRING_LENGTH;
                    175:                writeall(fdout, buffer, n, "stdout");
                    176:                if (n != bufsize)
                    177:                        break;
1.4     ! tedu      178:        }
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;
                    187:        size_t bufsize;
                    188:        char *p;
                    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))
                    202:                errx(1, "%s is an unsigned archive", sigfile);
                    203:        fake[8] = h.xflg;
                    204:
1.4     ! tedu      205:        p = verifyzdata(h.comment, h.endcomment-h.comment, sigfile,
1.1       espie     206:            pubkeyfile, keytype);
                    207:
                    208:        bufsize = MYBUFSIZE;
                    209:
                    210:        /* allow for arbitrary blocksize */
                    211:        if (sscanf(p, "blocksize=%zu\n", &bufsize)) {
                    212:                while (*(p++) != '\n')
                    213:                        continue;
                    214:        }
                    215:        fdout = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
                    216:        /* we don't actually copy the header, but put in a fake one with about
                    217:         * zero useful information.
                    218:         */
                    219:        writeall(fdout, fake, sizeof fake, msgfile);
                    220:        copy_blocks(fdout, fdin, p, h.endcomment, bufsize, bufend);
                    221:        close(fdout);
                    222:        close(fdin);
                    223: }
                    224:
                    225: void
                    226: zsign(const char *seckeyfile, const char *msgfile, const char *sigfile)
                    227: {
                    228:        size_t bufsize = MYBUFSIZE;
                    229:        int fdin, fdout;
                    230:        struct gzheader h;
                    231:        struct stat sb;
                    232:        size_t space;
                    233:        char *msg;
                    234:        char *p;
                    235:        uint8_t *buffer;
                    236:        uint8_t *sighdr;
                    237:
                    238:        fdin = xopen(msgfile, O_RDONLY, 0);
                    239:        if (fstat(fdin, &sb) == -1 || !S_ISREG(sb.st_mode))
                    240:                errx(1, "Sorry can only sign regular files");
                    241:
                    242:        readgz_header(&h, fdin);
                    243:
                    244:        if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
                    245:                err(1, "seek in %s", msgfile);
                    246:
1.4     ! tedu      247:        space = (sb.st_size / MYBUFSIZE) * SHA256_DIGEST_STRING_LENGTH +
1.1       espie     248:                80; /* long enough for blocksize=.... */
                    249:
                    250:        msg = xmalloc(space);
                    251:        buffer = xmalloc(bufsize);
                    252:        snprintf(msg, space, "blocksize=%zu\n", bufsize);
                    253:        p = strchr(msg, 0);
                    254:
                    255:        while (1) {
                    256:                size_t n = read(fdin, buffer, bufsize);
                    257:                if (n == -1)
                    258:                        err(1, "read from %s", msgfile);
                    259:                if (n == 0)
                    260:                        break;
                    261:                SHA256Data(buffer, n, p);
                    262:                p += SHA256_DIGEST_STRING_LENGTH;
                    263:                p[-1] = '\n';
                    264:                if (msg + space < p)
                    265:                        errx(1, "file too long %s", msgfile);
                    266:        }
                    267:        *p = 0;
                    268:
                    269:        fdout = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
                    270:        sighdr = createsig(seckeyfile, msgfile, msg, p-msg);
                    271:        fake[3] = FCOMMENT_FLAG;
                    272:        fake[8] = h.xflg;
                    273:
                    274:        writeall(fdout, fake, sizeof fake, sigfile);
                    275:        writeall(fdout, sighdr, strlen(sighdr), sigfile);
                    276:        free(sighdr);
                    277:        /* need the 0 ! */
                    278:        writeall(fdout, msg, p - msg + 1, sigfile);
                    279:        free(msg);
                    280:
                    281:        if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
                    282:                err(1, "seek in %s", msgfile);
                    283:
                    284:        while (1) {
                    285:                size_t n = read(fdin, buffer, bufsize);
                    286:                if (n == -1)
                    287:                        err(1, "read from %s", msgfile);
                    288:                if (n == 0)
                    289:                        break;
                    290:                writeall(fdout, buffer, n, sigfile);
                    291:        }
                    292:        free(buffer);
                    293:        close(fdout);
                    294: }
                    295: #endif