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