Charles.Forsyth20060303a828770d19 years agoHistory
326 lines · 7.4 KB · Brainfuck
RawDownload
# Inferno authentication protocolimplement Auth; include "sys.m";	sys: Sys; include "keyring.m"; include "security.m";	ssl: SSL; init(): string{	if(sys == nil)		sys = load Sys Sys->PATH;	return nil;	} server(algs: list of string, ai: ref Keyring->Authinfo, fd: ref Sys->FD, setid: int): (ref Sys->FD, string){	if(sys == nil)		sys = load Sys Sys->PATH;	kr := load Keyring Keyring->PATH;	if(kr == nil)		return (nil, sys->sprint("%r")); 	# mutual authentication	(id_or_err, secret) := kr->auth(fd, ai, setid);  	if(secret == nil){		if(ai == nil && id_or_err == "no authentication information")			id_or_err = "no server certificate";		return (nil, id_or_err);	}	if(0)		sys->fprint(sys->fildes(2), "secret is %s\n", dump(secret)); 	# have got a secret, get algorithm from client	# check if the client algorithm is in the server algorithm list	# client algorithm ::= ident (' ' ident)*	# where ident is defined by ssl(3)	algbuf := string kr->getmsg(fd);	if(algbuf == nil)		return (nil, sys->sprint("can't read client ssl algorithm: %r"));	alg := "";	(nil, calgs) := sys->tokenize(algbuf, " /");	for(; calgs != nil; calgs = tl calgs){		calg := hd calgs;		if(algs != nil){	# otherwise we suck it and see			for(sl := algs; sl != nil; sl = tl sl)				if(hd sl == calg)					break;			if(sl == nil)				return (nil, "unsupported client algorithm: " + calg);		}		alg += calg + " ";	}	if(alg != nil)		alg = alg[0:len alg - 1]; 	# don't push ssl if server supports nossl	if(alg == nil || alg == "none")		return (fd, id_or_err); 	# push ssl and turn on algorithms	ssl = load SSL SSL->PATH;	if(ssl == nil)		return (nil, sys->sprint("can't load ssl: %r"));	(c, err) := pushssl(fd, secret, secret, alg);	if(c == nil)		return (nil, "push ssl: " + err);	return (c, id_or_err);} client(alg: string, ai: ref Keyring->Authinfo, fd: ref Sys->FD): (ref Sys->FD, string){	if(sys == nil)		sys = load Sys Sys->PATH;	kr := load Keyring Keyring->PATH;	if(kr == nil)		return (nil, sys->sprint("%r")); 	if(alg == nil)		alg = "none"; 	# mutual authentication	(id_or_err, secret) := kr->auth(fd, ai, 0);	if(secret == nil)		return (nil, id_or_err); 	# send algorithm	buf := array of byte alg;	if(kr->sendmsg(fd, buf, len buf) < 0)		return (nil, sys->sprint("can't send ssl algorithm: %r")); 	# don't push ssl if server supports no ssl connection	if(alg == "none")		return (fd, id_or_err); 	# push ssl and turn on algorithm	ssl = load SSL SSL->PATH;	if(ssl == nil)		return (nil, sys->sprint("can't load ssl: %r"));	(c, err) := pushssl(fd, secret, secret, alg);	if(c == nil)		return (nil, "push ssl: " + err);	return (c, id_or_err);} auth(ai: ref Keyring->Authinfo, keyspec: string, alg: string, fd: ref Sys->FD): (ref Sys->FD, ref Keyring->Authinfo, string){	if(sys == nil)		sys = load Sys Sys->PATH;	kr := load Keyring Keyring->PATH;	if(kr == nil)		return (nil, nil, sys->sprint("can't load %s: %r", Keyring->PATH));	if(alg == nil)		alg = "none";	if(ai == nil && keyspec != nil){		ai = key(keyspec);		if(ai == nil)			return (nil, nil, sys->sprint("can't obtain key: %r"));	} 	# mutual authentication	(id_or_err, secret) := kr->auth(fd, ai, 0);	if(secret == nil)		return (nil, nil, id_or_err); 	# send algorithm	buf := array of byte alg;	if(kr->sendmsg(fd, buf, len buf) < 0)		return (nil, nil, sys->sprint("can't send ssl algorithm: %r")); 	if(0){		# TO DO		hisalg := string kr->getmsg(fd);		if(hisalg == nil)			return (nil, nil, sys->sprint("can't get remote algorithm: %r"));		# TO DO: compare the two, sort it out if they aren't equal	} 	# don't push ssl if server supports no ssl connection	if(alg == "none")		return (fd, nil, id_or_err); 	# push ssl and turn on algorithm	ssl = load SSL SSL->PATH;	if(ssl == nil)		return (nil, nil, sys->sprint("can't load ssl: %r"));	(c, err) := pushssl(fd, secret, secret, alg);	if(c == nil)		return (nil, nil, "push ssl: " + err);	return (c, nil, id_or_err);} dump(b: array of byte): string{	s := "";	for(i := 0; i < len b; i++)		s += sys->sprint("%.2ux", int b[i]);	return s;} # push an SSLv2 Record Layer onto the fdpushssl(fd: ref Sys->FD, secretin, secretout: array of byte, alg: string): (ref Sys->FD, string){	(err, c) := ssl->connect(fd);	if(err != nil)		return (nil, "can't connect ssl: " + err); 	err = ssl->secret(c, secretin, secretout);	if(err != nil)		return (nil, "can't write secret: " + err); 	if(sys->fprint(c.cfd, "alg %s", alg) < 0)		return (nil, sys->sprint("can't push algorithm %s: %r", alg)); 	return (c.dfd, nil);} key(keyspec: string): ref Keyring->Authinfo{	f := keyfile(keyspec);	if(f == nil)		return nil;	kr := load Keyring Keyring->PATH;	if(kr == nil){		sys->werrstr(sys->sprint("can't load %s: %r", Keyring->PATH));		return nil;	}	return kr->readauthinfo(f);} ## look for key in old style keyring directory;# closest match to [net!]addr[!svc]# keyfile(keyspec: string): string{	if(sys == nil)		sys = load Sys Sys->PATH;	al := parseattr(keyspec);	keyname := get(al, "key");	if(keyname != nil){		# explicit keyname overrides rest of spec		if(keyname[0] == '/' ||		   len keyname > 2 && keyname[0:2]=="./" ||		   len keyname > 3 && keyname[0:3]=="../")			return keyname;	# don't add directory		return keydir()+keyname;	}	net := "net";	svc := get(al, "service");	addr := get(al, "server");	(nf, flds) := sys->tokenize(addr, "!");	# compatibility	if(nf > 1){		net = hd flds;		addr = hd tl flds;	}	if(addr != nil)		keyname = addr;	else		keyname = "default";	kd := keydir();	dom := get(al, "dom");	if(dom != nil){		if((cert := exists(kd+dom)) != nil)			return cert;	}	if(keyname == "default")		return kd+"default";	if(net == "net")		l := "net!" :: "tcp!" :: nil;	else		l = net+"!" :: nil;	if(svc != nil){		for(nl := l; nl != nil; nl = tl nl){			cert := exists(kd+(hd nl)+keyname+"!"+svc);	# most specific			if(cert != nil)				return cert;		}	}	for(nl := l; nl != nil; nl = tl nl){		cert := exists(kd+(hd nl)+keyname);		if(cert != nil)			return cert;	}	cert := exists(kd+keyname);	# unadorned	if(cert != nil)		return cert;	if(keyname != "default"){		cert = exists(kd+"default");		if(cert != nil)			return cert;	}	return kd+keyname;} keydir(): string{	fd := sys->open("/dev/user", Sys->OREAD);	if(fd == nil)		return nil;	b := array[Sys->NAMEMAX] of byte;	nr := sys->read(fd, b, len b);	if(nr <= 0){		sys->werrstr("can't read /dev/user");		return nil;	}	user := string b[0:nr];	return "/usr/" + user + "/keyring/";} exists(f: string): string{	(ok, nil) := sys->stat(f);	if(0)sys->fprint(sys->fildes(2), "exists: %q %d\n", f, ok>=0);	if(ok >= 0)		return f;	return nil;} Aattr, Aval, Aquery: con iota; Attr: adt {	tag:	int;	name:	string;	val:	string;}; parseattr(s: string): list of ref Attr{	(nil, fld) := sys->tokenize(s, " \t\n");	# should do quoting; later	rfld := fld;	for(fld = nil; rfld != nil; rfld = tl rfld)		fld = (hd rfld) :: fld;	attrs: list of ref Attr;	for(; fld != nil; fld = tl fld){		n := hd fld;		a := "";		tag := Aattr;		for(i:=0; i<len n; i++)			if(n[i] == '='){				a = n[i+1:];				n = n[0:i];				tag = Aval;			}		if(len n == 0)			continue;		if(tag == Aattr && len n > 1 && n[len n-1] == '?'){			tag = Aquery;			n = n[0:len n-1];		}		attrs = ref Attr(tag, n, a) :: attrs;	}	return attrs;} get(al: list of ref Attr, n: string): string{	for(; al != nil; al = tl al)		if((a := hd al).name == n && a.tag == Aval)			return a.val;	return nil;}