shithub: tlsclient

Download patch

ref: 2cdc6577ef8a251bf1740439cf5bfc47050dab41
parent: e86bd6a50933e99899d63de755d81b9805827a15
parent: e9b045956ac6aa241777d15e52d87c87dd7c6d80
author: grobe0ba <[email protected]>
date: Thu Jul 20 16:07:53 EDT 2023

Merge branch 'upstream'

--- a/Makefile
+++ b/Makefile
@@ -26,6 +26,9 @@
 cpu.$O: cpu.c
 	$(CC) -Ithird_party/boringssl/src/include $(CFLAGS) cpu.c -o cpu.o
 
+mount.9ptls: mount.$O
+	$(CC) $(LDFLAGS) -o $@ $<
+
 %.$O: %.c
 	$(CC) $(CFLAGS) $< -o $@
 
@@ -35,11 +38,6 @@
 bsd.$O: bsd.c
 	$(CC) $(CFLAGS) bsd.c -o bsd.o
 
-.PHONY: clean
-clean:
-	rm -f *.o */*.o */*.a *.a $(TARG) pam_p9.so login_-dp9ik
-	$(MAKE) -C third_party/boringssl clean
-
 .PHONY: libauthsrv/libauthsrv.a
 libauthsrv/libauthsrv.a:
 	(cd libauthsrv; $(MAKE))
@@ -64,9 +62,13 @@
 linuxdist: tlsclient pam_p9.so 9cpu
 	tar cf tlsclient.tar tlsclient pam_p9.so 9cpu
 	gzip tlsclient.tar
+.PHONY: clean
+clean:
+	rm -f *.o lib*/*.o lib*/*.a tlsclient pam_p9.so login_-dp9ik mount.9ptls
+	$(MAKE) -C third_party/boringssl clean
 
-linux.tar.gz: tlsclient pam_p9.so tlsclient.1
-	tar cf - tlsclient pam_p9.so tlsclient.1 | gzip > $@
+linux.tar.gz: tlsclient pam_p9.so mount.9ptls tlsclient.1 mount.9ptls.8
+	tar cf - $^ | gzip > $@
 
 obsd.tar.gz: tlsclient.obsd login_-dp9ik tlsclient.1 login_-dp9ik.8
 	tar cf - tlsclient.obsd login_-dp9ik tlsclient.1 login_-dp9ik.8 | gzip > $@
@@ -76,3 +78,22 @@
 tlsclient.install: tlsclient tlsclient.1
 	cp tlsclient $(PREFIX)/bin
 	cp tlsclient.1 $(PREFIX)/man/man1/
+	mkdir -p $(PREFIX)/bin $(PREFIX)/share/man/man1
+	install -m755 tlsclient $(PREFIX)/bin/
+	install -m644 tlsclient.1 $(PREFIX)/share/man/man1/
+
+.PHONY: mount.9ptls.install
+mount.9ptls.install: mount.9ptls mount.9ptls.8
+	mkdir -p $(PREFIX)/share/man/man8/
+	install -m755 mount.9ptls /sbin/
+	install -m644 mount.9ptls.8 $(PREFIX)/share/man/man8/
+
+.PHONY: tlsclient.obsd.install
+tlsclient.obsd.install: tlsclient.obsd login_-dp9ik tlsclient.1 login_-dp9ik.8
+	install tlsclient.obsd $(PREFIX)/bin/tlsclient
+	install tlsclient.1 $(PREFIX)/man/man1/
+	install login_-dp9ik.8 $(PREFIX)/man/man8/
+	install -d $(PREFIX)/libexec/auth
+	install -g auth login_-dp9ik $(PREFIX)/libexec/auth/
+	install -d $(PREFIX)/libexec/git
+	install git-remote-hjgit $(PREFIX)/libexec/git
--- a/README
+++ b/README
@@ -13,6 +13,7 @@
 	git-remote-hjgit: git remote helper for using hjgit repos.
 	pam_p9.so: A pam module that authenticates against a 9front auth server.
 	login_-dp9ik: An OpenBSD bsd auth executable that auths against a 9front auth server.
+	mount.9ptls: A Linux mount helper that wraps a traditional Linux v9fs in a tlsclient tunnel.
 
 Most of the tlsclient code is pillaged from jsdrawterm: https://github.com/aiju/jsdrawterm
 The main difference between tlsclient and drawterm is that tlsclient has stripped out the
@@ -28,8 +29,25 @@
 	# with git-remote-hjgit in your $PATH
 	git clone hjgit://shithub.us/user/repo
 
+	# with mount.9ptls in /sbin
+	mount -t 9ptls -o user=moody,port=9090,uid=moody flan n
+
 Building:
 	$ make tlsclient
