shithub: tlsclient

Download patch

ref: 3e05a1d462b44a519621901be94566e9300eca93
parent: b181d62f485e617f54518a8e875b1a30bef02e54
author: Jacob Moody <moody@posixcafe.org>
date: Sat Jul 3 16:33:09 EDT 2021

Add pam module

Default to compiling code as PIC.
Split up cpu.c to make it a bit easier to use partial code from it.
Add pam.c and pam_p9.so make target.

--- a/Make.config
+++ b/Make.config
@@ -1,7 +1,7 @@
 AR=ar
 RANLIB=ranlib
 CC=gcc
-CFLAGS=-Wall -Wno-missing-braces -Wno-parentheses -ggdb -I$(ROOT) -I$(ROOT)/include -c -D_THREAD_SAFE -O2
+CFLAGS=-Wall -Wno-missing-braces -Wno-parentheses -ggdb -I$(ROOT) -I$(ROOT)/include -c -D_THREAD_SAFE -O2 -fPIC
 O=o
 LDADD=
 TARG=tlsclient
--- a/Makefile
+++ b/Makefile
@@ -8,18 +8,27 @@
 	libc/libc.a\
 	libsec/libsec.a\
 
-OFILES=cpu.$O
+OFILES=cpu.$O p9any.$O
 
 default: $(TARG)
 $(TARG): $(LIBS) $(OFILES)
 	$(CC) `pkg-config openssl --libs` $(LDFLAGS) -o $(TARG) $(OFILES) $(LIBS) $(LDADD)
 
+pam_p9.so: $(LIBS) p9any.$O pam.$O
+	$(CC) -shared -o pam_p9.so p9any.$O pam.$O $(LIBS)
+
 cpu.$O: cpu.c
 	$(CC) `pkg-config openssl --cflags` $(CFLAGS) cpu.c -o cpu.o
 
+p9any.$O: p9any.c
+	$(CC) $(CFLAGS) p9any.c -o p9any.o
+
+pam.$O: pam.c
+	$(CC) $(CFLAGS) pam.c -o pam.o
+
 .PHONY: clean
 clean:
-	rm -f *.o */*.o */*.a *.a $(TARG)
+	rm -f *.o */*.o */*.a *.a $(TARG) pam_p9.so
 
 .PHONY: libauthsrv/libauthsrv.a
 libauthsrv/libauthsrv.a:
--- a/README
+++ b/README
@@ -1,10 +1,15 @@
 tlsclient: tlsclient(1) for unix
 
-This repo contains three programs, tlsclient itself, 9cpu and git-remote-hjgit.
-9cpu acts as rcpu(1).
-Git-remote-hjgit is a git remote helper for unix git that allows use of hjgit repos.
+This repo contains:
+	9cpu: rcpu(1) on unix
+	tlsclient: tlsclient(1) on unix
+	git-remote-hjgit: git remote helper for using hjgit repos.
+	pam_p9.so: A pam module that authenticates against a 9front auth server.
 
 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
+plan9 kernel that runs in userspace. This means we use openssl for TLS and and don't provide
+things like /mnt/term, but gain some more flexibility.
 
 Usage:
 	tlsclient [ -R ] [ -u user] [ -h host ] [ -a auth ] -p port cmd...
@@ -15,6 +20,3 @@
 
 	# with git-remote-hjgit in your $PATH
 	git clone hjgit://shithub.us/user/repo
-
-Bugs:
-	Currently tlsclient uses gnutls, a better alternative is welcome.
--- a/cpu.c
+++ b/cpu.c
@@ -2,18 +2,12 @@
  * cpu.c - Make a connection to a cpu server
  */
 #include <stdio.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <arpa/inet.h>
-#include <netdb.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <signal.h>
-#include <netinet/in.h>
 #include <openssl/ssl.h>
-#include <openssl/err.h>
 
 #include <u.h>
 #include <args.h>
@@ -22,15 +16,12 @@
 #include <authsrv.h>
 #include <libsec.h>
 
-static AuthInfo *p9any(int);
-static int	getkey(Authkey*, char*, char*, char*, char*);
-static int	p9authtls(int);
+#include "fncs.h"
 
 char *argv0;
 
-static char	*host;
 char *authserver;
