auth: Restructure userok
[openafs.git] / src / auth / userok.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13
14 #include <afs/stds.h>
15 #include <afs/pthread_glock.h>
16 #include <sys/types.h>
17 #ifdef AFS_NT40_ENV
18 #include <winsock2.h>
19 #include <fcntl.h>
20 #include <io.h>
21 #else
22 #include <sys/file.h>
23 #include <netinet/in.h>
24 #include <netdb.h>
25 #include <unistd.h>
26 #endif
27 #include <sys/stat.h>
28 #include <stdlib.h>             /* for realpath() */
29 #include <errno.h>
30 #include <string.h>
31 #include <ctype.h>
32
33 #include <rx/xdr.h>
34 #include <rx/rx.h>
35 #include <stdio.h>
36 #include <afs/afsutil.h>
37 #include <afs/fileutil.h>
38
39 #ifdef AFS_ATHENA_STDENV
40 #include <krb.h>
41 #endif
42
43 #include "auth.h"
44 #include "cellconfig.h"
45 #include "keys.h"
46 #include "afs/audit.h"
47
48 #if !defined(UKERNEL)
49 int
50 afsconf_CheckAuth(void *arock, struct rx_call *acall)
51 {
52     struct afsconf_dir *adir = (struct afsconf_dir *) arock;
53     int rc;
54     LOCK_GLOBAL_MUTEX;
55     rc = ((afsconf_SuperUser(adir, acall, NULL) == 0) ? 10029 : 0);
56     UNLOCK_GLOBAL_MUTEX;
57     return rc;
58 }
59 #endif /* !defined(UKERNEL) */
60
61 static int
62 GetNoAuthFlag(struct afsconf_dir *adir)
63 {
64     if (access(AFSDIR_SERVER_NOAUTH_FILEPATH, 0) == 0) {
65         osi_audit(NoAuthEvent, 0, AUD_END);     /* some random server is running noauth */
66         return 1;               /* if /usr/afs/local/NoAuth file exists, allow access */
67     }
68     return 0;
69 }
70
71
72 int
73 afsconf_GetNoAuthFlag(struct afsconf_dir *adir)
74 {
75     int rc;
76
77     LOCK_GLOBAL_MUTEX;
78     rc = GetNoAuthFlag(adir);
79     UNLOCK_GLOBAL_MUTEX;
80     return rc;
81 }
82
83 void
84 afsconf_SetNoAuthFlag(struct afsconf_dir *adir, int aflag)
85 {
86     afs_int32 code;
87
88     LOCK_GLOBAL_MUTEX;
89     if (aflag == 0) {
90         /* turn off noauth flag */
91         code = (unlink(AFSDIR_SERVER_NOAUTH_FILEPATH) ? errno : 0);
92         osi_audit(NoAuthDisableEvent, code, AUD_END);
93     } else {
94         /* try to create file */
95         code =
96             open(AFSDIR_SERVER_NOAUTH_FILEPATH, O_CREAT | O_TRUNC | O_RDWR,
97                  0666);
98         if (code >= 0) {
99             close(code);
100             osi_audit(NoAuthEnableEvent, 0, AUD_END);
101         } else
102             osi_audit(NoAuthEnableEvent, errno, AUD_END);
103     }
104     UNLOCK_GLOBAL_MUTEX;
105 }
106
107 /* deletes a user from the UserList file */
108 int
109 afsconf_DeleteUser(struct afsconf_dir *adir, char *auser)
110 {
111     char tbuffer[1024];
112     char nbuffer[1024];
113     FILE *tf;
114     FILE *nf;
115     int flag;
116     char tname[64 + 1];
117     char *tp;
118     int found;
119     struct stat tstat;
120     afs_int32 code;
121
122     LOCK_GLOBAL_MUTEX;
123     strcompose(tbuffer, sizeof tbuffer, adir->name, "/",
124                AFSDIR_ULIST_FILE, NULL);
125 #ifndef AFS_NT40_ENV
126     {
127         /*
128          * We attempt to fully resolve this pathname, so that the rename
129          * of the temporary file will work even if UserList is a symlink
130          * into a different filesystem.
131          */
132         char resolved_path[1024];
133
134         if (realpath(tbuffer, resolved_path)) {
135             strcpy(tbuffer, resolved_path);
136         }
137     }
138 #endif /* AFS_NT40_ENV */
139     tf = fopen(tbuffer, "r");
140     if (!tf) {
141         UNLOCK_GLOBAL_MUTEX;
142         return -1;
143     }
144     code = stat(tbuffer, &tstat);
145     if (code < 0) {
146         UNLOCK_GLOBAL_MUTEX;
147         return code;
148     }
149     strcpy(nbuffer, tbuffer);
150     strcat(nbuffer, ".NXX");
151     nf = fopen(nbuffer, "w+");
152     if (!nf) {
153         fclose(tf);
154         UNLOCK_GLOBAL_MUTEX;
155         return EIO;
156     }
157     flag = 0;
158     found = 0;
159     while (1) {
160         /* check for our user id */
161         tp = fgets(nbuffer, sizeof(nbuffer), tf);
162         if (tp == NULL)
163             break;
164         code = sscanf(nbuffer, "%64s", tname);
165         if (code == 1 && strcmp(tname, auser) == 0) {
166             /* found the guy, don't copy to output file */
167             found = 1;
168         } else {
169             /* otherwise copy original line  to output */
170             fprintf(nf, "%s", nbuffer);
171         }
172     }
173     fclose(tf);
174     if (ferror(nf))
175         flag = 1;
176     if (fclose(nf) == EOF)
177         flag = 1;
178     strcpy(nbuffer, tbuffer);
179     strcat(nbuffer, ".NXX");    /* generate new file name again */
180     if (flag == 0) {
181         /* try the rename */
182         flag = renamefile(nbuffer, tbuffer);
183         if (flag == 0)
184             flag = chmod(tbuffer, tstat.st_mode);
185     } else
186         unlink(nbuffer);
187
188     /* finally, decide what to return to the caller */
189     UNLOCK_GLOBAL_MUTEX;
190     if (flag)
191         return EIO;             /* something mysterious went wrong */
192     if (!found)
193         return ENOENT;          /* entry wasn't found, no changes made */
194     return 0;                   /* everything was fine */
195 }
196
197 /* returns nth super user from the UserList file */
198 int
199 afsconf_GetNthUser(struct afsconf_dir *adir, afs_int32 an, char *abuffer,
200                    afs_int32 abufferLen)
201 {
202     char tbuffer[256];
203     FILE *tf;
204     char tname[64 + 1];
205     char *tp;
206     int flag;
207     afs_int32 code;
208
209     LOCK_GLOBAL_MUTEX;
210     strcompose(tbuffer, sizeof tbuffer, adir->name, "/",
211                AFSDIR_ULIST_FILE, NULL);
212     tf = fopen(tbuffer, "r");
213     if (!tf) {
214         UNLOCK_GLOBAL_MUTEX;
215         return 1;
216     }
217     flag = 1;
218     while (1) {
219         /* check for our user id */
220         tp = fgets(tbuffer, sizeof(tbuffer), tf);
221         if (tp == NULL)
222             break;
223         code = sscanf(tbuffer, "%64s", tname);
224         if (code == 1 && an-- == 0) {
225             flag = 0;
226             break;
227         }
228     }
229     if (flag == 0)
230         strcpy(abuffer, tname);
231     fclose(tf);
232     UNLOCK_GLOBAL_MUTEX;
233     return flag;
234 }
235
236 /* returns true iff user is in the UserList file */
237 static int
238 FindUser(struct afsconf_dir *adir, char *auser)
239 {
240     char tbuffer[256];
241     bufio_p bp;
242     char tname[64 + 1];
243     int flag;
244     afs_int32 code;
245     int rc;
246
247     strcompose(tbuffer, sizeof tbuffer, adir->name, "/", AFSDIR_ULIST_FILE,
248                NULL);
249     bp = BufioOpen(tbuffer, O_RDONLY, 0);
250     if (!bp)
251         return 0;
252     flag = 0;
253     while (1) {
254         /* check for our user id */
255         rc = BufioGets(bp, tbuffer, sizeof(tbuffer));
256         if (rc < 0)
257             break;
258         code = sscanf(tbuffer, "%64s", tname);
259         if (code == 1 && strcmp(tname, auser) == 0) {
260             flag = 1;
261             break;
262         }
263     }
264     BufioClose(bp);
265     return flag;
266 }
267
268 /* add a user to the user list, checking for duplicates */
269 int
270 afsconf_AddUser(struct afsconf_dir *adir, char *aname)
271 {
272     FILE *tf;
273     afs_int32 code;
274     char tbuffer[256];
275
276     LOCK_GLOBAL_MUTEX;
277     if (FindUser(adir, aname)) {
278         UNLOCK_GLOBAL_MUTEX;
279         return EEXIST;          /* already in the list */
280     }
281
282     strcompose(tbuffer, sizeof tbuffer, adir->name, "/", AFSDIR_ULIST_FILE,
283                NULL);
284     tf = fopen(tbuffer, "a+");
285     if (!tf) {
286         UNLOCK_GLOBAL_MUTEX;
287         return EIO;
288     }
289     fprintf(tf, "%s\n", aname);
290     code = 0;
291     if (ferror(tf))
292         code = EIO;
293     if (fclose(tf))
294         code = EIO;
295     UNLOCK_GLOBAL_MUTEX;
296     return code;
297 }
298
299 /* special CompFindUser routine that builds up a princ and then
300         calls finduser on it. If found, returns char * to user string,
301         otherwise returns NULL. The resulting string should be immediately
302         copied to other storage prior to release of mutex. */
303 static char *
304 CompFindUser(struct afsconf_dir *adir, char *name, char *sep, char *inst,
305              char *realm)
306 {
307     static char fullname[MAXKTCNAMELEN + MAXKTCNAMELEN + MAXKTCREALMLEN + 3];
308
309     /* always must have name */
310     if (!name || !name[0]) {
311         return NULL;
312     }
313     strcpy(fullname, name);
314
315     /* might have instance */
316     if (inst && inst[0]) {
317         if (!sep || !sep[0]) {
318             return NULL;
319         }
320
321         strcat(fullname, sep);
322         strcat(fullname, inst);
323     }
324
325     /* might have realm */
326     if (realm && realm[0]) {
327         strcat(fullname, "@");
328         strcat(fullname, realm);
329     }
330
331     if (FindUser(adir, fullname)) {
332         return fullname;
333     } else {
334         return NULL;
335     }
336 }
337
338 static int
339 kerberosSuperUser(struct afsconf_dir *adir, char *tname, char *tinst,
340                   char *tcell, char *namep)
341 {
342     char tcell_l[MAXKTCREALMLEN];
343     char *tmp;
344
345     /* keep track of which one actually authorized request */
346     char uname[MAXKTCNAMELEN + MAXKTCNAMELEN + MAXKTCREALMLEN + 3];
347
348     static char lcell[MAXCELLCHARS] = "";
349     static char lrealms[AFS_NUM_LREALMS][AFS_REALM_SZ];
350     static int  num_lrealms = -1;
351     int lrealm_match = 0, i;
352     int flag;
353
354     /* generate lowercased version of cell name */
355     strcpy(tcell_l, tcell);
356     tmp = tcell_l;
357     while (*tmp) {
358         *tmp = tolower(*tmp);
359         tmp++;
360     }
361
362     /* determine local cell name. It's static, so will only get
363      * calculated the first time through */
364     if (!lcell[0])
365         afsconf_GetLocalCell(adir, lcell, sizeof(lcell));
366
367     /* if running a krb environment, also get the local realm */
368     /* note - this assumes AFS_REALM_SZ <= MAXCELLCHARS */
369     /* just set it to lcell if it fails */
370     if (num_lrealms == -1) {
371         for (i=0; i<AFS_NUM_LREALMS; i++) {
372             if (afs_krb_get_lrealm(lrealms[i], i) != 0 /*KSUCCESS*/)
373                 break;
374         }
375
376         if (i == 0) {
377             strncpy(lrealms[0], lcell, AFS_REALM_SZ);
378             num_lrealms = 1;
379         } else {
380             num_lrealms = i;
381         }
382     }
383
384     /* See if the ticket cell matches one of the local realms */
385     lrealm_match = 0;
386     for ( i=0;i<num_lrealms;i++ ) {
387         if (!strcasecmp(lrealms[i], tcell)) {
388             lrealm_match = 1;
389             break;
390         }
391     }
392
393     /* If yes, then make sure that the name is not present in
394      * an exclusion list */
395     if (lrealm_match) {
396         if (tinst[0])
397             snprintf(uname,sizeof(uname),"%s.%s@%s",tname,tinst,tcell);
398         else
399             snprintf(uname,sizeof(uname),"%s@%s",tname,tcell);
400
401         if (afs_krb_exclusion(uname))
402             lrealm_match = 0;
403     }
404
405     /* start with no uname and no authorization */
406     strcpy(uname, "");
407     flag = 0;
408
409     /* localauth special case */
410     if (strlen(tinst) == 0 && strlen(tcell) == 0
411         && !strcmp(tname, AUTH_SUPERUSER)) {
412         strcpy(uname, "<LocalAuth>");
413         flag = 1;
414
415         /* cell of connection matches local cell or one of the realms */
416     } else if (!strcasecmp(tcell, lcell) || lrealm_match) {
417         if ((tmp = CompFindUser(adir, tname, ".", tinst, NULL))) {
418             strcpy(uname, tmp);
419             flag = 1;
420 #ifdef notyet
421         } else if ((tmp = CompFindUser(adir, tname, "/", tinst, NULL))) {
422             strcpy(uname, tmp);
423             flag = 1;
424 #endif
425         }
426         /* cell of conn doesn't match local cell or realm */
427     } else {
428         if ((tmp = CompFindUser(adir, tname, ".", tinst, tcell))) {
429             strcpy(uname, tmp);
430             flag = 1;
431 #ifdef notyet
432         } else if ((tmp = CompFindUser(adir, tname, "/", tinst, tcell))) {
433             strcpy(uname, tmp);
434             flag = 1;
435 #endif
436         } else if ((tmp = CompFindUser(adir, tname, ".", tinst, tcell_l))) {
437             strcpy(uname, tmp);
438             flag = 1;
439 #ifdef notyet
440         } else if ((tmp = CompFindUser(adir, tname, "/", tinst, tcell_l))) {
441             strcpy(uname, tmp);
442             flag = 1;
443 #endif
444         }
445     }
446
447     if (namep)
448         strcpy(namep, uname);
449
450     return flag;
451 }
452
453 static int
454 rxkadSuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
455 {
456     char tname[MAXKTCNAMELEN];  /* authentication from ticket */
457     char tinst[MAXKTCNAMELEN];
458     char tcell[MAXKTCREALMLEN];
459
460     afs_uint32 exp;
461     int code;
462
463     /* get auth details from server connection */
464     code = rxkad_GetServerInfo(acall->conn, NULL, &exp, tname, tinst, tcell,
465                                NULL);
466     if (code)
467         return 0;               /* bogus connection/other error */
468
469     /* don't bother checking anything else if tix have expired */
470 #ifdef AFS_PTHREAD_ENV
471     if (exp < clock_Sec())
472 #else
473     if (exp < FT_ApproxTime())
474 #endif
475         return 0;               /* expired tix */
476
477     return kerberosSuperUser(adir, tname, tinst, tcell, namep);
478 }
479
480 /* make sure user authenticated on rx call acall is in list of valid
481     users. Copy the "real name" of the authenticated user into namep
482     if a pointer is passed.
483 */
484 afs_int32
485 afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
486 {
487     struct rx_connection *tconn;
488     afs_int32 code;
489     int flag;
490
491     LOCK_GLOBAL_MUTEX;
492     if (!adir) {
493         UNLOCK_GLOBAL_MUTEX;
494         return 0;
495     }
496
497     if (afsconf_GetNoAuthFlag(adir)) {
498         if (namep)
499             strcpy(namep, "<NoAuth>");
500         UNLOCK_GLOBAL_MUTEX;
501         return 1;
502     }
503
504     tconn = rx_ConnectionOf(acall);
505     code = rx_SecurityClassOf(tconn);
506     if (code == 0) {
507         UNLOCK_GLOBAL_MUTEX;
508         return 0;               /* not authenticated at all, answer is no */
509     } else if (code == 1) {
510         /* bcrypt tokens */
511         UNLOCK_GLOBAL_MUTEX;
512         return 0;               /* not supported any longer */
513     } else if (code == 2) {
514         flag = rxkadSuperUser(adir, acall, namep);
515         UNLOCK_GLOBAL_MUTEX;
516         return flag;
517     } else {                    /* some other auth type */
518         UNLOCK_GLOBAL_MUTEX;
519         return 0;               /* mysterious, just say no */
520     }
521 }