+
+=======
+OpenBSD:
+	OpenBSD uses LibreSSL in place of OpenSSL. Unfortunately LibreSSL does
+	not have the PSK cipher suites for tlsclient. As such, the openssl11
+	package is required, and a wrapper recipe is provided:
+
+	$ make tlsclient.obsd
+
+Mount Helper:
+	Build:
+		$ make mount.9ptls
+	Install:
+		$ make mount.9ptls.install
 
 OpenBSD Authentication:
 	Build:
--- a/cpu.c
+++ b/cpu.c
@@ -4,6 +4,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <signal.h>
+#define OPENSSL_API_COMPAT 0x10000000L
 #include <openssl/ssl.h>
 
 #include <u.h>
@@ -105,7 +106,7 @@
 void
 usage(void)
 {
-	fprint(2, "Usage: %s [ -R ] [ -u user ] [ -h host ] [ -a authserver ] -p port cmd...\n", argv0);
+	fprint(2, "Usage: %s [ -Rb ] [ -u user ] [ -h host ] [ -a authserver ] -p port cmd...\n", argv0);
 	exits("usage");
 }
 
@@ -113,6 +114,7 @@
 main(int argc, char **argv)
 {
 	int Rflag;
+	int bflag;
 	int fd;
 	char buf2[1024];
 	char buf[1024];
@@ -126,7 +128,7 @@
 	pid_t xferc;
 
 	xferc = 0;
-	Rflag = 0;
+	Rflag = bflag = 0;
 	infd = 0;
 	outfd = 1;
 	user = getenv("USER");	
@@ -141,6 +143,8 @@
 		case 'a': authserver = EARGF(usage()); break;
 		case 'p': port = EARGF(usage()); break;
 		case 'R': Rflag++; break;
+		case 'b': bflag++; break;
+		default: usage(); break;
 	} ARGEND
 
 	if(Rflag)
@@ -213,6 +217,17 @@
 		snprint(buf2, sizeof buf2, "%7d\n", i);
 		tls_send(-1, buf2, strlen(buf2));
 		tls_send(-1, buf, i);
+	}
+
+	if(bflag){
+		switch(fork()){
+		case -1:
+			sysfatal("fork");
+		case 0:
+			break;
+		default:
+			return 0;
+		}
 	}
 
 	signal(SIGUSR1, suicide);
--- a/include/lib.h
+++ b/include/lib.h
@@ -291,6 +291,17 @@
 extern void exits(char*);
 extern long readn(int, void*, long);
 
+extern	ulong	truerand(void);
+
+extern	int	errstr(char*, uint);
+extern	int	rerrstr(char*, uint);
+extern	void	werrstr(char*, ...);
+
+/*
+ * crypt routines
+ */
+extern	int	encrypt(void*, void*, int);
+extern	int	decrypt(void*, void*, int);
 
 /*
  * Time-of-day
--- a/include/libsec.h
+++ b/include/libsec.h
@@ -468,10 +468,6 @@
 int	okThumbprint(uchar *hash, int len, Thumbprint *ok);
 int	okCertificate(uchar *cert, int len, Thumbprint *ok);
 
-/* readcert.c */
-uchar	*readcert(char *filename, int *pcertlen);
-PEMChain*readcertchain(char *filename);
-
 /* aes_xts.c */
 void aes_xts_encrypt(AESstate *tweak, AESstate *ecb, uvlong sectorNumber, uchar *input, uchar *output, ulong len);
 void aes_xts_decrypt(AESstate *tweak, AESstate *ecb, uvlong sectorNumber, uchar *input, uchar *output, ulong len);
--- a/libc/Makefile
+++ b/libc/Makefile
@@ -64,16 +64,11 @@
 	convS2M.$O\
 	ctime.$O\
 	crypt.$O\
-	dirfstat.$O\
-	dirfwstat.$O\
 	dirmodefmt.$O\
-	dirstat.$O\
-	dirwstat.$O\
 	fprint.$O\
 	getfields.$O\
 	getpid.$O\
 	netmkaddr.$O\
-	nsec.$O\
 	pow10.$O\
 	pushssl.$O\
 	pushtls.$O\