-char *user, *pass;
+static char *user, *pass;
 
 SSL_CTX *ssl_ctx;
 SSL *ssl_conn;
@@ -51,39 +42,23 @@
 	return nsecret;
 }
 
-void errstr(char *s){}
-
-int
-unix_dial(char *host, char *port)
+/*
+ * p9any authentication followed by tls-psk encryption
+ */
+static int
+p9authtls(int fd)
 {
-	int fd;
-	struct sockaddr_in server;
-	struct hostent *he;
-	struct in_addr **addr_list;
+	ai = p9any(user, pass, fd);
+	if(ai == nil)
+		sysfatal("can't authenticate");
 
-	he = gethostbyname(host);
-	if(he == nil){
-		sysfatal("could not resolve %s", host);
-	}
-	fd = socket(AF_INET, SOCK_STREAM, 0);
-	addr_list = (struct in_addr **) he->h_addr_list;
-	server.sin_addr.s_addr = inet_addr(inet_ntoa(*addr_list[0]));
-	server.sin_family = AF_INET;
-	server.sin_port = htons(atoi(port));
-	if(connect(fd, (struct sockaddr*)&server, sizeof(server)) < 0)
-		return -1;
+	SSL_set_fd(ssl_conn, fd);
+	if(SSL_connect(ssl_conn) < 0)
+		sysfatal("ssl could not connect");
+
 	return fd;
 }
 
-char*
-estrdup(char *s)
-{
-	s = strdup(s);
-	if(s == nil)
-		sysfatal("out of memory");
-	return s;
-}
-
 typedef size_t (*iofunc)(int, void*, size_t);
 size_t tls_send(int f, void *b, size_t n) { return SSL_write(ssl_conn, b, n); }
 size_t tls_recv(int f, void *b, size_t n) { return SSL_read(ssl_conn, b, n); }
