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