GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/signify/zsig.c Lines: 135 156 86.5 %
Date: 2017-11-07 Branches: 66 92 71.7 %

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
132
	buf = xmalloc(sz);
67
68
66
	while (1) {
69
75
		if (len == sz) {
70
9
			sz *= 2;
71
9
			buf = realloc(buf, sz);
72
9
			if (!buf)
73
				err(1, "realloc");
74
		}
75
75
		n = read(fd, buf+len, sz-len);
76
75
		if (n == -1)
77
			err(1, "read");
78
		/* incomplete info */
79
75
		if (n == 0)
80
			errx(1, "gzheader truncated");
81
210
		len += n;
82
210
		h->comment = NULL;
83
210
		h->name = NULL;
84
85

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

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

488
	while (BEGINS_WITH(p, "algorithm=SHA512/256") ||
210
244
	    BEGINS_WITH(p, "date=") ||
211
183
	    BEGINS_WITH(p, "key=") ||
212
122
	    sscanf(p, "blocksize=%zu\n", &bufsize) > 0) {
213
6087
		while (*(p++) != '\n')
214
			continue;
215
	}
216
217
61
	if (*p != '\n')
218
		errx(1, "invalid signature");
219
61
	*(p++) = 0;
220
221
61
	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
61
	writeall(fdout, fake, sizeof fake, msgfile);
226
61
	writeall(fdout, meta, p - meta, msgfile);
227
61
	copy_blocks(fdout, fdin, p, h.endcomment, bufsize, bufend);
228
61
	free(h.buffer);
229
61
	close(fdout);
230
61
	close(fdin);
231
61
}
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
6
	struct gzheader h;
239
3
	struct stat sb;
240
	size_t space;
241
	char *msg;
242
	char *p;
243
	uint8_t *buffer;
244
	uint8_t *sighdr;
245
3
	char date[80];
246
3
	time_t clock;
247
248
3
	fdin = xopen(msgfile, O_RDONLY, 0);
249

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