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