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