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