# Inferno authentication protocol implement Auth; include "sys.m"; sys: Sys; include "keyring.m"; kr: Keyring; include "security.m"; ssl: SSL; init(): string { if(sys == nil) sys = load Sys Sys->PATH; if(kr == nil) kr = load Keyring Keyring->PATH; ssl = load SSL SSL->PATH; if(ssl == nil) return sys->sprint("can't load ssl: %r"); return nil; } server(algs: list of string, ai: ref Keyring->Authinfo, fd: ref Sys->FD, setid: int): (ref Sys->FD, string) { # doing mutual authentication (id_or_err, secret) := kr->auth(fd, ai, setid); # get algorithm from client algbuf := string kr->getmsg(fd); if(algbuf == nil) return (nil, sys->sprint("auth server: can't get alg: %r")); # check if the client algorithm is in the server algorithm list # client algorithm ::= ident ( ident)* # where ident is defined by /services/server/config # build an algorithm string to initialize SSL with nobody := 0; for(sl := algs; sl != nil; sl = tl sl) if(hd sl == "nobody") nobody = 1; # server allows unauthenticated mount as `nobody' alg := ""; (nil, calgs) := sys->tokenize(algbuf, " /"); for(; calgs != nil; calgs = tl calgs){ calg := hd calgs; for(sl = algs; sl != nil; sl = tl sl) if(hd sl == calg) break; if(sl == nil) return (nil, "auth server: unknown client algorithm: " + calg); alg += calg + " "; } if(alg != nil) alg = alg[0:len alg - 1]; if(secret == nil) { if (ai == nil) id_or_err = "no server certificate"; else if(setid && nobody && alg == "none" && id_or_err=="remote: no authentication information") return (fd, "auth server: " + id_or_err); return (nil, "auth server: " + id_or_err); } # don't go further if no Authinfo if(ai == nil) return (nil, "auth server: no server authinfo"); # don't push ssl if server supports nossl if(alg == nil || alg == "nossl" || alg == "none") return (fd, id_or_err); # push ssl and turn on algorithms (c, err) := pushssl(fd, secret, secret, alg); if(c == nil) return (nil, "auth server: " + err); return (c, id_or_err); } # negotiate level of security by sending alg client(alg: string, ai: ref Keyring->Authinfo, fd: ref Sys->FD): (ref Sys->FD, string) { if(alg == nil) alg = "none"; # mutual authentication (id_or_err, secret) := kr->auth(fd, ai, 0); # sending algorithm buf := array of byte alg; if(kr->sendmsg(fd, buf, len buf) < 0) return (nil, sys->sprint("auth client: send alg failed: %r")); if(secret == nil && alg != "none") return (nil, "auth client: " + id_or_err); # don't push ssl if server supports no ssl connection if(alg == "nossl" || alg == "none") return (fd, id_or_err); # push ssl and turn on algorithm (c, err) := pushssl(fd, secret, secret, alg); if(c == nil) return (nil, "auth client: " + err); return (c, id_or_err); } # push an SSLv2 Record Layer onto the fd pushssl (fd: ref Sys->FD, secretin, secretout: array of byte, alg: string): (ref Sys->FD, string) { if(sys->bind("#D", "/n/ssl", Sys->MREPL) < 0) return (nil, sys->sprint("can't bind #D: %r")); (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); }