1implement Newns; 2# 3# Build a new namespace from a file 4# 5# new create a new namespace from current directory (use cd) 6# fork split the namespace before modification 7# nodev disallow device attaches 8# bind [-abrci] from to 9# mount [-abrci9] [net!]machine[!svc] to [spec] 10# import [-abrci9] [net!]machine[!svc] [remotedir] dir 11# unmount [-i] [from] to 12# cd directory 13# 14# -i to bind/mount/unmount means continue in the face of errors 15# 16include "sys.m"; 17 sys: Sys; 18 FD, FileIO: import Sys; 19 stderr: ref FD; 20 21include "draw.m"; 22 23include "bufio.m"; 24 bio: Bufio; 25 Iobuf: import bio; 26 27include "dial.m"; 28 dial: Dial; 29 Connection: import dial; 30 31include "newns.m"; 32 33#include "sh.m"; 34 35include "keyring.m"; 36 kr: Keyring; 37 38include "security.m"; 39 au: Auth; 40 41include "factotum.m"; 42 43include "arg.m"; 44 arg: Arg; 45 46include "string.m"; 47 str: String; 48 49newns(user: string, file: string): string 50{ 51 sys = load Sys Sys->PATH; 52 kr = load Keyring Keyring->PATH; 53 stderr = sys->fildes(2); 54 55 # Could do some authentication here, and bail if no good FIXME 56 if(user == nil) 57 ; 58 bio = load Bufio Bufio->PATH; 59 if(bio == nil) 60 return sys->sprint("cannot load %s: %r", Bufio->PATH); 61 62 arg = load Arg Arg->PATH; 63 if (arg == nil) 64 return sys->sprint("cannot load %s: %r", Arg->PATH); 65 66 au = load Auth Auth->PATH; 67 if(au == nil) 68 return sys->sprint("cannot load %s: %r", Auth->PATH); 69 err := au->init(); 70 if(err != nil) 71 return "Auth->init: "+err; 72 73 str = load String String->PATH; # no check, because we'll live without it 74 75 if(file == nil){ 76 file = "namespace"; 77 if(sys->stat(file).t0 < 0) 78 file = "/lib/namespace"; 79 } 80 81 mfp := bio->open(file, bio->OREAD); 82 if(mfp==nil) 83 return sys->sprint("cannot open %q: %r", file); 84 85 if(0 && user != nil){ 86 sys->pctl(Sys->FORKENV, nil); 87 setenv("user", user); 88 setenv("home", "/usr/"+user); 89 } 90 91 facfd := sys->open("/mnt/factotum/rpc", Sys->ORDWR); 92 return nsfile(mfp, facfd); 93} 94 95nsfile(b: ref Iobuf, facfd: ref Sys->FD): string 96{ 97 e := ""; 98 while((l := b.gets('\n')) != nil){ 99 if(str != nil) 100 slist := str->unquoted(l); 101 else 102 (nil, slist) = sys->tokenize(l, " \t\n\r"); # old way, in absence of String 103 if(slist == nil) 104 continue; 105 e = nsop(expand(slist), facfd); 106 if(e != "") 107 break; 108 } 109 return e; 110} 111 112expand(l: list of string): list of string 113{ 114 nl: list of string; 115 for(; l != nil; l = tl l){ 116 s := hd l; 117 for(i := 0; i < len s; i++) 118 if(s[i] == '$'){ 119 for(j := i+1; j < len s; j++) 120 if((c := s[j]) == '.' || c == '/' || c == '$') 121 break; 122 if(j > i+1){ 123 (ok, v) := getenv(s[i+1:j]); 124 if(!ok) 125 return nil; 126 s = s[0:i] + v + s[j:]; 127 i = i + len v; 128 } 129 } 130 nl = s :: nl; 131 } 132 l = nil; 133 for(; nl != nil; nl = tl nl) 134 l = hd nl :: l; 135 return l; 136} 137 138nsop(argv: list of string, facfd: ref Sys->FD): string 139{ 140 # ignore comments 141 if(argv == nil || (hd argv)[0] == '#') 142 return nil; 143 144 e := ""; 145 c := 0; 146 cmdstr := hd argv; 147 case cmdstr { 148 "." => 149 if(tl argv == nil) 150 return ".: needs a filename"; 151 nsf := hd tl argv; 152 mfp := bio->open(nsf, bio->OREAD); 153 if(mfp==nil) 154 return sys->sprint("can't open %q for read %r", nsf); 155 e = nsfile(mfp, facfd); 156 "new" => 157 c = Sys->NEWNS | Sys->FORKENV; 158 "clear" => 159 if(sys->pctl(Sys->FORKNS, nil) < 0 || 160 sys->bind("#/", "/", Sys->MREPL) < 0 || 161 sys->chdir("/") < 0 || 162 sys->pctl(Sys->NEWNS, nil) < 0) 163 return sys->sprint("%r"); 164 return nil; 165 "fork" => 166 c = Sys->FORKNS; 167 "nodev" => 168 c = Sys->NODEVS; 169 "bind" => 170 e = bind(argv); 171 "mount" => 172 e = mount(argv, facfd); 173 "unmount" => 174 e = unmount(argv); 175 "import" => 176 e = import9(argv, facfd); 177 "cd" => 178 if(len argv != 2) 179 return "cd: must have one argument"; 180 if(sys->chdir(hd tl argv) < 0) 181 return sys->sprint("%r"); 182 * => 183 e = "invalid namespace command"; 184 } 185 if(c != 0) { 186 if(sys->pctl(c, nil) < 0) 187 return sys->sprint("%r"); 188 } 189 return e; 190} 191 192Moptres: adt { 193 argv: list of string; 194 flags: int; 195 alg: string; 196 keyfile: string; 197 ignore: int; 198 use9: int; 199 doauth: int; 200}; 201 202mopt(argv: list of string): (ref Moptres, string) 203{ 204 r := ref Moptres(nil, 0, "none", nil, 0, 0, 1); 205 206 arg->init(argv); 207 while ((opt := arg->opt()) != 0) { 208 case opt { 209 'i' => r.ignore = 1; 210 'a' => r.flags |= sys->MAFTER; 211 'b' => r.flags |= sys->MBEFORE; 212 'c' => r.flags |= sys->MCREATE; 213 'r' => r.flags |= sys->MREPL; 214 'k' => 215 if((r.keyfile = arg->arg()) == nil) 216 return (nil, "mount: missing arg to -k option"); 217 'C' => 218 if((r.alg = arg->arg()) == nil) 219 return (nil, "mount: missing arg to -C option"); 220 '9' => 221 r.use9 = 1; 222 'A' => 223 r.doauth = 0; 224 * => 225 return (nil, sys->sprint("mount: bad option -%c", opt)); 226 } 227 } 228 if((r.flags & (Sys->MAFTER|Sys->MBEFORE)) == 0) 229 r.flags |= Sys->MREPL; 230 231 r.argv = arg->argv(); 232 return (r, nil); 233} 234 235bind(argv: list of string): string 236{ 237 (r, err) := mopt(argv); 238 if(err != nil) 239 return err; 240 241 if(len r.argv < 2) 242 return "bind: too few args"; 243 244 from := hd r.argv; 245 r.argv = tl r.argv; 246 todir := hd r.argv; 247 if(sys->bind(from, todir, r.flags) < 0) 248 return ig(r, sys->sprint("bind %s %s: %r", from, todir)); 249 250 return nil; 251} 252 253mount(argv: list of string, facfd: ref Sys->FD): string 254{ 255 fd: ref Sys->FD; 256 257 (r, err) := mopt(argv); 258 if(err != nil) 259 return err; 260 261 if(len r.argv < 2) 262 return ig(r, "mount: too few args"); 263 264 if(dial == nil){ 265 dial = load Dial Dial->PATH; 266 if(dial == nil) 267 return ig(r, "mount: can't load Dial"); 268 } 269 270 addr := hd r.argv; 271 r.argv = tl r.argv; 272 dest := dial->netmkaddr(addr, "net", "styx"); 273 dir := hd r.argv; 274 r.argv = tl r.argv; 275 if(r.argv != nil) 276 spec := hd r.argv; 277 278 c := dial->dial(dest, nil); 279 if(c == nil) 280 return ig(r, sys->sprint("dial: %s: %r", dest)); 281 282 if(r.doauth != 1){ 283 if(sys->mount(c.dfd, nil, dir, r.flags, spec) < 0) 284 return ig(r, sys->sprint("mount %q %q: %r", addr, dir)); 285 return nil; 286 } 287 288 if(r.use9){ 289 factotum := load Factotum Factotum->PATH; 290 if(factotum == nil) 291 return ig(r, sys->sprint("cannot load %s: %r", Factotum->PATH)); 292 factotum->init(); 293 afd := sys->fauth(fd, spec); 294 if(afd != nil) 295 factotum->proxy(afd, facfd, "proto=p9any role=client"); # ignore result; if it fails, mount will fail 296 if(sys->mount(fd, afd, dir, r.flags, spec) < 0) 297 return ig(r, sys->sprint("mount %q %q: %r", addr, dir)); 298 return nil; 299 } 300 301 user := user(); 302 kd := "/usr/" + user + "/keyring/"; 303 cert: string; 304 if (r.keyfile != nil) { 305 cert = r.keyfile; 306 if (cert[0] != '/') 307 cert = kd + cert; 308 if(sys->stat(cert).t0 < 0) 309 return ig(r, sys->sprint("cannot find certificate %q: %r", cert)); 310 } else { 311 cert = kd + addr; 312 if(sys->stat(cert).t0 < 0) 313 cert = kd + "default"; 314 } 315 ai := kr->readauthinfo(cert); 316 if(ai == nil) 317 return ig(r, sys->sprint("cannot read certificate from %q: %r", cert)); 318 319 err = au->init(); 320 if (err != nil) 321 return ig(r, sys->sprint("auth->init: %r")); 322 (fd, err) = au->client(r.alg, ai, c.dfd); 323 if(fd == nil) 324 return ig(r, sys->sprint("auth: %r")); 325 326 if(sys->mount(fd, nil, dir, r.flags, spec) < 0) 327 return ig(r, sys->sprint("mount %q %q: %r", addr, dir)); 328 329 return nil; 330} 331 332import9(argv: list of string, facfd: ref Sys->FD): string 333{ 334 (r, err) := mopt(argv); 335 if(err != nil) 336 return err; 337 338 if(len r.argv < 2) 339 return "import: too few args"; 340 if(facfd == nil) 341 return ig(r, "import: no factotum"); 342 factotum := load Factotum Factotum->PATH; 343 if(factotum == nil) 344 return ig(r, sys->sprint("cannot load %s: %r", Factotum->PATH)); 345 factotum->init(); 346 addr := hd r.argv; 347 r.argv = tl r.argv; 348 rdir := hd r.argv; 349 r.argv = tl r.argv; 350 dir := rdir; 351 if(r.argv != nil) 352 dir = hd r.argv; 353 354 if(dial == nil){ 355 dial = load Dial Dial->PATH; 356 if(dial == nil) 357 return ig(r, "import: can't load Dial"); 358 } 359 360 dest := dial->netmkaddr(addr, "net", "17007"); # exportfs; might not be in inferno's ndb yet 361 c := dial->dial(dest, nil); 362 if(c == nil) 363 return ig(r, sys->sprint("import: %s: %r", dest)); 364 fd := c.dfd; 365 if(factotum->proxy(fd, facfd, "proto=p9any role=client") == nil) 366 return ig(r, sys->sprint("import: %s: %r", dest)); 367 if(sys->fprint(fd, "%s", rdir) < 0) 368 return ig(r, sys->sprint("import: %s: %r", dest)); 369 buf := array[256] of byte; 370 if((n := sys->read(fd, buf, len buf)) != 2 || buf[0] != byte 'O' || buf[1] != byte 'K'){ 371 if(n >= 4) 372 sys->werrstr(string buf[0:n]); 373 return ig(r, sys->sprint("import: %s: %r", dest)); 374 } 375 # TO DO: new style: impo aan|nofilter clear|ssl|tls\n 376 afd := sys->fauth(fd, ""); 377 if(afd != nil) 378 factotum->proxy(afd, facfd, "proto=p9any role=client"); 379 if(sys->mount(fd, afd, dir, r.flags, "") < 0) 380 return ig(r, sys->sprint("import %q %q: %r", addr, dir)); 381 return nil; 382} 383 384unmount(argv: list of string): string 385{ 386 (r, err) := mopt(argv); 387 if(err != nil) 388 return err; 389 390 from, tu: string; 391 case len r.argv { 392 * => 393 return "unmount: takes 1 or 2 args"; 394 1 => 395 from = nil; 396 tu = hd r.argv; 397 2 => 398 from = hd r.argv; 399 tu = hd tl r.argv; 400 } 401 402 if(sys->unmount(from, tu) < 0) 403 return ig(r, sys->sprint("unmount: %r")); 404 405 return nil; 406} 407 408ig(r: ref Moptres, e: string): string 409{ 410 if(r.ignore) 411 return nil; 412 return e; 413} 414 415user(): string 416{ 417 sys = load Sys Sys->PATH; 418 419 fd := sys->open("/dev/user", sys->OREAD); 420 if(fd == nil) 421 return ""; 422 423 buf := array[Sys->NAMEMAX] of byte; 424 n := sys->read(fd, buf, len buf); 425 if(n < 0) 426 return ""; 427 428 return string buf[0:n]; 429} 430 431getenv(name: string): (int, string) 432{ 433 fd := sys->open("#e/"+name, Sys->OREAD); 434 if(fd == nil) 435 return (0, nil); 436 b := array[256] of byte; 437 n := sys->read(fd, b, len b); 438 if(n <= 0) 439 return (1, ""); 440 for(i := 0; i < n; i++) 441 if(b[i] == byte 0 || b[i] == byte '\n') 442 break; 443 return (1, string b[0:i]); 444} 445 446setenv(name: string, val: string) 447{ 448 fd := sys->create("#e/"+name, Sys->OWRITE, 8r664); 449 if(fd != nil) 450 sys->fprint(fd, "%s", val); 451} 452 453newuser(user: string, cap: string, nsfile: string): string 454{ 455 if(cap == nil) 456 return "no capability"; 457 458 sys = load Sys Sys->PATH; 459 fd := sys->open("#¤/capuse", Sys->OWRITE); 460 if(fd == nil) 461 return sys->sprint("opening #¤/capuse: %r"); 462 463 b := array of byte cap; 464 if(sys->write(fd, b, len b) < 0) 465 return sys->sprint("writing %s to #¤/capuse: %r", cap); 466 467 # mount factotum as new user (probably unhelpful if not factotum owner) 468 sys->unmount(nil, "/mnt/factotum"); 469 sys->bind("#sfactotum", "/mnt/factotum", Sys->MREPL); 470 471 return newns(user, nsfile); 472} 473