/* * exportfs - Export a plan 9 name space across a network */ #include #include #include #include #include enum { Encnone, Encssl, Enctls, }; #define DEBUG if(dbg)fprint #define DFD 2 int dbg; int netfd; int srvfd = -1; int nonone = 1; char *filterp; char *ealgs = "rc4_256 sha1"; char *aanfilter = "/bin/aan"; int encproto = Encnone; static void mksecret(char *, uchar *); static int localread9pmsg(int, void *, uint, ulong *); static char *anstring = "tcp!*!0"; int filter(int, char *); void fatal(char*, ...); void copylog(int, int, char*); int export(int, char*, int); void usage(void) { fprint(2, "usage: %s [-ads] [-f dbgfile] [-m msize] [-r root] [-S srvfile] [-e 'crypt hash'] [-A announce-string]\n", argv0); fprint(2, " %s -B address\n", argv0); fatal("usage"); } void main(int argc, char **argv) { char buf[ERRMAX], ebuf[ERRMAX]; int n, fd; char *dbfile, *srv, *file, *na, *nsfile, *keyspec; AuthInfo *ai; ulong initial; int negotiate; dbfile = "/tmp/exportdb"; srv = nil; srvfd = -1; na = nil; nsfile = nil; keyspec = ""; netfd = 0; dup(2, 1); negotiate = 1; ai = nil; fmtinstall('F', fcallfmt); fmtinstall('D', dirmodefmt); fmtinstall('H', encodefmt); ARGBEGIN{ case 'a': /* * We use p9any so we don't have to visit this code again, with the * cost that this code is incompatible with the old world, which * requires p9sk2. (The two differ in who talks first, so compatibility * is awkward.) */ ai = auth_proxy(0, auth_getkey, "proto=p9any role=server %s", keyspec); if(ai == nil) fatal("auth_proxy: %r"); if(nonone && strcmp(ai->cuid, "none") == 0) fatal("exportfs by none disallowed"); if(auth_chuid(ai, nsfile) < 0) fatal("auth_chuid: %r"); putenv("service", "exportfs"); break; case 'k': keyspec = EARGF(usage()); break; case 'e': ealgs = ARGF(); if(ealgs == nil) usage(); if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) ealgs = nil; break; case 'S': if(srvfd != -1) usage(); file = EARGF(usage()); if((srvfd = open(file, ORDWR)) < 0) sysfatal("open '%s': %r", file); break; case 'd': dbg++; break; case 'f': dbfile = EARGF(usage()); break; case 'n': nonone = 0; break; case 'N': nsfile = EARGF(usage()); break; case 'r': srv = EARGF(usage()); break; case 's': srv = "/"; break; case 'A': anstring = EARGF(usage()); break; case 'Z': negotiate = 0; break; default: usage(); }ARGEND USED(argc, argv); if(na){ if(srv == nil) sysfatal("-B requires -s"); if((fd = dial(netmkaddr(na, 0, "importfs"), 0, 0, 0)) < 0) sysfatal("can't dial %s: %r", na); ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client %s", keyspec); if(ai == nil) sysfatal("%r: %s", na); dup(fd, 0); dup(fd, 1); close(fd); } if(dbg){ n = create(dbfile, OWRITE|OTRUNC, 0666); dup(n, 2); dup(2, 1); if(n > 2) close(n); } if(srvfd >= 0 && srv){ fprint(2, "exportfs: -S cannot be used with -r or -s\n"); usage(); } DEBUG(DFD, "exportfs: started\n"); rfork(RFNOTEG); /* * Get tree to serve from network connection, * check we can get there and ack the connection */ if(srvfd != -1) { /* do nothing */ } else if(srv) { chdir(srv); DEBUG(DFD, "invoked as server for %s", srv); strncpy(buf, srv, sizeof buf); } else { buf[0] = 0; n = read(0, buf, sizeof(buf)-1); if(n < 0) { errstr(buf, sizeof buf); fprint(0, "read(0): %s", buf); DEBUG(DFD, "read(0): %s", buf); exits(buf); } buf[n] = 0; if(chdir(buf) < 0) { errstr(ebuf, sizeof ebuf); fprint(0, "chdir(%d:\"%s\"): %s", n, buf, ebuf); DEBUG(DFD, "chdir(%d:\"%s\"): %s", n, buf, ebuf); exits(ebuf); } } DEBUG(DFD, "exportfs: %s\n", buf); if(srv == nil && srvfd == -1 && write(0, "OK", 2) != 2) fatal("open ack write"); if(!negotiate) goto serve; if(readn(netfd, &initial, sizeof(ulong)) < sizeof(ulong)) fatal("can't read initial string: %r\n"); if (strncmp((char *)&initial, "impo", sizeof(ulong)) != 0) fatal("cannot negotiate"); { char buf[128], *p, *args[3]; // New import. Read import's parameters... initial = 0; p = buf; while (p - buf < sizeof buf) { if ((n = read(netfd, p, 1)) < 0) fatal("can't read impo arguments: %r\n"); if (n == 0) fatal("connection closed while reading arguments\n"); if (*p == '\n') *p = '\0'; if (*p++ == '\0') break; } if (tokenize(buf, args, nelem(args)) != 2) fatal("impo arguments invalid: impo%s...\n", buf); if (!strcmp(args[0], "aan")) filterp = aanfilter; else if (strcmp(args[0], "nofilter")) fatal("import filter argument unsupported: %s\n", args[0]); if (!strcmp(args[1], "ssl")) encproto = Encssl; else if (!strcmp(args[1], "tls")) encproto = Enctls; else if (strcmp(args[1], "clear")) fatal("import encryption proto unsupported: %s\n", args[1]); if (encproto == Enctls) sysfatal("%s: tls has not yet been implemented\n", argv[0]); } if (encproto != Encnone && ealgs && ai) { uchar key[16]; uchar digest[SHA1dlen]; char fromclientsecret[21]; char fromserversecret[21]; int i; memmove(key+4, ai->secret, ai->nsecret); /* exchange random numbers */ srand(truerand()); for(i = 0; i < 4; i++) key[i+12] = rand(); if (initial) fatal("Protocol botch: old import\n"); if(readn(netfd, key, 4) != 4) fatal("can't read key part; %r\n"); if(write(netfd, key+12, 4) != 4) fatal("can't write key part; %r\n"); /* scramble into two secrets */ sha1(key, sizeof(key), digest, nil); mksecret(fromclientsecret, digest); mksecret(fromserversecret, digest+10); if (filterp) netfd = filter(netfd, filterp); switch (encproto) { case Encssl: netfd = pushssl(netfd, ealgs, fromserversecret, fromclientsecret, nil); break; case Enctls: default: fatal("Unsupported encryption protocol\n"); } if(netfd < 0) fatal("can't establish ssl connection: %r"); } else if (filterp) { if (initial) fatal("Protocol botch: don't know how to deal with this\n"); netfd = filter(netfd, filterp); } serve: if(dbg){ int p[2]; pipe(p); switch(fork()){ case -1: fatal("fork"); case 0: close(p[0]); switch(fork()){ case -1: fatal("fork"); case 0: fprint(2, "%d: copylog\n", getpid()); copylog(netfd, p[1], ""); postnote(PNGROUP, getpid(), "die"); break; default: fprint(2, "%d: copylog\n", getpid()); copylog(p[1], netfd, " "); postnote(PNGROUP, getpid(), "die"); break; } _exits(nil); default: close(p[1]); close(netfd); netfd = p[0]; } } fprint(2, "%d: export\n", getpid()); export(netfd, ".", 0); postnote(PNGROUP, getpid(), "die"); fprint(2, "%d: export finished\n", getpid()); fatal(nil); } void copylog(int r, int w, char *prefix) { int n; static uchar buf[16384], buf0[16384]; Fcall f; vlong t; Tm *tm; for(;;){ n = read9pmsg(r, buf, sizeof buf); if(n <= 0) break; memmove(buf0, buf, n); t = nsec(); tm = localtime(t/1000000000); if(convM2S(buf0, n, &f) < 0){ fprint(2, "%s%02d:%02d:%02d.%06d convM2S failed\n", prefix, tm->hour, tm->min, tm->sec, (int)((t/1000)%1000000)); break; } fprint(2, "%s%02d:%02d:%02d.%06d %F\n", prefix, tm->hour, tm->min, tm->sec, (int)((t/1000)%1000000), &f); if(write(w, buf, n) < 0) break; } } void fatal(char *s, ...) { char buf[ERRMAX]; va_list arg; if (s) { va_start(arg, s); vsnprint(buf, ERRMAX, s, arg); va_end(arg); } if (s) sysfatal(buf); else exits(nil); } /* Network on fd1, mount driver on fd0 */ int filter(int fd, char *cmd) { int p[2], lfd, len, nb, argc; char newport[128], buf[128], devdir[40], *s, *file, *argv[16]; // Get a free port and post it to the client. if (announce(anstring, devdir) < 0) sysfatal("filter: Cannot announce %s: %r\n", anstring); snprint(buf, sizeof(buf), "%s/local", devdir); buf[sizeof buf - 1] = '\0'; if ((lfd = open(buf, OREAD)) < 0) sysfatal("filter: Cannot open %s: %r\n", buf); if ((len = read(lfd, newport, sizeof newport - 1)) < 0) sysfatal("filter: Cannot read %s: %r\n", buf); close(lfd); newport[len] = '\0'; if ((s = strchr(newport, '\n')) != nil) *s = '\0'; if ((nb = write(fd, newport, len)) < 0) sysfatal("getport; cannot write port; %r"); assert(nb == len); argc = tokenize(cmd, argv, nelem(argv)-2); if (argc == 0) sysfatal("filter: empty command"); argv[argc++] = buf; argv[argc] = nil; file = argv[0]; if (s = strrchr(argv[0], '/')) argv[0] = s+1; if(pipe(p) < 0) fatal("pipe"); switch(rfork(RFNOWAIT|RFPROC|RFFDG)) { case -1: fatal("rfork record module"); case 0: if (dup(p[0], 1) < 0) fatal("filter: Cannot dup to 1; %r\n"); if (dup(p[0], 0) < 0) fatal("filter: Cannot dup to 0; %r\n"); close(p[0]); close(p[1]); exec(file, argv); fatal("exec record module"); default: close(fd); close(p[0]); } return p[1]; } static void mksecret(char *t, uchar *f) { sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux", f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]); }