GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/signify/zsig.c Lines: 133 160 83.1 %
Date: 2017-11-13 Branches: 57 92 62.0 %

Line Branch Exec Source
1
/* $OpenBSD: zsig.c,v 1.15 2017/07/11 23:52:05 tedu Exp $ */
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 <time.h>
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;
40
	uint8_t *buffer;
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
49
#define GZHEADERLENGTH 10
50
#define MYBUFSIZE 65536LU
51
52
53
static uint8_t fake[10] = { 0x1f, 0x8b, 8, FCOMMENT_FLAG, 0, 0, 0, 0, 0, 3 };
54
55
static uint8_t *
56
readgz_header(struct gzheader *h, int fd)
57
{
58
	size_t sz = 1023;
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;
65
66
18
	buf = xmalloc(sz);
67
68
9
	while (1) {
69
9
		if (len == sz) {
70
			sz *= 2;
71
			buf = realloc(buf, sz);
72
			if (!buf)
73
				err(1, "realloc");
74
		}
75
9
		n = read(fd, buf+len, sz-len);
76
9
		if (n == -1)
77
			err(1, "read");
78
		/* incomplete info */
79
9
		if (n == 0)
80
			errx(1, "gzheader truncated");
81
27
		len += n;
82
27
		h->comment = NULL;
83
27
		h->name = NULL;
84
85

27
		switch(state) {
86
		case 0: /* check header proper */
87
			/* need ten bytes */
88
9
			if (len < GZHEADERLENGTH)
89
				continue;
90
9
			h->flg = buf[3];
91
18
			h->mtime = buf[4] | (buf[5] << 8U) | (buf[6] << 16U) |
92
9
			    (buf[7] << 24U);
93
9
			h->xflg = buf[8];
94
9
			h->os = buf[9];
95
			/* magic gzip header */
96

27
			if (buf[0] != 0x1f || buf[1] != 0x8b || buf[2] != 8)
97
				err(1, "invalid magic in gzheader");
98
			/* XXX special code that only caters to our needs */
99
9
			if (h->flg & ~ (FCOMMENT_FLAG | FNAME_FLAG))
100
				err(1, "invalid flags in gzheader");
101
			pos = GZHEADERLENGTH;
102
9
			state++;
103
			/*FALLTHRU*/
104
		case 1:
105
9
			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
9
			state++;
112
			/*FALLTHRU*/
113
		case 2:
114
9
			if (h->flg & FCOMMENT_FLAG) {
115
5
				p = memchr(buf+pos, 0, len - pos);
116
5
				if (!p)
117
					continue;
118
5
				h->comment = buf + pos;
119
5
				h->endcomment = p;
120
5
				pos = (p - buf) + 1;
121
5
			}
122
9
			if (h->flg & FNAME_FLAG)
123
				h->name = buf + GZHEADERLENGTH;
124
9
			h->headerlength = pos;
125
9
			h->buffer = buf;
126
9
			return buf + len;
127
		}
128
129
	}
130
}
131
132
static void
133
copy_blocks(int fdout, int fdin, const char *sha, const char *endsha,
134
    size_t bufsize, uint8_t *bufend)
135
{
136
	uint8_t *buffer;
137
	uint8_t *residual;
138
10
	uint8_t output[SHA512_256_DIGEST_STRING_LENGTH];
139
140
5
	buffer = xmalloc(bufsize);
141
5
	residual = (uint8_t *)endsha + 1;
142
143
5
	while (1) {
144
		/* get the next block */
145
		size_t n = 0;
146
		/* if we have residual data, we use it */
147
5
		if (residual != bufend) {
148
			/* how much can we copy */
149
5
			size_t len = bufend - residual;
150
5
			n = len >= bufsize ? bufsize : len;
151
5
			memcpy(buffer, residual, n);
152
5
			residual += n;
153
5
		}
154
		/* if we're not done yet, try to obtain more until EOF */
155
20
		while (n != bufsize) {
156
10
			ssize_t more = read(fdin, buffer+n, bufsize-n);
157
10
			if (more == -1)
158
				err(1, "read");
159
10
			n += more;
160
10
			if (more == 0)
161
5
				break;
162
5
		}
163
5
		SHA512_256Data(buffer, n, output);
164
5
		if (endsha - sha < SHA512_256_DIGEST_STRING_LENGTH-1)
165
			errx(4, "signature truncated");
166
5
		if (memcmp(output, sha, SHA512_256_DIGEST_STRING_LENGTH-1) != 0)
167
			errx(4, "signature mismatch");
168
5
		if (sha[SHA512_256_DIGEST_STRING_LENGTH-1] != '\n')
169
			errx(4, "signature mismatch");
170
5
		sha += SHA512_256_DIGEST_STRING_LENGTH;
171
5
		writeall(fdout, buffer, n, "stdout");
172
5
		if (n != bufsize)
173
5
			break;
174
	}
175
5
	free(buffer);
176
5
}
177
178
void
179
zverify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
180
    const char *keytype)
