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