plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

secstored.c (8464B)


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