Index | Thread | Search

From:
Peter Philipp <ima@callpeter.tel>
Subject:
if worthy, can someone complete this?
To:
tech@openbsd.org
Date:
Tue, 31 Dec 2024 07:33:18 +0100

Download raw body.

Thread
Dear Community,

Happy New Year 2025!  I have had some time to work on my own tree, doing a contribution to OpenBSD signify.
This is something I want to share, but it exposed a bug on my nameserver which will take some time to fix.
So in order to speed this up, If wanted in OpenBSD, I ask you to look into this with a non-broken nameserver,
other than that I will have some time in perhaps February 2025 to commit this.  Not looking for OK's right
now, but rather someone to finish this work and test.  Very minute changes needed as well as some experience
with DNSSEC.

This patch checks against a DNSSEC TLSA resource record for the signify signature, it must be used in
conjunction with unwind(8) otherwise that mode doesn't work.  It adds one flag to the signify -V mode.

There is two areas that this patch needs cleanup.  1. The offsets of the DNSSEC packet returned into the main()
at signify, may need to be adjusted.  2. the temporary file holding a dummy signify public key (which should
get deleted at end of program) needs work (3 line fix).  The latter is a little awkward since mkstemp() which
I would prefer does not return a fileame in any way, so mktemp() has to be used somehow.

Anyhow, I would have liked to give you a full patch myself, but priorities for the new year have shifted this
on the backburner, and maybe it's liked (even though it's unfinished).

Please any replies CC'ed to me as I'm not on the tech@ list right now.

-pjp

PS: T_TLSA (52 decimal) RR needs to be added to the system headers I believe.

diff /usr/src
commit - c3d0badf2f2115528d40d041f1a0e7094bf568e2
path + /usr/src
blob - de945715d4c1428c079f724fca4471d7223f2e62
file + usr.bin/signify/Makefile
--- usr.bin/signify/Makefile
+++ usr.bin/signify/Makefile
@@ -5,6 +5,7 @@ SRCS+=	zsig.c
 SRCS+=	fe25519.c sc25519.c
 SRCS+=	mod_ed25519.c mod_ge25519.c
 SRCS+=	crypto_api.c
+SRCS+=	dnssec.c
 
 PROG=	signify
 
blob - /dev/null
file + usr.bin/signify/dnssec.c (mode 644)
--- /dev/null
+++ usr.bin/signify/dnssec.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2024 Peter Philipp <pjp@delphinusdns.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.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <resolv.h>
+#include <ctype.h>
+#include <err.h>
+
+#define _RES_CONF_NAMESERVER		"nameserver "			/* the nameserver entry */
+#define _RES_CONF_NAMESERVER_LEN	11
+#define _RES_CONF_UNWIND		"# resolvd: unwind"		/* set with unwind usage */
+
+#define MAXANSWERSZ			65535	/* RFC 1035 limit of a DNS packet */
+#define T_TLSA				52	/* RFC 6698, sec. 7.1, XXX add to arpa/nameser.h later */
+
+extern struct __res_state _res;
+
+char *		tlsa_query(const char *, int, int *);
+int		using_unwind(void);
+
+/*
+ * check if using unwind, if yes return true
+ */
+
+int
+using_unwind(void)
+{
+	int res = 0;
+	char *p;
+	char buf[512];
+	FILE *f;
+
+	f = fopen(_PATH_RESCONF, "r");
+	if (f == NULL)
+		return(res);
+
+	while (fgets(buf, sizeof(buf), f) != NULL) {
+		p = &buf[0];
+		while (isspace(*p))
+			p++;
+
+		if (strncmp(p, _RES_CONF_NAMESERVER, _RES_CONF_NAMESERVER_LEN) == 0) {
+			if (strstr(buf, "# resolvd: unwind") != NULL)
+				res = 1;
+			else
+				res = 0;
+		}
+	}
+
+	fclose(f);
+
+	return (res);
+}
+
+
+/*
+ * returns zero if a public key file has been created with valid data, otherwise -1 on error
+ * creates the pubkeyfile only on success.
+ */
+
+char *
+tlsa_query(const char *dname, int pubkeysize, int *retlen)
+{
+	HEADER dheader;
+	char *answer = NULL, *ret = NULL;
+	int len;
+
+	
+
+	/* res_init(); not needed it's internally in res_query() */
+	//_res.options |= RES_USE_DNSSEC;
+
+	answer = calloc(MAXANSWERSZ, 1);
+	if (answer == NULL) {
+		warn("cannot allocate %d bytes", MAXANSWERSZ);
+		return(NULL);
+	}
+
+	if ((len = res_query(dname, C_IN, T_TLSA, answer, MAXANSWERSZ)) == -1) {
+		return(NULL);
+	}
+	
+	if (len >= HFIXEDSZ) {
+		memcpy((char *)&dheader, answer, HFIXEDSZ);
+
+		if (((len - HFIXEDSZ) < (pubkeysize)) || (dheader.ad != 1) || (ntohs(dheader.ancount) != 1))
+			goto err;
+
+		len -= (pubkeysize);
+		if (len < HFIXEDSZ)		/* make sure that there is an offset past header */
+			goto err;
+
+		ret = malloc(pubkeysize);
+		if (ret == NULL) {
+			warn("cannot allocate %d bytes", pubkeysize);
+			goto err;
+		}
+
+		memcpy(ret, &answer[len], pubkeysize);
+		*retlen = pubkeysize;
+	}
+
+err:
+	freezero(answer, MAXANSWERSZ);
+	return(ret);
+}
blob - 2d1a29ec3218fdbd84a506edf1489615b95db8fa
file + usr.bin/signify/signify.1
--- usr.bin/signify/signify.1
+++ usr.bin/signify/signify.1
@@ -24,6 +24,7 @@
 .Nm signify
 .Fl C
 .Op Fl q
