#include "all.h" struct { char* name; short uid; short lead; } minusers[] = { "adm", -1, -1, "none", 0, -1, "tor", 1, 1, "sys", 10000, 0, "map", 10001, 10001, "doc", 10002, 0, "upas", 10003, 10003, "cda", 10004, 0, "bootes", 10005, 10005, 0 }; static char buf[4096]; static Rune ichar[] = L"?=+-/:"; int fchar(void); int readln(char*, int); char* getword(char*, Rune, char*, int); void setminusers(void); Uid* chkuid(char *name, int chk); void pentry(char*, Uid*); Uid* uidtop(int); void do_newuser(int, char*[]); void cmd_users(int argc, char *argv[]) { Uid *ui; int u, g, o, line, n; char *file, *p, *uname, *ulead, *unext; file = "/adm/users"; if(argc > 1) file = argv[1]; if(strcmp(file, "default") == 0) { setminusers(); return; } wlock(&uidgc.uidlock); uidgc.uidbuf = getbuf(devnone, Cuidbuf, 0); if(walkto(file) || con_open(FID2, 0)) { print("cmd_users: cannot access %s\n", file); putbuf(uidgc.uidbuf); wunlock(&uidgc.uidlock); return; } uidgc.flen = 0; uidgc.find = 0; cons.offset = 0; u = 0; line = 0; for(;;) { line++; n = readln(buf, sizeof(buf)); if(n == 0) break; p = getword(buf, L':', "no : after number", line); if(p == 0) continue; ulead = getword(p, L':', "no : after name", line); if(ulead == 0) continue; if(strlen(p) > NAMELEN-1) { print("%s: name too long\n"); continue; } strcpy(uid[u].name, p); uid[u].uid = number(buf, 0, 10); memset(uid[u].key, 0, DESKEYLEN); u++; if(u >= conf.nuid) { print("conf.nuid too small (%d)\n", conf.nuid); break; } } cons.nuid = u; /* Parse group table */ uidgc.flen = 0; uidgc.find = 0; cons.offset = 0; g = 0; line = 0; for(;;) { line++; n = readln(buf, sizeof(buf)); if(n == 0) break; uname = getword(buf, L':', 0, 0); /* skip number */ if(uname == 0) continue; ulead = getword(uname, L':', 0, 0); /* skip name */ if(ulead == 0) continue; p = getword(ulead, L':', "no : after leader", line); if(p == 0) continue; ui = uidpstr(uname); if(ui == 0) continue; /* set to owner if name not known */ ui->lead = 0; if(ulead[0]) { o = strtouid(ulead, 0); if(o != 0) ui->lead = o; else ui->lead = ui->uid; } ui->gtab = &gidspace[g]; ui->ngrp = 0; for(;;) { if(p == 0) break; unext = getword(p, L',', 0, 0); o = strtouid(p, 0); if(o != 0) { gidspace[g++] = o; ui->ngrp++; } p = unext; } } /* Sorted by uid for use in uidtostr */ qsort(uid, cons.nuid, sizeof(uid[0]), byuid); cons.ngid = g; putbuf(uidgc.uidbuf); wunlock(&uidgc.uidlock); print("%d uids read, %d groups used\n", u, g); cmd_exec("auth"); } void cmd_newuser(int argc, char *argv[]) { if(argc <= 1) { print("usage: newuser args\n"); print(" name -- create a new user\n"); print(" name : -- create a new group\n"); print(" name ? -- show entry for user\n"); print(" name name -- rename\n"); print(" name =[name] -- add/alter/remove leader\n"); print(" name +name -- add member\n"); print(" name -name -- delete member\n"); return; } wlock(&uidgc.uidlock); do_newuser(argc, argv); wunlock(&uidgc.uidlock); cmd_exec("auth"); } void do_newuser(int argc, char *argv[]) { Rune *r; int nuid; short *s; Uid *ui, *u2; char *p, *md; int i, l, n; nuid = 10000; md = 0; if(argc == 2) { nuid = 1; argv[2] = ":"; } for(r = ichar; *r; r++) if(utfrune(argv[1], *r)) { print("illegal character in name\n"); return; } if(strlen(argv[1]) > NAMELEN-1) { print("name %s too long\n", argv[1]); return; } p = argv[2]; switch(*p) { case '?': ui = chkuid(argv[1], 1); if(ui == 0) return; pentry(buf, ui); print("%s", buf); return; case ':': if(chkuid(argv[1], 0)) return; while(uidtop(nuid) != 0) nuid++; if(cons.nuid >= conf.nuid) { print("conf.nuid too small (%d)\n", conf.nuid); return; } ui = &uid[cons.nuid++]; ui->uid = nuid; ui->lead = 0; if(nuid < 10000) { ui->lead = ui->uid; md = argv[1]; } strcpy(ui->name, argv[1]); ui->ngrp = 0; break; case '=': ui = chkuid(argv[1], 1); if(ui == 0) return; p++; if(*p == '\0') { ui->lead = 0; break; } u2 = chkuid(p, 1); if(u2 == 0) return; ui->lead = u2->uid; break; case '+': ui = chkuid(argv[1], 1); if(ui == 0) return; p++; u2 = chkuid(p, 1); if(u2 == 0) return; if(u2->uid == ui->uid) return; if(cons.ngid+ui->ngrp+1 >= conf.gidspace) { print("conf.gidspace too small (%d)\n", conf.gidspace); return; } for(i = 0; i < ui->ngrp; i++) { if(ui->gtab[i] == u2->uid) { print("member already in group\n"); return; } } s = gidspace+cons.ngid; memmove(s, ui->gtab, ui->ngrp*sizeof(*s)); ui->gtab = s; s[ui->ngrp++] = u2->uid; cons.ngid += ui->ngrp+1; break; case '-': ui = chkuid(argv[1], 1); if(ui == 0) return; p++; u2 = chkuid(p, 1); if(u2 == 0) return; for(i = 0; i < ui->ngrp; i++) if(ui->gtab[i] == u2->uid) break; if(i == ui->ngrp) { print("%s not in group\n", p); return; } s = ui->gtab+i; ui->ngrp--; memmove(s, s+1, (ui->ngrp-i)*sizeof(*s)); break; default: if(chkuid(argv[2], 0)) return; for(r = ichar; *r; r++) if(utfrune(argv[2], *r)) { print("illegal character in name\n"); return; } ui = chkuid(argv[1], 1); if(ui == 0) return; if(strlen(argv[2]) > NAMELEN-1) { print("name %s too long\n", argv[2]); return; } strcpy(ui->name, argv[2]); break; } qsort(uid, cons.nuid, sizeof(uid[0]), byuid); if(walkto("/adm/users") || con_open(FID2, MWRITE|MTRUNC)) { print("can't open /adm/users for write"); return; } cons.offset = 0; for(i = 0; i < cons.nuid; i++) { pentry(buf, &uid[i]); l = strlen(buf); n = con_write(FID2, buf, cons.offset, l); if(l != n) print("short write on /adm/users\n"); cons.offset += n; } if(md != 0) { wunlock(&uidgc.uidlock); sprint(buf, "create /usr/%s %s %s 755 d", md, md, md); print("%s\n", buf); cmd_exec(buf); wlock(&uidgc.uidlock); } } Uid* chkuid(char *name, int chk) { Uid *u; u = uidpstr(name); if(chk == 1) { if(u == 0) print("%s does not exist\n", name); } else { if(u != 0) print("%s already exists\n", name); } return u; } void pentry(char *buf, Uid *u) { int i, posn; Uid *p; posn = sprint(buf, "%d:%s:", u->uid, u->name); p = uidtop(u->lead); if(p && u->lead != 0) posn += sprint(buf+posn, "%s", p->name); posn += sprint(buf+posn, ":"); for(i = 0; i < u->ngrp; i++) { p = uidtop(u->gtab[i]); if(i != 0) posn += sprint(buf+posn, ","); if(p != 0) posn += sprint(buf+posn, "%s", p->name); else posn += sprint(buf+posn, "%d", u->gtab[i]); } sprint(buf+posn, "\n"); } void setminusers(void) { int u; for(u = 0; minusers[u].name; u++) { strcpy(uid[u].name, minusers[u].name); uid[u].uid = minusers[u].uid; uid[u].lead = minusers[u].lead; memset(uid[u].key, 0, DESKEYLEN); } cons.nuid = u; qsort(uid, u, sizeof(uid[0]), byuid); } Uid* uidpstr(char *name) { Uid *s, *e; s = uid; for(e = s+cons.nuid; s < e; s++) { if(strcmp(name, s->name) == 0) return s; } return 0; } char* getword(char *buf, Rune delim, char *error, int line) { char *p; p = utfrune(buf, delim); if(p == 0) { if(error) print("cmd_users: %s line %d\n", error, line); return 0; } *p = '\0'; return p+1; } int strtouid(char *name, int dolock) { Uid *u; int id; if(dolock) rlock(&uidgc.uidlock); u = uidpstr(name); id = 0; if(u != 0) id = u->uid; if(dolock) runlock(&uidgc.uidlock); return id; } Uid* uidtop(int id) { Uid *bot, *top, *new; bot = uid; top = bot + cons.nuid-1; while(bot <= top){ new = bot + (top - bot)/2; if(new->uid == id) return new; if(new->uid < id) bot = new + 1; else top = new - 1; } return 0; } void uidtostr(char *name, int id, int dolock) { Uid *p; if(dolock) rlock(&uidgc.uidlock); p = uidtop(id); if(p == 0) strcpy(name, "none"); else strcpy(name, p->name); if(dolock) runlock(&uidgc.uidlock); } int nametokey(char *name, char *key) { Uid *u; u = uidpstr(name); if(u != 0) { memmove(key, u->key, DESKEYLEN); return 0; } return 1; } int ingroup(int u, int g) { Uid *p; short *s, *e; if(u == g) return 1; rlock(&uidgc.uidlock); p = uidtop(g); if(p != 0) { s = p->gtab; for(e = s + p->ngrp; s < e; s++) { if(*s == u) { runlock(&uidgc.uidlock); return 1; } } } runlock(&uidgc.uidlock); return 0; } int leadgroup(int ui, int gi) { int i; Uid *u; rlock(&uidgc.uidlock); u = uidtop(gi); if(u == 0) { runlock(&uidgc.uidlock); return 0; } i = u->lead; runlock(&uidgc.uidlock); if(i == ui) return 1; if(i == 0) return ingroup(ui, gi); return 0; } int byuid(void *a1, void *a2) { Uid *u1, *u2; u1 = a1; u2 = a2; return u1->uid - u2->uid; } int readln(char *p, int len) { int n, c; n = 0; while(len--) { c = fchar(); if(c == -1 || c == '\n') break; n++; *p++ = c; } *p = '\0'; return n; }