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