1implement Factotum; 2 3# 4# client interface to factotum 5# 6# this is a near transliteration of Plan 9 code, subject to the Lucent Public License 1.02 7# 8 9include "sys.m"; 10 sys: Sys; 11 12include "string.m"; 13 14include "factotum.m"; 15 16debug := 0; 17 18init() 19{ 20 sys = load Sys Sys->PATH; 21} 22 23setdebug(i: int) 24{ 25 debug = i; 26} 27 28getaia(a: array of byte, n: int): (int, array of byte) 29{ 30 if(len a - n < 2) 31 return (-1, nil); 32 c := (int a[n+1]<<8) | int a[n+0]; 33 n += 2; 34 if(len a - n < c) 35 return (-1, nil); 36 b := array[c] of byte; # could avoid copy if known not to alias 37 b[0:] = a[n: n+c]; 38 return (n+c, b); 39} 40 41getais(a: array of byte, n: int): (int, string) 42{ 43 (n, a) = getaia(a, n); 44 return (n, string a); 45} 46 47Authinfo.unpack(a: array of byte): (int, ref Authinfo) 48{ 49 ai := ref Authinfo; 50 n: int; 51 (n, ai.cuid) = getais(a, 0); 52 (n, ai.suid) = getais(a, n); 53 (n, ai.cap) = getais(a, n); 54 (n, ai.secret) = getaia(a, n); 55 if(n < 0) 56 return (-1, nil); 57 return (n, ai); 58} 59 60open(): ref Sys->FD 61{ 62 return sys->open("/mnt/factotum/rpc", Sys->ORDWR); 63} 64 65mount(fd: ref Sys->FD, mnt: string, flags: int, aname: string, keyspec: string): (int, ref Authinfo) 66{ 67 ai: ref Authinfo; 68 afd := sys->fauth(fd, aname); 69 if(debug && afd == nil){ 70 sys->print("fauth %s: %r\n", aname); 71 return (-1, nil); 72 } 73 if(afd != nil){ 74 ai = proxy(afd, open(), "proto=p9any role=client "+keyspec); 75 if(debug && ai == nil){ 76 sys->print("proxy failed: %r\n"); 77 return (-1, nil); 78 } 79 } 80 return (sys->mount(fd, afd, mnt, flags, aname), ai); 81} 82 83dump(a: array of byte): string 84{ 85 s := sys->sprint("[%d]", len a); 86 for(i := 0; i < len a; i++){ 87 c := int a[i]; 88 if(c >= ' ' && c <= 16r7E) 89 s += sys->sprint("%c", c); 90 else 91 s += sys->sprint("\\x%.2ux", c); 92 } 93 return s; 94} 95 96verbof(buf: array of byte): (string, array of byte) 97{ 98 n := len buf; 99 for(i:=0; i<n && buf[i] != byte ' '; i++) 100 ; 101 s := string buf[0:i]; 102 if(i < n) 103 i++; 104 buf = buf[i:]; 105 case s { 106 "ok" or "error" or "done" or "phase" or 107 "protocol" or "needkey" or "toosmall" or "internal" => 108 return (s, buf); 109 * => 110 sys->werrstr(sys->sprint("malformed rpc response: %q", s)); 111 return ("rpc failure", buf); 112 } 113} 114 115dorpc(fd: ref Sys->FD, verb: string, val: array of byte): (string, array of byte) 116{ 117 (o, a) := rpc(fd, verb, val); 118 if(o != "needkey" && o != "badkey") 119 return (o, a); 120 return ("no key", a); # don't know how to get key 121} 122 123rpc(afd: ref Sys->FD, verb: string, a: array of byte): (string, array of byte) 124{ 125 va := array of byte verb; 126 l := len va; 127 na := len a; 128 if(na+l+1 > AuthRpcMax){ 129 sys->werrstr("rpc too big"); 130 return ("toobig", nil); 131 } 132 buf := array[na+l+1] of byte; 133 buf[0:] = va; 134 buf[l] = byte ' '; 135 buf[l+1:] = a; 136 if(debug) 137 sys->print("rpc: ->%s %s\n", verb, dump(a)); 138 if((n:=sys->write(afd, buf, len buf)) != len buf){ 139 if(n >= 0) 140 sys->werrstr("rpc short write"); 141 return ("rpc failure", nil); 142 } 143 buf = array[AuthRpcMax] of byte; 144 if((n=sys->read(afd, buf, len buf)) < 0){ 145 if(debug) 146 sys->print("<- (readerr) %r\n"); 147 return ("rpc failure", nil); 148 } 149 if(n < len buf) 150 buf[n] = byte 0; 151 buf = buf[0:n]; 152 153 # 154 # Set error string for good default behavior. 155 # 156 s: string; 157 (t, r) := verbof(buf); 158 if(debug) 159 sys->print("<- %s %#q\n", t, dump(r)); 160 case t { 161 "ok" or 162 "rpc failure" => 163 ; # don't touch 164 "error" => 165 if(len r == 0) 166 s = "unspecified rpc error"; 167 else 168 s = sys->sprint("%s", string r); 169 "needkey" => 170 s = sys->sprint("needkey %s", string r); 171 "badkey" => 172 (nf, flds) := sys->tokenize(string r, "\n"); 173 if(nf < 2) 174 s = sys->sprint("badkey %q", string r); 175 else 176 s = sys->sprint("badkey %q", hd tl flds); 177 break; 178 "phase" => 179 s = sys->sprint("phase error: %q", string r); 180 * => 181 s = sys->sprint("unknown rpc type %q (bug in rpc.c)", t); 182 } 183 if(s != nil) 184 sys->werrstr(s); 185 return (t, r); 186} 187 188Authinfo.read(fd: ref Sys->FD): ref Authinfo 189{ 190 (o, a) := rpc(fd, "authinfo", nil); 191 if(o != "ok") 192 return nil; 193 (n, ai) := Authinfo.unpack(a); 194 if(n <= 0) 195 sys->werrstr("bad auth info from factotum"); 196 return ai; 197} 198 199proxy(fd: ref Sys->FD, afd: ref Sys->FD, params: string): ref Authinfo 200{ 201 readc := chan of (array of byte, chan of (int, string)); 202 writec := chan of (array of byte, chan of (int, string)); 203 donec := chan of (ref Authinfo, string); 204 spawn genproxy(readc, writec, donec, afd, params); 205 for(;;)alt{ 206 (buf, reply) := <-readc => 207 n := sys->read(fd, buf, len buf); 208 if(n == -1) 209 reply <-= (-1, sys->sprint("%r")); 210 else 211 reply <-= (n, nil); 212 (buf, reply) := <-writec => 213 n := sys->write(fd, buf, len buf); 214 if(n == -1) 215 reply <-= (-1, sys->sprint("%r")); 216 else 217 reply <-= (n, nil); 218 (authinfo, err) := <-donec => 219 if(authinfo == nil) 220 sys->werrstr(err); 221 return authinfo; 222 } 223} 224 225# 226# do what factotum says 227# 228genproxy( 229 readc: chan of (array of byte, chan of (int, string)), 230 writec: chan of (array of byte, chan of (int, string)), 231 donec: chan of (ref Authinfo, string), 232 afd: ref Sys->FD, 233 params: string) 234{ 235 if(afd == nil){ 236 donec <-= (nil, "no authentication fd"); 237 return; 238 } 239 240 pa := array of byte params; 241 (o, a) := dorpc(afd, "start", pa); 242 if(o != "ok"){ 243 donec <-= (nil, sys->sprint("proxy start: %r")); 244 return; 245 } 246 247 ai: ref Authinfo; 248 err: string; 249done: 250 for(;;){ 251 (o, a) = dorpc(afd, "read", nil); 252 case o { 253 "done" => 254 if(len a > 0 && a[0] == byte 'h' && string a == "haveai") 255 ai = Authinfo.read(afd); 256 else 257 ai = ref Authinfo; # auth succeeded but empty authinfo 258 break done; 259 "ok" => 260 writec <-= (a[0:len a], reply := chan of (int, string)); 261 (n, e) := <-reply; 262 if(n != len a){ 263 err = "proxy write fd: "+e; 264 break done; 265 } 266 "phase" => 267 buf := array[AuthRpcMax] of {* => byte 0}; 268 n := 0; 269 for(;;){ 270 (o, a) = dorpc(afd, "write", buf[0:n]); 271 if(o != "toosmall") 272 break; 273 c := int string a; 274 if(c > AuthRpcMax) 275 break; 276 readc <-= (buf[n:c], reply := chan of (int, string)); 277 (m, e) := <-reply; 278 if(m <= 0){ 279 err = e; 280 if(m == 0) 281 err = sys->sprint("proxy short read"); 282 break done; 283 } 284 n += m; 285 } 286 if(o != "ok"){ 287 err = sys->sprint("proxy rpc write: %r"); 288 break done; 289 } 290 * => 291 err = sys->sprint("proxy rpc: %r"); 292 break done; 293 } 294 } 295 donec <-= (ai, err); 296} 297 298# 299# insecure passwords, role=client 300# 301 302getuserpasswd(keyspec: string): (string, string) 303{ 304 str := load String String->PATH; 305 if(str == nil) 306 return (nil, nil); 307 fd := open(); 308 if(fd == nil) 309 return (nil, nil); 310 if(((o, a) := dorpc(fd, "start", array of byte keyspec)).t0 != "ok" || 311 ((o, a) = dorpc(fd, "read", nil)).t0 != "ok"){ 312 sys->werrstr("factotum: "+o); 313 return (nil, nil); 314 } 315 flds := str->unquoted(string a); 316 if(len flds != 2){ 317 sys->werrstr("odd response from factotum"); 318 return (nil, nil); 319 } 320 return (hd flds, hd tl flds); 321} 322 323# 324# challenge/response, role=server 325# 326 327challenge(keyspec: string): ref Challenge 328{ 329 c := ref Challenge; 330 if((c.afd = open()) == nil) 331 return nil; 332 if(rpc(c.afd, "start", array of byte keyspec).t0 != "ok") 333 return nil; 334 (w, val) := rpc(c.afd, "read", nil); 335 if(w != "ok") 336 return nil; 337 c.chal = string val; 338 return c; 339} 340 341response(c: ref Challenge, resp: string): ref Authinfo 342{ 343 if(c.afd == nil){ 344 sys->werrstr("auth_response: connection not open"); 345 return nil; 346 } 347 if(resp == nil){ 348 sys->werrstr("auth_response: nil response"); 349 return nil; 350 } 351 352 if(c.user != nil){ 353 if(rpc(c.afd, "write", array of byte c.user).t0 != "ok"){ 354 # we're out of phase with factotum; give up 355 c.afd = nil; 356 return nil; 357 } 358 } 359 360 if(rpc(c.afd, "write", array of byte resp).t0 != "ok"){ 361 # don't close the connection; we might try again 362 return nil; 363 } 364 365 (w, val) := rpc(c.afd, "read", nil); 366 if(w != "done"){ 367 sys->werrstr(sys->sprint("unexpected factotum reply: %q %q", w, string val)); 368 c.afd = nil; 369 return nil; 370 } 371 ai := Authinfo.read(c.afd); 372 c.afd = nil; 373 return ai; 374} 375 376# 377# challenge/response, role=client 378# 379 380respond(chal: string, keyspec: string): (string, string) 381{ 382 if((afd := open()) == nil) 383 return (nil, nil); 384 385 if(dorpc(afd, "start", array of byte keyspec).t0 != "ok" || 386 dorpc(afd, "write", array of byte chal).t0 != "ok") 387 return (nil, nil); 388 (o, resp) := dorpc(afd, "read", nil); 389 if(o != "ok") 390 return (nil, nil); 391 392 return (string resp, findattrval(rpcattrs(afd), "user")); 393} 394 395rpcattrs(afd: ref Sys->FD): list of ref Attr 396{ 397 (o, a) := rpc(afd, "attr", nil); 398 if(o != "ok") 399 return nil; 400 return parseattrs(string a); 401} 402 403# 404# attributes 405# 406 407parseattrs(s: string): list of ref Attr 408{ 409 str := load String String->PATH; 410 fld := str->unquoted(s); 411 rfld := fld; 412 for(fld = nil; rfld != nil; rfld = tl rfld) 413 fld = (hd rfld) :: fld; 414 attrs: list of ref Attr; 415 for(; fld != nil; fld = tl fld){ 416 n := hd fld; 417 a := ""; 418 tag := Aattr; 419 for(i:=0; i<len n; i++) 420 if(n[i] == '='){ 421 a = n[i+1:]; 422 n = n[0:i]; 423 tag = Aval; 424 } 425 if(len n == 0) 426 continue; 427 if(tag == Aattr && len n > 1 && n[len n-1] == '?'){ 428 tag = Aquery; 429 n = n[0:len n-1]; 430 } 431 attrs = ref Attr(tag, n, a) :: attrs; 432 } 433 # TO DO: eliminate answered queries 434 return attrs; 435} 436 437Attr.text(a: self ref Attr): string 438{ 439 case a.tag { 440 Aattr => 441 return a.name; 442 Aval => 443 return sys->sprint("%q=%q", a.name, a.val); 444 Aquery => 445 return sys->sprint("%q?", a.name); 446 * => 447 return "??"; 448 } 449} 450 451attrtext(attrs: list of ref Attr): string 452{ 453 s := ""; 454 for(; attrs != nil; attrs = tl attrs){ 455 if(s != nil) 456 s[len s] = ' '; 457 s += (hd attrs).text(); 458 } 459 return s; 460} 461 462findattr(attrs: list of ref Attr, n: string): ref Attr 463{ 464 for(; attrs != nil; attrs = tl attrs) 465 if((a := hd attrs).tag != Aquery && a.name == n) 466 return a; 467 return nil; 468} 469 470findattrval(attrs: list of ref Attr, n: string): string 471{ 472 if((a := findattr(attrs, n)) != nil) 473 return a.val; 474 return nil; 475} 476 477delattr(l: list of ref Attr, n: string): list of ref Attr 478{ 479 rl: list of ref Attr; 480 for(; l != nil; l = tl l) 481 if((hd l).name != n) 482 rl = hd l :: rl; 483 return rev(rl); 484} 485 486copyattrs(l: list of ref Attr): list of ref Attr 487{ 488 rl: list of ref Attr; 489 for(; l != nil; l = tl l) 490 rl = hd l :: rl; 491 return rev(rl); 492} 493 494takeattrs(l: list of ref Attr, names: list of string): list of ref Attr 495{ 496 rl: list of ref Attr; 497 for(; l != nil; l = tl l){ 498 n := (hd l).name; 499 for(nl := names; nl != nil; nl = tl nl) 500 if((hd nl) == n){ 501 rl = hd l :: rl; 502 break; 503 } 504 } 505 return rev(rl); 506} 507 508publicattrs(l: list of ref Attr): list of ref Attr 509{ 510 rl: list of ref Attr; 511 for(; l != nil; l = tl l){ 512 a := hd l; 513 if(a.tag != Aquery || a.val == nil) 514 rl = a :: rl; 515 } 516 return rev(rl); 517} 518 519rev[T](l: list of T): list of T 520{ 521 rl: list of T; 522 for(; l != nil; l = tl l) 523 rl = hd l :: rl; 524 return rl; 525} 526