@@ -114,11 +89,12 @@
 main(int argc, char **argv)
 {
 	int Rflag;
-	int fd, res;
+	int fd;
 	char buf2[1024];
 	char buf[1024];
 	size_t n;
 	char *port;
+	char *host;
 	int pin[2];
 	int pout[2];
 	int infd, outfd;
@@ -161,8 +137,6 @@
 		sysfatal("could not init openssl");
 	ssl_conn = SSL_new(ssl_ctx);
 
-
-
 	if(*argv && !Rflag){
 		pipe(pin);
 		pipe(pout);
@@ -220,292 +194,3 @@
 		kill(execc, SIGTERM);
 }
 
-int
-readstr(int fd, char *str, int len)
-{
-	int n;
-
-	while(len) {
-		n = read(fd, str, 1);
-		if(n < 0) 
-			return -1;
-		if(*str == '\0')
-			return 0;
-		str++;
-		len--;
-	}
-	return -1;
-}
-
-/*
- * p9any authentication followed by tls-psk encryption
- */
-static int
-p9authtls(int fd)
-{
-	ai = p9any(fd);
-	if(ai == nil)
-		sysfatal("can't authenticate: %r");
-
-	SSL_set_fd(ssl_conn, fd);
-	if(SSL_connect(ssl_conn) < 0)
-		sysfatal("ssl could not connect");
-
-	return fd;
-}
-
-int
-authdial(char *net, char *dom)
-{
-	return unix_dial(authserver, "567");
-}
-
-static int
-getastickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
-{
-	int asfd, rv;
-	char *dom;
-
-	dom = tr->authdom;
-	asfd = authdial(nil, dom);
-	if(asfd < 0)
-		return -1;
-	if(y != nil){
-		PAKpriv p;
-
-		rv = -1;
-		tr->type = AuthPAK;
-		if(_asrequest(asfd, tr) != 0 || write(asfd, y, PAKYLEN) != PAKYLEN)
-			goto Out;
-
-		authpak_new(&p, key, (uchar*)tbuf, 1);
-		if(write(asfd, tbuf, PAKYLEN) != PAKYLEN)
-			goto Out;
-
-		if(_asrdresp(asfd, tbuf, 2*PAKYLEN) != 2*PAKYLEN)
-			goto Out;
-	
-		memmove(y, tbuf, PAKYLEN);
-		if(authpak_finish(&p, key, (uchar*)tbuf+PAKYLEN))
-			goto Out;
-	}
-	tr->type = AuthTreq;
-	rv = _asgetticket(asfd, tr, tbuf, tbuflen);
-Out:
-	close(asfd);
-	return rv;
-}
-
-static int
-mkservertickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
-{
-	Ticket t;
-	int ret;
-
-	if(strcmp(tr->authid, tr->hostid) != 0)
-		return -1;
-	memset(&t, 0, sizeof(t));
-	ret = 0;
-	if(y != nil){
-		PAKpriv p;
-
-		t.form = 1;
-		memmove(tbuf, y, PAKYLEN);
-		authpak_new(&p, key, y, 0);
-		authpak_finish(&p, key, (uchar*)tbuf);
-	}
-	memmove(t.chal, tr->chal, CHALLEN);
-	strcpy(t.cuid, tr->uid);
-	strcpy(t.suid, tr->uid);
-	genrandom((uchar*)t.key, sizeof(t.key));
-	t.num = AuthTc;
-	ret += convT2M(&t, tbuf+ret, tbuflen-ret, key);
-	t.num = AuthTs;
-	ret += convT2M(&t, tbuf+ret, tbuflen-ret, key);
-	memset(&t, 0, sizeof(t));
-
-	return ret;
-}
-
-static int
-gettickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
-{
-	int ret;
-	ret = getastickets(key, tr, y, tbuf, tbuflen);
-	if(ret > 0)
-		return ret;
-	return mkservertickets(key, tr, y, tbuf, tbuflen);
-}
-
-AuthInfo*
-p9any(int fd)
-{
-	char buf[1024], buf2[1024], *bbuf, *p, *proto, *dom;
-	uchar crand[2*NONCELEN], cchal[CHALLEN], y[PAKYLEN];
-	char tbuf[2*MAXTICKETLEN+MAXAUTHENTLEN+PAKYLEN], trbuf[TICKREQLEN+PAKYLEN];
-	Authkey authkey;
-	Authenticator auth;
-	int i, n, m, v2, dp9ik;
-	Ticketreq tr;
-	Ticket t;
-	AuthInfo *ai;
-
-	if(readstr(fd, buf, sizeof buf) < 0)
-		sysfatal("cannot read p9any negotiation: %r");
-	bbuf = buf;
-	v2 = 0;
-	if(strncmp(buf, "v.2 ", 4) == 0){
-		v2 = 1;
-		bbuf += 4;
-	}
-	dp9ik = 0;
-	proto = nil;
-	while(bbuf != nil){
-		if((p = strchr(bbuf, ' ')))
-			*p++ = 0;
-		if((dom = strchr(bbuf, '@')) == nil)
-			sysfatal("bad p9any domain");
-		*dom++ = 0;
-		if(strcmp(bbuf, "p9sk1") == 0 || strcmp(bbuf, "dp9ik") == 0){
-			proto = bbuf;
-			if(strcmp(proto, "dp9ik") == 0){
-				dp9ik = 1;
-				break;
-			}
-		}
-		bbuf = p;
-	}
-	if(proto == nil)
-		sysfatal("server did not offer p9sk1 or dp9ik");
-	proto = estrdup(proto);
-	sprint(buf2, "%s %s", proto, dom);
-	if(write(fd, buf2, strlen(buf2)+1) != strlen(buf2)+1)
-		sysfatal("cannot write user/domain choice in p9any");
-	if(v2){
-		if(readstr(fd, buf, sizeof buf) < 0)
-			sysfatal("cannot read OK in p9any: %r");
-		if(memcmp(buf, "OK\0", 3) != 0)
-			sysfatal("did not get OK in p9any: got %s", buf);
-	}
-	genrandom(crand, 2*NONCELEN);
-	genrandom(cchal, CHALLEN);
-	if(write(fd, cchal, CHALLEN) != CHALLEN)
-		sysfatal("cannot write p9sk1 challenge: %r");
-
-	n = TICKREQLEN;
-	if(dp9ik)
-		n += PAKYLEN;
-
-	if(readn(fd, trbuf, n) != n || convM2TR(trbuf, TICKREQLEN, &tr) <= 0)
-		sysfatal("cannot read ticket request in p9sk1: %r");
-
-again:
-	if(!getkey(&authkey, user, tr.authdom, proto, pass))
-		sysfatal("no password");
-
-	strecpy(tr.hostid, tr.hostid+sizeof tr.hostid, user);
-	strecpy(tr.uid, tr.uid+sizeof tr.uid, user);
-
-	if(dp9ik){
-		memmove(y, trbuf+TICKREQLEN, PAKYLEN);
-		n = gettickets(&authkey, &tr, y, tbuf, sizeof(tbuf));
-	} else {
-		n = gettickets(&authkey, &tr, nil, tbuf, sizeof(tbuf));
-	}
-	if(n <= 0)
-		sysfatal("cannot get auth tickets in p9sk1: %r");
-
-	m = convM2T(tbuf, n, &t, &authkey);
-	if(m <= 0 || t.num != AuthTc){
-		print("?password mismatch with auth server\n");
-		if(pass != nil && *pass)
-			sysfatal("wrong password");
-		goto again;
-	}
-	n -= m;
-	memmove(tbuf, tbuf+m, n);
-
-	if(dp9ik && write(fd, y, PAKYLEN) != PAKYLEN)
-		sysfatal("cannot send authpak public key back: %r");
-
-	auth.num = AuthAc;
-	memmove(auth.rand, crand, NONCELEN);
-	memmove(auth.chal, tr.chal, CHALLEN);
-	m = convA2M(&auth, tbuf+n, sizeof(tbuf)-n, &t);
-	n += m;
-
-	if(write(fd, tbuf, n) != n)
-		sysfatal("cannot send ticket and authenticator back: %r");
-
-	if((n=read(fd, tbuf, m)) != m || memcmp(tbuf, "cpu:", 4) == 0){
-		if(n <= 4)
-			sysfatal("cannot read authenticator");
-
-		/*
-		 * didn't send back authenticator:
-		 * sent back fatal error message.
-		 */
-		memmove(buf, tbuf, n);
-		i = readn(fd, buf+n, sizeof buf-n-1);
-		if(i > 0)
-			n += i;
-		buf[n] = 0;
-		sysfatal("server says: %s", buf);
-	}
-	
-	if(convM2A(tbuf, n, &auth, &t) <= 0
-	|| auth.num != AuthAs || tsmemcmp(auth.chal, cchal, CHALLEN) != 0){
-		print("?you and auth server agree about password.\n");
-		print("?server is confused.\n");
-		sysfatal("server lies");
-	}
-	memmove(crand+NONCELEN, auth.rand, NONCELEN);
-
-	// print("i am %s there.\n", t.suid);
-
-	ai = mallocz(sizeof(AuthInfo), 1);
-	ai->suid = estrdup(t.suid);
-	ai->cuid = estrdup(t.cuid);
-	if(dp9ik){
-		static char info[] = "Plan 9 session secret";
-		ai->nsecret = 256;
-		ai->secret = mallocz(ai->nsecret, 1);
-		hkdf_x(	crand, 2*NONCELEN,
-			(uchar*)info, sizeof(info)-1,
-			(uchar*)t.key, NONCELEN,
-			ai->secret, ai->nsecret,
-			hmac_sha2_256, SHA2_256dlen);
-	} else {
-		ai->nsecret = 8;
-		ai->secret = mallocz(ai->nsecret, 1);
-		des56to64((uchar*)t.key, ai->secret);
-	}
-
-	memset(&t, 0, sizeof(t));
-	memset(&auth, 0, sizeof(auth));
-	memset(&authkey, 0, sizeof(authkey));
-	memset(cchal, 0, sizeof(cchal));
-	memset(crand, 0, sizeof(crand));
-	free(proto);
-
-	return ai;
-}
-
-static int
-getkey(Authkey *key, char *user, char *dom, char *proto, char *pass)
-{
-	if(pass != nil && *pass)
-		pass = estrdup(pass);
-	else {
-		sysfatal("getkey: no password");
-	}
-	if(pass != nil){
-		memset(key, 0, sizeof(*key));
-		passtokey(key, pass);
-		if(strcmp(proto, "dp9ik") == 0) {
-			authpak_hash(key, user);
-		}
-		return 1;
-	}
-	return 0;
-}
--- /dev/null
+++ b/fncs.h
@@ -1,0 +1,4 @@
+int unix_dial(char*, char*);
+AuthInfo* p9any(char *user, char *pass, int fd);
+
+extern char *authserver;
--- /dev/null
+++ b/p9any.c
@@ -1,0 +1,356 @@
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+#include <u.h>
+#include <args.h>
+#include <libc.h>
+#include <auth.h>
+#include <authsrv.h>
+#include <libsec.h>
+
+#include "fncs.h"
+
+void errstr(char *s){}
+
+char*
+estrdup(char *s)
+{
+	s = strdup(s);
+	if(s == nil)
+		sysfatal("out of memory");
+	return s;
+}
+
+int
+unix_dial(char *host, char *port)
+{
+	int fd;
+	struct sockaddr_in server;
+	struct hostent *he;
+	struct in_addr **addr_list;
+
+	he = gethostbyname(host);
+	if(he == nil){
+		printf("could not resolve %s", host);
+		return -1;
+	}
+	fd = socket(AF_INET, SOCK_STREAM, 0);
+	addr_list = (struct in_addr **) he->h_addr_list;
+	server.sin_addr.s_addr = inet_addr(inet_ntoa(*addr_list[0]));
+	server.sin_family = AF_INET;
+	server.sin_port = htons(atoi(port));
+	if(connect(fd, (struct sockaddr*)&server, sizeof(server)) < 0)
+		return -1;
+	return fd;
+}
+
+static int
+getkey(Authkey *key, char *user, char *dom, char *proto, char *pass)
+{
+	if(pass != nil && *pass)
+		pass = estrdup(pass);
+	else {
+		printf("getkey: no password");
+		return 0;
+	}
+	if(pass != nil){
+		memset(key, 0, sizeof(*key));
+		passtokey(key, pass);
+		if(strcmp(proto, "dp9ik") == 0) {
+			authpak_hash(key, user);
+		}
+		return 1;
+	}
+	return 0;
+}
+
+int
+authdial(char *net, char *dom)
+{
+	return unix_dial(authserver, "567");
+}
+
+static int
+getastickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
+{
+	int asfd, rv;
+	char *dom;
+
+	dom = tr->authdom;
+	asfd = authdial(nil, dom);
+	if(asfd < 0)
+		return -1;
+	if(y != nil){
+		PAKpriv p;
+
+		rv = -1;
+		tr->type = AuthPAK;
+		if(_asrequest(asfd, tr) != 0 || write(asfd, y, PAKYLEN) != PAKYLEN)
+			goto Out;
+
+		authpak_new(&p, key, (uchar*)tbuf, 1);
+		if(write(asfd, tbuf, PAKYLEN) != PAKYLEN)
+			goto Out;
+
+		if(_asrdresp(asfd, tbuf, 2*PAKYLEN) != 2*PAKYLEN)
+			goto Out;
+	
+		memmove(y, tbuf, PAKYLEN);
+		if(authpak_finish(&p, key, (uchar*)tbuf+PAKYLEN))
+			goto Out;
+	}
+	tr->type = AuthTreq;
+	rv = _asgetticket(asfd, tr, tbuf, tbuflen);
+Out:
+	close(asfd);
+	return rv;
+}
+
+static int
+mkservertickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
+{
+	Ticket t;
+	int ret;
+
+	if(strcmp(tr->authid, tr->hostid) != 0)
+		return -1;
+	memset(&t, 0, sizeof(t));
+	ret = 0;
+	if(y != nil){
+		PAKpriv p;
+
+		t.form = 1;
+		memmove(tbuf, y, PAKYLEN);
+		authpak_new(&p, key, y, 0);
+		authpak_finish(&p, key, (uchar*)tbuf);
+	}
+	memmove(t.chal, tr->chal, CHALLEN);
+	strcpy(t.cuid, tr->uid);
+	strcpy(t.suid, tr->uid);
+	genrandom((uchar*)t.key, sizeof(t.key));
+	t.num = AuthTc;
+	ret += convT2M(&t, tbuf+ret, tbuflen-ret, key);
+	t.num = AuthTs;
+	ret += convT2M(&t, tbuf+ret, tbuflen-ret, key);
+	memset(&t, 0, sizeof(t));
+
+	return ret;
+}
+
+static int
+gettickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen)
+{
+	int ret;
+	ret = getastickets(key, tr, y, tbuf, tbuflen);
+	if(ret > 0)
+		return ret;
+	return mkservertickets(key, tr, y, tbuf, tbuflen);
+}
+
+int
+readstr(int fd, char *str, int len)
+{
+	int n;
+
+	while(len) {
+		n = read(fd, str, 1);
+		if(n < 0) 
+			return -1;
+		if(*str == '\0')
+			return 0;
+		str++;
+		len--;
+	}
+	return -1;
+}
+
+AuthInfo*
+p9any(char *user, char *pass, int fd)
+{
+	char buf[1024], buf2[1024], *bbuf, *p, *proto, *dom;
+	uchar crand[2*NONCELEN], cchal[CHALLEN], y[PAKYLEN];
+	char tbuf[2*MAXTICKETLEN+MAXAUTHENTLEN+PAKYLEN], trbuf[TICKREQLEN+PAKYLEN];
+	Authkey authkey;
+	Authenticator auth;
+	int i, n, m, v2, dp9ik;
+	Ticketreq tr;
+	Ticket t;
+	AuthInfo *ai;
+
+	if(readstr(fd, buf, sizeof buf) < 0){
+		printf("cannot read p9any negotiation");
+		return nil;
+	}
+	bbuf = buf;
+	v2 = 0;
+	if(strncmp(buf, "v.2 ", 4) == 0){
+		v2 = 1;
+		bbuf += 4;
+	}
+	dp9ik = 0;
+	proto = nil;
+	while(bbuf != nil){
+		if((p = strchr(bbuf, ' ')))
+			*p++ = 0;
+		if((dom = strchr(bbuf, '@')) == nil){
+			printf("bad p9any domain");
+			return nil;
+		}
+		*dom++ = 0;
+		if(strcmp(bbuf, "p9sk1") == 0 || strcmp(bbuf, "dp9ik") == 0){
+			proto = bbuf;
+			if(strcmp(proto, "dp9ik") == 0){
+				dp9ik = 1;
+				break;
+			}
+		}
+		bbuf = p;
+	}
+	if(proto == nil){
+		printf("server did not offer p9sk1 or dp9ik");
+		return nil;
+	}
+	proto = estrdup(proto);
+	sprint(buf2, "%s %s", proto, dom);
+	if(write(fd, buf2, strlen(buf2)+1) != strlen(buf2)+1){
+		printf("cannot write user/domain choice in p9any");
+		return nil;
+	}
+	if(v2){
+		if(readstr(fd, buf, sizeof buf) < 0){
+			printf("cannot read OK in p9any");
+			return nil;
+		}
+		if(memcmp(buf, "OK\0", 3) != 0){
+			printf("did not get OK in p9any: got %s", buf);
+			return nil;
+		}
+	}
+	genrandom(crand, 2*NONCELEN);
+	genrandom(cchal, CHALLEN);
+	if(write(fd, cchal, CHALLEN) != CHALLEN){
+		printf("cannot write p9sk1 challenge");
+		return nil;
+	}
+
+	n = TICKREQLEN;
+	if(dp9ik)
+		n += PAKYLEN;
+
+	if(readn(fd, trbuf, n) != n || convM2TR(trbuf, TICKREQLEN, &tr) <= 0){
+		printf("cannot read ticket request in p9sk1");
+		return nil;
+	}
+
+again:
+	if(!getkey(&authkey, user, tr.authdom, proto, pass)){
+		printf("no password");
+		return nil;
+	}
+
+	strecpy(tr.hostid, tr.hostid+sizeof tr.hostid, user);
+	strecpy(tr.uid, tr.uid+sizeof tr.uid, user);
+
+	if(dp9ik){
+		memmove(y, trbuf+TICKREQLEN, PAKYLEN);
+		n = gettickets(&authkey, &tr, y, tbuf, sizeof(tbuf));
+	} else {
+		n = gettickets(&authkey, &tr, nil, tbuf, sizeof(tbuf));
+	}
+	if(n <= 0){
+		printf("cannot get auth tickets in p9sk1");
+		return nil;
+	}
+
+	m = convM2T(tbuf, n, &t, &authkey);
+	if(m <= 0 || t.num != AuthTc){
+		printf("?password mismatch with auth server\n");
+		if(pass != nil && *pass){
+			printf("wrong password");
+			return nil;
+		}
+		goto again;
+	}
+	n -= m;
+	memmove(tbuf, tbuf+m, n);
+
+	if(dp9ik && write(fd, y, PAKYLEN) != PAKYLEN){
+		printf("cannot send authpak public key back");
+		return nil;
+	}
+
+	auth.num = AuthAc;
+	memmove(auth.rand, crand, NONCELEN);
+	memmove(auth.chal, tr.chal, CHALLEN);
+	m = convA2M(&auth, tbuf+n, sizeof(tbuf)-n, &t);
+	n += m;
+
+	if(write(fd, tbuf, n) != n){
+		printf("cannot send ticket and authenticator back");
+		return nil;
+	}
+
+	if((n=read(fd, tbuf, m)) != m || memcmp(tbuf, "cpu:", 4) == 0){
+		if(n <= 4){
+			printf("cannot read authenticator");
+			return nil;
+		}
+
+		/*
+		 * didn't send back authenticator:
+		 * sent back fatal error message.
+		 */
+		memmove(buf, tbuf, n);
+		i = readn(fd, buf+n, sizeof buf-n-1);
+		if(i > 0)
+			n += i;
+		buf[n] = 0;
+		printf("server says: %s", buf);
+		return nil;
+	}
+	
+	if(convM2A(tbuf, n, &auth, &t) <= 0
+	|| auth.num != AuthAs || tsmemcmp(auth.chal, cchal, CHALLEN) != 0){
+		print("?you and auth server agree about password.\n");
+		print("?server is confused.\n");
+		return nil;
+	}
+	memmove(crand+NONCELEN, auth.rand, NONCELEN);
+
+	// print("i am %s there.\n", t.suid);
+
+	ai = mallocz(sizeof(AuthInfo), 1);
+	ai->suid = estrdup(t.suid);
+	ai->cuid = estrdup(t.cuid);
+	if(dp9ik){
+		static char info[] = "Plan 9 session secret";
+		ai->nsecret = 256;
+		ai->secret = mallocz(ai->nsecret, 1);
+		hkdf_x(	crand, 2*NONCELEN,
+			(uchar*)info, sizeof(info)-1,
+			(uchar*)t.key, NONCELEN,
+			ai->secret, ai->nsecret,
+			hmac_sha2_256, SHA2_256dlen);
+	} else {
+		ai->nsecret = 8;
+		ai->secret = mallocz(ai->nsecret, 1);
+		des56to64((uchar*)t.key, ai->secret);
+	}
+
+	memset(&t, 0, sizeof(t));
+	memset(&auth, 0, sizeof(auth));
+	memset(&authkey, 0, sizeof(authkey));
+	memset(cchal, 0, sizeof(cchal));
+	memset(crand, 0, sizeof(crand));
+	free(proto);
+
+	return ai;
+}
+
--- /dev/null
+++ b/pam.c
@@ -1,0 +1,61 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+
+#include <u.h>
+#include <args.h>
+#include <libc.h>
+#include <auth.h>
+#include <authsrv.h>
+#include <libsec.h>
+
+#include "fncs.h"
+
+/* pasword change TODO */
+PAM_EXTERN int
+pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv )
+{
+	return PAM_SUCCESS;
+}
+
+/* For checking if the user has expired, has access to specific machine etc */
+PAM_EXTERN int
+pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
+{
+	return PAM_SUCCESS;
+}
+
+char *authserver;
+
+/* expected hook, this is where custom stuff happens */
+PAM_EXTERN int
+pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv )
+{
+	char *username;
+	char *password;
+	int fd;
+	AuthInfo *ai;
+
+	if(pam_get_user(pamh, &username, NULL) != PAM_SUCCESS)
+		return PAM_AUTH_ERR;
+	if(pam_get_authtok(pamh, PAM_AUTHTOK, &password, NULL) != PAM_SUCCESS)
+		return PAM_AUTH_ERR;
+
+	if(argc != 1)
+		return PAM_AUTH_ERR;
+
+	authserver = argv[0];
+
+	fd = unix_dial(authserver, "17019");
+	if(fd < 0)
+		return PAM_AUTH_ERR;
+
+	ai = p9any(username, password, fd);
+	if(ai == nil)
+		return PAM_AUTH_ERR;
+
+	return PAM_SUCCESS;
+}