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