xref: /plan9/sys/src/cmd/auth/secstore/secstored.c (revision 14cc0f535177405a84c5b73603a98e5db6674719)
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
usage(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
getdir(SConn * conn,char * id)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
getfile(SConn * conn,char * id,char * gf)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
putfile(SConn * conn,char * id,char * pf)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
removefile(SConn * conn,char * id,char * f)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*
remoteIP(char * ldir)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
dologin(int fd,char * S,int forceSTA)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
main(int argc,char ** argv)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