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