181
{
182
10
	struct gzheader h;
183
5
	size_t bufsize;
184
	char *p, *meta;
185
	uint8_t *bufend;
186
	int fdin, fdout;
187
188
	/* by default, verification will love pipes */
189
5
	if (!sigfile)
190
5
		sigfile = "-";
191
5
	if (!msgfile)
192
5
		msgfile = "-";
193
194
5
	fdin = xopen(sigfile, O_RDONLY | O_NOFOLLOW, 0);
195
196
5
	bufend = readgz_header(&h, fdin);
197
5
	if (!(h.flg & FCOMMENT_FLAG))
198
		errx(1, "unsigned gzip archive");
199
5
	fake[8] = h.xflg;
200
201
5
	p = verifyzdata(h.comment, h.endcomment-h.comment, sigfile,
202
	    pubkeyfile, keytype);
203
204
5
	bufsize = MYBUFSIZE;
205
206
	meta = p;
207
#define BEGINS_WITH(x, y) memcmp((x), (y), sizeof(y)-1) == 0
208
209

60
	while (BEGINS_WITH(p, "algorithm=SHA512/256") ||
210
20
	    BEGINS_WITH(p, "date=") ||
211
15
	    BEGINS_WITH(p, "key=") ||
212
10
	    sscanf(p, "blocksize=%zu\n", &bufsize) > 0) {
213
579
		while (*(p++) != '\n')
214
539
			continue;
215
	}
216
217
5
	if (*p != '\n')
218
		errx(1, "invalid signature");
219
5
	*(p++) = 0;
220
221
5
	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
5
	writeall(fdout, fake, sizeof fake, msgfile);
226
5
	writeall(fdout, meta, p - meta, msgfile);
227
5
	copy_blocks(fdout, fdin, p, h.endcomment, bufsize, bufend);
228
5
	free(h.buffer);
229
5
	close(fdout);
230
5
	close(fdin);
231
5
}
232
233
void
234
zsign(const char *seckeyfile, const char *msgfile, const char *sigfile)
235
{
236
	size_t bufsize = MYBUFSIZE;
237
	int fdin, fdout;
238
8
	struct gzheader h;
239
4
	struct stat sb;
240
	size_t space;
241
	char *msg;
242
	char *p;
243
	uint8_t *buffer;
244
	uint8_t *sighdr;
245
4
	char date[80];
246
4
	time_t clock;
247
248
4
	fdin = xopen(msgfile, O_RDONLY, 0);
249

8
	if (fstat(fdin, &sb) == -1 || !S_ISREG(sb.st_mode))
250
		errx(1, "Sorry can only sign regular files");
251
252
4
	readgz_header(&h, fdin);
253
	/* we don't care about the header, actually */
254
4
	free(h.buffer);
255
256
4
	if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
257
		err(1, "seek in %s", msgfile);
258
259
4
	space = (sb.st_size / MYBUFSIZE+1) * SHA512_256_DIGEST_STRING_LENGTH +
260
		1024; /* long enough for extra header information */
261
262
4
	msg = xmalloc(space);
263
4
	buffer = xmalloc(bufsize);
264
4
	time(&clock);
265
4
	strftime(date, sizeof date, "%Y-%m-%dT%H:%M:%SZ", gmtime(&clock));
266
4
	snprintf(msg, space,
267
	    "date=%s\n"
268
	    "key=%s\n"
269
	    "algorithm=SHA512/256\n"
270
	    "blocksize=%zu\n\n",
271
	    date, seckeyfile, bufsize);
272
4
	p = strchr(msg, 0);
273
274
8
	while (1) {
275
8
		size_t n = read(fdin, buffer, bufsize);
276
8
		if (n == -1)
277
			err(1, "read from %s", msgfile);
278
8
		if (n == 0)
279
4
			break;
280
4
		SHA512_256Data(buffer, n, p);
281
4
		p += SHA512_256_DIGEST_STRING_LENGTH;
282
4
		p[-1] = '\n';
283
4
		if (msg + space < p)
284
			errx(1, "file too long %s", msgfile);
285
4
	}
286
4
	*p = 0;
287
288
4
	fdout = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
289
4
	sighdr = createsig(seckeyfile, msgfile, msg, p-msg);
290
4
	fake[8] = h.xflg;
291
292
4
	writeall(fdout, fake, sizeof fake, sigfile);
293
4
	writeall(fdout, sighdr, strlen(sighdr), sigfile);
294
4
	free(sighdr);
295
	/* need the 0 ! */
296
4
	writeall(fdout, msg, p - msg + 1, sigfile);
297
4
	free(msg);
298
299
4
	if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
300
		err(1, "seek in %s", msgfile);
301
302
8
	while (1) {
303
8
		size_t n = read(fdin, buffer, bufsize);
304
8
		if (n == -1)
305
			err(1, "read from %s", msgfile);
306
8
		if (n == 0)
307
4
			break;
308
4
		writeall(fdout, buffer, n, sigfile);
309
4
	}
310
4
	free(buffer);
311
4
	close(fdout);
312
4
}
313
#endif