+.Op Fl d Ar name
 .Op Fl p Ar pubkey
 .Op Fl t Ar keytype
 .Fl x Ar sigfile
@@ -101,6 +102,14 @@ When signing with
 store a zero time stamp in the
 .Xr gzip 1
 header.
+.It Fl d Ar name
+Public key used by
+.Fl V
+to check a signature distributed by secure DNS. Used on a system utilizing
+.Xr unwind 8
+for DNSSEC validation, where
+.Ar name
+is the domain name queried with type TLSA RR.
 .It Fl p Ar pubkey
 Public key produced by
 .Fl G ,
@@ -156,6 +165,10 @@ Entered passphrase is incorrect.
 The message file was corrupted and its signature does not match.
 .It
 The message file is too large.
+.It
+In case of
+.Fl d
+a secure DNS lookup fails.
 .El
 .Sh EXAMPLES
 Create a new key pair:
@@ -193,7 +206,8 @@ $ ftp url | signify -Vz -t arc | tar ztf -
 .Xr pkg_add 1 ,
 .Xr sha256 1 ,
 .Xr fw_update 8 ,
-.Xr sysupgrade 8
+.Xr sysupgrade 8 ,
+.Xr unwind 8
 .Sh HISTORY
 The
 .Nm
blob - 1ab609ae5dd74b929b240ba5f196951df93018bb
file + usr.bin/signify/signify.c
--- usr.bin/signify/signify.c
+++ usr.bin/signify/signify.c
@@ -76,9 +76,10 @@ usage(const char *error)
 {
 	if (error)
 		fprintf(stderr, "%s\n", error);
+	/* -C line fits in exactly 80 character column terminals, hope that's okay */
 	fprintf(stderr, "usage:"
 #ifndef VERIFYONLY
-	    "\t%1$s -C [-q] [-p pubkey] [-t keytype] -x sigfile [file ...]\n"
+	    "\t%1$s -C [-q] [-d name] [-p pubkey] [-t keytype] -x sigfile [file ...]\n"
 	    "\t%1$s -G [-n] [-c comment] -p pubkey -s seckey\n"
 	    "\t%1$s -S [-enz] [-x sigfile] -s seckey -m message\n"
 #endif
@@ -752,13 +753,14 @@ int
 main(int argc, char **argv)
 {
 	const char *pubkeyfile = NULL, *msgfile = NULL, *sigfile = NULL;
+	const char *dname = NULL;
 	char sigfilebuf[PATH_MAX];
-	char *keytype = NULL;
+	char *keytype = NULL, *pubkey = NULL;
 #ifndef VERIFYONLY
 	const char *seckeyfile = NULL, *comment = "signify";
 	int none = 0;
 #endif
-	int ch;
+	int ch, len;
 	int embedded = 0;
 	int quiet = 0;
 	int gzip = 0;
@@ -770,10 +772,10 @@ main(int argc, char **argv)
 		VERIFY
 	} verb = NONE;
 
-	if (pledge("stdio rpath wpath cpath tty", NULL) == -1)
+	if (pledge("stdio rpath wpath cpath tty dns", NULL) == -1)
 		err(1, "pledge");
 
-	while ((ch = getopt(argc, argv, "CGSVzc:em:np:qs:t:x:")) != -1) {
+	while ((ch = getopt(argc, argv, "CGSVzc:d:em:np:qs:t:x:")) != -1) {
 		switch (ch) {
 #ifndef VERIFYONLY
 		case 'C':
@@ -794,6 +796,12 @@ main(int argc, char **argv)
 		case 'c':
 			comment = optarg;
 			break;
+		case 'd':
+			/* XXX the mktemp is the only call returning a filename which is needed at unlink */
+			//pubkeyfile = mktemp("/tmp/signify.XXXXXXXX");
+			pubkeyfile = "/tmp/signify.XXXXXXXX";
+			dname = optarg;
+			break;
 		case 'n':
 			none = 1;
 			break;
@@ -835,19 +843,36 @@ main(int argc, char **argv)
 	argc -= optind;
 	argv += optind;
 
+	if (dname != NULL) {
+		if (using_unwind()) {
+			if ((pubkey = tlsa_query(dname, sizeof(struct pubkey), &len)) == NULL)
+				errx(1, "unable to get secure DNS TLSA public key data");
+
+			writekeyfile(pubkeyfile, dname, pubkey, sizeof(struct pubkey), O_EXCL, 0644);
+			freezero(pubkey, len);
+		} else {
+			errx(1, "system is not using unwind(8) for validated DNS");
+		}
+	}
+
 	if (embedded && gzip)
 		errx(1, "can't combine -e and -z options");
 
 	if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
 		err(1, "setvbuf");
 
+	if (pledge("stdio rpath wpath cpath tty", NULL) == -1)
+		err(1, "pledge");
+
 #ifndef VERIFYONLY
 	if (verb == CHECK) {
-		if (pledge("stdio rpath", NULL) == -1)
+		if (pledge("stdio rpath cpath", NULL) == -1)
 			err(1, "pledge");
 		if (!sigfile)
 			usage("must specify sigfile");
 		check(pubkeyfile, sigfile, keytype, quiet, argc, argv);
+		if (dname != NULL)
+			unlink(pubkeyfile);
 		return 0;
 	}
 #endif
@@ -895,7 +920,7 @@ main(int argc, char **argv)
 			if (pledge("stdio rpath wpath cpath", NULL) == -1)
 				err(1, "pledge");
 		} else {
-			if (pledge("stdio rpath", NULL) == -1)
+			if (pledge("stdio rpath cpath", NULL) == -1)
 				err(1, "pledge");
 		}
 		if (gzip) {
@@ -914,5 +939,8 @@ main(int argc, char **argv)
 		break;
 	}
 
+	if (dname != NULL)
+		unlink(pubkeyfile);
+
 	return 0;
 }
blob - db7df8f07141ad4f31e92173f375d6e2a914f550
file + usr.bin/signify/signify.h
--- usr.bin/signify/signify.h
+++ usr.bin/signify/signify.h
@@ -29,5 +29,7 @@ extern void *verifyzdata(uint8_t *, unsigned long long
 extern uint8_t *createsig(const char *, const char *, uint8_t *,
     unsigned long long);
 
+extern char *tlsa_query(const char *, int, int *);
+extern int using_unwind(void);
 
 #endif
-- 

-
I remember when google, youtube, facebook, etc were open access