--- a/libc/dirfstat.c
+++ /dev/null
@@ -1,37 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <fcall.h>
-
-enum
-{
-	DIRSIZE	= STATFIXLEN + 16 * 4		/* enough for encoded stat buf + some reasonable strings */
-};
-
-Dir*
-dirfstat(int fd)
-{
-	Dir *d;
-	uchar *buf;
-	int n, nd, i;
-
-	nd = DIRSIZE;
-	for(i=0; i<2; i++){	/* should work by the second try */
-		d = malloc(sizeof(Dir) + BIT16SZ + nd);
-		if(d == nil)
-			return nil;
-		buf = (uchar*)&d[1];
-		n = fstat(fd, buf, BIT16SZ+nd);
-		if(n < BIT16SZ){
-			free(d);
-			return nil;
-		}
-		nd = GBIT16(buf);	/* upper bound on size of Dir + strings */
-		if(nd <= n){
-			convM2D(buf, n, d, (char*)&d[1]);
-			return d;
-		}
-		/* else sizeof(Dir)+BIT16SZ+nd is plenty */
-		free(d);
-	}
-	return nil;
-}
--- a/libc/dirfwstat.c
+++ /dev/null
@@ -1,19 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <fcall.h>
-
-int
-dirfwstat(int fd, Dir *d)
-{
-	uchar *buf;
-	int r;
-
-	r = sizeD2M(d);
-	buf = malloc(r);
-	if(buf == nil)
-		return -1;
-	convD2M(d, buf, r);
-	r = fwstat(fd, buf, r);
-	free(buf);
-	return r;
-}
--- a/libc/dirstat.c
+++ /dev/null
@@ -1,37 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <fcall.h>
-
-enum
-{
-	DIRSIZE	= STATFIXLEN + 16 * 4		/* enough for encoded stat buf + some reasonable strings */
-};
-
-Dir*
-dirstat(char *name)
-{
-	Dir *d;
-	uchar *buf;
-	int n, nd, i;
-
-	nd = DIRSIZE;
-	for(i=0; i<2; i++){	/* should work by the second try */
-		d = malloc(sizeof(Dir) + BIT16SZ + nd);
-		if(d == nil)
-			return nil;
-		buf = (uchar*)&d[1];
-		n = stat(name, buf, BIT16SZ+nd);
-		if(n < BIT16SZ){
-			free(d);
-			return nil;
-		}
-		nd = GBIT16((uchar*)buf);	/* upper bound on size of Dir + strings */
-		if(nd <= n){
-			convM2D(buf, n, d, (char*)&d[1]);
-			return d;
-		}
-		/* else sizeof(Dir)+BIT16SZ+nd is plenty */
-		free(d);
-	}
-	return nil;
-}
--- a/libc/dirwstat.c
+++ /dev/null
@@ -1,19 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <fcall.h>
-
-int
-dirwstat(char *name, Dir *d)
-{
-	uchar *buf;
-	int r;
-
-	r = sizeD2M(d);
-	buf = malloc(r);
-	if(buf == nil)
-		return -1;
-	convD2M(d, buf, r);
-	r = wstat(name, buf, r);
-	free(buf);
-	return r;
-}
--- a/libc/genrandom.c
+++ b/libc/genrandom.c
@@ -4,6 +4,9 @@
 #undef long
 #undef ulong
 #include <unistd.h>
+#ifdef __APPLE__
+#include <sys/random.h>
+#endif
 
 void
 genrandom(uchar *buf, int nbytes)
--- a/libc/nsec.c
+++ /dev/null
@@ -1,65 +1,0 @@
-#include <u.h>
-#include <libc.h>
-
-static uvlong order = (uvlong) 0x0001020304050607ULL;
-
-static void
-be2vlong(vlong *to, uchar *f)
-{
-	uchar *t, *o;
-	int i;
-
-	t = (uchar*)to;
-	o = (uchar*)&order;
-	for(i = 0; i < 8; i++)
-		t[o[i]] = f[i];
-}
-
-/*
- *  After a fork with fd's copied, both fd's are pointing to
- *  the same Chan structure.  Since the offset is kept in the Chan
- *  structure, the seek's and read's in the two processes can
- *  compete at moving the offset around.  Hence the retry loop.
- *
- *  Since the bintime version doesn't need a seek, it doesn't
- *  have the loop.
- */
-vlong
-nsec(void)
-{
-	char b[12+1];
-	static int f = -1;
-	static int usebintime;
-	int retries;
-	vlong t;
-
-	if(f < 0){
-		usebintime = 1;
-		f = open("/dev/bintime", OREAD|OCEXEC);
-		if(f < 0){
-			usebintime = 0;
-			f = open("/dev/nsec", OREAD|OCEXEC);
-			if(f < 0)
-				return 0;
-		}
-	}
-
-	if(usebintime){
-		if(read(f, b, sizeof(uvlong)) < 0)
-			goto error;
-		be2vlong(&t, (uchar*)b);
-		return t;
-	} else {
-		for(retries = 0; retries < 100; retries++){
-			if(seek(f, 0, 0) >= 0 && read(f, b, sizeof(b)-1) >= 0){
-				b[sizeof(b)-1] = 0;
-				return strtoll(b, 0, 0);
-			}
-		}
-	}
-
-error:
-	close(f);
-	f = -1;
-	return 0;
-}
--- a/libc/werrstr.c
+++ b/libc/werrstr.c
@@ -4,6 +4,15 @@
 char errbuf[ERRMAX];
 
 int
