about summary refs log tree commit diff
path: root/src/usr.bin
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2016-09-05 16:03:19 +0200
committerChristian Neukirchen <chneukirchen@gmail.com>2016-09-05 16:03:19 +0200
commitab3c9a49fecc2faa04c9977cbd60528edf8563e4 (patch)
treeb3bac2e1178fa6cd89f3cb829630183bb1181521 /src/usr.bin
parent5b6b3da8312a77047376dc3135e6159829a4b001 (diff)
downloadoutils-ab3c9a49fecc2faa04c9977cbd60528edf8563e4.tar.gz
outils-ab3c9a49fecc2faa04c9977cbd60528edf8563e4.tar.xz
outils-ab3c9a49fecc2faa04c9977cbd60528edf8563e4.zip
cvs update
Diffstat (limited to 'src/usr.bin')
-rw-r--r--src/usr.bin/signify/signify.h33
-rw-r--r--src/usr.bin/signify/zsig.c322
2 files changed, 355 insertions, 0 deletions
diff --git a/src/usr.bin/signify/signify.h b/src/usr.bin/signify/signify.h
new file mode 100644
index 0000000..6edb6a4
--- /dev/null
+++ b/src/usr.bin/signify/signify.h
@@ -0,0 +1,33 @@
+/* $OpenBSD: signify.h,v 1.1 2016/09/02 16:10:56 espie Exp $ */
+/*
+ * Copyright (c) 2016 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* common interface to signify.c/zsig.c */
+#ifndef signify_h
+#define signify_h
+extern void zverify(const char *, const char *, const char *, const char *);
+extern void zsign(const char *, const char *, const char *);
+
+extern void *xmalloc(size_t);
+extern void writeall(int, const void *, size_t, const char *);
+extern int xopen(const char *, int, mode_t);
+extern void *verifyzdata(uint8_t *, unsigned long long,
+    const char *, const char *, const char *);
+extern uint8_t *createsig(const char *, const char *, uint8_t *,
+    unsigned long long);
+
+
+#endif
diff --git a/src/usr.bin/signify/zsig.c b/src/usr.bin/signify/zsig.c
new file mode 100644
index 0000000..3611f28
--- /dev/null
+++ b/src/usr.bin/signify/zsig.c
@@ -0,0 +1,322 @@
+/* $OpenBSD: zsig.c,v 1.10 2016/09/04 17:00:22 espie Exp $ */
+/*
+ * Copyright (c) 2016 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef VERIFYONLY
+#include <stdint.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sha2.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
+#include "signify.h"
+
+struct gzheader {
+	uint8_t flg;
+	uint32_t mtime;
+	uint8_t xflg;
+	uint8_t os;
+	uint8_t *name;
+	uint8_t *comment;
+	uint8_t *endcomment;
+	unsigned long long headerlength;
+	uint8_t *buffer;
+};
+
+#define FTEXT_FLAG 1
+#define FHCRC_FLAG 2
+#define FEXTRA_FLAG 4
+#define FNAME_FLAG 8
+#define FCOMMENT_FLAG 16
+
+#define GZHEADERLENGTH 10
+#define MYBUFSIZE 65536LU
+
+
+static uint8_t fake[10] = { 0x1f, 0x8b, 8, FCOMMENT_FLAG, 0, 0, 0, 0, 0, 3 };
+
+/* XXX no static there, confuses the hell out of gcc which displays
+ * non-existent warnings.
+ */
+uint8_t *
+readgz_header(struct gzheader *h, int fd)
+{
+	size_t sz = 1024;
+	uint8_t *p;
+	size_t pos = 0;
+	size_t len = 0;
+	int state = 0;
+	ssize_t n;
+	uint8_t *buf;
+
+	buf = xmalloc(sz);
+
+	while (1) {
+		if (len == sz) {
+			sz *= 2;
+			buf = realloc(buf, sz);
+			if (!buf)
+				err(1, "realloc");
+		}
+		n = read(fd, buf+len, sz-len);
+		if (n == -1)
+			err(1, "read");
+		/* incomplete info */
+		if (n == 0)
+			errx(1, "gzheader truncated");
+		len += n;
+		h->comment = NULL;
+		h->name = NULL;
+
+		switch(state) {
+		case 0: /* check header proper */
+			/* need ten bytes */
+			if (len < GZHEADERLENGTH)
+				continue;
+			h->flg = buf[3];
+			h->mtime = buf[4] | (buf[5] << 8U) | (buf[6] << 16U) |
+			    (buf[7] << 24U);
+			h->xflg = buf[8];
+			h->os = buf[9];
+			/* magic gzip header */
+			if (buf[0] != 0x1f || buf[1] != 0x8b || buf[2] != 8)
+				err(1, "invalud magic in gzheader");
+			/* XXX special code that only caters to our needs */
+			if (h->flg & ~ (FCOMMENT_FLAG | FNAME_FLAG))
+				err(1, "invalid flags in gzheader");
+			pos = GZHEADERLENGTH;
+			state++;
+			/*FALLTHRU*/
+		case 1:
+			if (h->flg & FNAME_FLAG) {
+				p = memchr(buf+pos, 0, len - pos);
+				if (!p)
+					continue;
+				pos = (p - buf) + 1;
+			}
+			state++;
+			/*FALLTHRU*/
+		case 2:
+			if (h->flg & FCOMMENT_FLAG) {
+				p = memchr(buf+pos, 0, len - pos);
+				if (!p)
+					continue;
+				h->comment = buf + pos;
+				h->endcomment = p;
+				pos = (p - buf) + 1;
+			}
+			if (h->flg & FNAME_FLAG)
+				h->name = buf + GZHEADERLENGTH;
+			h->headerlength = pos;
+			h->buffer = buf;
+			return buf + len;
+		}
+
+	}
+}
+
+static void
+copy_blocks(int fdout, int fdin, const char *sha, const char *endsha,
+    size_t bufsize, uint8_t *bufend)
+{
+	uint8_t *buffer;
+	uint8_t *residual;
+	uint8_t output[SHA512_256_DIGEST_STRING_LENGTH];
+
+	buffer = xmalloc(bufsize);
+	residual = (uint8_t *)endsha + 1;
+
+	while (1) {
+		/* get the next block */
+		size_t n = 0;
+		/* if we have residual data, we use it */
+		if (residual != bufend) {
+			/* how much can we copy */
+			size_t len = bufend - residual;
+			if (len >= bufsize) {
+				memcpy(buffer, residual, bufsize);
+				n = bufsize;
+				residual += bufsize;
+			} else {
+				memcpy(buffer, residual, len);
+				residual += len;
+				n = len;
+			}
+		}
+		/* if we're not done yet, try to obtain more until EOF */
+		while (n != bufsize) {
+			ssize_t more = read(fdin, buffer+n, bufsize-n);
+			if (more == -1)
+				err(1, "read");
+			n += more;
+			if (more == 0)
+				break;
+		}
+		SHA512_256Data(buffer, n, output);
+		if (endsha - sha < SHA512_256_DIGEST_STRING_LENGTH-1)
+			errx(4, "signature truncated");
+		if (memcmp(output, sha, SHA512_256_DIGEST_STRING_LENGTH-1) != 0)
+			errx(4, "signature mismatch");
+		if (sha[SHA512_256_DIGEST_STRING_LENGTH-1] != '\n')
+			errx(4, "signature mismatch");
+		sha += SHA512_256_DIGEST_STRING_LENGTH;
+		writeall(fdout, buffer, n, "stdout");
+		if (n != bufsize)
+			break;
+	}
+	free(buffer);
+}
+
+void
+zverify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
+    const char *keytype)
+{
+	struct gzheader h;
+	size_t bufsize;
+	char *p, *meta;
+	uint8_t *bufend;
+	int fdin, fdout;
+
+	/* by default, verification will love pipes */
+	if (!sigfile)
+		sigfile = "-";
+	if (!msgfile)
+		msgfile = "-";
+
+	fdin = xopen(sigfile, O_RDONLY | O_NOFOLLOW, 0);
+
+	bufend = readgz_header(&h, fdin);
+	if (!(h.flg & FCOMMENT_FLAG))
+		errx(1, "unsigned gzip archive");
+	fake[8] = h.xflg;
+
+	p = verifyzdata(h.comment, h.endcomment-h.comment, sigfile,
+	    pubkeyfile, keytype);
+
+	bufsize = MYBUFSIZE;
+
+	meta = p;
+#define BEGINS_WITH(x, y) memcmp((x), (y), sizeof(y)-1) == 0
+
+	while (BEGINS_WITH(p, "algorithm=SHA512/256") ||
+	    BEGINS_WITH(p, "date=") ||
+	    BEGINS_WITH(p, "key=") ||
+	    sscanf(p, "blocksize=%zu\n", &bufsize) > 0) {
+		while (*(p++) != '\n')
+			continue;
+	}
+
+	if (*p != '\n')
+		errx(1, "invalid signature");
+	*(p++) = 0;
+
+	fdout = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
+	/* we don't actually copy the header, but put in a fake one with about
+	 * zero useful information.
+	 */
+	writeall(fdout, fake, sizeof fake, msgfile);
+	writeall(fdout, meta, p - meta, msgfile);
+	copy_blocks(fdout, fdin, p, h.endcomment, bufsize, bufend);
+	free(h.buffer);
+	close(fdout);
+	close(fdin);
+}
+
+void
+zsign(const char *seckeyfile, const char *msgfile, const char *sigfile)
+{
+	size_t bufsize = MYBUFSIZE;
+	int fdin, fdout;
+	struct gzheader h;
+	struct stat sb;
+	size_t space;
+	char *msg;
+	char *p;
+	uint8_t *buffer;
+	uint8_t *sighdr;
+	char date[80];
+	time_t clock;
+
+	fdin = xopen(msgfile, O_RDONLY, 0);
+	if (fstat(fdin, &sb) == -1 || !S_ISREG(sb.st_mode))
+		errx(1, "Sorry can only sign regular files");
+
+	readgz_header(&h, fdin);
+	/* we don't care about the header, actually */
+	free(h.buffer);
+
+	if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
+		err(1, "seek in %s", msgfile);
+
+	space = (sb.st_size / MYBUFSIZE+1) * SHA512_256_DIGEST_STRING_LENGTH +
+		1024; /* long enough for extra header information */
+
+	msg = xmalloc(space);
+	buffer = xmalloc(bufsize);
+	time(&clock);
+	strftime(date, sizeof date, "%Y-%m-%dT%H:%M:%SZ", gmtime(&clock));
+	snprintf(msg, space, 
+	    "date=%s\n"
+	    "key=%s\n"
+	    "algorithm=SHA512/256\n"
+	    "blocksize=%zu\n\n",
+	    date, seckeyfile, bufsize);
+	p = strchr(msg, 0);
+
+	while (1) {
+		size_t n = read(fdin, buffer, bufsize);
+		if (n == -1)
+			err(1, "read from %s", msgfile);
+		if (n == 0)
+			break;
+		SHA512_256Data(buffer, n, p);
+		p += SHA512_256_DIGEST_STRING_LENGTH;
+		p[-1] = '\n';
+		if (msg + space < p)
+			errx(1, "file too long %s", msgfile);
+	}
+	*p = 0;
+
+	fdout = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
+	sighdr = createsig(seckeyfile, msgfile, msg, p-msg);
+	fake[8] = h.xflg;
+
+	writeall(fdout, fake, sizeof fake, sigfile);
+	writeall(fdout, sighdr, strlen(sighdr), sigfile);
+	free(sighdr);
+	/* need the 0 ! */
+	writeall(fdout, msg, p - msg + 1, sigfile);
+	free(msg);
+
+	if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
+		err(1, "seek in %s", msgfile);
+
+	while (1) {
+		size_t n = read(fdin, buffer, bufsize);
+		if (n == -1)
+			err(1, "read from %s", msgfile);
+		if (n == 0)
+			break;
+		writeall(fdout, buffer, n, sigfile);
+	}
+	free(buffer);
+	close(fdout);
+}
+#endif