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

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