1# Inferno authentication protocol 2implement Auth; 3 4include "sys.m"; 5 sys: Sys; 6 7include "keyring.m"; 8 9include "security.m"; 10 ssl: SSL; 11 12init(): string 13{ 14 if(sys == nil) 15 sys = load Sys Sys->PATH; 16 return nil; 17} 18 19server(algs: list of string, ai: ref Keyring->Authinfo, fd: ref Sys->FD, setid: int): (ref Sys->FD, string) 20{ 21 if(sys == nil) 22 sys = load Sys Sys->PATH; 23 kr := load Keyring Keyring->PATH; 24 if(kr == nil) 25 return (nil, sys->sprint("%r")); 26 27 # mutual authentication 28 (id_or_err, secret) := kr->auth(fd, ai, setid); 29 30 if(secret == nil){ 31 if(ai == nil && id_or_err == "no authentication information") 32 id_or_err = "no server certificate"; 33 return (nil, id_or_err); 34 } 35 if(0) 36 sys->fprint(sys->fildes(2), "secret is %s\n", dump(secret)); 37 38 # have got a secret, get algorithm from client 39 # check if the client algorithm is in the server algorithm list 40 # client algorithm ::= ident (' ' ident)* 41 # where ident is defined by ssl(3) 42 algbuf := string kr->getmsg(fd); 43 if(algbuf == nil) 44 return (nil, sys->sprint("can't read client ssl algorithm: %r")); 45 alg := ""; 46 (nil, calgs) := sys->tokenize(algbuf, " /"); 47 for(; calgs != nil; calgs = tl calgs){ 48 calg := hd calgs; 49 if(algs != nil){ # otherwise we suck it and see 50 for(sl := algs; sl != nil; sl = tl sl) 51 if(hd sl == calg) 52 break; 53 if(sl == nil) 54 return (nil, "unsupported client algorithm: " + calg); 55 } 56 alg += calg + " "; 57 } 58 if(alg != nil) 59 alg = alg[0:len alg - 1]; 60 61 # don't push ssl if server supports nossl 62 if(alg == nil || alg == "none") 63 return (fd, id_or_err); 64 65 # push ssl and turn on algorithms 66 ssl = load SSL SSL->PATH; 67 if(ssl == nil) 68 return (nil, sys->sprint("can't load ssl: %r")); 69 (c, err) := pushssl(fd, secret, secret, alg); 70 if(c == nil) 71 return (nil, "push ssl: " + err); 72 return (c, id_or_err); 73} 74 75client(alg: string, ai: ref Keyring->Authinfo, fd: ref Sys->FD): (ref Sys->FD, string) 76{ 77 if(sys == nil) 78 sys = load Sys Sys->PATH; 79 kr := load Keyring Keyring->PATH; 80 if(kr == nil) 81 return (nil, sys->sprint("%r")); 82 83 if(alg == nil) 84 alg = "none"; 85 86 # mutual authentication 87 (id_or_err, secret) := kr->auth(fd, ai, 0); 88 if(secret == nil) 89 return (nil, id_or_err); 90 91 # send algorithm 92 buf := array of byte alg; 93 if(kr->sendmsg(fd, buf, len buf) < 0) 94 return (nil, sys->sprint("can't send ssl algorithm: %r")); 95 96 # don't push ssl if server supports no ssl connection 97 if(alg == "none") 98 return (fd, id_or_err); 99 100 # push ssl and turn on algorithm 101 ssl = load SSL SSL->PATH; 102 if(ssl == nil) 103 return (nil, sys->sprint("can't load ssl: %r")); 104 (c, err) := pushssl(fd, secret, secret, alg); 105 if(c == nil) 106 return (nil, "push ssl: " + err); 107 return (c, id_or_err); 108} 109 110auth(ai: ref Keyring->Authinfo, keyspec: string, alg: string, fd: ref Sys->FD): (ref Sys->FD, ref Keyring->Authinfo, string) 111{ 112 if(sys == nil) 113 sys = load Sys Sys->PATH; 114 kr := load Keyring Keyring->PATH; 115 if(kr == nil) 116 return (nil, nil, sys->sprint("can't load %s: %r", Keyring->PATH)); 117 if(alg == nil) 118 alg = "none"; 119 if(ai == nil && keyspec != nil){ 120 ai = key(keyspec); 121 if(ai == nil) 122 return (nil, nil, sys->sprint("can't obtain key: %r")); 123 } 124 125 # mutual authentication 126 (id_or_err, secret) := kr->auth(fd, ai, 0); 127 if(secret == nil) 128 return (nil, nil, id_or_err); 129 130 # send algorithm 131 buf := array of byte alg; 132 if(kr->sendmsg(fd, buf, len buf) < 0) 133 return (nil, nil, sys->sprint("can't send ssl algorithm: %r")); 134 135 if(0){ # TO DO 136 hisalg := string kr->getmsg(fd); 137 if(hisalg == nil) 138 return (nil, nil, sys->sprint("can't get remote algorithm: %r")); 139 # TO DO: compare the two, sort it out if they aren't equal 140 } 141 142 # don't push ssl if server supports no ssl connection 143 if(alg == "none") 144 return (fd, nil, id_or_err); 145 146 # push ssl and turn on algorithm 147 ssl = load SSL SSL->PATH; 148 if(ssl == nil) 149 return (nil, nil, sys->sprint("can't load ssl: %r")); 150 (c, err) := pushssl(fd, secret, secret, alg); 151 if(c == nil) 152 return (nil, nil, "push ssl: " + err); 153 return (c, nil, id_or_err); 154} 155 156dump(b: array of byte): string 157{ 158 s := ""; 159 for(i := 0; i < len b; i++) 160 s += sys->sprint("%.2ux", int b[i]); 161 return s; 162} 163 164# push an SSLv2 Record Layer onto the fd 165pushssl(fd: ref Sys->FD, secretin, secretout: array of byte, alg: string): (ref Sys->FD, string) 166{ 167 (err, c) := ssl->connect(fd); 168 if(err != nil) 169 return (nil, "can't connect ssl: " + err); 170 171 err = ssl->secret(c, secretin, secretout); 172 if(err != nil) 173 return (nil, "can't write secret: " + err); 174 175 if(sys->fprint(c.cfd, "alg %s", alg) < 0) 176 return (nil, sys->sprint("can't push algorithm %s: %r", alg)); 177 178 return (c.dfd, nil); 179} 180 181key(keyspec: string): ref Keyring->Authinfo 182{ 183 f := keyfile(keyspec); 184 if(f == nil) 185 return nil; 186 kr := load Keyring Keyring->PATH; 187 if(kr == nil){ 188 sys->werrstr(sys->sprint("can't load %s: %r", Keyring->PATH)); 189 return nil; 190 } 191 return kr->readauthinfo(f); 192} 193 194# 195# look for key in old style keyring directory; 196# closest match to [net!]addr[!svc] 197# 198 199keyfile(keyspec: string): string 200{ 201 if(sys == nil) 202 sys = load Sys Sys->PATH; 203 al := parseattr(keyspec); 204 keyname := get(al, "key"); 205 if(keyname != nil){ 206 # explicit keyname overrides rest of spec 207 if(keyname[0] == '/' || 208 len keyname > 2 && keyname[0:2]=="./" || 209 len keyname > 3 && keyname[0:3]=="../") 210 return keyname; # don't add directory 211 return keydir()+keyname; 212 } 213 net := "net"; 214 svc := get(al, "service"); 215 addr := get(al, "server"); 216 (nf, flds) := sys->tokenize(addr, "!"); # compatibility 217 if(nf > 1){ 218 net = hd flds; 219 addr = hd tl flds; 220 } 221 if(addr != nil) 222 keyname = addr; 223 else 224 keyname = "default"; 225 kd := keydir(); 226 dom := get(al, "dom"); 227 if(dom != nil){ 228 if((cert := exists(kd+dom)) != nil) 229 return cert; 230 } 231 if(keyname == "default") 232 return kd+"default"; 233 if(net == "net") 234 l := "net!" :: "tcp!" :: nil; 235 else 236 l = net+"!" :: nil; 237 if(svc != nil){ 238 for(nl := l; nl != nil; nl = tl nl){ 239 cert := exists(kd+(hd nl)+keyname+"!"+svc); # most specific 240 if(cert != nil) 241 return cert; 242 } 243 } 244 for(nl := l; nl != nil; nl = tl nl){ 245 cert := exists(kd+(hd nl)+keyname); 246 if(cert != nil) 247 return cert; 248 } 249 cert := exists(kd+keyname); # unadorned 250 if(cert != nil) 251 return cert; 252 if(keyname != "default"){ 253 cert = exists(kd+"default"); 254 if(cert != nil) 255 return cert; 256 } 257 return kd+keyname; 258} 259 260keydir(): string 261{ 262 fd := sys->open("/dev/user", Sys->OREAD); 263 if(fd == nil) 264 return nil; 265 b := array[Sys->NAMEMAX] of byte; 266 nr := sys->read(fd, b, len b); 267 if(nr <= 0){ 268 sys->werrstr("can't read /dev/user"); 269 return nil; 270 } 271 user := string b[0:nr]; 272 return "/usr/" + user + "/keyring/"; 273} 274 275exists(f: string): string 276{ 277 (ok, nil) := sys->stat(f); 278 if(0)sys->fprint(sys->fildes(2), "exists: %q %d\n", f, ok>=0); 279 if(ok >= 0) 280 return f; 281 return nil; 282} 283 284Aattr, Aval, Aquery: con iota; 285 286Attr: adt { 287 tag: int; 288 name: string; 289 val: string; 290}; 291 292parseattr(s: string): list of ref Attr 293{ 294 (nil, fld) := sys->tokenize(s, " \t\n"); # should do quoting; later 295 rfld := fld; 296 for(fld = nil; rfld != nil; rfld = tl rfld) 297 fld = (hd rfld) :: fld; 298 attrs: list of ref Attr; 299 for(; fld != nil; fld = tl fld){ 300 n := hd fld; 301 a := ""; 302 tag := Aattr; 303 for(i:=0; i<len n; i++) 304 if(n[i] == '='){ 305 a = n[i+1:]; 306 n = n[0:i]; 307 tag = Aval; 308 } 309 if(len n == 0) 310 continue; 311 if(tag == Aattr && len n > 1 && n[len n-1] == '?'){ 312 tag = Aquery; 313 n = n[0:len n-1]; 314 } 315 attrs = ref Attr(tag, n, a) :: attrs; 316 } 317 return attrs; 318} 319 320get(al: list of ref Attr, n: string): string 321{ 322 for(; al != nil; al = tl al) 323 if((a := hd al).name == n && a.tag == Aval) 324 return a.val; 325 return nil; 326} 327