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