windows-cellservdb-lookup-20090525
[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 (cell && !strcmp(cell, curEntry->cellInfo.name))
724                 code =
725                     ParseHostLine(tbuffer, &curEntry->cellInfo.hostAddr[i],
726                                   curEntry->cellInfo.hostName[i], &clones[i]);
727             else
728                 code =
729                     ParseHostLine(tbuffer, &curEntry->cellInfo.hostAddr[i],
730                                   curEntry->cellInfo.hostName[i], 0);
731             if (code) {
732                 if (code == AFSCONF_SYNTAX) {
733                     for (bp = tbuffer; *bp != '\n'; bp++) {     /* Take out the <cr> from the buffer */
734                         if (!*bp)
735                             break;
736                     }
737                     *bp = '\0';
738                     fprintf(stderr,
739                             "Can't properly parse host line \"%s\" in configuration file %s\n",
740                             tbuffer, tbuf1);
741                 }
742                 free(curEntry);
743                 fclose(tf);
744                 afsconf_CloseInternal(adir);
745                 return -1;
746             }
747             curEntry->cellInfo.numServers = ++i;
748         }
749     }
750     fclose(tf);                 /* close the file now */
751
752     /* end the last partially-completed cell */
753     if (curEntry) {
754         curEntry->next = adir->entries;
755         adir->entries = curEntry;
756     }
757
758 #ifdef AFS_NT40_ENV
759      /* 
760       * Windows maintains a CellServDB list in the Registry
761       * that supercedes the contents of the CellServDB file.
762       * Prepending these entries to the head of the list 
763       * is sufficient to enforce the precedence.
764       */
765      cm_EnumerateCellRegistry( enumCellRegistry.client,
766                                cm_enumCellRegistryProc,
767                                &enumCellRegistry);
768 #endif /* AFS_NT40_ENV */
769
770     /* Read in the alias list */
771     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLALIAS_FILE, NULL);
772
773     tf = fopen(tbuffer, "r");
774     while (tf) {
775         char *aliasPtr;
776
777         tp = fgets(tbuffer, sizeof(tbuffer), tf);
778         if (!tp)
779             break;
780         TrimLine(tbuffer, sizeof tbuffer);      /* remove white space */
781
782         if (tbuffer[0] == '\0' || tbuffer[0] == '\n' || tbuffer[0] == '#')
783             continue;           /* empty line */
784
785         tp = tbuffer;
786         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t')
787             tp++;
788         if (tp[0] == '\0')
789             continue;           /* invalid line */
790
791         while (tp[0] != '\0' && (tp[0] == ' ' || tp[0] == '\t'))
792             0[tp++] = '\0';
793         if (tp[0] == '\0')
794             continue;           /* invalid line */
795
796         aliasPtr = tp;
797         while (tp[0] != '\0' && tp[0] != ' ' && tp[0] != '\t' && tp[0] != '\r'
798                && tp[0] != '\n')
799             tp++;
800         tp[0] = '\0';
801
802         curAlias = malloc(sizeof(*curAlias));
803         memset(curAlias, 0, sizeof(*curAlias));
804
805         strlcpy(curAlias->aliasInfo.aliasName, aliasPtr, sizeof curAlias->aliasInfo.aliasName);
806         strlcpy(curAlias->aliasInfo.realName, tbuffer, sizeof curAlias->aliasInfo.realName);
807
808         curAlias->next = adir->alias_entries;
809         adir->alias_entries = curAlias;
810     }
811
812     if (tf != NULL)
813         fclose(tf);
814     /* now read the fs keys, if possible */
815     adir->keystr = (struct afsconf_keys *)0;
816     afsconf_IntGetKeys(adir);
817
818     return 0;
819 }
820
821 /* parse a line of the form
822  *"128.2.1.3   #hostname" or
823  *"[128.2.1.3]  #hostname" for clones
824  * into the appropriate pieces.  
825  */
826 static int
827 ParseHostLine(char *aline, register struct sockaddr_in *addr, char *aname,
828               char *aclone)
829 {
830     int c1, c2, c3, c4;
831     register afs_int32 code;
832     register char *tp;
833
834     if (*aline == '[') {
835         if (aclone)
836             *aclone = 1;
837         /* FIXME: length of aname unknown here */
838         code = sscanf(aline, "[%d.%d.%d.%d] #%s", &c1, &c2, &c3, &c4, aname);
839     } else {
840         if (aclone)
841             *aclone = 0;
842         /* FIXME: length of aname unknown here */
843         code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
844     }
845     if (code != 5)
846         return AFSCONF_SYNTAX;
847     addr->sin_family = AF_INET;
848     addr->sin_port = 0;
849 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
850     addr->sin_len = sizeof(struct sockaddr_in);
851 #endif
852     tp = (char *)&addr->sin_addr;
853     *tp++ = c1;
854     *tp++ = c2;
855     *tp++ = c3;
856     *tp++ = c4;
857     return 0;
858 }
859
860 /* parse a line of the form
861  * ">cellname [linkedcellname] [#comments]"
862  * into the appropriate pieces.
863  */
864 static int
865 ParseCellLine(register char *aline, register char *aname,
866               register char *alname)
867 {
868     register int code;
869     /* FIXME: length of aname, alname unknown here */
870     code = sscanf(aline, ">%s %s", aname, alname);
871     if (code == 1)
872         *alname = '\0';
873     if (code == 2) {
874         if (*alname == '#') {
875             *alname = '\0';
876         }
877     }
878     return (code > 0 ? 0 : AFSCONF_SYNTAX);
879 }
880
881 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
882 int
883 afsconf_CellApply(struct afsconf_dir *adir,
884                   int (*aproc) (struct afsconf_cell * cell, void *arock,
885                                 struct afsconf_dir * dir), void *arock)
886 {
887     register struct afsconf_entry *tde;
888     register afs_int32 code;
889     LOCK_GLOBAL_MUTEX;
890     for (tde = adir->entries; tde; tde = tde->next) {
891         code = (*aproc) (&tde->cellInfo, arock, adir);
892         if (code) {
893             UNLOCK_GLOBAL_MUTEX;
894             return code;
895         }
896     }
897     UNLOCK_GLOBAL_MUTEX;
898     return 0;
899 }
900
901 /* call aproc(entry, arock, adir) for all cell aliases.
902  * Proc must return 0, or we'll stop early and return the code it returns
903  */
904 int
905 afsconf_CellAliasApply(struct afsconf_dir *adir,
906                        int (*aproc) (struct afsconf_cellalias * alias,
907                                      void *arock, struct afsconf_dir * dir),
908                        void *arock)
909 {
910     register struct afsconf_aliasentry *tde;
911     register afs_int32 code;
912     LOCK_GLOBAL_MUTEX;
913     for (tde = adir->alias_entries; tde; tde = tde->next) {
914         code = (*aproc) (&tde->aliasInfo, arock, adir);
915         if (code) {
916             UNLOCK_GLOBAL_MUTEX;
917             return code;
918         }
919     }
920     UNLOCK_GLOBAL_MUTEX;
921     return 0;
922 }
923
924 afs_int32 afsconf_SawCell = 0;
925
926 int
927 afsconf_GetExtendedCellInfo(struct afsconf_dir *adir, char *acellName,
928                             char *aservice, struct afsconf_cell *acellInfo,
929                             char clones[])
930 {
931     afs_int32 code;
932     char *cell;
933
934     code = afsconf_GetCellInfo(adir, acellName, aservice, acellInfo);
935     if (code)
936         return code;
937
938     if (acellName)
939         cell = acellName;
940     else
941         cell = (char *)&acellInfo->name;
942
943     code = afsconf_OpenInternal(adir, cell, clones);
944     return code;
945 }
946
947 #ifdef AFS_AFSDB_ENV
948 #if !defined(AFS_NT40_ENV)
949 int
950 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
951                      struct afsconf_cell *acellInfo)
952 {
953     afs_int32 code;
954     int tservice, i, len;
955     unsigned char answer[1024];
956     unsigned char *p;
957     char *dotcellname;
958     int cellnamelength;
959     char realCellName[256];
960     char host[256];
961     int server_num = 0;
962     int minttl = 0;
963     int try_init = 0;
964
965     /* The resolver isn't always MT-safe.. Perhaps this ought to be
966      * replaced with a more fine-grained lock just for the resolver
967      * operations.
968      */
969
970  retryafsdb:
971     if ( ! strchr(acellName,'.') ) {
972        cellnamelength=strlen(acellName);
973        dotcellname=malloc(cellnamelength+2);
974        memcpy(dotcellname,acellName,cellnamelength);
975        dotcellname[cellnamelength]='.';
976        dotcellname[cellnamelength+1]=0;
977        LOCK_GLOBAL_MUTEX;
978        len = res_search(dotcellname, C_IN, T_AFSDB, answer, sizeof(answer));
979        if ( len < 0 ) {
980           len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
981        }
982        UNLOCK_GLOBAL_MUTEX;
983        free(dotcellname);
984     } else {
985        LOCK_GLOBAL_MUTEX;
986        len = res_search(acellName, C_IN, T_AFSDB, answer, sizeof(answer));
987        UNLOCK_GLOBAL_MUTEX;
988     }
989     if (len < 0) {
990         if (try_init < 1) {
991             try_init++;
992             res_init();
993             goto retryafsdb;
994         }
995         return AFSCONF_NOTFOUND;
996     }
997
998     p = answer + sizeof(HEADER);        /* Skip header */
999     code = dn_expand(answer, answer + len, p, host, sizeof(host));
1000     if (code < 0)
1001         return AFSCONF_NOTFOUND;
1002
1003     p += code + QFIXEDSZ;       /* Skip name */
1004
1005     while (p < answer + len) {
1006         int type, ttl, size;
1007
1008         code = dn_expand(answer, answer + len, p, host, sizeof(host));
1009         if (code < 0)
1010             return AFSCONF_NOTFOUND;
1011
1012         p += code;              /* Skip the name */
1013         type = (p[0] << 8) | p[1];
1014         p += 4;                 /* Skip type and class */
1015         ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
1016         p += 4;                 /* Skip the TTL */
1017         size = (p[0] << 8) | p[1];
1018         p += 2;                 /* Skip the size */
1019
1020         if (type == T_AFSDB) {
1021             struct hostent *he;
1022             short afsdb_type;
1023
1024             afsdb_type = (p[0] << 8) | p[1];
1025             if (afsdb_type == 1) {
1026                 /*
1027                  * We know this is an AFSDB record for our cell, of the
1028                  * right AFSDB type.  Write down the true cell name that
1029                  * the resolver gave us above.
1030                  */
1031                 strlcpy(realCellName, host, sizeof realCellName);
1032             }
1033
1034             code = dn_expand(answer, answer + len, p + 2, host, sizeof(host));
1035             if (code < 0)
1036                 return AFSCONF_NOTFOUND;
1037
1038             if ((afsdb_type == 1) && (server_num < MAXHOSTSPERCELL) &&
1039                 /* Do we want to get TTL data for the A record as well? */
1040                 (he = gethostbyname(host))) {
1041                 afs_int32 ipaddr;
1042                 memcpy(&ipaddr, he->h_addr, he->h_length);
1043                 acellInfo->hostAddr[server_num].sin_addr.s_addr = ipaddr;
1044                 strncpy(acellInfo->hostName[server_num], host,
1045                         sizeof(acellInfo->hostName[server_num]));
1046                 server_num++;
1047
1048                 if (!minttl || ttl < minttl)
1049                     minttl = ttl;
1050             }
1051         }
1052
1053         p += size;
1054     }
1055
1056     if (server_num == 0)        /* No AFSDB records */
1057         return AFSCONF_NOTFOUND;
1058
1059     /* Convert the real cell name to lowercase */
1060     for (p = (unsigned char *)realCellName; *p; p++)
1061         *p = tolower(*p);
1062
1063     strncpy(acellInfo->name, realCellName, sizeof(acellInfo->name));
1064     acellInfo->numServers = server_num;
1065
1066     if (aservice) {
1067         tservice = afsconf_FindService(aservice);
1068         if (tservice < 0)
1069             return AFSCONF_NOTFOUND;    /* service not found */
1070         for (i = 0; i < acellInfo->numServers; i++) {
1071             acellInfo->hostAddr[i].sin_port = tservice;
1072         }
1073     }
1074
1075     acellInfo->timeout = minttl ? (time(0) + minttl) : 0;
1076
1077     return 0;
1078 }
1079 #else /* windows */
1080 int
1081 afsconf_GetAfsdbInfo(char *acellName, char *aservice,
1082                      struct afsconf_cell *acellInfo)
1083 {
1084     register afs_int32 i;
1085     int tservice;
1086     struct afsconf_entry DNSce;
1087     afs_int32 cellHostAddrs[AFSMAXCELLHOSTS];
1088     char cellHostNames[AFSMAXCELLHOSTS][MAXHOSTCHARS];
1089     unsigned short ipRanks[AFSMAXCELLHOSTS];
1090     int numServers;
1091     int rc;
1092     int ttl;
1093
1094     DNSce.cellInfo.numServers = 0;
1095     DNSce.next = NULL;
1096     rc = getAFSServer(acellName, cellHostAddrs, cellHostNames, ipRanks, &numServers,
1097                       &ttl);
1098     /* ignore the ttl here since this code is only called by transitory programs
1099      * like klog, etc. */
1100     if (rc < 0)
1101         return -1;
1102     if (numServers == 0)
1103         return -1;
1104
1105     for (i = 0; i < numServers; i++) {
1106         memcpy(&acellInfo->hostAddr[i].sin_addr.s_addr, &cellHostAddrs[i],
1107                sizeof(long));
1108         memcpy(acellInfo->hostName[i], cellHostNames[i], MAXHOSTCHARS);
1109         acellInfo->hostAddr[i].sin_family = AF_INET;
1110
1111         /* sin_port supplied by connection code */
1112     }
1113
1114     acellInfo->numServers = numServers;
1115     strlcpy(acellInfo->name, acellName, sizeof acellInfo->name);
1116     if (aservice) {
1117         LOCK_GLOBAL_MUTEX;
1118         tservice = afsconf_FindService(aservice);
1119         UNLOCK_GLOBAL_MUTEX;
1120         if (tservice < 0) {
1121             return AFSCONF_NOTFOUND;    /* service not found */
1122         }
1123         for (i = 0; i < acellInfo->numServers; i++) {
1124             acellInfo->hostAddr[i].sin_port = tservice;
1125         }
1126     }
1127     acellInfo->linkedCell = NULL;       /* no linked cell */
1128     acellInfo->flags = 0;
1129     return 0;
1130 }
1131 #endif /* windows */
1132 #endif /* AFS_AFSDB_ENV */
1133
1134 int
1135 afsconf_GetCellInfo(struct afsconf_dir *adir, char *acellName, char *aservice,
1136                     struct afsconf_cell *acellInfo)
1137 {
1138     register struct afsconf_entry *tce;
1139     struct afsconf_aliasentry *tcae;
1140     struct afsconf_entry *bestce;
1141     register afs_int32 i;
1142     int tservice;
1143     char *tcell;
1144     int cnLen;
1145     int ambig;
1146     char tbuffer[64];
1147
1148     LOCK_GLOBAL_MUTEX;
1149     if (adir)
1150         afsconf_Check(adir);
1151     if (acellName) {
1152         tcell = acellName;
1153         cnLen = (int)(strlen(tcell) + 1);
1154         lcstring(tcell, tcell, cnLen);
1155         afsconf_SawCell = 1;    /* will ignore the AFSCELL switch on future */
1156         /* call to afsconf_GetLocalCell: like klog  */
1157     } else {
1158         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
1159         if (i) {
1160             UNLOCK_GLOBAL_MUTEX;
1161             return i;
1162         }
1163         tcell = tbuffer;
1164     }
1165     cnLen = strlen(tcell);
1166     bestce = (struct afsconf_entry *)0;
1167     ambig = 0;
1168     if (!adir) {
1169         UNLOCK_GLOBAL_MUTEX;
1170         return 0;
1171     }
1172
1173     /* Look through the list of aliases */
1174     for (tcae = adir->alias_entries; tcae; tcae = tcae->next) {
1175         if (strcasecmp(tcae->aliasInfo.aliasName, tcell) == 0) {
1176             tcell = tcae->aliasInfo.realName;
1177             break;
1178         }
1179     }
1180
1181     for (tce = adir->entries; tce; tce = tce->next) {
1182         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
1183             /* found our cell */
1184             bestce = tce;
1185             ambig = 0;
1186             break;
1187         }
1188         if (strlen(tce->cellInfo.name) < cnLen)
1189             continue;           /* clearly wrong */
1190         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
1191             if (bestce)
1192                 ambig = 1;      /* ambiguous unless we get exact match */
1193             bestce = tce;
1194         }
1195     }
1196     if (!ambig && bestce && bestce->cellInfo.numServers) {
1197         *acellInfo = bestce->cellInfo;  /* structure assignment */
1198         if (aservice) {
1199             tservice = afsconf_FindService(aservice);
1200             if (tservice < 0) {
1201                 UNLOCK_GLOBAL_MUTEX;
1202                 return AFSCONF_NOTFOUND;        /* service not found */
1203             }
1204             for (i = 0; i < acellInfo->numServers; i++) {
1205                 acellInfo->hostAddr[i].sin_port = tservice;
1206             }
1207         }
1208         acellInfo->timeout = 0;
1209         UNLOCK_GLOBAL_MUTEX;
1210         return 0;
1211     } else {
1212         UNLOCK_GLOBAL_MUTEX;
1213 #ifdef AFS_AFSDB_ENV
1214         return afsconf_GetAfsdbInfo(tcell, aservice, acellInfo);
1215 #else
1216         return AFSCONF_NOTFOUND;
1217 #endif /* AFS_AFSDB_ENV */
1218     }
1219 }
1220
1221 int
1222 afsconf_GetLocalCell(register struct afsconf_dir *adir, char *aname,
1223                      afs_int32 alen)
1224 {
1225     static int afsconf_showcell = 0;
1226     char *afscell_path;
1227     afs_int32 code = 0;
1228
1229     LOCK_GLOBAL_MUTEX;
1230     /*
1231      * If a cell switch was specified in a command, then it should override the 
1232      * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
1233      * is set and the cell name in the adir structure is used.
1234      * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
1235      */
1236     if (!afsconf_SawCell && (afscell_path = getenv("AFSCELL"))) {
1237         if (!afsconf_showcell) {
1238             fprintf(stderr, "Note: Operation is performed on cell %s\n",
1239                     afscell_path);
1240             afsconf_showcell = 1;
1241         }
1242         strncpy(aname, afscell_path, alen);
1243     } else {
1244         afsconf_Check(adir);
1245         if (adir->cellName) {
1246             strncpy(aname, adir->cellName, alen);
1247         } else
1248             code = AFSCONF_UNKNOWN;
1249     }
1250
1251     UNLOCK_GLOBAL_MUTEX;
1252     return (code);
1253 }
1254
1255 int
1256 afsconf_Close(struct afsconf_dir *adir)
1257 {
1258     LOCK_GLOBAL_MUTEX;
1259     afsconf_CloseInternal(adir);
1260     if (adir->name)
1261         free(adir->name);
1262     free(adir);
1263     UNLOCK_GLOBAL_MUTEX;
1264     return 0;
1265 }
1266
1267 static int
1268 afsconf_CloseInternal(register struct afsconf_dir *adir)
1269 {
1270     register struct afsconf_entry *td, *nd;
1271     struct afsconf_aliasentry *ta, *na;
1272     register char *tname;
1273
1274     tname = adir->name;         /* remember name, since that's all we preserve */
1275
1276     /* free everything we can find */
1277     if (adir->cellName)
1278         free(adir->cellName);
1279     for (td = adir->entries; td; td = nd) {
1280         nd = td->next;
1281         if (td->cellInfo.linkedCell)
1282             free(td->cellInfo.linkedCell);
1283         free(td);
1284     }
1285     for (ta = adir->alias_entries; ta; ta = na) {
1286         na = ta->next;
1287         free(ta);
1288     }
1289     if (adir->keystr)
1290         free(adir->keystr);
1291
1292     /* reinit */
1293     memset(adir, 0, sizeof(struct afsconf_dir));
1294     adir->name = tname;         /* restore it */
1295     return 0;
1296 }
1297
1298 static int
1299 afsconf_Reopen(register struct afsconf_dir *adir)
1300 {
1301     register afs_int32 code;
1302     code = afsconf_CloseInternal(adir);
1303     if (code)
1304         return code;
1305     code = afsconf_OpenInternal(adir, 0, 0);
1306     return code;
1307 }
1308
1309 /* called during opening of config file */
1310 int
1311 afsconf_IntGetKeys(struct afsconf_dir *adir)
1312 {
1313     char tbuffer[256];
1314     register int fd;
1315     struct afsconf_keys *tstr;
1316     register afs_int32 code;
1317
1318 #ifdef AFS_NT40_ENV
1319     /* NT client config dir has no KeyFile; don't risk attempting open
1320      * because there might be a random file of this name if dir is shared.
1321      */
1322     if (IsClientConfigDirectory(adir->name)) {
1323         adir->keystr = ((struct afsconf_keys *)
1324                         malloc(sizeof(struct afsconf_keys)));
1325         adir->keystr->nkeys = 0;
1326         return 0;
1327     }
1328 #endif /* AFS_NT40_ENV */
1329
1330     LOCK_GLOBAL_MUTEX;
1331     /* compute the key name and other setup */
1332     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1333     tstr = (struct afsconf_keys *)malloc(sizeof(struct afsconf_keys));
1334     adir->keystr = tstr;
1335
1336     /* read key file */
1337     fd = open(tbuffer, O_RDONLY);
1338     if (fd < 0) {
1339         tstr->nkeys = 0;
1340         UNLOCK_GLOBAL_MUTEX;
1341         return 0;
1342     }
1343     code = read(fd, tstr, sizeof(struct afsconf_keys));
1344     close(fd);
1345     if (code < sizeof(afs_int32)) {
1346         tstr->nkeys = 0;
1347         UNLOCK_GLOBAL_MUTEX;
1348         return 0;
1349     }
1350
1351     /* convert key structure to host order */
1352     tstr->nkeys = ntohl(tstr->nkeys);
1353
1354     if (code < sizeof(afs_int32) + (tstr->nkeys*sizeof(struct afsconf_key))) {
1355         tstr->nkeys = 0;
1356         UNLOCK_GLOBAL_MUTEX;
1357         return 0;
1358     }
1359
1360     for (fd = 0; fd < tstr->nkeys; fd++)
1361         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
1362
1363     UNLOCK_GLOBAL_MUTEX;
1364     return 0;
1365 }
1366
1367 /* get keys structure */
1368 int
1369 afsconf_GetKeys(struct afsconf_dir *adir, struct afsconf_keys *astr)
1370 {
1371     register afs_int32 code;
1372
1373     LOCK_GLOBAL_MUTEX;
1374     code = afsconf_Check(adir);
1375     if (code) {
1376         UNLOCK_GLOBAL_MUTEX;
1377         return AFSCONF_FAILURE;
1378     }
1379     memcpy(astr, adir->keystr, sizeof(struct afsconf_keys));
1380     UNLOCK_GLOBAL_MUTEX;
1381     return 0;
1382 }
1383
1384 /* get latest key */
1385 afs_int32
1386 afsconf_GetLatestKey(struct afsconf_dir * adir, afs_int32 * avno, 
1387                      struct ktc_encryptionKey *akey)
1388 {
1389     register int i;
1390     int maxa;
1391     register struct afsconf_key *tk;
1392     register afs_int32 best;
1393     struct afsconf_key *bestk;
1394     register afs_int32 code;
1395
1396     LOCK_GLOBAL_MUTEX;
1397     code = afsconf_Check(adir);
1398     if (code) {
1399         UNLOCK_GLOBAL_MUTEX;
1400         return AFSCONF_FAILURE;
1401     }
1402     maxa = adir->keystr->nkeys;
1403
1404     best = -1;                  /* highest kvno we've seen yet */
1405     bestk = (struct afsconf_key *)0;    /* ptr to structure providing best */
1406     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1407         if (tk->kvno == 999)
1408             continue;           /* skip bcrypt keys */
1409         if (tk->kvno > best) {
1410             best = tk->kvno;
1411             bestk = tk;
1412         }
1413     }
1414     if (bestk) {                /* found any  */
1415         if (akey)
1416             memcpy(akey, bestk->key, 8);        /* copy out latest key */
1417         if (avno)
1418             *avno = bestk->kvno;        /* and kvno to caller */
1419         UNLOCK_GLOBAL_MUTEX;
1420         return 0;
1421     }
1422     UNLOCK_GLOBAL_MUTEX;
1423     return AFSCONF_NOTFOUND;    /* didn't find any keys */
1424 }
1425
1426 /* get a particular key */
1427 int
1428 afsconf_GetKey(void *rock, int avno, struct ktc_encryptionKey *akey)
1429 {
1430     struct afsconf_dir *adir = (struct afsconf_dir *) rock;
1431     register int i, maxa;
1432     register struct afsconf_key *tk;
1433     register afs_int32 code;
1434
1435     LOCK_GLOBAL_MUTEX;
1436     code = afsconf_Check(adir);
1437     if (code) {
1438         UNLOCK_GLOBAL_MUTEX;
1439         return AFSCONF_FAILURE;
1440     }
1441     maxa = adir->keystr->nkeys;
1442
1443     for (tk = adir->keystr->key, i = 0; i < maxa; i++, tk++) {
1444         if (tk->kvno == avno) {
1445             memcpy(akey, tk->key, 8);
1446             UNLOCK_GLOBAL_MUTEX;
1447             return 0;
1448         }
1449     }
1450
1451     UNLOCK_GLOBAL_MUTEX;
1452     return AFSCONF_NOTFOUND;
1453 }
1454
1455 /* save the key structure in the appropriate file */
1456 static int
1457 SaveKeys(struct afsconf_dir *adir)
1458 {
1459     struct afsconf_keys tkeys;
1460     register int fd;
1461     register afs_int32 i;
1462     char tbuffer[256];
1463
1464     memcpy(&tkeys, adir->keystr, sizeof(struct afsconf_keys));
1465
1466     /* convert it to net byte order */
1467     for (i = 0; i < tkeys.nkeys; i++)
1468         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
1469     tkeys.nkeys = htonl(tkeys.nkeys);
1470
1471     /* rewrite keys file */
1472     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
1473     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
1474     if (fd < 0)
1475         return AFSCONF_FAILURE;
1476     i = write(fd, &tkeys, sizeof(tkeys));
1477     if (i != sizeof(tkeys)) {
1478         close(fd);
1479         return AFSCONF_FAILURE;
1480     }
1481     if (close(fd) < 0)
1482         return AFSCONF_FAILURE;
1483     return 0;
1484 }
1485
1486 int
1487 afsconf_AddKey(struct afsconf_dir *adir, afs_int32 akvno, char akey[8],
1488                afs_int32 overwrite)
1489 {
1490     register struct afsconf_keys *tk;
1491     register struct afsconf_key *tkey;
1492     register afs_int32 i;
1493     int foundSlot;
1494
1495     LOCK_GLOBAL_MUTEX;
1496     tk = adir->keystr;
1497
1498     if (akvno != 999) {
1499         if (akvno < 0 || akvno > 255) {
1500             UNLOCK_GLOBAL_MUTEX;
1501             return ERANGE;
1502         }
1503     }
1504     foundSlot = 0;
1505     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1506         if (tkey->kvno == akvno) {
1507             if (!overwrite) {
1508                 UNLOCK_GLOBAL_MUTEX;
1509                 return AFSCONF_KEYINUSE;
1510             }
1511             foundSlot = 1;
1512             break;
1513         }
1514     }
1515     if (!foundSlot) {
1516         if (tk->nkeys >= AFSCONF_MAXKEYS) {
1517             UNLOCK_GLOBAL_MUTEX;
1518             return AFSCONF_FULL;
1519         }
1520         tkey = &tk->key[tk->nkeys++];
1521     }
1522     tkey->kvno = akvno;
1523     memcpy(tkey->key, akey, 8);
1524     i = SaveKeys(adir);
1525     afsconf_Touch(adir);
1526     UNLOCK_GLOBAL_MUTEX;
1527     return i;
1528 }
1529
1530 /* this proc works by sliding the other guys down, rather than using a funny
1531     kvno value, so that callers can count on getting a good key in key[0].
1532 */
1533 int
1534 afsconf_DeleteKey(struct afsconf_dir *adir, afs_int32 akvno)
1535 {
1536     register struct afsconf_keys *tk;
1537     register struct afsconf_key *tkey;
1538     register int i;
1539     int foundFlag = 0;
1540
1541     LOCK_GLOBAL_MUTEX;
1542     tk = adir->keystr;
1543
1544     for (i = 0, tkey = tk->key; i < tk->nkeys; i++, tkey++) {
1545         if (tkey->kvno == akvno) {
1546             foundFlag = 1;
1547             break;
1548         }
1549     }
1550     if (!foundFlag) {
1551         UNLOCK_GLOBAL_MUTEX;
1552         return AFSCONF_NOTFOUND;
1553     }
1554
1555     /* otherwise slide the others down.  i and tkey point at the guy to delete */
1556     for (; i < tk->nkeys - 1; i++, tkey++) {
1557         tkey->kvno = (tkey + 1)->kvno;
1558         memcpy(tkey->key, (tkey + 1)->key, 8);
1559     }
1560     tk->nkeys--;
1561     i = SaveKeys(adir);
1562     afsconf_Touch(adir);
1563     UNLOCK_GLOBAL_MUTEX;
1564     return i;
1565 }