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