36260e55f5ffc946388b80444884f897ab8567a2
[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     strcpy(tcell_l, tcell);
357     tmp = tcell_l;
358     while (*tmp) {
359         *tmp = tolower(*tmp);
360         tmp++;
361     }
362
363     /* determine local cell name. It's static, so will only get
364      * calculated the first time through */
365     if (!lcell[0])
366         afsconf_GetLocalCell(adir, lcell, sizeof(lcell));
367
368     /* if running a krb environment, also get the local realm */
369     /* note - this assumes AFS_REALM_SZ <= MAXCELLCHARS */
370     /* just set it to lcell if it fails */
371     if (num_lrealms == -1) {
372         for (i=0; i<AFS_NUM_LREALMS; i++) {
373             if (afs_krb_get_lrealm(lrealms[i], i) != 0 /*KSUCCESS*/)
374                 break;
375         }
376
377         if (i == 0) {
378             strncpy(lrealms[0], lcell, AFS_REALM_SZ);
379             num_lrealms = 1;
380         } else {
381             num_lrealms = i;
382         }
383     }
384
385     /* See if the ticket cell matches one of the local realms */
386     lrealm_match = 0;
387     for ( i=0;i<num_lrealms;i++ ) {
388         if (!strcasecmp(lrealms[i], tcell)) {
389             lrealm_match = 1;
390             break;
391         }
392     }
393
394     /* If yes, then make sure that the name is not present in
395      * an exclusion list */
396     if (lrealm_match) {
397         if (tinst[0])
398             snprintf(uname,sizeof(uname),"%s.%s@%s",tname,tinst,tcell);
399         else
400             snprintf(uname,sizeof(uname),"%s@%s",tname,tcell);
401
402         if (afs_krb_exclusion(uname))
403             lrealm_match = 0;
404     }
405
406     /* start with no uname and no authorization */
407     strcpy(uname, "");
408     flag = 0;
409
410     /* localauth special case */
411     if (strlen(tinst) == 0 && strlen(tcell) == 0
412         && !strcmp(tname, AUTH_SUPERUSER)) {
413         strcpy(uname, "<LocalAuth>");
414         flag = 1;
415
416         /* cell of connection matches local cell or one of the realms */
417     } else if (!strcasecmp(tcell, lcell) || lrealm_match) {
418         if ((tmp = CompFindUser(adir, tname, ".", tinst, NULL))) {
419             strcpy(uname, tmp);
420             flag = 1;
421 #ifdef notyet
422         } else if ((tmp = CompFindUser(adir, tname, "/", tinst, NULL))) {
423             strcpy(uname, tmp);
424             flag = 1;
425 #endif
426         }
427         /* cell of conn doesn't match local cell or realm */
428     } else {
429         if ((tmp = CompFindUser(adir, tname, ".", tinst, tcell))) {
430             strcpy(uname, tmp);
431             flag = 1;
432 #ifdef notyet
433         } else if ((tmp = CompFindUser(adir, tname, "/", tinst, tcell))) {
434             strcpy(uname, tmp);
435             flag = 1;
436 #endif
437         } else if ((tmp = CompFindUser(adir, tname, ".", tinst, tcell_l))) {
438             strcpy(uname, tmp);
439             flag = 1;
440 #ifdef notyet
441         } else if ((tmp = CompFindUser(adir, tname, "/", tinst, tcell_l))) {
442             strcpy(uname, tmp);
443             flag = 1;
444 #endif
445         }
446     }
447
448     if (namep)
449         strcpy(namep, uname);
450
451     return flag;
452 }
453
454 static int
455 rxkadSuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
456 {
457     char tname[MAXKTCNAMELEN];  /* authentication from ticket */
458     char tinst[MAXKTCNAMELEN];
459     char tcell[MAXKTCREALMLEN];
460
461     afs_uint32 exp;
462     int code;
463
464     /* get auth details from server connection */
465     code = rxkad_GetServerInfo(acall->conn, NULL, &exp, tname, tinst, tcell,
466                                NULL);
467     if (code)
468         return 0;               /* bogus connection/other error */
469
470     return kerberosSuperUser(adir, tname, tinst, tcell, namep);
471 }
472
473 /* make sure user authenticated on rx call acall is in list of valid
474     users. Copy the "real name" of the authenticated user into namep
475     if a pointer is passed.
476 */
477 afs_int32
478 afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall, char *namep)
479 {
480     struct rx_connection *tconn;
481     afs_int32 code;
482     int flag;
483
484     LOCK_GLOBAL_MUTEX;
485     if (!adir) {
486         UNLOCK_GLOBAL_MUTEX;
487         return 0;
488     }
489
490     if (afsconf_GetNoAuthFlag(adir)) {
491         if (namep)
492             strcpy(namep, "<NoAuth>");
493         UNLOCK_GLOBAL_MUTEX;
494         return 1;
495     }
496
497     tconn = rx_ConnectionOf(acall);
498     code = rx_SecurityClassOf(tconn);
499     if (code == 0) {
500         UNLOCK_GLOBAL_MUTEX;
501         return 0;               /* not authenticated at all, answer is no */
502     } else if (code == 1) {
503         /* bcrypt tokens */
504         UNLOCK_GLOBAL_MUTEX;
505         return 0;               /* not supported any longer */
506     } else if (code == 2) {
507         flag = rxkadSuperUser(adir, acall, namep);
508         UNLOCK_GLOBAL_MUTEX;
509         return flag;
510     } else {                    /* some other auth type */
511         UNLOCK_GLOBAL_MUTEX;
512         return 0;               /* mysterious, just say no */
513     }
514 }