newns.b 9.8 KB

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