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