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