+errstr(char *buf, uint n)
+{
+	if(n > ERRMAX)
+		n = ERRMAX;
+	utfecpy(errbuf, errbuf+n, buf);
+	return utflen(buf);
+}
+
+int
 rerrstr(char *buf, uint n)
 {
 	utfecpy(buf, buf+n, errbuf);
--- a/libsec/aesgcmtest.c
+++ /dev/null
@@ -1,314 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <mp.h>
-#include <libsec.h>
-
-typedef struct Test Test;
-struct Test
-{
-	char *K;
-	char *P;
-	char *A;
-	char *IV;
-	char *T;
-};
-
-Test tests[] = {
-	{	/* Test Case 1 */
-		"00000000000000000000000000000000",
-		"",
-		"",
-		"000000000000000000000000",
-
-		"58E2FCCEFA7E3061367F1D57A4E7455A"
-	},
-	{	/* Test Case 2 */
-		"00000000000000000000000000000000",
-		"00000000000000000000000000000000",
-		"",
-		"000000000000000000000000",
-
-		"AB6E47D42CEC13BDF53A67B21257BDDF",
-	},
-	{	/* Test Case 3 */
-		"feffe9928665731c6d6a8f9467308308",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b391aafd255",
-		"",
-		"cafebabefacedbaddecaf888",
-
-		"4D5C2AF327CD64A62CF35ABD2BA6FAB4"
-	},
-	{	/* Test Case 4 */
-		"feffe9928665731c6d6a8f9467308308",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b39",
-		"feedfacedeadbeeffeedfacedeadbeef"
-		"abaddad2",
-		"cafebabefacedbaddecaf888",
-
-		"5BC94FBC3221A5DB94FAE95AE7121A47"
-	},
-	{	/* Test Case 5 */
-		"feffe9928665731c6d6a8f9467308308",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b39",
-		"feedfacedeadbeeffeedfacedeadbeef"
-		"abaddad2",
-		"cafebabefacedbad",
-
-		"3612D2E79E3B0785561BE14AACA2FCCB"
-	},
-	{	/* Test Case 6 */
-		"feffe9928665731c6d6a8f9467308308",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b39",
-		"feedfacedeadbeeffeedfacedeadbeef"
-		"abaddad2",
-		"9313225df88406e555909c5aff5269aa"
-		"6a7a9538534f7da1e4c303d2a318a728"
-		"c3c0c95156809539fcf0e2429a6b5254"
-		"16aedbf5a0de6a57a637b39b",
-
-		"619CC5AEFFFE0BFA462AF43C1699D050"
-	},
-	{	/* Test Case 7 */
-		"00000000000000000000000000000000"
-		"0000000000000000",
-		"",
-		"",
-		"000000000000000000000000",
-
-		"CD33B28AC773F74BA00ED1F312572435"
-	},
-	{	/* Test Case 8 */
-		"00000000000000000000000000000000"
-		"0000000000000000",
-		"00000000000000000000000000000000",
-		"",
-		"000000000000000000000000",
-
-		"2FF58D80033927AB8EF4D4587514F0FB"
-	},
-	{	/* Test Case 9 */
-		"feffe9928665731c6d6a8f9467308308"
-		"feffe9928665731c",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b391aafd255",
-		"",
-		"cafebabefacedbaddecaf888",
-
-		"9924A7C8587336BFB118024DB8674A14"
-	},
-	{	/* Test Case 10 */
-		"feffe9928665731c6d6a8f9467308308"
-		"feffe9928665731c",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b39",
-		"feedfacedeadbeeffeedfacedeadbeef"
-		"abaddad2",
-		"cafebabefacedbaddecaf888",
-
-		"2519498E80F1478F37BA55BD6D27618C"
-	},
-	{	/* Test Case 11 */
-		"feffe9928665731c6d6a8f9467308308"
-		"feffe9928665731c",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b39",
-		"feedfacedeadbeeffeedfacedeadbeef"
-		"abaddad2",
-		"cafebabefacedbad",
-
-		"65DCC57FCF623A24094FCCA40D3533F8"
-	},
-	{	/* Test Case 12 */
-		"feffe9928665731c6d6a8f9467308308"
-		"feffe9928665731c",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b39",
-		"feedfacedeadbeeffeedfacedeadbeef"
-		"abaddad2",
-		"9313225df88406e555909c5aff5269aa"
-		"6a7a9538534f7da1e4c303d2a318a728"
-		"c3c0c95156809539fcf0e2429a6b5254"
-		"16aedbf5a0de6a57a637b39b",
-
-		"DCF566FF291C25BBB8568FC3D376A6D9"
-	},
-	{	/* Test Case 13 */
-		"00000000000000000000000000000000"
-		"00000000000000000000000000000000",
-		"",
-		"",
-		"000000000000000000000000",
-
-		"530F8AFBC74536B9A963B4F1C4CB738B"
-	},
-	{	/* Test Case 14 */
-		"00000000000000000000000000000000"
-		"00000000000000000000000000000000",
-		"00000000000000000000000000000000",
-		"",
-		"000000000000000000000000",
-
-		"D0D1C8A799996BF0265B98B5D48AB919"
-	},
-	{	/* Test Case 15 */
-		"feffe9928665731c6d6a8f9467308308"
-		"feffe9928665731c6d6a8f9467308308",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b391aafd255",
-		"",
-		"cafebabefacedbaddecaf888",
-
-		"B094DAC5D93471BDEC1A502270E3CC6C"
-	},
-	{	/* Test Case 16 */
-		"feffe9928665731c6d6a8f9467308308"
-		"feffe9928665731c6d6a8f9467308308",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b39",
-		"feedfacedeadbeeffeedfacedeadbeef"
-		"abaddad2",
-		"cafebabefacedbaddecaf888",
-
-		"76FC6ECE0F4E1768CDDF8853BB2D551B"
-	},
-	{	/* Test Case 17 */
-		"feffe9928665731c6d6a8f9467308308"
-		"feffe9928665731c6d6a8f9467308308",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b39",
-		"feedfacedeadbeeffeedfacedeadbeef"
-		"abaddad2",
-		"cafebabefacedbad",
-
-		"3A337DBF46A792C45E454913FE2EA8F2"
-	},
-	{	/* Test Case 18 */
-		"feffe9928665731c6d6a8f9467308308"
-		"feffe9928665731c6d6a8f9467308308",
-		"d9313225f88406e5a55909c5aff5269a"
-		"86a7a9531534f7da2e4c303d8a318a72"
-		"1c3c0c95956809532fcf0e2449a6b525"
-		"b16aedf5aa0de657ba637b39",
-		"feedfacedeadbeeffeedfacedeadbeef"
-		"abaddad2",
-		"9313225df88406e555909c5aff5269aa"
-		"6a7a9538534f7da1e4c303d2a318a728"
-		"c3c0c95156809539fcf0e2429a6b5254"
-		"16aedbf5a0de6a57a637b39b",
-
-		"A44A8266EE1C8EB0C8B5D4CF5AE9F19A"
-	},
-};
-
-int
-parsehex(char *s, uchar *h, char *l)
-{
-	char *e;
-	mpint *m;
-	int n;
-
-	n = strlen(s);
-	if(n == 0)
-		return 0;
-	assert((n & 1) == 0);
-	n >>= 1;
-	e = nil;
-	m = strtomp(s, &e, 16, nil);
-	if(m == nil || *e != '\0')
-		abort();
-	mptober(m, h, n);
-	if(l != nil)
-		print("%s = %.*H\n", l, n, h);
-	return n;
-}
-
-void
-runtest(Test *t)
-{
-	AESGCMstate s;
-	uchar key[1024], plain[1024], aad[1024], iv[1024], tag[16], tmp[16];
-	int nkey, nplain, naad, niv;
-
-	nkey = parsehex(t->K, key, "K");
-	nplain = parsehex(t->P, plain, "P");
-	naad = parsehex(t->A, aad, "A");
-	niv = parsehex(t->IV, iv, "IV");
-
-	setupAESGCMstate(&s, key, nkey, iv, niv);
-	aesgcm_encrypt(plain, nplain, aad, naad, tag, &s);
-	print("C = %.*H\n", nplain, plain);
-	print("T = %.*H\n", 16, tag);
-
-	parsehex(t->T, tmp, nil);
-	assert(memcmp(tmp, tag, 16) == 0);
-}
-
-void
-perftest(void)
-{
-	AESGCMstate s;
-	static uchar zeros[16];
-	uchar buf[1024*1024], tag[16];
-	vlong now;
-	int i, delta;
-
-	now = nsec();
-	for(i=0; i<100; i++){
-		memset(buf, 0, sizeof(buf));
-		if(1){
-			setupAESGCMstate(&s, zeros, 16, zeros, 12);
-			aesgcm_encrypt(buf, sizeof(buf), nil, 0, tag, &s);
-		} else {
-			setupAESstate(&s, zeros, 16, zeros);
-			aesCBCencrypt(buf, sizeof(buf), &s);
-		}
-	}
-	delta = (nsec() - now) / 1000000000LL;
-	fprint(2, "%ds = %d/s\n", delta, i*sizeof(buf) / delta);
-}
-
-void
-main(int argc, char **argv)
-{
-	int i;
-
-	fmtinstall('H', encodefmt);
-
-	ARGBEGIN {
-	case 'p':
-		perftest();
-		exits(nil);
-	} ARGEND;
-
-	for(i=0; i<nelem(tests); i++){
-		print("Test Case %d\n", i+1);
-		runtest(&tests[i]);
-		print("\n");
-	}
-}
--- a/libsec/hkdf.c
+++ b/libsec/hkdf.c
@@ -3,11 +3,7 @@
 
 /* rfc5869 */
 void
-hkdf_x(salt, nsalt, info, ninfo, key, nkey, d, dlen, x, xlen)
-	uchar *salt, *info, *key, *d;
-	ulong nsalt, ninfo, nkey, dlen;
-	DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
-	int xlen;
+hkdf_x(uchar *salt, ulong nsalt, uchar *info, ulong ninfo, uchar *key, ulong nkey, uchar *d, ulong dlen, DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen)
 {
 	uchar prk[256], tmp[256], cnt;
 	DigestState *ds;
--- a/libsec/pbkdf2.c
+++ b/libsec/pbkdf2.c
@@ -3,11 +3,7 @@
 
 /* rfc2898 */
 void
-pbkdf2_x(p, plen, s, slen, rounds, d, dlen, x, xlen)
-	uchar *p, *s, *d;
-	ulong plen, slen, dlen, rounds;
-	DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
-	int xlen;
+pbkdf2_x(uchar *p, ulong plen, uchar *s, ulong slen, ulong rounds, uchar *d, ulong dlen, DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen)
 {
 	uchar block[256], tmp[256];
 	ulong i, j, k, n;
--- a/libsec/readcert.c
+++ /dev/null
@@ -1,63 +1,0 @@
-#include "os.h"
-#include <libsec.h>
-
-static char*
-readfile(char *name)
-{
-	int fd;
-	char *s;
-	Dir *d;
-
-	fd = open(name, OREAD);
-	if(fd < 0)
-		return nil;
-	if((d = dirfstat(fd)) == nil) {
-		close(fd);
-		return nil;
-	}
-	s = malloc(d->length + 1);
-	if(s == nil || readn(fd, s, d->length) != d->length){
-		free(s);
-		free(d);
-		close(fd);
-		return nil;
-	}
-	close(fd);
-	s[d->length] = '\0';
-	free(d);
-	return s;
-}
-
-uchar*
-readcert(char *filename, int *pcertlen)
-{
-	char *pem;
-	uchar *binary;
-
-	pem = readfile(filename);
-	if(pem == nil){
-		werrstr("can't read %s: %r", filename);
-		return nil;
-	}
-	binary = decodePEM(pem, "CERTIFICATE", pcertlen, nil);
-	free(pem);
-	if(binary == nil){
-		werrstr("can't parse %s", filename);
-		return nil;
-	}
-	return binary;
-}
-
-PEMChain *
-readcertchain(char *filename)
-{
-	char *chfile;
-
-	chfile = readfile(filename);
-	if (chfile == nil) {
-		werrstr("can't read %s: %r", filename);
-		return nil;
-	}
-	return decodepemchain(chfile, "CERTIFICATE");
-}
-
--- a/libsec/rsatest.c
+++ /dev/null
@@ -1,56 +1,0 @@
-#include "os.h"
-#include <mp.h>
-#include <libsec.h>
-#include <bio.h>
-
-void
-main(void)
-{
-	int n;
-	vlong start;
-	char *p;
-	uchar buf[4096];
-	Biobuf b;
-	RSApriv *rsa;
-	mpint *clr, *enc, *clr2;
-
-	fmtinstall('B', mpfmt);
-
-	rsa = rsagen(1024, 16, 0);
-	if(rsa == nil)
-		sysfatal("rsagen");
-	Binit(&b, 0, OREAD);
-	clr = mpnew(0);
-	clr2 = mpnew(0);
-	enc = mpnew(0);
-
-	strtomp("123456789abcdef123456789abcdef123456789abcdef123456789abcdef", nil, 16, clr);
-	rsaencrypt(&rsa->pub, clr, enc);
-	
-	start = nsec();
-	for(n = 0; n < 10; n++)
-		rsadecrypt(rsa, enc, clr);
-	print("%lld\n", nsec()-start);
-
-	start = nsec();
-	for(n = 0; n < 10; n++)
-		mpexp(enc, rsa->dk, rsa->pub.n, clr2);
-	print("%lld\n", nsec()-start);
-
-	if(mpcmp(clr, clr2) != 0)
-		print("%B != %B\n", clr, clr2);
-	
-	print("> ");
-	while(p = Brdline(&b, '\n')){
-		n = Blinelen(&b);
-		letomp((uchar*)p, n, clr);
-		print("clr %B\n", clr);
-		rsaencrypt(&rsa->pub, clr, enc);
-		print("enc %B\n", enc);
-		rsadecrypt(rsa, enc, clr);
-		print("clr %B\n", clr);
-		n = mptole(clr, buf, sizeof(buf), nil);
-		write(1, buf, n);
-		print("> ");
-	}
-}
--- a/libsec/scrypt.c
+++ b/libsec/scrypt.c
@@ -69,9 +69,7 @@
 }
 
 char*
-scrypt(p, plen, s, slen, N, R, P, d, dlen)
-	ulong plen, slen, dlen, N, R, P;
-	uchar *p, *s, *d;
+scrypt(uchar *p, ulong plen, uchar *s, ulong slen, ulong N, ulong R, ulong P, uchar *d, ulong dlen)
 {
 	static char oom[] = "out of memory";
 
--- /dev/null
+++ b/mount.9ptls.8
@@ -1,0 +1,63 @@
+.Dd July 20, 2023
+.Dt MOUNT.9PTLS 8
+.Os tlsclient
+.
+.Sh NAME
+.Nm mount.9ptls
+.Nd tlsclient mount helper
+.Sh SYNOPSIS
+.Nm
+.Op Fl sfnvh
+.Op Fl N Ar namespace
+.Op Fl o Ar options
+.Ar fileserver
+.Ar mountpoint
+.
+.Sh DESCRIPTION
+The
+.Nm
+helper is used by
+.Xr mount 8
+to wrap a 9p filesystem through a dp9ik authenticated
+tls tunnel provided by
+.Xr tlsclient 1 .
+This is accomplished by interpreting the arguments provided,
+setting up the connection and then passing the file descriptiors
+to the kernel 9p mount.
+.Ar Fileserver
+is connected to over TCP, doing DNS resolution as required.
+As there is no standard port for this type of service a
+.Fl o Ar port
+option is required.
+.
+.Sh OPTIONS
+The
+.Fl s ,
+.Fl f ,
+.Fl n ,
+.Fl v ,
+and
+.Fl N
+flags are passed to
+.Xr mount 8
+without any interpretation by
+.Nm .
+Most
+.Ar options
+are passed through untouched, with
+.Nm
+ingesting the
+.Ar port
+and
+.Ar auth
+.Ar options
+for itself. The former specifying the
+port to connect to
+.Ar fileserver
+on and the later specifying the hostname of the mutal authentication
+server that is to be used in the dp9ik handshake.
+.
+.Sh SEE ALSO
+.Xr tlsclient 1 ,
+.Xr fstab 5 ,
+.Xr mount 8
--- /dev/null
+++ b/mount.c
@@ -1,0 +1,223 @@
+#ifdef __linux__
+#define _GNU_SOURCE
+#endif
+
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <err.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define nelem(x) (sizeof x / sizeof x[0])
+
+enum {
+	OPT_MAX = 4108,
+	PATH_MAX = 4096,
+};
+
+static char *mountargv[256];
+static int mountargc = 0;
+
+static char *optargv[256];
+static int optargc = 0;
+
+static char *port = NULL;
+static char *user = NULL;
+static char *authbox = NULL;
+
+static void
+appendarg(char *s)
+{
+	if(mountargc >= nelem(mountargv)-1)
+		errx(EINVAL, "argument overflow");
+	mountargv[mountargc++] = strdup(s);
+	mountargv[mountargc] = NULL;
+}
+
+static void
+_appendopt(char *key, char *val)
+{
+	char buf[OPT_MAX];
+
+	if(optargc >= nelem(optargv)-1)
+		errx(EINVAL, "option overflow");
+	if(val == NULL)
+		snprintf(buf, sizeof buf, "%s%s", optargc == 0 ? "" : ",", key);
+	else
+		snprintf(buf, sizeof buf, "%s%s=%s", optargc == 0 ? "" : ",", key, val);
+	optargv[optargc++] = strdup(buf);
+	optargv[optargc] = NULL;
+}
+
+static void
+appendopt(char *key, char *val)
+{
+	if(strcmp(key, "port") == 0){
+		port = strdup(val);
+		return;
+	} else if(strcmp(key, "auth") == 0){
+		authbox = strdup(val);
+		return;
+	} else if(strcmp(key, "user") == 0){
+		user = strdup(val);
+		/* passthrough as well */
+	} else if(strcmp(key, "trans") == 0){
+		errx(EINVAL, "trans=fd is set by 9ptls and can not be overriden");
+	} else if(strcmp(key, "rfdno") == 0 || strcmp(key, "wfdno") == 0){
+		errx(EINVAL, "rfdno and wfdno are reserved by 9ptls and can not be overriden");
+	}
+	_appendopt(key, val);
+}
+
+static void
+parseoptions(char *opt)
+{
+	char *s;
+	char *key, *val;
+
+	key = val = NULL;
+	for(s = opt; *s != '\0'; s++){
+		switch(*s){
+		case '=':
+			if(key == NULL)
+				errx(EINVAL, "option argument has no key, only a value");
+			*s = '\0';
+			if(s[1] == '\0')
+				errx(EINVAL, "key %s has no value", key);
+			val = s+1;
+			continue;
+		case ',':
+			if(key == NULL)
+				errx(EINVAL, "extra comma");
+			*s = '\0';
+			appendopt(key, val);
+			key = val = NULL;
+			continue;
+		}
+		if(key == NULL)
+			key = s;
+	}
+	if(key != NULL && val != NULL)
+		appendopt(key, val);
+
+	_appendopt("trans", "fd");
+	_appendopt("rfdno", "0");
+	_appendopt("wfdno", "1");
+}
+
+static void
+flattenoptions(char *opt, int n)
+{
+	char *s, *e;
+	int i, j;
+
+	s = opt;
+	e = opt + n - 2;
+
+	for(i = 0; i < optargc; i++){
+		j = strlen(optargv[i]);
+		if(s+j >= e)
+			n = e-s;
+		memcpy(s, optargv[i], j);
+		s[j] = '\0';
+		s += j;
+	}
+}
+
+void
+usage(void)
+{
+	errx(EINVAL, "Usage: mount.9ptls [-sfnvh] [-o options] [-N namespace] <host> <mountpoint>");
+}
+
+int
+main(int argc, char **argv)
+{
+	int c;
+	int sflag, fflag, nflag, vflag;
+	char options[OPT_MAX];
+	char namespace[PATH_MAX];
+
+	sflag = fflag = nflag = vflag = 0;
+	options[0] = namespace[0] = '\0';
+
+	while((c = getopt_long(argc, argv, "sfnvo:N:h?", 0, 0)) != -1){
+		switch(c){
+		case 's':
+			sflag = 1;
+			break;
+		case 'f':
+			fflag = 1;
+			break;
+		case 'n':
+			nflag = 1;
+			break;
+		case 'v':
+			vflag = 1;
+			break;
+		case 'o':
+			snprintf(options, sizeof options, "%s", optarg);
+			break;
+		case 'N':
+			snprintf(namespace, sizeof namespace, "%s", optarg);
+			break;
+		case '?': case 'h':
+			if(optopt)
+				errx(EINVAL, "invalid option '%c'", optopt);
+			usage();
+			break;
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if(argc != 2)
+		usage();
+
+	parseoptions(options);
+	if(port == NULL)
+		errx(EINVAL, "a port option must be given");
+	if(user == NULL && (user = getenv("USER")) == NULL)
+		errx(EINVAL, "user option not given and count not infer");
+	flattenoptions(options, sizeof options);
+
+	appendarg("tlsclient");
+	appendarg("-b");
+	appendarg("-h");
+	appendarg(argv[0]);
+	if(authbox != NULL){
+		appendarg("-a");
+		appendarg(authbox);
+	}
+	appendarg("-u");
+	appendarg(user);
+	appendarg("-p");
+	appendarg(port);
+
+	appendarg("mount");
+	if(sflag)
+		appendarg("-s");
+	if(fflag)
+		appendarg("-f");
+	if(nflag)
+		appendarg("-n");
+	if(vflag)
+		appendarg("-v");
+	if(namespace[0] != '\0'){
+		appendarg("-N");
+		appendarg(namespace);
+	}
+	appendarg("-i");
+	appendarg("-t");
+	appendarg("9p");
+	appendarg("-o");
+	appendarg(options);
+	appendarg(argv[0]);
+	appendarg(argv[1]);
+
+	execvp("tlsclient", mountargv);
+	err(EXIT_FAILURE, "could not exec");
+}
--- a/p9any.c
+++ b/p9any.c
@@ -17,8 +17,6 @@
 
 #include "fncs.h"
 
-void errstr(char *s){}
-
 char*
 estrdup(char *s)
 {
@@ -35,7 +33,6 @@
 	int error;
 	int save_errno;
 	int s;
-	const char *cause = NULL;
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_family = AF_UNSPEC;
 	hints.ai_socktype = SOCK_STREAM;
@@ -50,11 +47,9 @@
 	for (res = res0; res; res = res->ai_next) {
 		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
 		if (s == -1) {
-			cause = "socket";
 			continue;
 		}
 		if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
-			cause = "connect";
 			save_errno = errno;
 			close(s);
 			errno = save_errno;
--- a/tlsclient.1
+++ b/tlsclient.1
@@ -4,7 +4,7 @@
 .SH SYNOPSIS
 .B tlsclient
 [
-.B -R
+.B -bR
 ]
 [
 .B -u
@@ -51,6 +51,9 @@
 mode, if
 .I command
 is not specified a rc login shell is used.
+The
+.B -b
+flag causes tlsclient to fork into the background.
 .SH PASSWORDS
 By default
 .B tlsclient