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