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