]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/hjfs/auth.c
hjfs: disable shutdown when listening on network connections
[plan9front.git] / sys / src / cmd / hjfs / auth.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "dat.h"
5 #include "fns.h"
6
7 typedef struct User User;
8 typedef struct PUser PUser;
9
10 struct User {
11         short uid;
12         char name[USERLEN];
13         short lead;
14         int nmemb;
15         short *memb;
16 };
17
18 struct PUser {
19         short uid;
20         char name[USERLEN];
21         char lead[USERLEN];
22         int nmemb;
23         char (*memb)[USERLEN];
24 };
25
26 User udef[] = {
27         {-1, "adm", -1, 0, nil},
28         {0, "none", -1, 0, nil},
29         {1, "tor", 1, 0, nil},
30         {2, "glenda", 2, 0, nil},
31         {10000, "sys", NOUID, 0, nil},
32         {10001, "map", 10001, 0, nil},
33         {10002, "doc", NOUID, 0, nil},
34         {10003, "upas", 10003, 0, nil},
35         {10004, "font", NOUID, 0, nil},
36 };
37
38 static int
39 validuser(char *n)
40 {
41         char *p;
42
43         if(*n == 0)
44                 return 0;
45         for(p = n; *p != 0; p++)
46                 if((uchar) *p < ' ' || strchr("?=+-/:", *p) != nil)
47                         return 0;
48         return n - p < USERLEN;
49 }
50
51 static void
52 usersparseline(char *l, PUser **u, int *nu)
53 {
54         PUser v;
55         char *f[5], *r, *s;
56         int c;
57         
58         if(*l == 0 || *l == '#')
59                 return;
60         c = getfields(l, f, 5, 0, ":");
61         if(c < 4)
62                 return;
63         v.uid = strtol(f[0], &r, 10);
64         if(*r != 0)
65                 return;
66         if(!validuser(f[1]) || *f[2] != 0 && !validuser(f[2]))
67                 return;
68         strcpy(v.name, f[1]);
69         strcpy(v.lead, f[2]);
70         v.memb = nil;
71         v.nmemb = 0;
72         r = f[3];
73         while(r != nil && *r != 0){
74                 s = strchr(r, ',');
75                 if(s != nil)
76                         *s = 0;
77                 if(!validuser(r)){
78                         free(v.memb);
79                         return;
80                 }
81                 v.memb = erealloc(v.memb, (v.nmemb + 1) * USERLEN);
82                 strcpy(v.memb[v.nmemb++], r);
83                 if(s == nil)
84                         r = nil;
85                 else
86                         r = s + 1;
87         }
88         *u = erealloc(*u, (*nu + 1) * sizeof(PUser));
89         memcpy(&(*u)[(*nu)++], &v, sizeof(PUser));
90 }
91
92 static int
93 puserlook(PUser *u, int nu, char *name)
94 {
95         PUser *v;
96
97         if(*name == 0)
98                 return NOUID;
99         for(v = u; v < u + nu; v++)
100                 if(strcmp(v->name, name) == 0)
101                         return v->uid;
102         return NOUID;
103 }
104
105 static int
106 uidcomp(void *a, void *b)
107 {
108         short *aa, *bb;
109         
110         aa = a;
111         bb = b;
112         return *aa - *bb;
113 }
114
115 static int
116 usercomp(void *a, void *b)
117 {
118         User *aa, *bb;
119         
120         aa = a;
121         bb = b;
122         return aa->uid - bb->uid;
123 }
124
125 int
126 usersload(Fs *fs, Chan *ch)
127 {
128         char *buf, *p, *q;
129         int bufl, i, j, rc, nu;
130         PUser *u;
131         User *v;
132         
133         buf = nil;
134         bufl = 0;
135         u = nil;
136         v = nil;
137         nu = 0;
138         for(;;){
139                 if((bufl & 1023) == 0)
140                         buf = erealloc(buf, bufl + 1024);
141                 rc = chanread(ch, buf + bufl, 1024, bufl);
142                 if(rc < 0)
143                         goto err;
144                 if(rc == 0)
145                         break;
146                 bufl += rc;
147         }
148         if(buf == nil)
149                 goto done;
150         buf[bufl] = 0;
151         for(p = buf; q = strchr(p, '\n'); p = q + 1){
152                 *q = 0;
153                 usersparseline(p, &u, &nu);
154         }
155         usersparseline(p, &u, &nu);
156         free(buf);
157         if(nu == 0)
158                 goto done;
159         v = emalloc(sizeof(User) * nu);
160         for(i = 0; i < nu; i++){
161                 v[i].uid = u[i].uid;
162                 strcpy(v[i].name, u[i].name);
163                 v[i].lead = puserlook(u, nu, u[i].lead);
164                 v[i].nmemb = u[i].nmemb;
165                 v[i].memb = emalloc(sizeof(short) * v[i].nmemb);
166                 for(j = 0; j < v[i].nmemb; j++)
167                         v[i].memb[j] = puserlook(u, nu, u[i].memb[j]);
168                 qsort(v[i].memb, v[i].nmemb, sizeof(ushort), uidcomp);
169         }
170         qsort(v, nu, sizeof(User), usercomp);
171 done:
172         wlock(&fs->udatal);
173         if(fs->udata != nil){
174                 for(i = 0; i < fs->nudata; i++)
175                         free(((User *)fs->udata)[i].memb);
176                 free(fs->udata);
177         }
178         fs->udata = v;
179         fs->nudata = nu;
180         wunlock(&fs->udatal);
181         return 0;
182 err:
183         free(buf);
184         return -1;
185 }
186
187 int
188 userssave(Fs *fs, Chan *ch)
189 {
190         User *u, *v;
191         int nu, i;
192         char buf[512], ubuf[USERLEN], *p, *e;
193         uvlong off;
194         
195         rlock(&fs->udatal);
196         u = fs->udata;
197         if(u == nil){
198                 u = udef;
199                 nu = nelem(udef);
200         }else
201                 nu = fs->nudata;
202         off = 0;
203         for(v = u; v < u + nu; v++){
204                 p = buf;
205                 e = buf + sizeof(buf);
206                 p = seprint(p, e, "%d:%s:", v->uid, v->name);
207                 if(v->lead != NOUID)
208                         p = strecpy(p, e, uid2name(fs, v->lead, ubuf));
209                 if(p < e)
210                         *p++ = ':';
211                 for(i = 0; i < v->nmemb; i++){
212                         if(v->memb[i] == NOUID)
213                                 continue;
214                         if(p < e && i > 0)
215                                 *p++ = ',';
216                         p = strecpy(p, e, uid2name(fs, v->memb[i], ubuf));
217                 }
218                 *p++ = '\n';
219                 if(ch == nil)
220                         write(2, buf, p - buf);
221                 else if(chanwrite(ch, buf, p - buf, off) < p - buf)
222                         goto err;
223                 off += p - buf;
224         }
225         runlock(&fs->udatal);
226         return 0;
227 err:
228         runlock(&fs->udatal);
229         return -1;
230 }
231
232 static User *
233 lookupuid(Fs *fs, short uid)
234 {
235         User *u;
236         int i, j, k;
237
238         u = fs->udata;
239         i = 0;
240         j = fs->nudata;
241         if(u == nil){
242                 u = udef;
243                 j = nelem(udef);
244         }
245         if(j == 0)
246                 return nil;
247         while(i < j){
248                 k = (i + j) / 2;
249                 if(u[k].uid < uid)
250                         i = k + 1;
251                 else
252                         j = k;
253         }
254         if(u[i].uid == uid)
255                 return &u[i];
256         return nil;
257 }
258
259 int
260 ingroup(Fs *fs, short uid, short gid, int leader)
261 {
262         User *g;
263         int i, j, k;
264
265         if(uid == gid)
266                 return 1;
267         rlock(&fs->udatal);
268         g = lookupuid(fs, gid);
269         if(g == nil)
270                 goto nope;
271         if(g->lead == uid)
272                 goto yes;
273         if(leader && g->lead != NOUID)
274                 goto nope;
275         if(g->nmemb == 0)
276                 goto nope;
277         i = 0;
278         j = g->nmemb;
279         while(i < j){
280                 k = (i + j) / 2;
281                 if(g->memb[k] < uid)
282                         i = k + 1;
283                 else
284                         j = k;
285         }
286         if(g->memb[i] == uid)
287                 goto yes;
288 nope:
289         runlock(&fs->udatal);
290         return 0;
291 yes:
292         runlock(&fs->udatal);
293         return 1;
294 }
295
296 int
297 permcheck(Fs *fs, Dentry *d, short uid, int mode)
298 {
299         int perm;
300
301         if((fs->flags & FSNOPERM) != 0)
302                 return 1;
303         perm = d->mode & 0777;
304         if(d->uid == uid)
305                 perm >>= 6;
306         else if(ingroup(fs, uid, d->gid, 0))
307                 perm >>= 3;
308         switch(mode & 3){
309         case OREAD:
310                 return (perm & 4) != 0;
311         case OWRITE:
312                 return (perm & 2) != 0;
313         case OEXEC:
314                 return (perm & 1) != 0;
315         case ORDWR:
316                 return (perm & 6) == 6;
317         }
318         return 0;
319 }
320
321 char *
322 uid2name(Fs *fs, short uid, char *buf)
323 {
324         User *u;
325         
326         rlock(&fs->udatal);
327         u = lookupuid(fs, uid);
328         if(buf == nil)
329                 buf = emalloc(USERLEN);
330         if(u == nil)
331                 snprint(buf, USERLEN, "%d", uid);
332         else
333                 snprint(buf, USERLEN, "%s", u->name);
334         runlock(&fs->udatal);
335         return buf;
336 }
337
338 int
339 name2uid(Fs *fs, char *name, short *uid)
340 {
341         char *r;
342         User *u, *v;
343
344         *uid = strtol(name, &r, 10);
345         if(*r == 0)
346                 return 1;
347         rlock(&fs->udatal);
348         u = fs->udata;
349         v = u + fs->nudata;
350         if(u == nil){
351                 u = udef;
352                 v = udef + nelem(udef);
353         }
354         for(; u < v; u++)
355                 if(strcmp(u->name, name) == 0){
356                         *uid = u->uid;
357                         runlock(&fs->udatal);
358                         return 1;
359                 }
360         runlock(&fs->udatal);
361         werrstr(Einval);
362         return -1;
363 }
364
365 static void
366 createuserdir(Fs *fs, char *name, short uid)
367 {
368         Chan *ch;
369
370         ch = chanattach(fs, CHFNOPERM);
371         if(ch == nil)
372                 return;
373         ch->uid = uid;
374         if(chanwalk(ch, "usr") > 0)
375                 chancreat(ch, name, DMDIR | 0775, OREAD);
376         chanclunk(ch);
377 }
378
379 int
380 cmdnewuser(int argc, char **argv)
381 {
382         short uid, gid;
383         User *u, *v;
384         Fs *fs;
385         int resort, createdir, i, j;
386         extern Fs *fsmain;
387
388         if(argc < 2)
389                 return -9001;
390         if(!validuser(argv[1])){
391                 werrstr(Einval);
392                 return -1;
393         }
394         fs = fsmain;
395         resort = 0;
396         createdir = 0;
397         wlock(&fs->udatal);
398         if(fs->udata == nil){
399                 wunlock(&fs->udatal);
400                 werrstr("newuser: no user database");
401                 return -1;
402         }
403         uid = 0;
404         gid = 10000;
405         for(u = fs->udata; u < fs->udata + fs->nudata; u++){
406                 if(strcmp(u->name, argv[1]) == 0)
407                         goto found;
408                 if(u->uid == uid)
409                         uid++;
410                 if(u->uid == gid)
411                         gid++;
412         }
413         resort = 1;
414         fs->udata = erealloc(fs->udata, sizeof(User) * (fs->nudata + 1));
415         u = fs->udata + fs->nudata++;
416         strcpy(u->name, argv[1]);
417         u->nmemb = 0;
418         u->memb = nil;
419         u->uid = gid;
420         u->lead = NOUID;
421         if(argc == 2 || strcmp(argv[2], ":") != 0){
422                 u->lead = u->uid = uid;
423                 createdir = 1;
424         }
425 found:
426         for(i = 2; i < argc; i++){
427                 if(strcmp(argv[i], ":") == 0)
428                         continue;
429                 if(*argv[i] != '+' && *argv[i] != '-' && *argv[i] != '='){
430                         if(!validuser(argv[i]))
431                                 goto erropt;
432                         strcpy(u->name, argv[i]);
433                         continue;
434                 }
435                 for(v = fs->udata; v < fs->udata + fs->nudata; v++)
436                         if(strcmp(v->name, argv[i] + 1) == 0)
437                                 break;
438                 if(v == fs->udata + fs->nudata)
439                         goto erropt;
440                 if(*argv[i] == '='){
441                         u->lead = v->uid;
442                         continue;
443                 }
444                 for(j = 0; j < u->nmemb && u->memb[j] < v->uid; j++)
445                         ;
446                 if(*argv[i] == '-'){
447                         if(u->memb[j] != v->uid)
448                                 goto erropt;
449                         memmove(&u->memb[j], &u->memb[j + 1], sizeof(short) * (u->nmemb - j - 1));
450                         u->memb = erealloc(u->memb, sizeof(short) * --u->nmemb);
451                 }else{
452                         u->memb = erealloc(u->memb, sizeof(short) * ++u->nmemb);
453                         memmove(&u->memb[j + 1], &u->memb[j], sizeof(short) * (u->nmemb - j - 1));
454                         u->memb[j] = v->uid;
455                 }
456                 continue;
457         erropt:
458                 dprint("hjfs: newuser: ignoring erroneous option %s\n", argv[i]);
459         }
460         if(resort)
461                 qsort(fs->udata, fs->nudata, sizeof(User), usercomp);
462         wunlock(&fs->udatal);
463         writeusers(fs);
464         if(createdir)
465                 createuserdir(fs, argv[1], uid);
466         return 1;
467 }