shithub: tlsclient

Download patch

ref: 9ff908d2e99557534056889b6673d7710c37a4c7
parent: 3ab0889db514b8eeb3e675429c9562615211b53e
author: Jacob Moody <moody@posixcafe.org>
date: Thu Jul 20 12:32:58 EDT 2023

mount.9ptls

--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,9 @@
 cpu.$O: cpu.c
 	$(CC) `pkg-config $(OPENSSL) --cflags` $(CFLAGS) $< -o $@
 
+mount.9ptls: mount.$O
+	$(CC) $(LDFLAGS) -o $@ $<
+
 %.$O: %.c
 	$(CC) $(CFLAGS) $< -o $@
 
@@ -39,7 +42,7 @@
 
 .PHONY: clean
 clean:
-	rm -f *.o lib*/*.o lib*/*.a tlsclient pam_p9.so login_-dp9ik
+	rm -f *.o lib*/*.o lib*/*.a tlsclient pam_p9.so login_-dp9ik mount.9ptls
 
 linux.tar.gz: tlsclient pam_p9.so tlsclient.1
 	tar cf - tlsclient pam_p9.so tlsclient.1 | gzip > $@
--- /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");
+}