xref: /plan9/sys/src/cmd/vnc/auth.c (revision 5e4924093ecb86f7174bf23023955abc83fb6962)
19a747e4fSDavid du Colombier #include "vnc.h"
29a747e4fSDavid du Colombier #include <libsec.h>
39a747e4fSDavid du Colombier #include <auth.h>
49a747e4fSDavid du Colombier 
5b7b24591SDavid du Colombier char *serveraddr;
6b7b24591SDavid du Colombier 
79a747e4fSDavid du Colombier /*
89a747e4fSDavid du Colombier  * Encrypt n bytes using the password
99a747e4fSDavid du Colombier  * as key, padded with zeros to 8 bytes.
109a747e4fSDavid du Colombier  */
119a747e4fSDavid du Colombier enum
129a747e4fSDavid du Colombier {
139a747e4fSDavid du Colombier 	VerLen	= 12
149a747e4fSDavid du Colombier };
159a747e4fSDavid du Colombier 
169a747e4fSDavid du Colombier static char version[VerLen+1] = "RFB 003.003\n";
179a747e4fSDavid du Colombier 
189a747e4fSDavid du Colombier static uchar tab[256];
199a747e4fSDavid du Colombier 
209a747e4fSDavid du Colombier /* VNC reverses the bits of each byte before using as a des key */
219a747e4fSDavid du Colombier static void
mktab(void)229a747e4fSDavid du Colombier mktab(void)
239a747e4fSDavid du Colombier {
249a747e4fSDavid du Colombier 	int i, j, k;
259a747e4fSDavid du Colombier 	static int once;
269a747e4fSDavid du Colombier 
279a747e4fSDavid du Colombier 	if(once)
289a747e4fSDavid du Colombier 		return;
299a747e4fSDavid du Colombier 	once = 1;
309a747e4fSDavid du Colombier 
319a747e4fSDavid du Colombier 	for(i=0; i<256; i++){
329a747e4fSDavid du Colombier 		j=i;
339a747e4fSDavid du Colombier 		tab[i] = 0;
349a747e4fSDavid du Colombier 		for(k=0; k<8; k++){
359a747e4fSDavid du Colombier 			tab[i] = (tab[i]<<1) | (j&1);
369a747e4fSDavid du Colombier 			j >>= 1;
379a747e4fSDavid du Colombier 		}
389a747e4fSDavid du Colombier 	}
399a747e4fSDavid du Colombier }
409a747e4fSDavid du Colombier 
419a747e4fSDavid du Colombier static void
vncencrypt(uchar * buf,int n,char * pw)429a747e4fSDavid du Colombier vncencrypt(uchar *buf, int n, char *pw)
439a747e4fSDavid du Colombier {
449a747e4fSDavid du Colombier 	uchar *p;
459a747e4fSDavid du Colombier 	uchar key[9];
469a747e4fSDavid du Colombier 	DESstate s;
479a747e4fSDavid du Colombier 
489a747e4fSDavid du Colombier 	mktab();
499a747e4fSDavid du Colombier 	memset(key, 0, sizeof key);
509a747e4fSDavid du Colombier 	strncpy((char*)key, pw, 8);
519a747e4fSDavid du Colombier 	for(p=key; *p; p++)
529a747e4fSDavid du Colombier 		*p = tab[*p];
539a747e4fSDavid du Colombier 
549a747e4fSDavid du Colombier 	setupDESstate(&s, key, nil);
559a747e4fSDavid du Colombier 	desECBencrypt(buf, n, &s);
569a747e4fSDavid du Colombier }
579a747e4fSDavid du Colombier 
589a747e4fSDavid du Colombier static int
readln(char * prompt,char * line,int len)599a747e4fSDavid du Colombier readln(char *prompt, char *line, int len)
609a747e4fSDavid du Colombier {
619a747e4fSDavid du Colombier 	char *p;
629a747e4fSDavid du Colombier 	int fd, ctl, n, nr;
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier 	fd = open("/dev/cons", ORDWR);
659a747e4fSDavid du Colombier 	if(fd < 0)
669a747e4fSDavid du Colombier 		sysfatal("couldn't open cons");
679a747e4fSDavid du Colombier 	ctl = open("/dev/consctl", OWRITE);
689a747e4fSDavid du Colombier 	if(ctl < 0)
699a747e4fSDavid du Colombier 		sysfatal("couldn't open consctl");
709a747e4fSDavid du Colombier 	write(ctl, "rawon", 5);
719a747e4fSDavid du Colombier 	fprint(fd, "%s", prompt);
729a747e4fSDavid du Colombier 	nr = 0;
739a747e4fSDavid du Colombier 	p = line;
749a747e4fSDavid du Colombier 	for(;;){
759a747e4fSDavid du Colombier 		n = read(fd, p, 1);
769a747e4fSDavid du Colombier 		if(n < 0){
779a747e4fSDavid du Colombier 			close(fd);
789a747e4fSDavid du Colombier 			close(ctl);
799a747e4fSDavid du Colombier 			return -1;
809a747e4fSDavid du Colombier 		}
819a747e4fSDavid du Colombier 		if(n == 0 || *p == '\n' || *p == '\r'){
829a747e4fSDavid du Colombier 			*p = '\0';
839a747e4fSDavid du Colombier 			write(fd, "\n", 1);
849a747e4fSDavid du Colombier 			close(fd);
859a747e4fSDavid du Colombier 			close(ctl);
869a747e4fSDavid du Colombier 			return nr;
879a747e4fSDavid du Colombier 		}
889a747e4fSDavid du Colombier 		if(*p == '\b'){
899a747e4fSDavid du Colombier 			if(nr > 0){
909a747e4fSDavid du Colombier 				nr--;
919a747e4fSDavid du Colombier 				p--;
929a747e4fSDavid du Colombier 			}
939a747e4fSDavid du Colombier 		}else if(*p == 21){		/* cntrl-u */
949a747e4fSDavid du Colombier 			fprint(fd, "\n%s", prompt);
959a747e4fSDavid du Colombier 			nr = 0;
969a747e4fSDavid du Colombier 			p = line;
979a747e4fSDavid du Colombier 		}else{
989a747e4fSDavid du Colombier 			nr++;
999a747e4fSDavid du Colombier 			p++;
1009a747e4fSDavid du Colombier 		}
1019a747e4fSDavid du Colombier 		if(nr == len){
1029a747e4fSDavid du Colombier 			fprint(fd, "line too long; try again\n%s", prompt);
1039a747e4fSDavid du Colombier 			nr = 0;
1049a747e4fSDavid du Colombier 			p = line;
1059a747e4fSDavid du Colombier 		}
1069a747e4fSDavid du Colombier 	}
1079a747e4fSDavid du Colombier }
1089a747e4fSDavid du Colombier 
1099a747e4fSDavid du Colombier int
vncsrvhandshake(Vnc * v)110f8e525acSDavid du Colombier vncsrvhandshake(Vnc *v)
1119a747e4fSDavid du Colombier {
1129a747e4fSDavid du Colombier 	char msg[VerLen+1];
1139a747e4fSDavid du Colombier 
114f8e525acSDavid du Colombier 	strecpy(msg, msg+sizeof msg, version);
1159a747e4fSDavid du Colombier 	if(verbose)
116f8e525acSDavid du Colombier 		fprint(2, "server version: %s", msg);
1179a747e4fSDavid du Colombier 	vncwrbytes(v, msg, VerLen);
1189a747e4fSDavid du Colombier 	vncflush(v);
1199a747e4fSDavid du Colombier 
1209a747e4fSDavid du Colombier 	vncrdbytes(v, msg, VerLen);
1219a747e4fSDavid du Colombier 	if(verbose)
1229a747e4fSDavid du Colombier 		fprint(2, "client version: %s", msg);
1239a747e4fSDavid du Colombier 	return 0;
1249a747e4fSDavid du Colombier }
1259a747e4fSDavid du Colombier 
1269a747e4fSDavid du Colombier int
vnchandshake(Vnc * v)1279a747e4fSDavid du Colombier vnchandshake(Vnc *v)
1289a747e4fSDavid du Colombier {
1299a747e4fSDavid du Colombier 	char msg[VerLen+1];
1309a747e4fSDavid du Colombier 
131f8e525acSDavid du Colombier 	msg[VerLen] = 0;
1329a747e4fSDavid du Colombier 	vncrdbytes(v, msg, VerLen);
133d9306527SDavid du Colombier 	if(strncmp(msg, "RFB ", 4) != 0){
1349a747e4fSDavid du Colombier 		werrstr("bad rfb version \"%s\"", msg);
1359a747e4fSDavid du Colombier 		return -1;
1369a747e4fSDavid du Colombier 	}
1379a747e4fSDavid du Colombier 	if(verbose)
138f8e525acSDavid du Colombier 		fprint(2, "server version: %s", msg);
1399a747e4fSDavid du Colombier 	strcpy(msg, version);
1409a747e4fSDavid du Colombier 	vncwrbytes(v, msg, VerLen);
1419a747e4fSDavid du Colombier 	vncflush(v);
1429a747e4fSDavid du Colombier 	return 0;
1439a747e4fSDavid du Colombier }
1449a747e4fSDavid du Colombier 
1459a747e4fSDavid du Colombier int
vncauth(Vnc * v,char * keypattern)146*5e492409SDavid du Colombier vncauth(Vnc *v, char *keypattern)
1479a747e4fSDavid du Colombier {
1489a747e4fSDavid du Colombier 	char pw[128], *reason;
1499a747e4fSDavid du Colombier 	uchar chal[VncChalLen];
1509a747e4fSDavid du Colombier 	ulong auth;
151d9306527SDavid du Colombier 	char *p, *server;
1529a747e4fSDavid du Colombier 
153*5e492409SDavid du Colombier 	if(keypattern == nil)
154*5e492409SDavid du Colombier 		keypattern = "";
1559a747e4fSDavid du Colombier 	auth = vncrdlong(v);
1569a747e4fSDavid du Colombier 	switch(auth){
1579a747e4fSDavid du Colombier 	default:
1589a747e4fSDavid du Colombier 		werrstr("unknown auth type 0x%lux", auth);
1599a747e4fSDavid du Colombier 		if(verbose)
1609a747e4fSDavid du Colombier 			fprint(2, "unknown auth type 0x%lux", auth);
1619a747e4fSDavid du Colombier 		return -1;
1629a747e4fSDavid du Colombier 
1639a747e4fSDavid du Colombier 	case AFailed:
1649a747e4fSDavid du Colombier 		reason = vncrdstring(v);
1659a747e4fSDavid du Colombier 		werrstr("%s", reason);
1669a747e4fSDavid du Colombier 		if(verbose)
1679a747e4fSDavid du Colombier 			fprint(2, "auth failed: %s\n", reason);
1689a747e4fSDavid du Colombier 		return -1;
1699a747e4fSDavid du Colombier 
1709a747e4fSDavid du Colombier 	case ANoAuth:
1719a747e4fSDavid du Colombier 		if(verbose)
1729a747e4fSDavid du Colombier 			fprint(2, "no auth needed");
1739a747e4fSDavid du Colombier 		break;
1749a747e4fSDavid du Colombier 
1759a747e4fSDavid du Colombier 	case AVncAuth:
1769a747e4fSDavid du Colombier 		vncrdbytes(v, chal, VncChalLen);
177d9306527SDavid du Colombier 		server = strdup(serveraddr);
178d9306527SDavid du Colombier 		p = strrchr(server, ':');
179d9306527SDavid du Colombier 		if(p)
180d9306527SDavid du Colombier 			*p = 0;
181b7b24591SDavid du Colombier 		if(auth_respond(chal, VncChalLen, nil, 0, chal, VncChalLen, auth_getkey,
182*5e492409SDavid du Colombier 			"proto=vnc role=client server=%s %s", server, keypattern) != VncChalLen){
183f8e525acSDavid du Colombier 			/* BUG This is for drawterm users who don't start their own factotums */
1849a747e4fSDavid du Colombier 			readln("password: ", pw, sizeof(pw));
1859a747e4fSDavid du Colombier 			vncencrypt(chal, VncChalLen, pw);
1869a747e4fSDavid du Colombier 			memset(pw, 0, sizeof pw);
187b7b24591SDavid du Colombier 		}
188d9306527SDavid du Colombier 		free(server);
1899a747e4fSDavid du Colombier 		vncwrbytes(v, chal, VncChalLen);
1909a747e4fSDavid du Colombier 		vncflush(v);
1919a747e4fSDavid du Colombier 
1929a747e4fSDavid du Colombier 		auth = vncrdlong(v);
1939a747e4fSDavid du Colombier 		switch(auth){
1949a747e4fSDavid du Colombier 		default:
195f8e525acSDavid du Colombier 			werrstr("unknown server response 0x%lux", auth);
1969a747e4fSDavid du Colombier 			return -1;
1979a747e4fSDavid du Colombier 		case VncAuthFailed:
198f8e525acSDavid du Colombier 			werrstr("server says authentication failed");
1999a747e4fSDavid du Colombier 			return -1;
2009a747e4fSDavid du Colombier 		case VncAuthTooMany:
201f8e525acSDavid du Colombier 			werrstr("server says too many tries");
2029a747e4fSDavid du Colombier 			return -1;
2039a747e4fSDavid du Colombier 		case VncAuthOK:
2049a747e4fSDavid du Colombier 			break;
2059a747e4fSDavid du Colombier 		}
2069a747e4fSDavid du Colombier 		break;
2079a747e4fSDavid du Colombier 	}
2089a747e4fSDavid du Colombier 	return 0;
2099a747e4fSDavid du Colombier }
2109a747e4fSDavid du Colombier 
2119a747e4fSDavid du Colombier int
vncsrvauth(Vnc * v)212f8e525acSDavid du Colombier vncsrvauth(Vnc *v)
2139a747e4fSDavid du Colombier {
2149a747e4fSDavid du Colombier 	Chalstate *c;
215f8e525acSDavid du Colombier 	AuthInfo *ai;
2169a747e4fSDavid du Colombier 
217f8e525acSDavid du Colombier 	if((c = auth_challenge("proto=vnc role=server user=%q", getuser()))==nil)
218f8e525acSDavid du Colombier 		sysfatal("vncchal: %r");
219f8e525acSDavid du Colombier 	if(c->nchal != VncChalLen)
220f8e525acSDavid du Colombier 		sysfatal("vncchal got %d bytes wanted %d", c->nchal, VncChalLen);
2219a747e4fSDavid du Colombier 	vncwrlong(v, AVncAuth);
2229a747e4fSDavid du Colombier 	vncwrbytes(v, c->chal, VncChalLen);
2239a747e4fSDavid du Colombier 	vncflush(v);
2249a747e4fSDavid du Colombier 
2259a747e4fSDavid du Colombier 	vncrdbytes(v, c->chal, VncChalLen);
2269a747e4fSDavid du Colombier 	c->resp = c->chal;
2279a747e4fSDavid du Colombier 	c->nresp = VncChalLen;
2289a747e4fSDavid du Colombier 	ai = auth_response(c);
2299a747e4fSDavid du Colombier 	auth_freechal(c);
2309a747e4fSDavid du Colombier 	if(ai == nil){
231f8e525acSDavid du Colombier 		fprint(2, "vnc auth failed: server factotum: %r\n");
232f8e525acSDavid du Colombier 		vncwrlong(v, VncAuthFailed);
233f8e525acSDavid du Colombier 		vncflush(v);
2349a747e4fSDavid du Colombier 		return -1;
2359a747e4fSDavid du Colombier 	}
2369a747e4fSDavid du Colombier 	auth_freeAI(ai);
2379a747e4fSDavid du Colombier 	vncwrlong(v, VncAuthOK);
2389a747e4fSDavid du Colombier 	vncflush(v);
2399a747e4fSDavid du Colombier 
2409a747e4fSDavid du Colombier 	return 0;
2419a747e4fSDavid du Colombier }
2429a747e4fSDavid du Colombier 
243