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