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