xref: /plan9/sys/src/cmd/cwfs/uidgid.c (revision 01a344a29f2ff35133953eaef092a50fc8c3163b)
1 #include "all.h"
2 
3 struct {
4 	char*	name;
5 	Userid	uid;
6 	Userid	lead;
7 } minusers[] = {
8 	"adm",		-1,	-1,
9 	"none",		0,	-1,
10 	"tor",		1,	1,
11 	"sys",		10000,	0,
12 	"map",		10001,	10001,
13 	"doc",		10002,	0,
14 	"upas",		10003,	10003,
15 	"font",		10004,	0,
16 	"bootes",	10005,	10005,
17 	0
18 };
19 
20 static char buf[4096];
21 static Rune ichar[] = L"?=+-/:";
22 
23 Uid*	chkuid(char *name, int chk);
24 void	do_newuser(int, char*[]);
25 char*	getword(char*, Rune, char*, int);
26 void	pentry(char*, Uid*);
27 int	readln(char*, int);
28 void	setminusers(void);
29 Uid*	uidtop(int);
30 
31 void
cmd_users(int argc,char * argv[])32 cmd_users(int argc, char *argv[])
33 {
34 	Uid *ui;
35 	int u, g, o, line;
36 	char *file, *p, *uname, *ulead, *unext;
37 
38 	file = "/adm/users";
39 	if(argc > 1)
40 		file = argv[1];
41 
42 	if(strcmp(file, "default") == 0) {
43 		setminusers();
44 		return;
45 	}
46 
47 	uidgc.uidbuf = getbuf(devnone, Cuidbuf, 0);
48 	if(walkto(file) || con_open(FID2, 0)) {
49 		print("cmd_users: cannot access %s\n", file);
50 		putbuf(uidgc.uidbuf);
51 		return;
52 	}
53 
54 	uidgc.flen = 0;
55 	uidgc.find = 0;
56 	cons.offset = 0;
57 	cons.nuid = 0;
58 
59 	u = 0;
60 	line = 0;
61 	while(readln(buf, sizeof buf) != 0) {
62 		line++;
63 		p = getword(buf, L':', "no : after number", line);
64 		if(p == nil)
65 			continue;
66 		ulead = getword(p, L':', "no : after name", line);
67 		if(ulead == nil)
68 			continue;
69 
70 		if(strlen(p) > NAMELEN-1) {
71 			print("%s: name too long\n", p);
72 			continue;
73 		}
74 		strcpy(uid[u].name, p);
75 		uid[u].uid = number(buf, 0, 10);
76 		uid[u].lead = 0;
77 		uid[u].ngrp = 0;
78 		u++;
79 		if(u >= conf.nuid) {
80 			print("conf.nuid too small (%ld)\n", conf.nuid);
81 			break;
82 		}
83 	}
84 
85 	/* Sorted by uid for use in uidtostr */
86 	wlock(&uidgc.uidlock);
87 	qsort(uid, u, sizeof(uid[0]), byuid);
88 	cons.nuid = u;
89 	wunlock(&uidgc.uidlock);
90 
91 	/* Parse group table */
92 	uidgc.flen = 0;
93 	uidgc.find = 0;
94 	cons.offset = 0;
95 	cons.ngid = 0;
96 
97 	g = 0;
98 	line = 0;
99 	while(readln(buf, sizeof buf) != 0) {
100 		line++;
101 		uname = getword(buf, L':', 0, 0);	/* skip number */
102 		if(uname == nil)
103 			continue;
104 
105 		ulead = getword(uname, L':', 0, 0);	/* skip name */
106 		if(ulead == nil)
107 			continue;
108 
109 		p = getword(ulead, L':', "no : after leader", line);
110 		if(p == nil)
111 			continue;
112 
113 		ui = uidpstr(uname);
114 		if(ui == nil)
115 			continue;
116 
117 		/* set to owner if name not known */
118 		ui->lead = 0;
119 		if(ulead[0]) {
120 			o = strtouid(ulead);
121 			if(o >= 0)
122 				ui->lead = o;
123 			else
124 				ui->lead = ui->uid;
125 		}
126 		ui->gtab = &gidspace[g];
127 		ui->ngrp = 0;
128 		while (p != nil) {
129 			unext = getword(p, L',', 0, 0);
130 			o = strtouid(p);
131 			if(o >= 0) {
132 				gidspace[g++] = o;
133 				ui->ngrp++;
134 			}
135 			p = unext;
136 		}
137 	}
138 
139 	cons.ngid = g;
140 
141 	putbuf(uidgc.uidbuf);
142 	print("%d uids read, %d groups used\n", cons.nuid, cons.ngid);
143 }
144 
145 void
cmd_newuser(int argc,char * argv[])146 cmd_newuser(int argc, char *argv[])
147 {
148 	if(argc <= 1) {
149 		print("usage: newuser args\n");
150 		print("\tname -- create a new user\n");
151 		print("\tname : -- create a new group\n");
152 		print("\tname ? -- show entry for user\n");
153 		print("\tname name -- rename\n");
154 		print("\tname =[name] -- add/alter/remove leader\n");
155 		print("\tname +name -- add member\n");
156 		print("\tname -name -- delete member\n");
157 		return;
158 	}
159 	do_newuser(argc, argv);
160 }
161 
162 void
do_newuser(int argc,char * argv[])163 do_newuser(int argc, char *argv[])
164 {
165 	int i, l, n, nuid;
166 	char *p, *md, *q;
167 	Rune *r;
168 	Userid *s;
169 	Uid *ui, *u2;
170 
171 	nuid = 10000;
172 	md = 0;
173 	if(argc == 2) {
174 		nuid = 1;
175 		argv[2] = ":";
176 	}
177 
178 	for(r = ichar; *r; r++)
179 		if(utfrune(argv[1], *r)) {
180 			print("illegal character in name\n");
181 			return;
182 		}
183 	if(strlen(argv[1]) > NAMELEN-1) {
184 		print("name %s too long\n", argv[1]);
185 		return;
186 	}
187 
188 	p = argv[2];
189 	switch(*p) {
190 	case '?':
191 		ui = chkuid(argv[1], 1);
192 		if(ui == 0)
193 			return;
194 		pentry(buf, ui);
195 		n = strlen(buf);
196 		p = buf;
197 		while(n > PRINTSIZE-5) {
198 			q = p;
199 			p += PRINTSIZE-5;
200 			n -= PRINTSIZE-5;
201 			i = *p;
202 			*p = 0;
203 			print("%s", q);
204 			*p = i;
205 		}
206 		print("%s\n", p);
207 		return;
208 
209 	case ':':
210 		if(chkuid(argv[1], 0))
211 			return;
212 		while(uidtop(nuid) != 0)
213 			nuid++;
214 		if(cons.nuid >= conf.nuid) {
215 			print("conf.nuid too small (%ld)\n", conf.nuid);
216 			return;
217 		}
218 
219 		wlock(&uidgc.uidlock);
220 		ui = &uid[cons.nuid++];
221 		ui->uid = nuid;
222 		ui->lead = 0;
223 		if(nuid < 10000) {
224 			ui->lead = ui->uid;
225 			md = argv[1];
226 		}
227 		strcpy(ui->name, argv[1]);
228 		ui->ngrp = 0;
229 		qsort(uid, cons.nuid, sizeof(uid[0]), byuid);
230 		wunlock(&uidgc.uidlock);
231 		break;
232 
233 	case '=':
234 		ui = chkuid(argv[1], 1);
235 		if(ui == 0)
236 			return;
237 		p++;
238 		if(*p == '\0') {
239 			ui->lead = 0;
240 			break;
241 		}
242 		u2 = chkuid(p, 1);
243 		if(u2 == 0)
244 			return;
245 		ui->lead = u2->uid;
246 		break;
247 
248 	case '+':
249 		ui = chkuid(argv[1], 1);
250 		if(ui == 0)
251 			return;
252 		p++;
253 		u2 = chkuid(p, 1);
254 		if(u2 == 0)
255 			return;
256 		if(u2->uid == ui->uid)
257 			return;
258 		if(cons.ngid+ui->ngrp+1 >= conf.gidspace) {
259 			print("conf.gidspace too small (%ld)\n", conf.gidspace);
260 			return;
261 		}
262 		for(i = 0; i < ui->ngrp; i++) {
263 			if(ui->gtab[i] == u2->uid) {
264 				print("member already in group\n");
265 				return;
266 			}
267 		}
268 
269 		wlock(&uidgc.uidlock);
270 		s = gidspace+cons.ngid;
271 		memmove(s, ui->gtab, ui->ngrp*sizeof(*s));
272 		ui->gtab = s;
273 		s[ui->ngrp++] = u2->uid;
274 		cons.ngid += ui->ngrp+1;
275 		wunlock(&uidgc.uidlock);
276 		break;
277 
278 	case '-':
279 		ui = chkuid(argv[1], 1);
280 		if(ui == 0)
281 			return;
282 		p++;
283 		u2 = chkuid(p, 1);
284 		if(u2 == 0)
285 			return;
286 		for(i = 0; i < ui->ngrp; i++)
287 			if(ui->gtab[i] == u2->uid)
288 				break;
289 
290 		if(i == ui->ngrp) {
291 			print("%s not in group\n", p);
292 			return;
293 		}
294 
295 		wlock(&uidgc.uidlock);
296 		s = ui->gtab+i;
297 		ui->ngrp--;
298 		memmove(s, s+1, (ui->ngrp-i)*sizeof(*s));
299 		wunlock(&uidgc.uidlock);
300 		break;
301 
302 	default:
303 		if(chkuid(argv[2], 0))
304 			return;
305 
306 		for(r = ichar; *r; r++)
307 			if(utfrune(argv[2], *r)) {
308 				print("illegal character in name\n");
309 				return;
310 			}
311 
312 		ui = chkuid(argv[1], 1);
313 		if(ui == 0)
314 			return;
315 
316 		if(strlen(argv[2]) > NAMELEN-1) {
317 			print("name %s too long\n", argv[2]);
318 			return;
319 		}
320 
321 		wlock(&uidgc.uidlock);
322 		strcpy(ui->name, argv[2]);
323 		wunlock(&uidgc.uidlock);
324 		break;
325 	}
326 
327 
328 	if(walkto("/adm/users") || con_open(FID2, OWRITE|OTRUNC)) {
329 		print("can't open /adm/users for write\n");
330 		return;
331 	}
332 
333 	cons.offset = 0;
334 	for(i = 0; i < cons.nuid; i++) {
335 		pentry(buf, &uid[i]);
336 		l = strlen(buf);
337 		n = con_write(FID2, buf, cons.offset, l);
338 		if(l != n)
339 			print("short write on /adm/users\n");
340 		cons.offset += n;
341 	}
342 
343 	if(md != 0) {
344 		sprint(buf, "create /usr/%s %s %s 755 d", md, md, md);
345 		print("%s\n", buf);
346 		cmd_exec(buf);
347 	}
348 }
349 
350 Uid*
chkuid(char * name,int chk)351 chkuid(char *name, int chk)
352 {
353 	Uid *u;
354 
355 	u = uidpstr(name);
356 	if(chk == 1) {
357 		if(u == 0)
358 			print("%s does not exist\n", name);
359 	}
360 	else {
361 		if(u != 0)
362 			print("%s already exists\n", name);
363 	}
364 	return u;
365 }
366 
367 void
pentry(char * buf,Uid * u)368 pentry(char *buf, Uid *u)
369 {
370 	int i, posn;
371 	Uid *p;
372 
373 	posn = sprint(buf, "%d:%s:", u->uid, u->name);
374 	p = uidtop(u->lead);
375 	if(p && u->lead != 0)
376 		posn += sprint(buf+posn, "%s", p->name);
377 
378 	posn += sprint(buf+posn, ":");
379 	for(i = 0; i < u->ngrp; i++) {
380 		p = uidtop(u->gtab[i]);
381 		if(i != 0)
382 			posn += sprint(buf+posn, ",");
383 		if(p != 0)
384 			posn += sprint(buf+posn, "%s", p->name);
385 		else
386 			posn += sprint(buf+posn, "%d", u->gtab[i]);
387 	}
388 	sprint(buf+posn, "\n");
389 }
390 
391 void
setminusers(void)392 setminusers(void)
393 {
394 	int u;
395 
396 	for(u = 0; minusers[u].name; u++) {
397 		strcpy(uid[u].name, minusers[u].name);
398 		uid[u].uid = minusers[u].uid;
399 		uid[u].lead = minusers[u].lead;
400 	}
401 	cons.nuid = u;
402 	qsort(uid, u, sizeof(uid[0]), byuid);
403 }
404 
405 Uid*
uidpstr(char * name)406 uidpstr(char *name)
407 {
408 	Uid *s, *e;
409 
410 	s = uid;
411 	for(e = s+cons.nuid; s < e; s++) {
412 		if(strcmp(name, s->name) == 0)
413 			return s;
414 	}
415 	return 0;
416 }
417 
418 char*
getword(char * buf,Rune delim,char * error,int line)419 getword(char *buf, Rune delim, char *error, int line)
420 {
421 	char *p;
422 
423 	p = utfrune(buf, delim);
424 	if(p == 0) {
425 		if(error)
426 			print("cmd_users: %s line %d\n", error, line);
427 		return 0;
428 	}
429 	*p = '\0';
430 	return p+1;
431 }
432 
433 int
strtouid(char * name)434 strtouid(char *name)
435 {
436 	Uid *u;
437 	int id;
438 
439 	rlock(&uidgc.uidlock);
440 
441 	u = uidpstr(name);
442 	id = -2;
443 	if(u != 0)
444 		id = u->uid;
445 
446 	runlock(&uidgc.uidlock);
447 
448 	return id;
449 }
450 
451 Uid*
uidtop(int id)452 uidtop(int id)
453 {
454 	Uid *bot, *top, *new;
455 
456 	bot = uid;
457 	top = bot + cons.nuid-1;
458 
459 	while(bot <= top){
460 		new = bot + (top - bot)/2;
461 		if(new->uid == id)
462 			return new;
463 		if(new->uid < id)
464 			bot = new + 1;
465 		else
466 			top = new - 1;
467 	}
468 	return 0;
469 }
470 
471 void
uidtostr(char * name,int id,int dolock)472 uidtostr(char *name, int id, int dolock)
473 {
474 	Uid *p;
475 
476 	if(dolock)
477 		rlock(&uidgc.uidlock);
478 
479 	p = uidtop(id);
480 	if(p == 0)
481 		strcpy(name, "none");
482 	else
483 		strcpy(name, p->name);
484 
485 	if(dolock)
486 		runlock(&uidgc.uidlock);
487 }
488 
489 int
ingroup(int u,int g)490 ingroup(int u, int g)
491 {
492 	Uid *p;
493 	Userid *s, *e;
494 
495 	if(u == g)
496 		return 1;
497 
498 	rlock(&uidgc.uidlock);
499 	p = uidtop(g);
500 	if(p != 0) {
501 		s = p->gtab;
502 		for(e = s + p->ngrp; s < e; s++) {
503 			if(*s == u) {
504 				runlock(&uidgc.uidlock);
505 				return 1;
506 			}
507 		}
508 	}
509 	runlock(&uidgc.uidlock);
510 	return 0;
511 }
512 
513 int
leadgroup(int ui,int gi)514 leadgroup(int ui, int gi)
515 {
516 	int i;
517 	Uid *u;
518 
519 	/* user 'none' cannot be a group leader */
520 	if(ui == 0)
521 		return 0;
522 
523 	rlock(&uidgc.uidlock);
524 	u = uidtop(gi);
525 	if(u == 0) {
526 		runlock(&uidgc.uidlock);
527 		return 0;
528 	}
529 	i = u->lead;
530 	runlock(&uidgc.uidlock);
531 	if(i == ui)
532 		return 1;
533 	if(i == 0)
534 		return ingroup(ui, gi);
535 
536 	return 0;
537 }
538 
539 int
byuid(void * a1,void * a2)540 byuid(void *a1, void *a2)
541 {
542 	Uid *u1, *u2;
543 
544 	u1 = a1;
545 	u2 = a2;
546 	return u1->uid - u2->uid;
547 }
548 
549 int
fchar(void)550 fchar(void)
551 {
552 	int n;
553 
554 	n = BUFSIZE;
555 	if(n > MAXDAT)
556 		n = MAXDAT;
557 	if(uidgc.find >= uidgc.flen) {
558 		uidgc.find = 0;
559 		uidgc.flen = con_read(FID2, uidgc.uidbuf->iobuf, cons.offset, n);
560 		if(uidgc.flen <= 0)
561 			return -1;
562 		cons.offset += uidgc.flen;
563 	}
564 	return (uchar)uidgc.uidbuf->iobuf[uidgc.find++];
565 }
566 
567 int
readln(char * p,int len)568 readln(char *p, int len)
569 {
570 	int n, c;
571 
572 	n = 0;
573 	while(len--) {
574 		c = fchar();
575 		if(c == -1 || c == '\n')
576 			break;
577 		n++;
578 		*p++ = c;
579 	}
580 	*p = '\0';
581 	return n;
582 }
583