checkauth-unlock-before-return-20080228
[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 RCSID
14     ("$Header$");
15
16 #include <afs/stds.h>
17 #include <afs/pthread_glock.h>
18 #include <sys/types.h>
19 #ifdef AFS_NT40_ENV
20 #include <winsock2.h>
21 #include <fcntl.h>
22 #include <io.h>
23 #else
24 #include <sys/file.h>
25 #include <netinet/in.h>
26 #include <netdb.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(register struct afsconf_dir *adir, 
52                   register struct rx_call *acall)
53 {
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     register 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, register char *auser)
111 {
112     char tbuffer[1024];
113     char nbuffer[1024];
114     register FILE *tf;
115     register FILE *nf;
116     register int flag;
117     char tname[64 + 1];
118     char *tp;
119     int found;
120     struct stat tstat;
121     register 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     register FILE *tf;
205     char tname[64 + 1];
206     register char *tp;
207     register int flag;
208     register 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, register char *auser)
240 {
241     char tbuffer[256];
242     register bufio_p bp;
243     char tname[64 + 1];
244     register int flag;
245     register 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     register 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
340 /* make sure user authenticated on rx call acall is in list of valid
341     users. Copy the "real name" of the authenticated user into namep
342     if a pointer is passed.
343 */
344 afs_int32
345 afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
346 {
347     register struct rx_connection *tconn;
348     register afs_int32 code;
349     int flag;
350
351     LOCK_GLOBAL_MUTEX;
352     if (!adir) {
353         UNLOCK_GLOBAL_MUTEX;
354         return 0;
355     }
356
357     if (afsconf_GetNoAuthFlag(adir)) {
358         if (namep)
359             strcpy(namep, "<NoAuth>");
360         UNLOCK_GLOBAL_MUTEX;
361         return 1;
362     }
363
364     tconn = rx_ConnectionOf(acall);
365     code = rx_SecurityClassOf(tconn);
366     if (code == 0) {
367         UNLOCK_GLOBAL_MUTEX;
368         return 0;               /* not authenticated at all, answer is no */
369     } else if (code == 1) {
370         /* bcrypt tokens */
371         UNLOCK_GLOBAL_MUTEX;
372         return 0;               /* not supported any longer */
373     } else if (code == 2) {
374         char tname[MAXKTCNAMELEN];      /* authentication from ticket */
375         char tinst[MAXKTCNAMELEN];
376         char tcell[MAXKTCREALMLEN];
377         char tcell_l[MAXKTCREALMLEN];
378         char *tmp;
379
380         /* keep track of which one actually authorized request */
381         char uname[MAXKTCNAMELEN + MAXKTCNAMELEN + MAXKTCREALMLEN + 3];
382
383         afs_uint32 exp;
384         static char lcell[MAXCELLCHARS] = "";
385         static char lrealms[AFS_NUM_LREALMS][AFS_REALM_SZ];
386         static int  num_lrealms = -1;
387         int lrealm_match = 0, i;
388
389         /* get auth details from server connection */
390         code =
391             rxkad_GetServerInfo(acall->conn, NULL, &exp, tname, tinst, tcell,
392                                 NULL);
393         if (code) {
394             UNLOCK_GLOBAL_MUTEX;
395             return 0;           /* bogus connection/other error */
396         }
397
398         /* don't bother checking anything else if tix have expired */
399 #ifdef AFS_PTHREAD_ENV
400         if (exp < clock_Sec()) {
401 #else
402         if (exp < FT_ApproxTime()) {
403 #endif
404             UNLOCK_GLOBAL_MUTEX;
405             return 0;           /* expired tix */
406         }
407
408         /* generate lowercased version of cell name */
409         strcpy(tcell_l, tcell);
410         tmp = tcell_l;
411         while (*tmp) {
412             *tmp = tolower(*tmp);
413             tmp++;
414         }
415
416         /* determine local cell name. It's static, so will only get
417          * calculated the first time through */
418         if (!lcell[0])
419             afsconf_GetLocalCell(adir, lcell, sizeof(lcell));
420
421         /* if running a krb environment, also get the local realm */
422         /* note - this assumes AFS_REALM_SZ <= MAXCELLCHARS */
423         /* just set it to lcell if it fails */
424         if (num_lrealms == -1) {
425             for (i=0; i<AFS_NUM_LREALMS; i++) {
426                 if (afs_krb_get_lrealm(lrealms[i], i) != 0 /*KSUCCESS*/)
427                     break;
428             }
429
430             if (i == 0) {
431                 strncpy(lrealms[0], lcell, AFS_REALM_SZ);
432                 num_lrealms = 1;
433             } else {
434                 num_lrealms = i;
435             }
436         }
437
438         /* See if the ticket cell matches one of the local realms */
439         lrealm_match = 0;
440         for ( i=0;i<num_lrealms;i++ ) {
441             if (!strcasecmp(lrealms[i], tcell)) {
442                 lrealm_match = 1;
443                 break;
444             }
445         }
446
447         /* If yes, then make sure that the name is not present in 
448          * an exclusion list */
449         if (lrealm_match) {
450             if (tinst[0])
451                 snprintf(uname,sizeof(uname),"%s.%s@%s",tname,tinst,tcell);
452             else
453                 snprintf(uname,sizeof(uname),"%s@%s",tname,tcell);
454
455             if (afs_krb_exclusion(uname))
456                 lrealm_match = 0;
457         }
458
459         /* start with no uname and no authorization */
460         strcpy(uname, "");
461         flag = 0;
462
463         /* localauth special case */
464         if (strlen(tinst) == 0 && strlen(tcell) == 0
465             && !strcmp(tname, AUTH_SUPERUSER)) {
466             strcpy(uname, "<LocalAuth>");
467             flag = 1;
468
469             /* cell of connection matches local cell or one of the realms */
470         } else if (!strcasecmp(tcell, lcell) || lrealm_match) {
471             if ((tmp = CompFindUser(adir, tname, ".", tinst, NULL))) {
472                 strcpy(uname, tmp);
473                 flag = 1;
474 #ifdef notyet
475             } else if ((tmp = CompFindUser(adir, tname, "/", tinst, NULL))) {
476                 strcpy(uname, tmp);
477                 flag = 1;
478 #endif
479             }
480             /* cell of conn doesn't match local cell or realm */
481         } else {
482             if ((tmp = CompFindUser(adir, tname, ".", tinst, tcell))) {
483                 strcpy(uname, tmp);
484                 flag = 1;
485 #ifdef notyet
486             } else if ((tmp = CompFindUser(adir, tname, "/", tinst, tcell))) {
487                 strcpy(uname, tmp);
488                 flag = 1;
489 #endif
490             } else if ((tmp = CompFindUser(adir, tname, ".", tinst, tcell_l))) {
491                 strcpy(uname, tmp);
492                 flag = 1;
493 #ifdef notyet
494             } else if ((tmp = CompFindUser(adir, tname, "/", tinst, tcell_l))) {
495                 strcpy(uname, tmp);
496                 flag = 1;
497 #endif
498             }
499         }
500
501         if (namep)
502             strcpy(namep, uname);
503         UNLOCK_GLOBAL_MUTEX;
504         return flag;
505     } else {                    /* some other auth type */
506         UNLOCK_GLOBAL_MUTEX;
507         return 0;               /* mysterious, just say no */
508     }
509 }