secstored.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <ndb.h>
  5. #include <mp.h>
  6. #include <libsec.h>
  7. #include "SConn.h"
  8. #include "secstore.h"
  9. int secureidcheck(char *, char *); // from /sys/src/cmd/auth/
  10. extern char* dirls(char *path);
  11. int verbose;
  12. Ndb *db;
  13. static void
  14. usage(void)
  15. {
  16. fprint(2, "secstored: [-S servername] [-s tcp!*!5356] [-v] [-x netmtpt]\n");
  17. exits("usage");
  18. }
  19. static int
  20. getdir(SConn *conn, char *id)
  21. {
  22. char *ls, *s;
  23. int n, len;
  24. s = emalloc(Maxmsg);
  25. snprint(s, Maxmsg, "%s/store/%s", SECSTORE_DIR, id);
  26. if((ls = dirls(s)) == nil)
  27. len = 0;
  28. else
  29. len = strlen(ls);
  30. /* send file size */
  31. snprint(s, Maxmsg, "%d", len);
  32. conn->write(conn, (uchar*)s, strlen(s));
  33. /* send directory listing in Maxmsg chunks */
  34. n = Maxmsg;
  35. while(len > 0){
  36. if(len < Maxmsg)
  37. n = len;
  38. conn->write(conn, (uchar*)ls, n);
  39. ls += n;
  40. len -= n;
  41. }
  42. free(s);
  43. free(ls);
  44. return 0;
  45. }
  46. char *
  47. validatefile(char *f)
  48. {
  49. char *nl;
  50. if(f==nil || *f==0)
  51. return nil;
  52. if(nl = strchr(f, '\n'))
  53. *nl = 0;
  54. if(strchr(f,'/') != nil || strcmp(f,"..")==0 || strlen(f) >= 300){
  55. syslog(0, LOG, "no slashes allowed: %s\n", f);
  56. return nil;
  57. }
  58. return f;
  59. }
  60. static int
  61. getfile(SConn *conn, char *id, char *gf)
  62. {
  63. int n, gd, len;
  64. char *s;
  65. if(strcmp(gf,".")==0)
  66. return getdir(conn, id);
  67. /* send file size */
  68. s = emalloc(Maxmsg);
  69. snprint(s, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, gf);
  70. gd = open(s, OREAD);
  71. if(gd < 0){
  72. syslog(0, LOG, "can't open %s: %r\n", s);
  73. free(s);
  74. conn->write(conn, (uchar*)"-1", 2);
  75. return -1;
  76. }
  77. len = seek(gd, 0, 2);
  78. if(len < 0 || len > MAXFILESIZE){
  79. syslog(0, LOG, "implausible filesize %d for %s\n", len, gf);
  80. free(s);
  81. conn->write(conn, (uchar*)"-3", 2);
  82. return -1;
  83. }
  84. seek(gd, 0, 0);
  85. snprint(s, Maxmsg, "%d", len);
  86. conn->write(conn, (uchar*)s, strlen(s));
  87. /* send file in Maxmsg chunks */
  88. while(len > 0){
  89. n = read(gd, s, Maxmsg);
  90. if(n <= 0){
  91. syslog(0, LOG, "read error on %s: %r\n", gf);
  92. free(s);
  93. return -1;
  94. }
  95. conn->write(conn, (uchar*)s, n);
  96. len -= n;
  97. }
  98. close(gd);
  99. free(s);
  100. return 0;
  101. }
  102. static int
  103. putfile(SConn *conn, char *id, char *pf)
  104. {
  105. int n, nw, pd;
  106. long len;
  107. char s[Maxmsg+1];
  108. /* get file size */
  109. n = readstr(conn, s);
  110. if(n < 0){
  111. syslog(0, LOG, "remote: %s: %r\n", s);
  112. return -1;
  113. }
  114. len = atoi(s);
  115. if(len == -1){
  116. syslog(0, LOG, "remote file %s does not exist\n", pf);
  117. return -1;
  118. }else if(len < 0 || len > MAXFILESIZE){
  119. syslog(0, LOG, "implausible filesize %ld for %s\n", len, pf);
  120. return -1;
  121. }
  122. /* get file in Maxmsg chunks */
  123. if(strchr(pf,'/') != nil || strcmp(pf,"..")==0){
  124. syslog(0, LOG, "no slashes allowed: %s\n", pf);
  125. return -1;
  126. }
  127. snprint(s, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, pf);
  128. pd = create(s, OWRITE, 0660);
  129. if(pd < 0){
  130. syslog(0, LOG, "can't open %s: %r\n", s);
  131. return -1;
  132. }
  133. while(len > 0){
  134. n = conn->read(conn, (uchar*)s, Maxmsg);
  135. if(n <= 0){
  136. syslog(0, LOG, "empty file chunk\n");
  137. return -1;
  138. }
  139. nw = write(pd, s, n);
  140. if(nw != n){
  141. syslog(0, LOG, "write error on %s: %r", pf);
  142. return -1;
  143. }
  144. len -= n;
  145. }
  146. close(pd);
  147. return 0;
  148. }
  149. static int
  150. removefile(SConn *conn, char *id, char *f)
  151. {
  152. Dir *d;
  153. char buf[Maxmsg];
  154. snprint(buf, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, f);
  155. if((d = dirstat(buf)) == nil){
  156. snprint(buf, sizeof buf, "remove failed: %r");
  157. writerr(conn, buf);
  158. return -1;
  159. }else if(d->mode & DMDIR){
  160. snprint(buf, sizeof buf, "can't remove a directory");
  161. writerr(conn, buf);
  162. free(d);
  163. return -1;
  164. }
  165. free(d);
  166. if(remove(buf) < 0){
  167. snprint(buf, sizeof buf, "remove failed: %r");
  168. writerr(conn, buf);
  169. return -1;
  170. }
  171. return 0;
  172. }
  173. /* given line directory from accept, returns ipaddr!port */
  174. static char*
  175. remoteIP(char *ldir)
  176. {
  177. int fd, n;
  178. char rp[100], ap[500];
  179. snprint(rp, sizeof rp, "%s/remote", ldir);
  180. fd = open(rp, OREAD);
  181. if(fd < 0)
  182. return strdup("?!?");
  183. n = read(fd, ap, sizeof ap);
  184. if(n <= 0 || n == sizeof ap){
  185. fprint(2, "error %d reading %s: %r\n", n, rp);
  186. return strdup("?!?");
  187. }
  188. close(fd);
  189. ap[n--] = 0;
  190. if(ap[n] == '\n')
  191. ap[n] = 0;
  192. return strdup(ap);
  193. }
  194. static int
  195. dologin(int fd, char *S, int forceSTA)
  196. {
  197. int i, n, rv;
  198. char *file;
  199. char msg[Maxmsg+1];
  200. PW *pw;
  201. SConn *conn;
  202. pw = nil;
  203. rv = -1;
  204. // collect the first message
  205. if((conn = newSConn(fd)) == nil)
  206. return -1;
  207. if(readstr(conn, msg) < 0){
  208. fprint(2, "remote: %s: %r\n", msg);
  209. writerr(conn, "can't read your first message");
  210. goto Out;
  211. }
  212. // authenticate
  213. if(PAKserver(conn, S, msg, &pw) < 0){
  214. if(pw != nil)
  215. syslog(0, LOG, "secstore denied for %s", pw->id);
  216. goto Out;
  217. }
  218. if((forceSTA || pw->status&STA) != 0){
  219. conn->write(conn, (uchar*)"STA", 3);
  220. if(readstr(conn, msg) < 10 || strncmp(msg, "STA", 3) != 0){
  221. syslog(0, LOG, "no STA from %s", pw->id);
  222. goto Out;
  223. }
  224. if(secureidcheck(pw->id, msg+3) <= 0){
  225. syslog(0, LOG, "secureidcheck denied %s", pw->id);
  226. goto Out;
  227. }
  228. }
  229. conn->write(conn, (uchar*)"OK", 2);
  230. syslog(0, LOG, "AUTH %s", pw->id);
  231. // perform operations as asked
  232. while((n = readstr(conn, msg)) > 0){
  233. syslog(0, LOG, "[%s] %s", pw->id, msg);
  234. if(strncmp(msg, "GET ", 4) == 0){
  235. file = validatefile(msg+4);
  236. if(file==nil || getfile(conn, pw->id, file) < 0)
  237. goto Err;
  238. }else if(strncmp(msg, "PUT ", 4) == 0){
  239. file = validatefile(msg+4);
  240. if(file==nil || putfile(conn, pw->id, file) < 0){
  241. syslog(0, LOG, "failed PUT %s/%s", pw->id, file);
  242. goto Err;
  243. }
  244. }else if(strncmp(msg, "RM ", 3) == 0){
  245. file = validatefile(msg+3);
  246. if(file==nil || removefile(conn, pw->id, file) < 0){
  247. syslog(0, LOG, "failed RM %s/%s", pw->id, file);
  248. goto Err;
  249. }
  250. }else if(strncmp(msg, "CHPASS", 6) == 0){
  251. if(readstr(conn, msg) < 0){
  252. syslog(0, LOG, "protocol botch CHPASS for %s", pw->id);
  253. writerr(conn, "protocol botch while setting PAK");
  254. goto Out;
  255. }
  256. pw->Hi = strtomp(msg, nil, 64, pw->Hi);
  257. for(i=0; i < 4 && putPW(pw) < 0; i++)
  258. syslog(0, LOG, "password change failed for %s (%d): %r", pw->id, i);
  259. if(i==4)
  260. goto Out;
  261. }else if(strncmp(msg, "BYE", 3) == 0){
  262. rv = 0;
  263. break;
  264. }else{
  265. writerr(conn, "unrecognized operation");
  266. break;
  267. }
  268. }
  269. if(n <= 0)
  270. syslog(0, LOG, "%s closed connection without saying goodbye\n", pw->id);
  271. Out:
  272. freePW(pw);
  273. conn->free(conn);
  274. return rv;
  275. Err:
  276. writerr(conn, "operation failed");
  277. goto Out;
  278. }
  279. void
  280. main(int argc, char **argv)
  281. {
  282. int afd, dfd, lcfd, forceSTA = 0;
  283. char adir[40], ldir[40], *remote;
  284. char *serve = "tcp!*!5356", *p, aserve[128], net[128];
  285. char *S = "secstore";
  286. Ndb *db2;
  287. setnetmtpt(net, sizeof(net), nil);
  288. ARGBEGIN{
  289. case 's':
  290. serve = EARGF(usage());
  291. break;
  292. case 'S':
  293. S = EARGF(usage());
  294. break;
  295. case 'x':
  296. p = ARGF();
  297. if(p == nil)
  298. usage();
  299. setnetmtpt(net, sizeof(net), p);
  300. forceSTA = 1; // for any non-standard network setting, be paranoid
  301. break;
  302. case 'v':
  303. verbose++;
  304. break;
  305. default:
  306. usage();
  307. }ARGEND;
  308. if(!verbose)
  309. switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
  310. case -1:
  311. sysfatal("fork: %r");
  312. case 0:
  313. break;
  314. default:
  315. exits(0);
  316. }
  317. snprint(aserve, sizeof aserve, "%s/%s", net, serve);
  318. afd = announce(aserve, adir);
  319. if(afd < 0)
  320. sysfatal("%s: %r\n", aserve);
  321. syslog(0, LOG, "ANNOUNCE %s", aserve);
  322. for(;;){
  323. if((lcfd = listen(adir, ldir)) < 0)
  324. exits("can't listen");
  325. switch(fork()){
  326. case -1:
  327. fprint(2, "secstore forking: %r\n");
  328. close(lcfd);
  329. break;
  330. case 0:
  331. // "/lib/ndb/common.radius does not exist" if db set before fork
  332. db = ndbopen("/lib/ndb/auth");
  333. if(db == 0)
  334. syslog(0, LOG, "no /lib/ndb/auth");
  335. db2 = ndbopen(0);
  336. if(db2 == 0)
  337. syslog(0, LOG, "no /lib/ndb/local");
  338. db = ndbcat(db, db2);
  339. if((dfd = accept(lcfd, ldir)) < 0)
  340. exits("can't accept");
  341. alarm(30*60*1000); // 30 min
  342. remote = remoteIP(ldir);
  343. syslog(0, LOG, "secstore from %s", remote);
  344. free(remote);
  345. dologin(dfd, S, forceSTA);
  346. exits(nil);
  347. default:
  348. close(lcfd);
  349. break;
  350. }
  351. }
  352. }