Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / auth / cellconfig.c
1 /*
2  * (C) COPYRIGHT IBM CORPORATION 1987
3  * LICENSED MATERIALS - PROPERTY OF IBM
4  */
5 #include <afs/param.h>
6 #include <afs/stds.h>
7 #include <afs/pthread_glock.h>
8 #ifdef UKERNEL
9 #include "../afs/sysincludes.h"
10 #include "../afs/afsincludes.h"
11 #else /* UKERNEL */
12 #include <sys/types.h>
13 #ifdef AFS_NT40_ENV
14 #include <winsock2.h>
15 #include <sys/utime.h>
16 #include <io.h>
17 #include <WINNT/afssw.h>
18 #else
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <netdb.h>
22 #include <sys/file.h>
23 #include <sys/time.h>
24 #endif
25 #include <errno.h>
26 #include <ctype.h>
27 #include <time.h>
28 #include <stdio.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #endif /* UKERNEL */
32 #include <afs/afsutil.h>
33 #include "cellconfig.h"
34 #include "keys.h"
35
36 static ParseHostLine();
37 static ParseCellLine();
38
39 static struct afsconf_servPair serviceTable [] = {
40     "afs",      7000,
41     "afscb",    7001,
42     "afsprot",  7002,
43     "afsvldb",  7003,
44     "afskauth", 7004,
45     "afsvol",   7005,
46     "afserror", 7006,
47     "afsnanny", 7007,
48     "afsupdate",7008,
49     "afsrmtsys",7009,
50     0, 0                /* insert new services before this spot */
51 };
52
53 /*
54  * Basic Rule: we touch "<AFSCONF_DIR>/CellServDB" every time we change anything, so
55  * our code can tell if there is new info in the key files, the cell server db
56  * files or any of the other files (and reopen the thing) if the date on
57  * CellServDB changes.
58  */
59
60 /* return port number in network byte order in the low 16 bits of a long; return -1 if not found */
61 static afs_int32 afsconf_FindService(aname)
62 register char *aname; {
63     /* lookup a service name */
64     struct servent *ts;
65     register struct afsconf_servPair *tsp;
66 #if     defined(AFS_OSF_ENV) || defined(AFS_DEC_ENV)
67     ts = getservbyname(aname, "");
68 #else
69     ts = getservbyname(aname, (char *) 0);
70 #endif
71     if (ts) {
72         /* we found it in /etc/services, so we use this value */
73         return ts->s_port;  /* already in network byte order */
74     }
75     /* not found in /etc/services, see if it is one of ours */
76     for(tsp = serviceTable;; tsp++) {
77         if (tsp->name == (char *) 0) return -1;
78         if (!strcmp(tsp->name, aname)) return htons(tsp->port);
79     }
80 }
81
82 static int TrimLine(abuffer)
83 char *abuffer; {
84     char tbuffer[256];
85     register char *tp;
86     register int tc;
87
88     tp = abuffer;
89     while (tc = *tp) {
90         if (!isspace(tc)) break;
91         tp++;
92     }
93     strcpy(tbuffer, tp);
94     strcpy(abuffer, tbuffer);
95     return 0;
96 }
97
98 #ifdef AFS_NT40_ENV
99 /*
100  * IsClientConfigDirectory() -- determine if path matches well-known
101  *     client configuration directory.
102  */
103 static int IsClientConfigDirectory(const char *path)
104 {
105     const char *cdir = AFSDIR_CLIENT_ETC_DIRPATH;
106     int i;
107
108     for (i = 0; cdir[i] != '\0' && path[i] != '\0'; i++) {
109         int cc = tolower(cdir[i]);
110         int pc = tolower(path[i]);
111
112         if (cc == '\\') {
113             cc = '/';
114         }
115         if (pc == '\\') {
116             pc = '/';
117         }
118         if (cc != pc) {
119             return 0;
120         }
121     }
122
123     /* hit end of one or both; allow mismatch in existence of trailing slash */
124     if (cdir[i] != '\0') {
125         if ((cdir[i] != '\\' && cdir[i] != '/') || (cdir[i + 1] != '\0')) {
126             return 0;
127         }
128     }
129     if (path[i] != '\0') {
130         if ((path[i] != '\\' && path[i] != '/') || (path[i + 1] != '\0')) {
131             return 0;
132         }
133     }
134     return 1;
135 }
136 #endif /* AFS_NT40_ENV */
137
138
139 static int afsconf_Check(adir)
140 register struct afsconf_dir *adir; {
141     char tbuffer[256];
142     struct stat tstat;
143     register afs_int32 code;
144
145 #ifdef AFS_NT40_ENV
146     /* NT client CellServDB has different file name than NT server or Unix */
147     if (IsClientConfigDirectory(adir->name)) {
148         strcompose(tbuffer, 256,
149                    adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
150     } else {
151         strcompose(tbuffer, 256,
152                    adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
153     }
154 #else
155     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
156 #endif /* AFS_NT40_ENV */
157
158     code = stat(tbuffer, &tstat);
159     if (code < 0) {
160         return code;
161     }
162     /* did file change? */
163     if (tstat.st_mtime == adir->timeRead) {
164         return 0;
165     }
166     /* otherwise file has changed, so reopen it */
167     return afsconf_Reopen(adir);
168 }
169
170 /* set modtime on file */
171 static afsconf_Touch(adir)
172 register struct afsconf_dir *adir; {
173     char tbuffer[256];
174     struct timeval tvp[2];
175
176 #ifdef AFS_NT40_ENV
177     /* NT client CellServDB has different file name than NT server or Unix */
178     if (IsClientConfigDirectory(adir->name)) {
179         strcompose(tbuffer, 256,
180                    adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
181     } else {
182         strcompose(tbuffer, 256,
183                    adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
184     }
185 #else
186     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
187 #endif /* AFS_NT40_ENV */
188
189     adir->timeRead = 0; /* just in case */
190 #ifdef AFS_NT40_ENV
191     return _utime(tbuffer, NULL);
192 #else
193     gettimeofday(&tvp[0], (char *) 0);
194     tvp[1] = tvp[0];
195     return utimes(tbuffer, tvp);
196 #endif  /* AFS_NT40_ENV */
197 }
198
199 struct afsconf_dir *afsconf_Open(adir)
200 register char *adir; {
201     register struct afsconf_dir *tdir;
202     register afs_int32 code;
203
204     LOCK_GLOBAL_MUTEX
205     /* zero structure and fill in name; rest is done by internal routine */
206     tdir = (struct afsconf_dir *) malloc(sizeof(struct afsconf_dir));
207     bzero(tdir, sizeof(struct afsconf_dir));
208     tdir->name = (char *) malloc(strlen(adir)+1);
209     strcpy(tdir->name, adir);
210
211     code = afsconf_OpenInternal(tdir);
212     if (code) {
213         char *afsconf_path, *getenv(), afs_confdir[128];
214
215         free(tdir->name);
216         /* Check global place only when local Open failed for whatever reason */
217         if (!(afsconf_path = getenv("AFSCONF"))) {
218             /* 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... */
219             char *home_dir;
220             FILE *fp;
221             int len;
222
223             if (!(home_dir = getenv("HOME"))) {
224                 /* Our last chance is the "/.AFSCONF" file */
225                 fp = fopen("/.AFSCONF", "r");
226                 if (fp == 0) {
227                     free(tdir);
228                     UNLOCK_GLOBAL_MUTEX
229                     return (struct afsconf_dir *) 0;
230                 }
231                 fgets(afs_confdir, 128, fp);
232                 fclose(fp);
233             } else {
234                 char pathname[256];
235
236                 sprintf(pathname, "%s/%s", home_dir, ".AFSCONF");
237                 fp = fopen(pathname, "r");
238                 if (fp == 0) {
239                     /* Our last chance is the "/.AFSCONF" file */
240                     fp = fopen("/.AFSCONF", "r");
241                     if (fp == 0) {
242                         free(tdir);
243                         UNLOCK_GLOBAL_MUTEX
244                         return (struct afsconf_dir *) 0;
245                     }
246                     fgets(afs_confdir, 128, fp);
247                     fclose(fp);
248                 }
249                 fgets(afs_confdir, 128, fp);
250                 fclose(fp);             
251             }
252             len = strlen(afs_confdir);
253             if (len == 0) {
254                 free(tdir);
255                 UNLOCK_GLOBAL_MUTEX
256                 return (struct afsconf_dir *) 0;
257             }
258             if (afs_confdir[len-1] == '\n') {
259                 afs_confdir[len-1] = 0;
260             }
261             afsconf_path = afs_confdir;
262         }
263         tdir->name = (char *) malloc(strlen(afsconf_path)+1);
264         strcpy(tdir->name, afsconf_path);
265         code = afsconf_OpenInternal(tdir);
266         if (code) {
267             free(tdir->name);
268             free(tdir);
269             UNLOCK_GLOBAL_MUTEX
270             return (struct afsconf_dir *) 0;
271         }
272     }
273     UNLOCK_GLOBAL_MUTEX
274     return tdir;
275 }
276
277
278 static int GetCellUnix(struct afsconf_dir *adir)
279 {
280     int rc;
281     char tbuffer[256];
282     FILE *tf;
283
284     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_THISCELL_FILE, NULL);
285     tf = fopen(tbuffer, "r");
286     if (tf) {
287         rc = fscanf(tf, "%s", tbuffer);
288         if (rc == 1) {
289             adir->cellName = (char *) malloc(strlen(tbuffer)+1);
290             strcpy(adir->cellName, tbuffer);
291         }
292         fclose(tf);
293     }
294     else {
295         return -1;
296     }
297     return 0;
298 }
299
300
301 #ifdef AFS_NT40_ENV
302 static int GetCellNT(struct afsconf_dir *adir)
303 {
304     if (IsClientConfigDirectory(adir->name)) {
305         /* NT client config dir; ThisCell is in registry (no file). */
306         return afssw_GetClientCellName(&adir->cellName);
307     } else {
308         /* NT server config dir; works just like Unix */
309         return GetCellUnix(adir);
310     }
311 }
312 #endif /* AFS_NT40_ENV */
313
314
315 static int afsconf_OpenInternal(adir)
316 register struct afsconf_dir *adir; {
317     FILE *tf;
318     register char *tp, *bp;
319     register struct afsconf_entry *curEntry;
320     register afs_int32 code;
321     afs_int32 i;
322     char tbuffer[256], tbuf1[256];
323     struct stat tstat;
324
325     /* figure out the cell name */
326 #ifdef AFS_NT40_ENV
327     i = GetCellNT(adir);
328 #else
329     i = GetCellUnix(adir);
330 #endif
331     if (i) {
332         return i;
333     }
334
335     /* now parse the individual lines */
336     curEntry = 0;
337
338 #ifdef AFS_NT40_ENV
339     /* NT client/server have a CellServDB that is the same format as Unix.
340      * However, the NT client uses a different file name
341      */
342     if (IsClientConfigDirectory(adir->name)) {
343         /* NT client config dir */
344         strcompose(tbuffer, 256,
345                    adir->name, "/", AFSDIR_CELLSERVDB_FILE_NTCLIENT, NULL);
346     } else {
347         /* NT server config dir */
348         strcompose(tbuffer, 256,
349                    adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
350     }
351 #else
352     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_CELLSERVDB_FILE, NULL);
353 #endif  /* AFS_NT40_ENV */
354
355     if (!stat(tbuffer, &tstat)) {
356         adir->timeRead = tstat.st_mtime;
357     } else {
358         adir->timeRead = 0;
359     }
360
361     strcpy(tbuf1, tbuffer);
362     tf = fopen(tbuffer, "r");
363     if (!tf) {
364         return -1;
365     }
366     while (1) {
367         tp = fgets(tbuffer, sizeof(tbuffer), tf);
368         if (!tp) break;
369         TrimLine(tbuffer);  /* remove white space */
370         if (tbuffer[0] == 0 || tbuffer[0] == '\n') continue;   /* empty line */
371         if (tbuffer[0] == '>') {
372             char linkedcell[MAXCELLCHARS];
373             /* start new cell item */
374             if (curEntry) {
375                 /* thread this guy on the list */
376                 curEntry->next = adir->entries;
377                 adir->entries = curEntry;
378                 curEntry = 0;
379             }
380             curEntry = (struct afsconf_entry *) malloc(sizeof(struct afsconf_entry));
381             bzero(curEntry, sizeof(struct afsconf_entry));
382             code = ParseCellLine(tbuffer, curEntry->cellInfo.name, linkedcell);
383             if (code) {
384                 afsconf_CloseInternal(adir);
385                 fclose(tf);
386                 return -1;
387             }
388             if (linkedcell[0] != '\0') {
389                 curEntry->cellInfo.linkedCell =
390                     (char *) malloc(strlen(linkedcell) + 1);
391                 strcpy(curEntry->cellInfo.linkedCell, linkedcell);
392             }
393         }
394         else {
395             /* new host in the current cell */
396             if (!curEntry) {
397                 afsconf_CloseInternal(adir);
398                 fclose(tf);
399                 return -1;
400             }
401             i = curEntry->cellInfo.numServers;
402             code = ParseHostLine(tbuffer, (char *) &curEntry->cellInfo.hostAddr[i], curEntry->cellInfo.hostName[i]);
403             if (code) {
404                 if (code == AFSCONF_SYNTAX) {
405                     for (bp=tbuffer; *bp != '\n'; bp++) {       /* Take out the <cr> from the buffer */
406                         if (!*bp) break;
407                     }
408                     *bp= '\0';
409                     fprintf(stderr, "Can't properly parse host line \"%s\" in configuration file %s\n", tbuffer, tbuf1);
410                 }
411                 free(curEntry);
412                 fclose(tf);
413                 afsconf_CloseInternal(adir);
414                 return -1;
415             }
416             curEntry->cellInfo.numServers = ++i;
417         }
418     }
419     fclose(tf); /* close the file now */
420
421     /* end the last partially-completed cell */
422     if (curEntry) {
423         curEntry->next = adir->entries;
424         adir->entries = curEntry;
425     }
426     
427     /* now read the fs keys, if possible */
428     adir->keystr = (struct afsconf_keys *) 0;
429     afsconf_IntGetKeys(adir);
430
431     return 0;
432 }
433
434 /* parse a line of the form
435  *"128.2.1.3    #hostname"
436  * into the appropriate pieces.  
437  */
438 static ParseHostLine(aline, addr, aname)
439 register struct sockaddr_in *addr;
440 char *aline, *aname; {
441     int c1, c2, c3, c4;
442     register afs_int32 code;
443     register char *tp;
444
445     code = sscanf(aline, "%d.%d.%d.%d #%s", &c1, &c2, &c3, &c4, aname);
446     if (code != 5) return AFSCONF_SYNTAX;
447     addr->sin_family = AF_INET;
448     addr->sin_port = 0;
449     tp = (char *) &addr->sin_addr;
450     *tp++ = c1;
451     *tp++ = c2;
452     *tp++ = c3;
453     *tp++ = c4;
454     return 0;
455 }
456
457 /* parse a line of the form
458  * ">cellname [linkedcellname] [#comments]"
459  * into the appropriate pieces.
460  */
461 static ParseCellLine(aline, aname, alname)
462 register char *aline, *aname, *alname; {
463     register int code;
464     code = sscanf(aline, ">%s %s", aname, alname);
465     if (code == 1) *alname = '\0';
466     if (code == 2) {
467         if (*alname == '#') {
468             *alname = '\0';
469         }
470     }
471     return (code > 0 ? 0 : AFSCONF_SYNTAX);
472 }
473
474 /* call aproc(entry, arock, adir) for all cells.  Proc must return 0, or we'll stop early and return the code it returns */
475 afsconf_CellApply(adir, aproc, arock)
476 struct afsconf_dir *adir;
477 int (*aproc)();
478 char *arock; {
479     register struct afsconf_entry *tde;
480     register afs_int32 code;
481     LOCK_GLOBAL_MUTEX
482     for(tde=adir->entries; tde; tde=tde->next) {
483         code = (*aproc)(&tde->cellInfo, arock, adir);
484         if (code) {
485             UNLOCK_GLOBAL_MUTEX
486             return code;
487         }
488     }
489     UNLOCK_GLOBAL_MUTEX
490     return 0;
491 }
492
493 afs_int32 afsconf_SawCell = 0;
494 afsconf_GetCellInfo(adir, acellName, aservice, acellInfo)
495 struct afsconf_dir *adir;
496 char *aservice;
497 char *acellName;
498 struct afsconf_cell *acellInfo; {
499     register struct afsconf_entry *tce;
500     struct afsconf_entry *bestce;
501     register afs_int32 i;
502     int tservice;
503     char *tcell;
504     int cnLen, ambig;
505     char tbuffer[64];
506
507     LOCK_GLOBAL_MUTEX
508     if (adir) afsconf_Check(adir);
509     if (acellName) {
510         tcell = acellName;
511         cnLen = strlen(tcell)+1;
512         lcstring (tcell, tcell, cnLen);
513         afsconf_SawCell = 1;                       /* will ignore the AFSCELL switch on future */
514                                                    /* call to afsconf_GetLocalCell: like klog  */
515     } else {
516         i = afsconf_GetLocalCell(adir, tbuffer, sizeof(tbuffer));
517         if (i) {
518             UNLOCK_GLOBAL_MUTEX
519             return i;
520         }
521         tcell = tbuffer;
522     }
523     cnLen = strlen(tcell);
524     bestce = (struct afsconf_entry *) 0;
525     ambig = 0;
526     if (!adir) {
527         UNLOCK_GLOBAL_MUTEX
528         return 0;
529     }
530     for(tce=adir->entries;tce;tce=tce->next) {
531         if (strcasecmp(tce->cellInfo.name, tcell) == 0) {
532             /* found our cell */
533             bestce = tce;
534             ambig = 0;
535             break;
536         }
537         if (strlen(tce->cellInfo.name) < cnLen) continue;   /* clearly wrong */
538         if (strncasecmp(tce->cellInfo.name, tcell, cnLen) == 0) {
539             if (bestce) ambig = 1;  /* ambiguous unless we get exact match */
540             bestce = tce;
541         }
542     }
543     if (!ambig && bestce) {
544         *acellInfo = bestce->cellInfo;  /* structure assignment */
545         if (aservice) {
546             tservice = afsconf_FindService(aservice);
547             if (tservice < 0) {
548                 UNLOCK_GLOBAL_MUTEX
549                 return AFSCONF_NOTFOUND;  /* service not found */
550             }
551             for(i=0;i<acellInfo->numServers;i++) {
552                 acellInfo->hostAddr[i].sin_port = tservice;
553             }
554         }
555         UNLOCK_GLOBAL_MUTEX
556         return 0;
557     }
558     else {
559         UNLOCK_GLOBAL_MUTEX
560         return AFSCONF_NOTFOUND;
561     }
562 }
563
564 afsconf_GetLocalCell(adir, aname, alen)
565 register struct afsconf_dir *adir;
566 char *aname;
567 afs_int32 alen; {
568     static int  afsconf_showcell = 0;
569     char        *afscell_path;
570     char        *getenv();
571     afs_int32        code = 0;
572
573    LOCK_GLOBAL_MUTEX
574    /*
575     * If a cell switch was specified in a command, then it should override the 
576     * AFSCELL variable.  If a cell was specified, then the afsconf_SawCell flag
577     * is set and the cell name in the adir structure is used.
578     * Read the AFSCELL var each time: in case it changes (unsetenv AFSCELL).
579     */
580    if ( !afsconf_SawCell && (afscell_path= getenv("AFSCELL")) ) {     
581         if ( !afsconf_showcell ) {
582             fprintf(stderr, "Note: Operation is performed on cell %s\n", afscell_path);
583             afsconf_showcell = 1;
584         }
585         strncpy(aname, afscell_path, alen);
586     } else {                                    
587         afsconf_Check(adir);
588         if (adir->cellName) {
589             strncpy(aname, adir->cellName, alen);
590         }
591         else code = AFSCONF_UNKNOWN;
592     }
593
594     UNLOCK_GLOBAL_MUTEX
595     return(code);
596 }
597
598 afsconf_Close(adir)
599 struct afsconf_dir *adir; {
600     LOCK_GLOBAL_MUTEX
601     afsconf_CloseInternal(adir);
602     if (adir->name) free(adir->name);
603     free(adir);
604     UNLOCK_GLOBAL_MUTEX
605     return 0;
606 }
607
608 static int afsconf_CloseInternal(adir)
609 register struct afsconf_dir *adir; {
610     register struct afsconf_entry *td, *nd;
611     register char *tname;
612
613     tname = adir->name; /* remember name, since that's all we preserve */
614
615     /* free everything we can find */
616     if (adir->cellName) free(adir->cellName);
617     for(td=adir->entries;td;td=nd) {
618         nd = td->next;
619         if (td->cellInfo.linkedCell)
620             free(td->cellInfo.linkedCell);
621         free(td);
622     }
623     if (adir->keystr) free(adir->keystr);
624
625     /* reinit */
626     bzero(adir, sizeof(struct afsconf_dir));
627     adir->name = tname;     /* restore it */
628     return 0;
629 }
630
631 static int afsconf_Reopen(adir)
632 register struct afsconf_dir *adir; {
633     register afs_int32 code;
634     code = afsconf_CloseInternal(adir);
635     if (code) return code;
636     code = afsconf_OpenInternal(adir);
637     return code;
638 }
639
640 /* called during opening of config file */
641 afsconf_IntGetKeys(adir)
642 struct afsconf_dir *adir;
643 {
644     char tbuffer[256];
645     register int fd;
646     struct afsconf_keys *tstr;
647     register afs_int32 code;
648
649 #ifdef AFS_NT40_ENV
650     /* NT client config dir has no KeyFile; don't risk attempting open
651      * because there might be a random file of this name if dir is shared.
652      */
653     if (IsClientConfigDirectory(adir->name)) {
654         adir->keystr = ((struct afsconf_keys *)
655                         malloc(sizeof(struct afsconf_keys)));
656         adir->keystr->nkeys = 0;
657         return 0;
658     }
659 #endif /* AFS_NT40_ENV */
660
661     LOCK_GLOBAL_MUTEX
662     /* compute the key name and other setup */
663
664     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
665     tstr = (struct afsconf_keys *) malloc(sizeof (struct afsconf_keys));
666     adir->keystr = tstr;
667
668     /* read key file */
669     fd = open(tbuffer, O_RDONLY);
670     if (fd < 0) {
671         tstr->nkeys = 0;
672         UNLOCK_GLOBAL_MUTEX
673         return 0;
674     }
675     code = read(fd, tstr, sizeof(struct afsconf_keys));
676     close(fd);
677     if (code < sizeof(afs_int32)) {
678         tstr->nkeys = 0;
679         UNLOCK_GLOBAL_MUTEX
680         return 0;
681     }
682
683     /* convert key structure to host order */
684     tstr->nkeys = ntohl(tstr->nkeys);
685     for(fd=0;fd<tstr->nkeys;fd++)
686         tstr->key[fd].kvno = ntohl(tstr->key[fd].kvno);
687
688     UNLOCK_GLOBAL_MUTEX
689     return 0;
690 }
691
692 /* get keys structure */
693 afsconf_GetKeys(adir, astr)
694 struct afsconf_dir *adir;
695 struct afsconf_keys *astr;
696 {
697     LOCK_GLOBAL_MUTEX
698     afsconf_Check(adir);
699     bcopy(adir->keystr, astr, sizeof(struct afsconf_keys));
700     UNLOCK_GLOBAL_MUTEX
701     return 0;
702 }
703
704 /* get latest key */
705 afs_int32 afsconf_GetLatestKey(adir, avno, akey)
706   IN struct afsconf_dir *adir;
707   OUT afs_int32 *avno;
708   OUT char *akey;
709 {
710     register int i;
711     int maxa;
712     register struct afsconf_key *tk;
713     register afs_int32 best;
714     struct afsconf_key *bestk;
715     
716     LOCK_GLOBAL_MUTEX
717     afsconf_Check(adir);
718     maxa = adir->keystr->nkeys;
719
720     best = -1;      /* highest kvno we've seen yet */
721     bestk = (struct afsconf_key *) 0;   /* ptr to structure providing best */
722     for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
723         if (tk->kvno == 999) continue;  /* skip bcrypt keys */
724         if (tk->kvno > best) {
725             best = tk->kvno;
726             bestk = tk;
727         }
728     }
729     if (bestk) {    /* found any  */
730         if (akey) bcopy(bestk->key, akey, 8); /* copy out latest key */
731         if (avno) *avno = bestk->kvno;  /* and kvno to caller */
732         UNLOCK_GLOBAL_MUTEX
733         return 0;
734     }
735     UNLOCK_GLOBAL_MUTEX
736     return AFSCONF_NOTFOUND;    /* didn't find any keys */
737 }
738
739 /* get a particular key */
740 afsconf_GetKey(adir, avno, akey)
741 struct afsconf_dir *adir;
742 afs_int32 avno;
743 char *akey;
744 {
745     register int i, maxa;
746     register struct afsconf_key *tk;
747
748     LOCK_GLOBAL_MUTEX
749     afsconf_Check(adir);
750     maxa = adir->keystr->nkeys;
751
752     for(tk = adir->keystr->key,i=0;i<maxa;i++,tk++) {
753         if (tk->kvno == avno) {
754             bcopy(tk->key, akey, 8);
755             UNLOCK_GLOBAL_MUTEX
756             return 0;
757         }
758     }
759
760     UNLOCK_GLOBAL_MUTEX
761     return AFSCONF_NOTFOUND;
762 }
763
764 /* save the key structure in the appropriate file */
765 static SaveKeys(adir)
766 struct afsconf_dir *adir;
767 {
768     struct afsconf_keys tkeys;
769     register int fd;
770     register afs_int32 i;
771     char tbuffer[256];
772
773     bcopy(adir->keystr, &tkeys, sizeof(struct afsconf_keys));
774
775     /* convert it to net byte order */
776     for(i = 0; i<tkeys.nkeys; i++ )
777         tkeys.key[i].kvno = htonl(tkeys.key[i].kvno);
778     tkeys.nkeys = htonl(tkeys.nkeys);
779     
780     /* rewrite keys file */
781     strcompose(tbuffer, 256, adir->name, "/", AFSDIR_KEY_FILE, NULL);
782     fd = open(tbuffer, O_RDWR | O_CREAT | O_TRUNC, 0600);
783     if (fd < 0) return AFSCONF_FAILURE;
784     i = write(fd, &tkeys, sizeof(tkeys));
785     if (i != sizeof(tkeys)) {
786         close(fd);
787         return AFSCONF_FAILURE;
788     }
789     if (close(fd) < 0) return AFSCONF_FAILURE;
790     return 0;
791 }
792
793 afsconf_AddKey(adir, akvno, akey, overwrite)
794 struct afsconf_dir *adir;
795 afs_int32 akvno, overwrite;
796 char akey[8];
797 {
798     register struct afsconf_keys *tk;
799     register struct afsconf_key *tkey;
800     register afs_int32 i;
801     int foundSlot;
802
803     LOCK_GLOBAL_MUTEX
804     tk = adir->keystr;
805     
806     if (akvno != 999) {
807         if (akvno < 0 || akvno > 255) {
808             UNLOCK_GLOBAL_MUTEX
809             return ERANGE;
810         }
811     }
812     foundSlot = 0;
813     for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
814         if (tkey->kvno == akvno) {
815             if (!overwrite) {
816                 UNLOCK_GLOBAL_MUTEX
817                 return AFSCONF_KEYINUSE;
818             }
819             foundSlot = 1;
820             break;
821         }
822     }
823     if (!foundSlot) {
824         if (tk->nkeys >= AFSCONF_MAXKEYS) {
825             UNLOCK_GLOBAL_MUTEX
826             return AFSCONF_FULL;
827         }
828         tkey = &tk->key[tk->nkeys++];
829     }
830     tkey->kvno = akvno;
831     bcopy(akey, tkey->key, 8);
832     i = SaveKeys(adir);
833     afsconf_Touch(adir);
834     UNLOCK_GLOBAL_MUTEX
835     return i;
836 }
837
838 /* this proc works by sliding the other guys down, rather than using a funny
839     kvno value, so that callers can count on getting a good key in key[0].
840 */
841 afsconf_DeleteKey(adir, akvno)
842 struct afsconf_dir *adir;
843 afs_int32 akvno;
844 {
845     register struct afsconf_keys *tk;
846     register struct afsconf_key *tkey;
847     register int i;
848     int foundFlag = 0;
849
850     LOCK_GLOBAL_MUTEX
851     tk = adir->keystr;
852
853     for(i=0, tkey = tk->key; i<tk->nkeys; i++, tkey++) {
854         if (tkey->kvno == akvno) {
855             foundFlag = 1;
856             break;
857         }
858     }
859     if (!foundFlag) {
860         UNLOCK_GLOBAL_MUTEX
861         return AFSCONF_NOTFOUND;
862     }
863
864     /* otherwise slide the others down.  i and tkey point at the guy to delete */
865     for(;i<tk->nkeys-1; i++,tkey++) {
866         tkey->kvno = (tkey+1)->kvno;
867         bcopy((tkey+1)->key, tkey->key, 8);
868     }
869     tk->nkeys--;
870     i = SaveKeys(adir);
871     afsconf_Touch(adir);
872     UNLOCK_GLOBAL_MUTEX
873     return i;
874 }