auth: remove static local realms
[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 #include <afs/stds.h>
13
14 #include <roken.h>
15 #include <afs/opr.h>
16
17 #include <ctype.h>
18
19 #include <afs/pthread_glock.h>
20 #include <rx/xdr.h>
21 #include <rx/rx.h>
22 #include <rx/rx_identity.h>
23 #include <afs/afsutil.h>
24 #include <afs/fileutil.h>
25
26 #include "base64.h"
27 #include "auth.h"
28 #include "cellconfig.h"
29 #include "keys.h"
30 #include "afs/audit.h"
31
32 /* The display names for localauth and noauth identities; they aren't used
33  * inside tickets or anything, but just serve as something to display in logs,
34  * etc. */
35 #define AFS_LOCALAUTH_NAME "<LocalAuth>"
36 #define AFS_LOCALAUTH_LEN  (sizeof(AFS_LOCALAUTH_NAME)-1)
37 #define AFS_NOAUTH_NAME "<NoAuth>"
38 #define AFS_NOAUTH_LEN  (sizeof(AFS_NOAUTH_NAME)-1)
39
40 static int ParseLine(char *buffer, struct rx_identity *user);
41
42 static void
43 UserListFileName(struct afsconf_dir *adir,
44                  char *buffer, size_t len)
45 {
46     strcompose(buffer, len, adir->name, "/",
47                AFSDIR_ULIST_FILE, NULL);
48 }
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     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 /*!
110  * Remove an identity from the UserList file
111  *
112  * This function removes the given identity from the user list file.
113  * For the purposes of identifying entries to remove, only the
114  * type and exportedName portions of the identity are used. Callers
115  * should remember that a given identity may be listed in the file in
116  * a number of different ways.
117  *
118  * @param adir
119  *      A structure representing the configuration directory currently
120  *      in use
121  * @param user
122  *      The RX identity to delete
123  *
124  * @returns
125  *      0 on success, an error code on failure
126  */
127
128 int
129 afsconf_DeleteIdentity(struct afsconf_dir *adir, struct rx_identity *user)
130 {
131     char tbuffer[1024];
132     char nbuffer[1024];
133     char *copy;
134     FILE *tf;
135     FILE *nf;
136     int flag;
137     char *tp;
138     int found;
139     struct stat tstat;
140     struct rx_identity identity;
141     afs_int32 code;
142
143     memset(&identity, 0, sizeof(struct rx_identity));
144
145     LOCK_GLOBAL_MUTEX;
146     UserListFileName(adir, tbuffer, sizeof tbuffer);
147 #ifndef AFS_NT40_ENV
148     {
149         /*
150          * We attempt to fully resolve this pathname, so that the rename
151          * of the temporary file will work even if UserList is a symlink
152          * into a different filesystem.
153          */
154         char resolved_path[1024];
155
156         if (realpath(tbuffer, resolved_path)) {
157             strcpy(tbuffer, resolved_path);
158         }
159     }
160 #endif /* AFS_NT40_ENV */
161     tf = fopen(tbuffer, "r");
162     if (!tf) {
163         UNLOCK_GLOBAL_MUTEX;
164         return -1;
165     }
166     code = stat(tbuffer, &tstat);
167     if (code < 0) {
168         UNLOCK_GLOBAL_MUTEX;
169         return code;
170     }
171     strcpy(nbuffer, tbuffer);
172     strcat(nbuffer, ".NXX");
173     nf = fopen(nbuffer, "w+");
174     if (!nf) {
175         fclose(tf);
176         UNLOCK_GLOBAL_MUTEX;
177         return EIO;
178     }
179     flag = 0;
180     found = 0;
181     while (1) {
182         /* check for our user id */
183         tp = fgets(nbuffer, sizeof(nbuffer), tf);
184         if (tp == NULL)
185             break;
186
187         copy = strdup(nbuffer);
188         if (copy == NULL) {
189             flag = 1;
190             break;
191         }
192         code = ParseLine(copy, &identity);
193         if (code == 0 && rx_identity_match(user, &identity)) {
194             /* found the guy, don't copy to output file */
195             found = 1;
196         } else {
197             /* otherwise copy original line to output */
198             fprintf(nf, "%s", nbuffer);
199         }
200         free(copy);
201         rx_identity_freeContents(&identity);
202     }
203     fclose(tf);
204     if (ferror(nf))
205         flag = 1;
206     if (fclose(nf) == EOF)
207         flag = 1;
208     strcpy(nbuffer, tbuffer);
209     strcat(nbuffer, ".NXX");    /* generate new file name again */
210     if (flag == 0) {
211         /* try the rename */
212         flag = renamefile(nbuffer, tbuffer);
213         if (flag == 0)
214             flag = chmod(tbuffer, tstat.st_mode);
215     } else
216         unlink(nbuffer);
217
218     /* finally, decide what to return to the caller */
219     UNLOCK_GLOBAL_MUTEX;
220     if (flag)
221         return EIO;             /* something mysterious went wrong */
222     if (!found)
223         return ENOENT;          /* entry wasn't found, no changes made */
224     return 0;                   /* everything was fine */
225 }
226
227 /*!
228  * Remove a legacy Kerberos 4 name from the UserList file.
229  *
230  * This function removes a Kerberos 4 name from the super user list. It
231  * can only remove names which were added by the afsconf_AddUser interface,
232  * or with an explicit Kerberos v4 type.
233  *
234  * @param[in] adir
235  *      A structure representing the configuration directory
236  * @param[in] name
237  *      The Kerberos v4 name to remove
238  *
239  * @returns
240  *      0 on success, an error code upon failure.
241  *
242  * Note that this function is deprecated. New callers should use
243  * afsconf_DeleteIdentity instead.
244  */
245
246 int
247 afsconf_DeleteUser(struct afsconf_dir *adir, char *name)
248 {
249     struct rx_identity *user;
250     int code;
251
252     user = rx_identity_new(RX_ID_KRB4, name, name, strlen(name));
253     if (!user)
254         return ENOMEM;
255
256     code = afsconf_DeleteIdentity(adir, user);
257
258     rx_identity_free(&user);
259
260     return code;
261 }
262
263 /* This is a multi-purpose funciton for use by either
264  * GetNthIdentity or GetNthUser. The parameter 'id' indicates
265  * whether we are counting all identities (if true), or just
266  * ones which can be represented by the old-style interfaces
267  */
268 static int
269 GetNthIdentityOrUser(struct afsconf_dir *dir, int count,
270                      struct rx_identity **identity, int id)
271 {
272     bufio_p bp;
273     char tbuffer[1024];
274     struct rx_identity fileUser;
275     afs_int32 code;
276
277     LOCK_GLOBAL_MUTEX;
278     UserListFileName(dir, tbuffer, sizeof(tbuffer));
279     bp = BufioOpen(tbuffer, O_RDONLY, 0);
280     if (!bp) {
281         UNLOCK_GLOBAL_MUTEX;
282         return EIO;
283     }
284     while (1) {
285         code = BufioGets(bp, tbuffer, sizeof(tbuffer));
286         if (code < 0)
287             break;
288
289         code = ParseLine(tbuffer, &fileUser);
290         if (code != 0)
291             break;
292
293         if (id || fileUser.kind == RX_ID_KRB4)
294             count--;
295
296         if (count < 0)
297             break;
298         else
299             rx_identity_freeContents(&fileUser);
300     }
301     if (code == 0) {
302         *identity = rx_identity_copy(&fileUser);
303         rx_identity_freeContents(&fileUser);
304     }
305
306     BufioClose(bp);
307
308     UNLOCK_GLOBAL_MUTEX;
309     return code;
310 }
311
312 /*!
313  * Return the Nth super user identity from the UserList
314  *
315  * @param[in] dir
316  *      A structure representing the configuration directory
317  * @param[in] count
318  *      A count (from zero) of the entries to return from the
319  *      UserList
320  * @param[out] identity
321  *      A pointer to the Nth identity
322  * @returns
323  *      0 on success, non-zero on failure
324  */
325
326 int
327 afsconf_GetNthIdentity(struct afsconf_dir *dir, int count,
328                        struct rx_identity **identity)
329 {
330    return GetNthIdentityOrUser(dir, count, identity, 1);
331 }
332
333 /*!
334  * Return the Nth Kerberos v4 identity from the UserList
335  *
336  * This returns the Nth old, kerberos v4 style name from
337  * the UserList file. In counting entries it skips any other
338  * name types it encounters - so will hide any new-style
339  * identities from its callers.
340  *
341  * @param[in] dir
342  *      A structure representing the configuration directory
343  * @param[in] count
344  *      A count (from zero) of the entries to return from the
345  *      UserList
346  * @param abuffer
347  *      A string in which to write the name of the Nth identity
348  * @param abufferLen
349  *      The length of the buffer passed in abuffer
350  * @returns
351  *      0 on success, non-zero on failure
352  *
353  * This function is deprecated, all new callers should use
354  * GetNthIdentity instead. This function is particularly dangerous
355  * as it will hide any new-style identities from callers.
356  */
357
358 int
359 afsconf_GetNthUser(struct afsconf_dir *adir, afs_int32 an, char *abuffer,
360                    afs_int32 abufferLen)
361 {
362     struct rx_identity *identity;
363     int code;
364
365     code = GetNthIdentityOrUser(adir, an, &identity, 0);
366     if (code == 0) {
367         strlcpy(abuffer, identity->displayName, abufferLen);
368         rx_identity_free(&identity);
369     }
370     return code;
371 }
372
373 /*!
374  * Parse a UserList list
375  *
376  * Parse a line of data from a UserList file
377  *
378  * This parses a line of data in a UserList, and populates the passed
379  * rx_identity structure with the information about the user.
380  *
381  * @param buffer        A string containing the line to be parsed
382  * @param user          The user structure to be populated
383  *
384  * Note that the user->displayName, and user->exportedName.val fields
385  * must be freed with free() by the caller.
386  *
387  * This function damages the buffer thats passed to it. Callers are
388  * expected to make a copy if they want the buffer preserved.
389  *
390  * @return
391  *      0 on success, non-zero on failure.
392  */
393
394 static int
395 ParseLine(char *buffer, struct rx_identity *user)
396 {
397     char *ptr;
398     char *ename;
399     char *displayName;
400     char *decodedName;
401     char name[64+1];
402     int len;
403     int kind;
404     int code;
405
406     if (buffer[0] == ' ') { /* extended names have leading space */
407         ptr = buffer + 1;
408         code = sscanf(ptr, "%i", &kind);
409         if (code != 1)
410             return EINVAL;
411
412         strsep(&ptr, " "); /* skip the bit we just read with scanf */
413         ename = strsep(&ptr, " "); /* Pull out the ename */
414         displayName = strsep(&ptr, " "); /* Display name runs to the end */
415         if (ename == NULL || displayName == NULL)
416             return EINVAL;
417
418         decodedName = malloc(strlen(ename));
419         if (decodedName == NULL)
420             return ENOMEM;
421
422         len = base64_decode(ename, decodedName);
423         if (len<0) {
424             free(decodedName);
425             return EINVAL;
426         }
427
428         rx_identity_populate(user, kind, displayName, decodedName, len);
429         free(decodedName);
430
431         return 0; /* Success ! */
432     }
433
434     /* No extended name, try for a legacy name */
435     code = sscanf(buffer, "%64s", name);
436     if (code != 1)
437         return EINVAL;
438
439     rx_identity_populate(user, RX_ID_KRB4, name, name, strlen(name));
440     return 0;
441 }
442
443 /*!
444  * Check if a given identity is in the UserList file,
445  * and thus is a super user
446  *
447  * @param adir
448  *      A structure representing the configuration directory to check
449  * @param user
450  *      The identity to check
451  * @returns
452  *      True if the user is listed in the UserList, otherwise false
453  */
454
455 int
456 afsconf_IsSuperIdentity(struct afsconf_dir *adir,
457                         struct rx_identity *user)
458 {
459     bufio_p bp;
460     char tbuffer[1024];
461     struct rx_identity fileUser;
462     int match;
463     afs_int32 code;
464
465     UserListFileName(adir, tbuffer, sizeof tbuffer);
466     bp = BufioOpen(tbuffer, O_RDONLY, 0);
467     if (!bp)
468         return 0;
469     match = 0;
470     while (!match) {
471         code = BufioGets(bp, tbuffer, sizeof(tbuffer));
472         if (code < 0)
473             break;
474
475         code = ParseLine(tbuffer, &fileUser);
476         if (code != 0)
477            break;
478
479         match = rx_identity_match(user, &fileUser);
480
481         rx_identity_freeContents(&fileUser);
482     }
483     BufioClose(bp);
484     return match;
485 }
486
487 /* add a user to the user list, checking for duplicates */
488 int
489 afsconf_AddIdentity(struct afsconf_dir *adir, struct rx_identity *user)
490 {
491     FILE *tf;
492     afs_int32 code;
493     char *ename;
494     char tbuffer[256];
495
496     LOCK_GLOBAL_MUTEX;
497     if (afsconf_IsSuperIdentity(adir, user)) {
498         UNLOCK_GLOBAL_MUTEX;
499         return EEXIST;          /* already in the list */
500     }
501
502     UserListFileName(adir, tbuffer, sizeof tbuffer);
503     tf = fopen(tbuffer, "a+");
504     if (!tf) {
505         UNLOCK_GLOBAL_MUTEX;
506         return EIO;
507     }
508     if (user->kind == RX_ID_KRB4) {
509         fprintf(tf, "%s\n", user->displayName);
510     } else {
511         base64_encode(user->exportedName.val, user->exportedName.len,
512                       &ename);
513         fprintf(tf, " %d %s %s\n", user->kind, ename, user->displayName);
514         free(ename);
515     }
516     code = 0;
517     if (ferror(tf))
518         code = EIO;
519     if (fclose(tf))
520         code = EIO;
521     UNLOCK_GLOBAL_MUTEX;
522     return code;
523 }
524
525 int
526 afsconf_AddUser(struct afsconf_dir *adir, char *aname)
527 {
528     struct rx_identity *user;
529     int code;
530
531     user = rx_identity_new(RX_ID_KRB4, aname, aname, strlen(aname));
532     if (user == NULL)
533         return ENOMEM;
534
535     code = afsconf_AddIdentity(adir, user);
536
537     rx_identity_free(&user);
538
539     return code;
540 }
541
542 /* special CompFindUser routine that builds up a princ and then
543         calls finduser on it. If found, returns char * to user string,
544         otherwise returns NULL. The resulting string should be immediately
545         copied to other storage prior to release of mutex. */
546 static int
547 CompFindUser(struct afsconf_dir *adir, char *name, char *sep, char *inst,
548              char *realm, struct rx_identity **identity)
549 {
550     static char fullname[MAXKTCNAMELEN + MAXKTCNAMELEN + MAXKTCREALMLEN + 3];
551     struct rx_identity *testId;
552
553     /* always must have name */
554     if (!name || !name[0]) {
555         return 0;
556     }
557     strcpy(fullname, name);
558
559     /* might have instance */
560     if (inst && inst[0]) {
561         if (!sep || !sep[0]) {
562             return 0;
563         }
564
565         strcat(fullname, sep);
566         strcat(fullname, inst);
567     }
568
569     /* might have realm */
570     if (realm && realm[0]) {
571         strcat(fullname, "@");
572         strcat(fullname, realm);
573     }
574
575     testId = rx_identity_new(RX_ID_KRB4, fullname, fullname, strlen(fullname));
576     if (afsconf_IsSuperIdentity(adir, testId)) {
577         if (identity)
578              *identity = testId;
579         else
580              rx_identity_free(&testId);
581         return 1;
582     }
583
584     rx_identity_free(&testId);
585     return 0;
586 }
587
588 static int
589 kerberosSuperUser(struct afsconf_dir *adir, char *tname, char *tinst,
590                   char *tcell, struct rx_identity **identity)
591 {
592     char tcell_l[MAXKTCREALMLEN] = "";
593     char *tmp;
594     int code;
595     afs_int32 islocal;
596     int flag;
597
598     /* generate lowercased version of cell name */
599     if (tcell) {
600         strcpy(tcell_l, tcell);
601         tmp = tcell_l;
602         while (*tmp) {
603             *tmp = tolower(*tmp);
604             tmp++;
605         }
606     }
607
608     code = afsconf_IsLocalRealmMatch(adir, &islocal, tname, tinst, tcell);
609     if (code) {
610         return 0;
611     }
612
613     /* start with no authorization */
614     flag = 0;
615
616     /* localauth special case */
617     if ((tinst == NULL || strlen(tinst) == 0) &&
618         (tcell == NULL || strlen(tcell) == 0)
619         && !strcmp(tname, AUTH_SUPERUSER)) {
620         if (identity)
621             *identity = rx_identity_new(RX_ID_KRB4, AFS_LOCALAUTH_NAME,
622                                         AFS_LOCALAUTH_NAME, AFS_LOCALAUTH_LEN);
623         flag = 1;
624
625         /* cell of connection matches local cell or one of the realms */
626     } else if (islocal) {
627         if (CompFindUser(adir, tname, ".", tinst, NULL, identity)) {
628             flag = 1;
629         }
630         /* cell of conn doesn't match local cell or realm */
631     } else {
632         if (CompFindUser(adir, tname, ".", tinst, tcell, identity)) {
633             flag = 1;
634         } else if (CompFindUser(adir, tname, ".", tinst, tcell_l, identity)) {
635             flag = 1;
636         }
637     }
638
639     return flag;
640 }
641
642 static int
643 rxkadSuperUser(struct afsconf_dir *adir, struct rx_call *acall,
644                struct rx_identity **identity)
645 {
646     char tname[MAXKTCNAMELEN];  /* authentication from ticket */
647     char tinst[MAXKTCNAMELEN];
648     char tcell[MAXKTCREALMLEN];
649
650     afs_uint32 exp;
651     int code;
652
653     /* get auth details from server connection */
654     code = rxkad_GetServerInfo(rx_ConnectionOf(acall), NULL, &exp, tname,
655                                tinst, tcell, NULL);
656     if (code)
657         return 0;               /* bogus connection/other error */
658
659     return kerberosSuperUser(adir, tname, tinst, tcell, identity);
660 }
661
662 /*!
663  * Check whether the user authenticated on a given RX call is a super
664  * user or not. If they are, return a pointer to the identity of that
665  * user.
666  *
667  * @param[in] adir
668  *      The configuration directory currently in use
669  * @param[in] acall
670  *      The RX call whose authenticated identity is being checked
671  * @param[out] identity
672  *      The RX identity of the user. Caller must free this structure.
673  * @returns
674  *      True if the user is a super user, or if the server is running
675  *      in noauth mode. Otherwise, false.
676  */
677 afs_int32
678 afsconf_SuperIdentity(struct afsconf_dir *adir, struct rx_call *acall,
679                       struct rx_identity **identity)
680 {
681     struct rx_connection *tconn;
682     afs_int32 code;
683     int flag;
684
685     LOCK_GLOBAL_MUTEX;
686     if (!adir) {
687         UNLOCK_GLOBAL_MUTEX;
688         return 0;
689     }
690
691     if (afsconf_GetNoAuthFlag(adir)) {
692         if (identity)
693             *identity = rx_identity_new(RX_ID_KRB4, AFS_NOAUTH_NAME,
694                                         AFS_NOAUTH_NAME, AFS_NOAUTH_LEN);
695         UNLOCK_GLOBAL_MUTEX;
696         return 1;
697     }
698
699     tconn = rx_ConnectionOf(acall);
700     code = rx_SecurityClassOf(tconn);
701     if (code == 0) {
702         UNLOCK_GLOBAL_MUTEX;
703         return 0;               /* not authenticated at all, answer is no */
704     } else if (code == 1) {
705         /* bcrypt tokens */
706         UNLOCK_GLOBAL_MUTEX;
707         return 0;               /* not supported any longer */
708     } else if (code == 2) {
709         flag = rxkadSuperUser(adir, acall, identity);
710         UNLOCK_GLOBAL_MUTEX;
711         return flag;
712     } else {                    /* some other auth type */
713         UNLOCK_GLOBAL_MUTEX;
714         return 0;               /* mysterious, just say no */
715     }
716 }
717
718 /*!
719  * Check whether the user authenticated on a given RX call is a super
720  * user or not. If they are, return a pointer to the name of that
721  * user.
722  *
723  * @param[in] adir
724  *      The configuration directory currently in use
725  * @param[in] acall
726  *      The RX call whose authenticated identity is being checked
727  * @param[out] namep
728  *      A printable version of the name of the user
729  * @returns
730  *      True if the user is a super user, or if the server is running
731  *      in noauth mode. Otherwise, false.
732  *
733  * This function is provided for backwards compatibility. New callers
734  * should use the afsconf_SuperIdentity function.
735  */
736
737 afs_int32
738 afsconf_SuperUser(struct afsconf_dir *adir, struct rx_call *acall,
739                   char *namep)
740 {
741     struct rx_identity *identity;
742     int ret;
743
744     if (namep) {
745         ret = afsconf_SuperIdentity(adir, acall, &identity);
746         if (ret) {
747             if (identity->kind == RX_ID_KRB4) {
748                 strlcpy(namep, identity->displayName, MAXKTCNAMELEN-1);
749             } else {
750                 snprintf(namep, MAXKTCNAMELEN-1, "eName: %s",
751                          identity->displayName);
752             }
753             rx_identity_free(&identity);
754         }
755     } else {
756         ret = afsconf_SuperIdentity(adir, acall, NULL);
757     }
758
759     return ret;
760 }