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