1dc4a517e20149dcc369a289d54af6ff0e86ce0b
[openafs.git] / src / auth / cellconfig.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 RCSID
14     ("$Header$");
15
16 #include <afs/stds.h>
17 #include <afs/pthread_glock.h>
18 #ifdef UKERNEL
19 #include "afs/sysincludes.h"
20 #include "afsincludes.h"
21 #include "des/des.h"
22 #include "rx/rxkad.h"
23 #include <netdb.h>
24 #else /* UKERNEL */
25 #include <sys/types.h>
26 #ifdef AFS_NT40_ENV
27 #include <winsock2.h>
28 #include <sys/utime.h>
29 #include <io.h>
30 #include <WINNT/afssw.h>
31 #else
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <netdb.h>
35 #include <sys/file.h>
36 #include <sys/time.h>
37 #ifdef AFS_AFSDB_ENV
38 #include <arpa/nameser.h>
39 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
40 #include <arpa/nameser_compat.h>
41 #endif
42 #include <resolv.h>
43 #endif /* AFS_AFSDB_ENV */
44 #endif /* AFS_NT40_ENV */
45 #include <afs/afsint.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <time.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <sys/stat.h>
52 #include <fcntl.h>
53 #include <string.h>
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57 #include <rx/rxkad.h>
58 #include <rx/rx.h>
59 #endif /* UKERNEL */
60 #include <afs/afsutil.h>
61 #include "cellconfig.h"
62 #include "keys.h"
63 #ifdef AFS_NT40_ENV
64 #ifdef AFS_AFSDB_ENV
65 #include <cm.h>
66 #include <cm_config.h>
67 /* cm_dns.h depends on cellconfig.h */
68 #include <cm_nls.h>
69 #include <cm_dns.h>
70 #endif /* AFS_AFSDB_ENV */
71 #endif
72 #include <rx/rx.h>
73 #include <rx/rxkad.h>
74
75 static struct afsconf_servPair serviceTable[] = {
76     {"afs", 7000,},
77     {"afscb", 7001,},
78     {"afsprot", 7002,},
79     {"afsvldb", 7003,},
80     {"afskauth", 7004,},
81     {"afsvol", 7005,},
82     {"afserror", 7006,},
83     {"afsnanny", 7007,},
84     {"afsupdate", 7008,},
85     {"afsrmtsys", 7009,},
86     {"afsres", 7010,},          /* residency database for MR-AFS */
87     {"afsremio", 7011,},        /* remote I/O interface for MR-AFS */
88     {0, 0}                      /* insert new services before this spot */
89 };
90
91 /* Prototypes */
92 static afs_int32 afsconf_FindService(register const char *aname);
93 static int TrimLine(char *abuffer, int abufsize);
94 #ifdef AFS_NT40_ENV
95 static int IsClientConfigDirectory(const char *path);
96 static int GetCellNT(struct afsconf_dir *adir);
97 #endif
98 static int afsconf_Check(register struct afsconf_dir *adir);
99 static int afsconf_Touch(register struct afsconf_dir *adir);
100 static int GetCellUnix(struct afsconf_dir *adir);
101 static int afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
102                                 char clones[]);
103 static int ParseHostLine(char *aline, register struct sockaddr_in *addr,
104                          char *aname, char *aclone);
105 static int ParseCellLine(register char *aline, register char *aname,
106                          register char *alname);
107 static int afsconf_CloseInternal(register struct afsconf_dir *adir);
108 static int afsconf_Reopen(register struct afsconf_dir *adir);
109 static int SaveKeys(struct afsconf_dir *adir);
110
111 #ifndef T_AFSDB
112 #define T_AFSDB 18              /* per RFC1183 section 1 */
113 #endif
114
115 /*
116  * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
117  * our code can tell if there is new info in the key files, the cell server db
118  * files or any of the other files (and reopen the thing) if the date on
119  * CellServDB changes.
120  */
121
122 #if defined(AFS_SUN5_ENV) && !defined(__sparcv9)
123 /* Solaris through 10 in 32 bit mode will return EMFILE if fopen can't
124    get an fd <= 255. We allow the fileserver to claim more fds than that.
125    This has always been a problem since pr_Initialize would have the same
126    issue, but hpr_Initialize makes it more likely that we would see this. 
127    Work around it. This is not generic. It's coded with the needs of
128    afsconf_* in mind only.
129
130    http://www.opensolaris.org/os/community/onnv/flag-days/pages/2006042001/
131 */
132
133 #define BUFFER 4096
134
135 struct afsconf_iobuffer {
136     int _file;
137     char *buffer;
138     char *ptr;
139     char *endptr;
140 };
141
142 typedef struct afsconf_iobuffer afsconf_FILE;
143
144 static afsconf_FILE *
145 afsconf_fopen(const char *fname, const char *fmode)
146 {
147     int fd;
148     afsconf_FILE *iop;
149     
150     if ((fd = open(fname, O_RDONLY)) == -1) {
151         return NULL;
152     }
153     
154     iop = malloc(sizeof(struct afsconf_iobuffer));
155     if (iop == NULL) {
156         (void) close(fd);
157         errno = ENOMEM;
158         return NULL;
159     }
160     iop->_file = fd;
161     iop->buffer = malloc(BUFFER);
162     if (iop->buffer == NULL) {
163         (void) close(fd);
164         free((void *) iop);
165         errno = ENOMEM;
166         return NULL;
167     }
168     iop->ptr = iop->buffer;
169     iop->endptr = iop->buffer;
170     return iop;
171 }
172
173 static int
174 afsconf_fclose(afsconf_FILE *iop)
175 {
176     if (iop == NULL) {
177         return 0;
178     }
179     close(iop->_file);
180     free((void *)iop->buffer);
181     free((void *)iop);
182     return 0;
183 }
184
185 static char *
186 afsconf_fgets(char *s, int n, afsconf_FILE *iop)
187 {
188     char *p;
189     
190     p = s;
191     for (;;) {
192         char c;
193         
194         if (iop->ptr == iop->endptr) {
195             ssize_t len;
196             
197             if ((len = read(iop->_file, (void *)iop->buffer, BUFFER)) == -1) {
198                 return NULL;
199             }
200             if (len == 0) {
201                 *p = 0;
202                 if (s == p) {
203                     return NULL;
204                 }
205                 return s;
206             }
207             iop->ptr = iop->buffer;
208             iop->endptr = iop->buffer + len;
209         }
210         c = *iop->ptr++;
211         *p++ = c;
212         if ((p - s) == (n - 1)) {
213             *p = 0;
214             return s;
215         }
216         if (c == '\n') {
217             *p = 0;
218             return s;
219         }
220     }
221 }
222 #define fopen afsconf_fopen
223 #define fclose afsconf_fclose
224 #define fgets afsconf_fgets
225 #else
226 #define afsconf_FILE FILE
227 #endif /* AFS_SUN5_ENV && ! __sparcv9 */
228
229 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
230 static afs_int32
231 afsconf_FindService(register const char *aname)
232 {
233     /* lookup a service name */
234     struct servent *ts;
235     register struct afsconf_servPair *tsp;
236
237 #if     defined(AFS_OSF_ENV) 
238     ts = getservbyname(aname, "");
239 #else
240     ts = (struct servent *) getservbyname(aname, NULL);
241 #endif
242     if (ts) {
243         /* we found it in /etc/services, so we use this value */
244         return ts->s_port;      /* already in network byte order */
245     }
246
247     /* not found in /etc/services, see if it is one of ours */
248     for (tsp = serviceTable;; tsp++) {
249         if (tsp->name == NULL)
250             return -1;
251         if (!strcmp(tsp->name, aname))
252             return htons(tsp->port);
253     }
254 }
255
256 static int
257 TrimLine(char *abuffer, int abufsize)
258 {
259     char tbuffer[256];
260     register char *tp;
261     register int tc;
262
263     tp = abuffer;
264     while ((tc = *tp)) {
265         if (!isspace(tc))
266             break;
267         tp++;
268     }
269     strlcpy(tbuffer, tp, sizeof tbuffer);
270     strlcpy(abuffer, tbuffer, abufsize);
271     return 0;
272 }
273
274 #ifdef AFS_NT40_ENV
275 /*
276  * IsClientConfigDirectory() -- determine if path matches well-known
277  *     client configuration directory.
278  */
279 static int
280 IsClientConfigDirectory(const char *path)
281 {
282     const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
283     int i;
284
285     for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
286         int cc = tolower(cdir[i]);
287         int pc = tolower(path[i]);
288
289         if (cc == '\\') {
290             cc = '/';
291         }
292         if (pc == '\\') {
293             pc = '/';
294         }
295         if (cc != pc) {
296             return 0;
297         }
298     }
299
300     /* hit end of one or both; allow mismatch in existence of trailing slash */
301     if (cdir[i] != '\0') {
302         if ((cdir[i] != '\\' && cdir[i] != '/') || (cdir[i + 1] != '\0')) {
303             return 0;
304         }
305     }
306     if (path[i] != '\0') {
307         if ((path[i] != '\\' && path[i] != '/') || (path[i + 1] != '\0')) {
308             return 0;
309         }
310     }
311     return 1;
312 }
313 #endif /* AFS_NT40_ENV */
314
315
316 static int
317 afsconf_Check(register struct afsconf_dir *adir)
318 {
319     char tbuffer[256];
320 #ifdef AFS_NT40_ENV
321     char *p;
322 #endif
323     struct stat tstat;
324     register afs_int32 code;
325
326 #ifdef AFS_NT40_ENV
327     /* NT client CellServDB has different file name than NT server or Unix */
328     if (IsClientConfigDirectory(adir->name)) {
329         if (!afssw_GetClientCellServDBDir(&p)) {
330             strcompose(tbuffer, sizeof(tbuffer), p, "/",
331                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
332             free(p);
333         } else {
334             int len;
335             strncpy(tbuffer, adir->name, sizeof(tbuffer));
336             len = (int)strlen(tbuffer);
337             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
338                 strncat(tbuffer, "\\", sizeof(tbuffer));
339             }
340             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
341                     sizeof(tbuffer));
342             tbuffer[sizeof(tbuffer) - 1] = '\0';
343         }
344     } else {
345         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
346                    NULL);
347     }
348 #else
349     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
350 #endif /* AFS_NT40_ENV */
351
352     code = stat(tbuffer, &tstat);
353     if (code < 0) {
354         return code;
355     }
356     /* did file change? */
357     if (tstat.st_mtime == adir->timeRead) {
358         return 0;
359     }
360     /* otherwise file has changed, so reopen it */
361     return afsconf_Reopen(adir);
362 }
363
364 /* set modtime on file */
365 static int
366 afsconf_Touch(register struct afsconf_dir *adir)
367 {
368     char tbuffer[256];
369 #ifndef AFS_NT40_ENV
370     struct timeval tvp[2];
371 #else
372     char *p;
373 #endif
374
375     adir->timeRead = 0;         /* just in case */
376
377 #ifdef AFS_NT40_ENV
378     /* NT client CellServDB has different file name than NT server or Unix */
379
380     if (IsClientConfigDirectory(adir->name)) {
381         if (!afssw_GetClientCellServDBDir(&p)) {
382             strcompose(tbuffer, sizeof(tbuffer), p, "/",
383                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
384             free(p);
385         } else {
386             int len = (int)strlen(tbuffer);
387             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
388                 strncat(tbuffer, "\\", sizeof(tbuffer));
389             }
390             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
391                     sizeof(tbuffer));
392             tbuffer[sizeof(tbuffer) - 1] = '\0';
393         }
394     } else {
395         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
396                    NULL);
397     }
398
399     return _utime(tbuffer, NULL);
400
401 #else
402     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
403     gettimeofday(&tvp[0], NULL);
404     tvp[1] = tvp[0];
405     return utimes(tbuffer, tvp);
406 #endif /* AFS_NT40_ENV */
407 }
408
409 struct afsconf_dir *
410 afsconf_Open(register const char *adir)
411 {
412     register struct afsconf_dir *tdir;
413     register afs_int32 code;
414
415     LOCK_GLOBAL_MUTEX;
416     /* zero structure and fill in name; rest is done by internal routine */
417     tdir = (struct afsconf_dir *)malloc(sizeof(struct afsconf_dir));
418     memset(tdir, 0, sizeof(struct afsconf_dir));
419     tdir->name = strdup(adir);
420
421     code = afsconf_OpenInternal(tdir, 0, 0);
422     if (code) {
423         char *afsconf_path, afs_confdir[128];
424
425         free(tdir->name);
426         /* Check global place only when local Open failed for whatever reason */
427         if (!(afsconf_path = getenv("AFSCONF"))) {
428             /* The "AFSCONF" environment (or contents of "/.AFSCONF") will be typically set to something like "/afs/<cell>/common/etc" where, by convention, the default files for "ThisCell" and "CellServDB" will reside; note that a major drawback is that a given afs client on that cell may NOT contain the same contents... */
429             char *home_dir;
430             afsconf_FILE *fp;
431             size_t len;
432
433             if (!(home_dir = getenv("HOME"))) {
434                 /* Our last chance is the "/.AFSCONF" file */
435                 fp = fopen("/.AFSCONF", "r");
436                 if (fp == 0) {
437                     free(tdir);
438                     UNLOCK_GLOBAL_MUTEX;
439                     return (struct afsconf_dir *)0;
440                 }
441                 fgets(afs_confdir, 128, fp);
442                 fclose(fp);
443             } else {
444                 char pathname[256];
445
446                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
447                 fp = fopen(pathname, "r");
448                 if (fp == 0) {
449                     /* Our last chance is the "/.AFSCONF" file */
450                     fp = fopen("/.AFSCONF", "r");
451                     if (fp == 0) {
452                         free(tdir);
453                         UNLOCK_GLOBAL_MUTEX;
454                         return (struct afsconf_dir *)0;
455                     }
456                 }
457                 fgets(afs_confdir, 128, fp);
458                 fclose(fp);
459             }
460             len = strlen(afs_confdir);
461             if (len == 0) {
462                 free(tdir);
463                 UNLOCK_GLOBAL_MUTEX;
464                 return (struct afsconf_dir *)0;
465             }
466             if (afs_confdir[len - 1] == '\n') {
467                 afs_confdir[len - 1] = 0;
468             }
469             afsconf_path = afs_confdir;
470         }
471         tdir->name = strdup(afsconf_path);
472         code = afsconf_OpenInternal(tdir, 0, 0);
473         if (code) {
474             free(tdir->name);
475             free(tdir);
476             UNLOCK_GLOBAL_MUTEX;
477             return (struct afsconf_dir *)0;
478         }
479     }
480     UNLOCK_GLOBAL_MUTEX;
481     return tdir;
482 }
483
484 static int
485 GetCellUnix(struct afsconf_dir *adir)
486 {
487     char *rc;
488     char tbuffer[256];
489     char *start, *p;
490     afsconf_FILE *fp;
491     
492     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
493     fp = fopen(tbuffer, "r");
494     if (fp == 0) {
495         return -1;
496     }
497     rc = fgets(tbuffer, 256, fp);
498     fclose(fp);
499     if (rc == NULL)
500         return -1;
501
502     start = tbuffer;
503     while (*start != '\0' && isspace(*start))
504         start++;
505     p = start;
506     while (*p != '\0' && !isspace(*p))
507         p++;
508     *p = '\0';
509     if (*start == '\0')
510         return -1;
511
512     adir->cellName = strdup(start);
513     return 0;
514 }
515
516
517 #ifdef AFS_NT40_ENV
518 static int
519 GetCellNT(struct afsconf_dir *adir)
520 {
521     if (IsClientConfigDirectory(adir->name)) {
522         /* NT client config dir; ThisCell is in registry (no file). */
523         return afssw_GetClientCellName(&adir->cellName);
524     } else {
525         /* NT server config dir; works just like Unix */
526         return GetCellUnix(adir);
527     }
528 }
529
530 /* The following procedures and structs are used on Windows only
531  * to enumerate the Cell information distributed within the 
532  * Windows registry.  (See src/WINNT/afsd/cm_config.c)
533  */
534 typedef struct _cm_enumCellRegistry {
535     afs_uint32 client;  /* non-zero if client query */
536     struct afsconf_dir *adir;
537 } cm_enumCellRegistry_t;
538
539 static long
540 cm_serverConfigProc(void *rockp, struct sockaddr_in *addrp, 
541                     char *hostNamep, unsigned short rank)
542 {
543     struct afsconf_cell *cellInfop = (struct afsconf_cell *)rockp;
544
545     if (cellInfop->numServers == MAXHOSTSPERCELL)
546         return 0;
547
548     cellInfop->hostAddr[cellInfop->numServers] = *addrp;
549     strncpy(cellInfop->hostName[cellInfop->numServers], hostNamep, MAXHOSTCHARS);
550     cellInfop->hostName[cellInfop->numServers][MAXHOSTCHARS-1] = '\0';
551     cellInfop->numServers++;
552
553     return 0;
554 }
555
556 static long
557 cm_enumCellRegistryProc(void *rockp, char * cellNamep)
558 {
559     long code;
560     cm_enumCellRegistry_t *enump = (cm_enumCellRegistry_t *)rockp;
561     char linkedName[256] = "";
562     int timeout = 0;
563     struct afsconf_entry *newEntry;
564
565
566     newEntry = malloc(sizeof(struct afsconf_entry));
567     if (newEntry == NULL)
568         return ENOMEM;
569     newEntry->cellInfo.numServers = 0;
570
571     code = cm_SearchCellRegistry(enump->client, cellNamep, NULL, linkedName, cm_serverConfigProc, &newEntry->cellInfo);
572     if (code == CM_ERROR_FORCE_DNS_LOOKUP)
573         code = cm_SearchCellByDNS(cellNamep, NULL, &timeout, cm_serverConfigProc, &newEntry->cellInfo);
574
575     if (code == 0) {
576         strncpy(newEntry->cellInfo.name, cellNamep, MAXCELLCHARS);
577         newEntry->cellInfo.name[MAXCELLCHARS-1];
578         if (linkedName[0])
579             newEntry->cellInfo.linkedCell = strdup(linkedName);
580         else
581             newEntry->cellInfo.linkedCell = NULL;
582         newEntry->cellInfo.timeout = timeout;
583         newEntry->cellInfo.flags = 0;
584
585         newEntry->next = enump->adir->entries;
586         enump->adir->entries = newEntry;
587     } else {
588         free(newEntry);
589     }
590     return code;
591 }       
592 #endif /* AFS_NT40_ENV */
593
594
595 static int
596 afsconf_OpenInternal(register struct afsconf_dir *adir, char *cell,
597                      char clones[])
598 {
599     afsconf_FILE *tf;
600     register char *tp, *bp;
601     register struct afsconf_entry *curEntry;
602     struct afsconf_aliasentry *curAlias;
603     register afs_int32 code;
604     afs_int32 i;
605     char tbuffer[256], tbuf1[256];
606     struct stat tstat;
607 #ifdef AFS_NT40_ENV
608     cm_enumCellRegistry_t enumCellRegistry = {0, 0};
609 #endif /* AFS_NT40_ENV */
610
611     /* figure out the local cell name */
612 #ifdef AFS_NT40_ENV
613     i = GetCellNT(adir);
614     enumCellRegistry.adir = adir;
615 #else
616     i = GetCellUnix(adir);
617 #endif
618
619 #ifndef AFS_FREELANCE_CLIENT    /* no local cell not fatal in freelance */
620     if (i) {
621         return i;
622     }
623 #endif
624
625     /* now parse the individual lines */
626     curEntry = 0;
627
628 #ifdef AFS_NT40_ENV
629     /* NT client/server have a CellServDB that is the same format as Unix.
630      * However, the NT client uses a different file name
631      */
632     if (IsClientConfigDirectory(adir->name)) {
633         /* NT client config dir */
634         char *p;
635
636         enumCellRegistry.client = 1;
637
638         if (!afssw_GetClientCellServDBDir(&p)) {
639             strcompose(tbuffer, sizeof(tbuffer), p, "/",
640                        AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
641             free(p);
642         } else {
643             int len;
644             strncpy(tbuffer, adir->name, sizeof(tbuffer));
645             len = (int)strlen(tbuffer);
646             if (tbuffer[len - 1] != '\\' && tbuffer[len - 1] != '/') {
647                 strncat(tbuffer, "\\", sizeof(tbuffer));
648             }
649             strncat(tbuffer, AFSDIR_CELLSERVDB_FILE_NTCLIENT,
650                     sizeof(tbuffer));
651             tbuffer[sizeof(tbuffer) - 1] = '\0';
652         }
653     } else {
654         /* NT server config dir */
655         strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE,
656                    NULL);
657     }
658 #else
659     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
660 #endif /* AFS_NT40_ENV */
661
662     if (!stat(tbuffer, &tstat)) {
663         adir->timeRead = tstat.st_mtime;
664     } else {
665         adir->timeRead = 0;
666     }
667
668     strlcpy(tbuf1, tbuffer, sizeof tbuf1);
669     tf = fopen(tbuffer, "r");
670     if (!tf) {
671         return -1;
672     }
673
674     /* The CellServDB file is now open.  
675      * The following code parses the contents of the 
676      * file and creates a list with the first cell entry
677      * in the CellServDB file at the end of the list.
678      * 
679      * No checking is performed for duplicates.
680      * The side effects of this process are that duplicate
681      * entries appended to the end of the CellServDB file
682      * take precedence and are found in a shorter period 
683      * of time.
684      */
685
686     while (1) {
687         tp = fgets(tbuffer, sizeof(tbuffer), tf);
688         if (!tp)
689             break;
690         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
691         if (tbuffer[0] == 0 || tbuffer[0] == '\n')
692             continue;           /* empty line */
693         if (tbuffer[0] == '>') {
694             char linkedcell[MAXCELLCHARS];
695             /* start new cell item */
696             if (curEntry) {
697                 /* thread this guy on the list */
698                 curEntry->next = adir->entries;
699                 adir->entries = curEntry;
700                 curEntry = 0;
701             }
702             curEntry =
703                 (struct afsconf_entry *)malloc(sizeof(struct afsconf_entry));
704             memset(curEntry, 0, sizeof(struct afsconf_entry));
705             code =
706                 ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
707             if (code) {
708                 afsconf_CloseInternal(adir);
709                 fclose(tf);
710                 free(curEntry);
711                 return -1;
712             }
713             if (linkedcell[0] != '\0')
714                 curEntry->cellInfo.linkedCell = strdup(linkedcell);
715         } else {
716             /* new host in the current cell */
717             if (!curEntry) {
718                 afsconf_CloseInternal(adir);
719                 fclose(tf);
720                 return -1;
721             }
722             i = curEntry->cellInfo.numServers;
723             if (i < MAXHOSTSPERCELL) {
724                 if (cell && !strcmp(cell, curEntry->cellInfo.name))
725                     code =
726                         ParseHostLine(tbuffer, 
727                                       &curEntry->cellInfo.hostAddr[i],
728                                       curEntry->cellInfo.hostName[i], 
729                                       &clones[i]);
730                 else
731                     code =
732                         ParseHostLine(tbuffer, 
733                                       &curEntry->cellInfo.hostAddr[i],
734                                       curEntry->cellInfo.hostName[i], 0);
735
736                 if (code) {
737                     if (code == AFSCONF_SYNTAX) {
738                         for (bp = tbuffer; *bp != '\n'; bp++) { /* Take out the <cr> from the buffer */
739                             if (!*bp)
740                                 break;
741                         }
742                         *bp = '\0';
743                         fprintf(stderr,
744                                 "Can't properly parse host line \"%s\" in configuration file %s\n",
745                                 tbuffer, tbuf1);
746                     }
747                     free(curEntry);
748                     fclose(tf);
749                     afsconf_CloseInternal(adir);
750                     return -1;
751                 }
752                 curEntry->cellInfo.numServers = ++i;
753             } else {
754                 fprintf(stderr,
755                         "Too many hosts for cell %s in configuration file %s\n", 
756                         curEntry->cellInfo.name, tbuf1);
757             }
758         }
759     }
760     fclose(tf);                 /* close the file now */
761
762     /* end the last partially-completed cell */
763     if (curEntry) {
764         curEntry->next = adir->entries;
765         adir->entries = curEntry;
766     }
767
768 #ifdef AFS_NT40_ENV
769      /* 
770       * Windows maintains a CellServDB list in the Registry
771       * that supercedes the contents of the CellServDB file.
772       * Prepending these entries to the head of the list 
773       * is sufficient to enforce the precedence.
774       */
775      cm_EnumerateCellRegistry( enumCellRegistry.client,
776                                cm_enumCellRegistryProc,
777                                &enumCellRegistry);
778 #endif /* AFS_NT40_ENV */
779
780     /* Read in the alias list */
781     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLALIAS_FILE, NULL);
782
783     tf = fopen(tbuffer, "r");
784     while (tf) {
785         char *aliasPtr;
786
787         tp = fgets(tbuffer, sizeof(tbuffer), tf);
788         if (!tp)
789             break;
790         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
791
792         if (tbuffer[0] == '\0' || tbuffer[0] == '\n' || tbuffer[0] == '#')
793             continue;           /* empty line */
794
795         tp = tbuffer;
796         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t')
797             tp++;
798         if (tp[0] == '\0')
799             continue;           /* invalid line */
800
801         while (tp[0] != '\0' && (tp[0] == ' ' || tp[0] == '\t'))
802             0[tp++] = '\0';
803         if (tp[0] == '\0')
804             continue;           /* invalid line */
805
806         aliasPtr = tp;
807         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t' && tp[0] != '\r'
808                && tp[0] != '\n')
809             tp++;
810         tp[0] = '\0';
811
812         curAlias = malloc(sizeof(*curAlias));
813         memset(curAlias, 0, sizeof(*curAlias));
814
815         strlcpy(curAlias->aliasInfo.aliasName, aliasPtr, sizeof curAlias->aliasInfo.aliasName);
816         strlcpy(curAlias->aliasInfo.realName, tbuffer, sizeof curAlias->aliasInfo.realName);
817
818         curAlias->next = adir->alias_entries;
819         adir->alias_entries = curAlias;
820     }
821
822     if (tf != NULL)
823         fclose(tf);
824     /* now read the fs keys, if possible */
825     adir->keystr = (struct afsconf_keys *)0;
826     afsconf_IntGetKeys(adir);
827
828     return 0;
829 }
830
831 /* parse a line of the form
832  *"128.2.1.3   #hostname" or
833  *"[128.2.1.3]  #hostname" for clones
834  * into the appropriate pieces.  
835  */
836 static int
837 ParseHostLine(char *aline, register struct sockaddr_in *addr, char *aname,
838               char *aclone)
839 {
840     int c1, c2, c3, c4;
841     register afs_int32 code;
842     register char *tp;
843
844     if (*aline == '[') {
845         if (aclone)
846             *aclone = 1;
847         /* FIXME: length of aname unknown here */
848         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
849     } else {
850         if (aclone)
851             *aclone = 0;
852         /* FIXME: length of aname unknown here */
853         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
854     }
855     if (code != 5)
856         return AFSCONF_SYNTAX;
857     addr->sin_family = AF_INET;
858     addr->sin_port = 0;
859 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
860     addr->sin_len = sizeof(struct sockaddr_in);
861 #endif
862     tp = (char *)&addr->sin_addr;
863     *tp++ = c1;
864     *tp++ = c2;
865     *tp++ = c3;
866     *tp++ = c4;
867     return 0;
868 }
869
870 /* parse a line of the form
871  * ">cellname [linkedcellname] [#comments]"
872  * into the appropriate pieces.
873  */
874 static int
875 ParseCellLine(register char *aline, register char *aname,
876               register char *alname)
877 {
878     register int code;
879     /* FIXME: length of aname, alname unknown here */
880     code = sscanf(aline, ">%s %s", aname, alname);
881     if (code == 1)
882         *alname = '\0';
883     if (code == 2) {
884         if (*alname == '#') {
885             *alname = '\0';
886         }
887     }
888     return (code > 0 ? 0 : AFSCONF_SYNTAX);
889 }
890
891 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
892 int
893 afsconf_CellApply(struct afsconf_dir *adir,
894                   int (*aproc) (struct afsconf_cell * cell, void *arock,
895                                 struct afsconf_dir * dir), void *arock)
896 {
897     register struct afsconf_entry *tde;
898     register afs_int32 code;
899     LOCK_GLOBAL_MUTEX;
900     for (tde = adir->entries; tde; tde = tde->next) {
901         code = (*aproc) (&tde->cellInfo, arock, adir);
902         if (code) {
903             UNLOCK_GLOBAL_MUTEX;
904             return code;
905         }
906     }
907     UNLOCK_GLOBAL_MUTEX;
908     return 0;
909 }
910
911 /* call aproc(entry, arock, adir) for all cell aliases.
912  * Proc must return 0, or we'll stop early and return the code it returns
913  */
914 int
915 afsconf_CellAliasApply(struct afsconf_dir *adir,
916                        int (*aproc) (struct afsconf_cellalias * alias,
917                                      void *arock, struct afsconf_dir * dir),
918                        void *arock)
919 {
920     register struct afsconf_aliasentry *tde;
921     register afs_int32 code;
922     LOCK_GLOBAL_MUTEX;
923     for (tde = adir->alias_entries; tde; tde = tde->next) {
924         code = (*aproc) (&tde->aliasInfo, arock, adir);
925         if (code) {
926             UNLOCK_GLOBAL_MUTEX;
927             return code;
928         }
929     }
930     UNLOCK_GLOBAL_MUTEX;
931     return 0;
932 }
933
934 afs_int32 afsconf_SawCell = 0;
935
936 int
937 afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
938                             char *aservice, struct afsconf_cell *acellInfo,
939                             char clones[])
940 {
941     afs_int32 code;
942     char *cell;
943
944     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
945     if (code)
946         return code;
947
948     if (acellName)
949         cell = acellName;
950     else
951         cell = (char *)&acellInfo->name;
952
953     code = afsconf_OpenInternal(adir, cell, clones);
954     return code;
955 }
956
957 #ifdef AFS_AFSDB_ENV
958 #if !defined(AFS_NT40_ENV)
959 int
960 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
961                      struct afsconf_cell *acellInfo)
962 {
963     afs_int32 code;
964     int tservice, i, len;
965     unsigned char answer[1024];
966     unsigned char *p;
967     char *dotcellname;
968     int cellnamelength;
969     char realCellName[256];
970     char host[256];
971     int server_num = 0;
972     int minttl = 0;
973     int try_init = 0;
974
975     /* The resolver isn't always MT-safe.. Perhaps this ought to be
976      * replaced with a more fine-grained lock just for the resolver
977      * operations.
978      */
979
980  retryafsdb:
981     if ( ! strchr(acellName,'.') ) {
982        cellnamelength=strlen(acellName);
983        dotcellname=malloc(cellnamelength+2);
984        memcpy(dotcellname,acellName,cellnamelength);
985        dotcellname[cellnamelength]='.';
986        dotcellname[cellnamelength+1]=0;
987        LOCK_GLOBAL_MUTEX;
988        len = res_search(dotcellname, C_IN, T_AFSDB, answer, sizeof(answer));
989        if ( len < 0 ) {
990           len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
991        }
992        UNLOCK_GLOBAL_MUTEX;
993        free(dotcellname);
994     } else {
995        LOCK_GLOBAL_MUTEX;
996        len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
997        UNLOCK_GLOBAL_MUTEX;
998     }
999     if (len < 0) {
1000         if (try_init < 1) {
1001             try_init++;
1002             res_init();
1003             goto retryafsdb;
1004         }
1005         return AFSCONF_NOTFOUND;
1006     }
1007
1008     p = answer + sizeof(HEADER);        /* Skip header */
1009     code = dn_expand(answer, answer + len, p, host, sizeof(host));
1010     if (code < 0)
1011         return AFSCONF_NOTFOUND;
1012
1013     p += code + QFIXEDSZ;       /* Skip name */
1014
1015     while (p < answer + len) {
1016         int type, ttl, size;
1017
1018         code = dn_expand(answer, answer + len, p, host, sizeof(host));
1019         if (code < 0)
1020             return AFSCONF_NOTFOUND;
1021
1022         p += code;              /* Skip the name */
1023         type = (p[0] << 8) | p[1];
1024         p += 4;                 /* Skip type and class */
1025         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
1026         p += 4;                 /* Skip the TTL */
1027         size = (p[0] << 8) | p[1];
1028         p += 2;                 /* Skip the size */
1029
1030         if (type == T_AFSDB) {
1031             struct hostent *he;
1032             short afsdb_type;
1033
1034             afsdb_type = (p[0] << 8) | p[1];
1035             if (afsdb_type == 1) {
1036                 /*
1037                  * We know this is an AFSDB record for our cell, of the
1038                  * right AFSDB type.  Write down the true cell name that
1039                  * the resolver gave us above.
1040                  */
1041                 strlcpy(realCellName, host, sizeof realCellName);
1042             }
1043
1044             code = dn_expand(answer, answer + len, p + 2, host, sizeof(host));
1045             if (code < 0)
1046                 return AFSCONF_NOTFOUND;
1047
1048             if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) &&
1049                 /* Do we want to get TTL data for the A record as well? */
1050                 (he = gethostbyname(host))) {
1051                 afs_int32 ipaddr;
1052                 memcpy(&ipaddr, he->h_addr, he->h_length);
1053                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
1054                 strncpy(acellInfo->hostName[server_num], host,
1055                         sizeof(acellInfo->hostName[server_num]));
1056                 server_num++;
1057
1058                 if (!minttl || ttl < minttl)
1059                     minttl = ttl;
1060             }
1061         }
1062
1063         p += size;
1064     }
1065
1066     if (server_num == 0)        /* No AFSDB records */
1067         return AFSCONF_NOTFOUND;
1068
1069     /* Convert the real cell name to lowercase */
1070     for (p = (unsigned char *)realCellName; *p; p++)
1071         *p = tolower(*p);
1072
1073     strncpy(acellInfo->name, realCellName, sizeof(acellInfo->name));
1074     acellInfo->numServers = server_num;
1075
1076     if (aservice) {
1077         tservice = afsconf_FindService(aservice);
1078         if (tservice < 0)
1079             return AFSCONF_NOTFOUND;    /* service not found */
1080         for (i = 0; i < acellInfo->numServers; i++) {
1081             acellInfo->hostAddr[i].sin_port = tservice;
1082         }
1083     }
1084
1085     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
1086
1087     return 0;
1088 }
1089 #else /* windows */
1090 int
1091 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
1092                      struct afsconf_cell *acellInfo)
1093 {
1094     register afs_int32 i;
1095     int tservice;
1096     struct afsconf_entry DNSce;
1097     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
1098     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
1099     unsigned short ipRanks[AFSMAXCELLHOSTS];
1100     int numServers;
1101     int rc;
1102     int ttl;
1103
1104     DNSce.cellInfo.numServers = 0;
1105     DNSce.next = NULL;
1106     rc = getAFSServer(acellName, cellHostAddrs, cellHostNames, ipRanks, &numServers,
1107                       &ttl);
1108     /* ignore the ttl here since this code is only called by transitory programs
1109      * like klog, etc. */
1110     if (rc < 0)
1111         return -1;
1112     if (numServers == 0)
1113         return -1;
1114
1115     for (i = 0; i < numServers; i++) {
1116         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
1117                sizeof(long));
1118         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
1119         acellInfo->hostAddr[i].sin_family = AF_INET;
1120
1121         /* sin_port supplied by connection code */
1122     }
1123
1124     acellInfo->numServers = numServers;
1125     strlcpy(acellInfo->name, acellName, sizeof acellInfo->name);
1126     if (aservice) {
1127         LOCK_GLOBAL_MUTEX;
1128         tservice = afsconf_FindService(aservice);
1129         UNLOCK_GLOBAL_MUTEX;
1130         if (tservice < 0) {
1131             return AFSCONF_NOTFOUND;    /* service not found */
1132         }
1133         for (i = 0; i < acellInfo->numServers; i++) {
1134             acellInfo->hostAddr[i].sin_port = tservice;
1135         }
1136     }
1137     acellInfo->linkedCell = NULL;       /* no linked cell */
1138     acellInfo->flags = 0;
1139     return 0;
1140 }
1141 #endif /* windows */
1142 #endif /* AFS_AFSDB_ENV */
1143
1144 int
1145 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1146                     struct afsconf_cell *acellInfo)
1147 {
1148     register struct afsconf_entry *tce;
1149     struct afsconf_aliasentry *tcae;
1150     struct afsconf_entry *bestce;
1151     register afs_int32 i;
1152     int tservice;
1153     char *tcell;
1154     int cnLen;
1155     int ambig;
1156     char tbuffer[64];
1157
1158     LOCK_GLOBAL_MUTEX;
1159     if (adir)
1160         afsconf_Check(adir);
1161     if (acellName) {
1162         tcell = acellName;
1163         cnLen = (int)(strlen(tcell) + 1);
1164         lcstring(tcell, tcell, cnLen);
1165         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1166         /* call to afsconf_GetLocalCell: like klog  */
1167     } else {
1168         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1169         if (i) {
1170             UNLOCK_GLOBAL_MUTEX;
1171             return i;
1172         }
1173         tcell = tbuffer;
1174     }
1175     cnLen = strlen(tcell);
1176     bestce = (struct afsconf_entry *)0;
1177     ambig = 0;
1178     if (!adir) {
1179         UNLOCK_GLOBAL_MUTEX;
1180         return 0;
1181     }
1182
1183     /* Look through the list of aliases */
1184     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1185         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1186             tcell = tcae->aliasInfo.realName;
1187             break;
1188         }
1189     }
1190
1191     for (tce = adir->entries; tce; tce = tce->next) {
1192         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1193             /* found our cell */
1194             bestce = tce;
1195             ambig = 0;
1196             break;
1197         }
1198         if (strlen(tce->cellInfo.name) < cnLen)
1199             continue;           /* clearly wrong */
1200         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1201             if (bestce)
1202                 ambig = 1;      /* ambiguous unless we get exact match */
1203             bestce = tce;
1204         }
1205     }
1206     if (!ambig && bestce && bestce->cellInfo.numServers) {
1207         *acellInfo = bestce->cellInfo;  /* structure assignment */
1208         if (aservice) {
1209             tservice = afsconf_FindService(aservice);
1210             if (tservice < 0) {
1211                 UNLOCK_GLOBAL_MUTEX;
1212                 return AFSCONF_NOTFOUND;        /* service not found */
1213             }
1214             for (i = 0; i < acellInfo->numServers; i++) {
1215                 acellInfo->hostAddr[i].sin_port = tservice;
1216             }
1217         }
1218         acellInfo->timeout = 0;
1219         UNLOCK_GLOBAL_MUTEX;
1220         return 0;
1221     } else {
1222         UNLOCK_GLOBAL_MUTEX;
1223 #ifdef AFS_AFSDB_ENV
1224         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1225 #else
1226         return AFSCONF_NOTFOUND;
1227 #endif /* AFS_AFSDB_ENV */
1228     }
1229 }
1230
1231 int
1232 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1233                      afs_int32 alen)
1234 {
1235     static int afsconf_showcell = 0;
1236     char *afscell_path;
1237     afs_int32 code = 0;
1238
1239     LOCK_GLOBAL_MUTEX;
1240     /*
1241      * If a cell switch was specified in a command, then it should override the 
1242      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1243      * is set and the cell name in the adir structure is used.
1244      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1245      */
1246     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1247         if (!afsconf_showcell) {
1248             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1249                     afscell_path);
1250             afsconf_showcell = 1;
1251         }
1252         strncpy(aname, afscell_path, alen);
1253     } else {
1254         afsconf_Check(adir);
1255         if (adir->cellName) {
1256             strncpy(aname, adir->cellName, alen);
1257         } else
1258             code = AFSCONF_UNKNOWN;
1259     }
1260
1261     UNLOCK_GLOBAL_MUTEX;
1262     return (code);
1263 }
1264
1265 int
1266 afsconf_Close(struct afsconf_dir *adir)
1267 {
1268     LOCK_GLOBAL_MUTEX;
1269     afsconf_CloseInternal(adir);
1270     if (adir->name)
1271         free(adir->name);
1272     free(adir);
1273     UNLOCK_GLOBAL_MUTEX;
1274     return 0;
1275 }
1276
1277 static int
1278 afsconf_CloseInternal(register struct afsconf_dir *adir)
1279 {
1280     register struct afsconf_entry *td, *nd;
1281     struct afsconf_aliasentry *ta, *na;
1282     register char *tname;
1283
1284     tname = adir->name;         /* remember name, since that's all we preserve */
1285
1286     /* free everything we can find */
1287     if (adir->cellName)
1288         free(adir->cellName);
1289     for (td = adir->entries; td; td = nd) {
1290         nd = td->next;
1291         if (td->cellInfo.linkedCell)
1292             free(td->cellInfo.linkedCell);
1293         free(td);
1294     }
1295     for (ta = adir->alias_entries; ta; ta = na) {
1296         na = ta->next;
1297         free(ta);
1298     }
1299     if (adir->keystr)
1300         free(adir->keystr);
1301
1302     /* reinit */
1303     memset(adir, 0, sizeof(struct afsconf_dir));
1304     adir->name = tname;         /* restore it */
1305     return 0;
1306 }
1307
1308 static int
1309 afsconf_Reopen(register struct afsconf_dir *adir)
1310 {
1311     register afs_int32 code;
1312     code = afsconf_CloseInternal(adir);
1313     if (code)
1314         return code;
1315     code = afsconf_OpenInternal(adir, 0, 0);
1316     return code;
1317 }
1318
1319 /* called during opening of config file */
1320 int
1321 afsconf_IntGetKeys(struct afsconf_dir *adir)
1322 {
1323     char tbuffer[256];
1324     register int fd;
1325     struct afsconf_keys *tstr;
1326     register afs_int32 code;
1327
1328 #ifdef AFS_NT40_ENV
1329     /* NT client config dir has no KeyFile; don't risk attempting open
1330      * because there might be a random file of this name if dir is shared.
1331      */
1332     if (IsClientConfigDirectory(adir->name)) {
1333         adir->keystr = ((struct afsconf_keys *)
1334                         malloc(sizeof(struct afsconf_keys)));
1335         adir->keystr->nkeys = 0;
1336         return 0;
1337     }
1338 #endif /* AFS_NT40_ENV */
1339
1340     LOCK_GLOBAL_MUTEX;
1341     /* compute the key name and other setup */
1342     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1343     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1344     adir->keystr = tstr;
1345
1346     /* read key file */
1347     fd = open(tbuffer, O_RDONLY);
1348     if (fd < 0) {
1349         tstr->nkeys = 0;
1350         UNLOCK_GLOBAL_MUTEX;
1351         return 0;
1352     }
1353     code = read(fd, tstr, sizeof(struct afsconf_keys));
1354     close(fd);
1355     if (code < sizeof(afs_int32)) {
1356         tstr->nkeys = 0;
1357         UNLOCK_GLOBAL_MUTEX;
1358         return 0;
1359     }
1360
1361     /* convert key structure to host order */
1362     tstr->nkeys = ntohl(tstr->nkeys);
1363
1364     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1365         tstr->nkeys = 0;
1366         UNLOCK_GLOBAL_MUTEX;
1367         return 0;
1368     }
1369
1370     for (fd = 0; fd < tstr->nkeys; fd++)
1371         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1372
1373     UNLOCK_GLOBAL_MUTEX;
1374     return 0;
1375 }
1376
1377 /* get keys structure */
1378 int
1379 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1380 {
1381     register afs_int32 code;
1382
1383     LOCK_GLOBAL_MUTEX;
1384     code = afsconf_Check(adir);
1385     if (code) {
1386         UNLOCK_GLOBAL_MUTEX;
1387         return AFSCONF_FAILURE;
1388     }
1389     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1390     UNLOCK_GLOBAL_MUTEX;
1391     return 0;
1392 }
1393
1394 /* get latest key */
1395 afs_int32
1396 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, 
1397                      struct ktc_encryptionKey *akey)
1398 {
1399     register int i;
1400     int maxa;
1401     register struct afsconf_key *tk;
1402     register afs_int32 best;
1403     struct afsconf_key *bestk;
1404     register afs_int32 code;
1405
1406     LOCK_GLOBAL_MUTEX;
1407     code = afsconf_Check(adir);
1408     if (code) {
1409         UNLOCK_GLOBAL_MUTEX;
1410         return AFSCONF_FAILURE;
1411     }
1412     maxa = adir->keystr->nkeys;
1413
1414     best = -1;                  /* highest kvno we've seen yet */
1415     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1416     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1417         if (tk->kvno == 999)
1418             continue;           /* skip bcrypt keys */
1419         if (tk->kvno > best) {
1420             best = tk->kvno;
1421             bestk = tk;
1422         }
1423     }
1424     if (bestk) {                /* found any  */
1425         if (akey)
1426             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1427         if (avno)
1428             *avno = bestk->kvno;        /* and kvno to caller */
1429         UNLOCK_GLOBAL_MUTEX;
1430         return 0;
1431     }
1432     UNLOCK_GLOBAL_MUTEX;
1433     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1434 }
1435
1436 /* get a particular key */
1437 int
1438 afsconf_GetKey(void *rock, int avno, struct ktc_encryptionKey *akey)
1439 {
1440     struct afsconf_dir *adir = (struct afsconf_dir *) rock;
1441     register int i, maxa;
1442     register struct afsconf_key *tk;
1443     register afs_int32 code;
1444
1445     LOCK_GLOBAL_MUTEX;
1446     code = afsconf_Check(adir);
1447     if (code) {
1448         UNLOCK_GLOBAL_MUTEX;
1449         return AFSCONF_FAILURE;
1450     }
1451     maxa = adir->keystr->nkeys;
1452
1453     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1454         if (tk->kvno == avno) {
1455             memcpy(akey, tk->key, 8);
1456             UNLOCK_GLOBAL_MUTEX;
1457             return 0;
1458         }
1459     }
1460
1461     UNLOCK_GLOBAL_MUTEX;
1462     return AFSCONF_NOTFOUND;
1463 }
1464
1465 /* save the key structure in the appropriate file */
1466 static int
1467 SaveKeys(struct afsconf_dir *adir)
1468 {
1469     struct afsconf_keys tkeys;
1470     register int fd;
1471     register afs_int32 i;
1472     char tbuffer[256];
1473
1474     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1475
1476     /* convert it to net byte order */
1477     for (i = 0; i < tkeys.nkeys; i++)
1478         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1479     tkeys.nkeys = htonl(tkeys.nkeys);
1480
1481     /* rewrite keys file */
1482     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1483     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1484     if (fd < 0)
1485         return AFSCONF_FAILURE;
1486     i = write(fd, &tkeys, sizeof(tkeys));
1487     if (i != sizeof(tkeys)) {
1488         close(fd);
1489         return AFSCONF_FAILURE;
1490     }
1491     if (close(fd) < 0)
1492         return AFSCONF_FAILURE;
1493     return 0;
1494 }
1495
1496 int
1497 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1498                afs_int32 overwrite)
1499 {
1500     register struct afsconf_keys *tk;
1501     register struct afsconf_key *tkey;
1502     register afs_int32 i;
1503     int foundSlot;
1504
1505     LOCK_GLOBAL_MUTEX;
1506     tk = adir->keystr;
1507
1508     if (akvno != 999) {
1509         if (akvno < 0 || akvno > 255) {
1510             UNLOCK_GLOBAL_MUTEX;
1511             return ERANGE;
1512         }
1513     }
1514     foundSlot = 0;
1515     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1516         if (tkey->kvno == akvno) {
1517             if (!overwrite) {
1518                 UNLOCK_GLOBAL_MUTEX;
1519                 return AFSCONF_KEYINUSE;
1520             }
1521             foundSlot = 1;
1522             break;
1523         }
1524     }
1525     if (!foundSlot) {
1526         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1527             UNLOCK_GLOBAL_MUTEX;
1528             return AFSCONF_FULL;
1529         }
1530         tkey = &tk->key[tk->nkeys++];
1531     }
1532     tkey->kvno = akvno;
1533     memcpy(tkey->key, akey, 8);
1534     i = SaveKeys(adir);
1535     afsconf_Touch(adir);
1536     UNLOCK_GLOBAL_MUTEX;
1537     return i;
1538 }
1539
1540 /* this proc works by sliding the other guys down, rather than using a funny
1541     kvno value, so that callers can count on getting a good key in key[0].
1542 */
1543 int
1544 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1545 {
1546     register struct afsconf_keys *tk;
1547     register struct afsconf_key *tkey;
1548     register int i;
1549     int foundFlag = 0;
1550
1551     LOCK_GLOBAL_MUTEX;
1552     tk = adir->keystr;
1553
1554     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1555         if (tkey->kvno == akvno) {
1556             foundFlag = 1;
1557             break;
1558         }
1559     }
1560     if (!foundFlag) {
1561         UNLOCK_GLOBAL_MUTEX;
1562         return AFSCONF_NOTFOUND;
1563     }
1564
1565     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1566     for (; i < tk->nkeys - 1; i++, tkey++) {
1567         tkey->kvno = (tkey + 1)->kvno;
1568         memcpy(tkey->key, (tkey + 1)->key, 8);
1569     }
1570     tk->nkeys--;
1571     i = SaveKeys(adir);
1572     afsconf_Touch(adir);
1573     UNLOCK_GLOBAL_MUTEX;
1574     return i;
1575 }