Standardize License information
[openafs.git] / src / libadmin / cfg / cfghost.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 /* This file implements configuration functions in the following categories:
11  *   cfg_Host*()         - manipulate static server configuration information.
12  */
13
14 #include <afs/param.h>
15 #include <afs/stds.h>
16
17 #include <stddef.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25
26 #include <pthread.h>
27
28 #ifdef AFS_NT40_ENV
29 #include <WINNT/vptab.h>
30 #include <WINNT/afsreg.h>
31 #include <io.h>
32 #include <direct.h>
33 #else
34 #include <unistd.h>
35 #endif /* AFS_NT40_ENV */
36
37 #include <afs/afs_Admin.h>
38 #include <afs/afs_AdminErrors.h>
39 #include <afs/afs_utilAdmin.h>
40 #include <afs/afs_bosAdmin.h>
41 #include <afs/afs_clientAdmin.h>
42 #include <afs/afs_kasAdmin.h>
43 #include <afs/afs_ptsAdmin.h>
44
45
46 #include <afs/kautils.h>
47 #include <afs/bnode.h>
48 #include <afs/prerror.h>
49 #include <afs/keys.h>
50 #include <afs/dirpath.h>
51 #include <afs/cellconfig.h>
52
53 #include "cfginternal.h"
54 #include "afs_cfgAdmin.h"
55 #include "../adminutil/afs_AdminInternal.h"
56
57
58
59 /* Local declarations and definitions */
60
61 static int
62 KasKeyIsZero(kas_encryptionKey_t *kasKey);
63
64 static int
65 KasKeyEmbeddedInString(const char *keyString, kas_encryptionKey_t *kasKey);
66
67
68
69
70 /* ---------------- Exported Server Host functions ------------------ */
71
72
73 /*
74  * cfg_HostQueryStatus() -- Query status of static server configuration
75  *     on host, i.e., status of required configuration files, etc.
76  *     Upon successful completion *configStP is set to the server
77  *     configuration status, with a value of zero (0) indicating that
78  *     the configuration is valid.
79  *
80  *     If server configuration is not valid then *cellNameP is set to NULL;
81  *     otherwise, *cellNameP is an allocated buffer containing server cell.
82  *
83  *     Warning: in determining if server configuration is valid, no check
84  *     is made for consistency with other servers in cell; also, the
85  *     internal consistency of configuration files may not be verified.
86  */
87 int ADMINAPI
88 cfg_HostQueryStatus(const char *hostName,    /* name of host */
89                     afs_status_p configStP,  /* server config status */
90                     char **cellNameP,        /* server's cell */
91                     afs_status_p st)         /* completion status */
92 {
93     int rc = 1;
94     afs_status_t tst2, tst = 0;
95     afs_status_t serverSt = 0;
96     char *serverCellName = NULL;
97
98     /* validate parameters */
99
100     if (hostName == NULL || *hostName == '\0') {
101         tst = ADMCFGHOSTNAMENULL;
102     } else if (strlen(hostName) > (MAXHOSTCHARS - 1)) {
103         tst = ADMCFGHOSTNAMETOOLONG;
104     } else if (configStP == NULL) {
105         tst = ADMCFGCONFIGSTATUSPNULL;
106     } else if (cellNameP == NULL) {
107         tst = ADMCFGCELLNAMEPNULL;
108     }
109
110     /* remote configuration not yet supported; hostName must be local host */
111
112     if (tst == 0) {
113         short isLocal;
114
115         if (!cfgutil_HostNameIsLocal(hostName, &isLocal, &tst2)) {
116             tst = tst2;
117         } else if (!isLocal) {
118             tst = ADMCFGNOTSUPPORTED;
119         }
120     }
121
122     /* check for existence and readability of required server config files */
123
124     if (tst == 0) {
125         int i;
126         const char *cfgfile[4];
127
128         cfgfile[0] = AFSDIR_SERVER_THISCELL_FILEPATH;
129         cfgfile[1] = AFSDIR_SERVER_CELLSERVDB_FILEPATH;
130         cfgfile[2] = AFSDIR_SERVER_KEY_FILEPATH;
131         cfgfile[3] = AFSDIR_SERVER_ULIST_FILEPATH;
132
133         for (i = 0; i < 4; i++) {
134             int fd;
135             if ((fd = open(cfgfile[i], O_RDONLY)) < 0) {
136                 break;
137             }
138             (void) close(fd);
139         }
140
141         if (i < 4) {
142             if (errno == EACCES) {
143                 tst = ADMNOPRIV;
144             } else {
145                 serverSt = ADMCFGSERVERBASICINFOINVALID;
146             }
147         }
148     }
149
150     /* verify the required server config files to the degree possible */
151
152     if (tst == 0 && serverSt == 0) {
153         struct afsconf_dir *confdir;
154
155         if ((confdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH)) == NULL) {
156             /* one or more config files appears to be invalid */
157             serverSt = ADMCFGSERVERBASICINFOINVALID;
158         } else {
159             struct afsconf_entry *cellentry;
160
161             if (confdir->cellName == NULL || *confdir->cellName == '\0') {
162                 /* no cell set for server */
163                 serverSt = ADMCFGSERVERNOTINCELL;
164             } else if (confdir->keystr == NULL ||
165                        confdir->keystr->nkeys == 0) {
166                 /* no server keys */
167                 serverSt = ADMCFGSERVERNOKEYS;
168             } else {
169                 for (cellentry = confdir->entries;
170                      cellentry != NULL;
171                      cellentry = cellentry->next) {
172                     if (!strcasecmp(confdir->cellName,
173                                     cellentry->cellInfo.name)) {
174                         break;
175                     }
176                 }
177
178                 if (cellentry == NULL) {
179                     serverSt = ADMCFGSERVERCELLNOTINDB;
180                 } else if (cellentry->cellInfo.numServers <= 0) {
181                     serverSt = ADMCFGSERVERCELLHASNODBENTRIES;
182                 }
183             }
184
185             if (tst == 0 && serverSt == 0) {
186                 /* everything looks good; malloc cell name buffer to return */
187                 serverCellName =
188                     (char *) malloc(strlen(cellentry->cellInfo.name) + 1);
189                 if (serverCellName == NULL) {
190                     tst = ADMNOMEM;
191                 } else {
192                     strcpy(serverCellName, cellentry->cellInfo.name);
193                 }
194             }
195
196             (void)afsconf_Close(confdir);
197         }
198     }
199
200     if (tst == 0) {
201         /* return server status and cell name */
202         *configStP = serverSt;
203
204         if (serverSt == 0) {
205             *cellNameP = serverCellName;
206         } else {
207             *cellNameP = NULL;
208         }
209     } else {
210         /* indicate failure */
211         rc = 0;
212
213         /* free cell name if allocated before failure */
214         if (serverCellName != NULL) {
215             free(serverCellName);
216         }
217     }
218     if (st != NULL) {
219         *st = tst;
220     }
221     return rc;
222 }
223
224
225 /*
226  * cfg_HostOpen() -- Obtain host configuration handle.
227  */
228 int ADMINAPI
229 cfg_HostOpen(void *cellHandle,       /* cell handle */
230              const char *hostName,   /* name of host to configure */
231              void **hostHandleP,     /* host config handle */
232              afs_status_p st)        /* completion status */
233 {
234     int rc = 1;
235     afs_status_t tst2, tst = 0;
236     cfg_host_p cfg_host;
237     char fullHostName[MAXHOSTCHARS];
238
239     /* validate parameters and resolve host name to fully qualified name */
240
241     if (!CellHandleIsValid(cellHandle, &tst2)) {
242         tst = tst2;
243     } else if (hostName == NULL || *hostName == '\0') {
244         tst = ADMCFGHOSTNAMENULL;
245     } else if (strlen(hostName) > (MAXHOSTCHARS - 1)) {
246         tst = ADMCFGHOSTNAMETOOLONG;
247     } else if (hostHandleP == NULL) {
248         tst = ADMCFGHOSTHANDLEPNULL;
249     } else if (!cfgutil_HostNameGetFull(hostName, fullHostName, &tst2)) {
250         tst = tst2;
251     }
252
253     /* remote configuration not yet supported; hostName must be local host */
254
255     if (tst == 0) {
256         short isLocal;
257
258         if (!cfgutil_HostNameIsLocal(hostName, &isLocal, &tst2)) {
259             tst = tst2;
260         } else if (!isLocal) {
261             tst = ADMCFGNOTSUPPORTED;
262         }
263     }
264
265     /* allocate a host configuration handle */
266
267     if (tst == 0) {
268         char *localHostName;
269
270         if ((cfg_host = (cfg_host_p)malloc(sizeof(cfg_host_t))) == NULL) {
271             tst = ADMNOMEM;
272         } else if ((localHostName =
273                     (char *)malloc(strlen(fullHostName) + 1)) == NULL) {
274             free(cfg_host);
275             tst = ADMNOMEM;
276         } else {
277             /* initialize handle */
278             cfg_host->begin_magic = BEGIN_MAGIC;
279             cfg_host->is_valid = 1;
280             cfg_host->hostName = localHostName;
281             cfg_host->is_local = 1;  /* not yet supporting remote config */
282             cfg_host->cellHandle = cellHandle;
283             cfg_host->bosHandle = NULL;
284             cfg_host->end_magic = END_MAGIC;
285
286             strcpy(localHostName, fullHostName);
287
288             if (!afsclient_CellNameGet(cfg_host->cellHandle,
289                                        &cfg_host->cellName, &tst2)) {
290                 tst = tst2;
291             } else if (pthread_mutex_init(&cfg_host->mutex, NULL)) {
292                 tst = ADMMUTEXINIT;
293             }
294
295             if (tst != 0) {
296                 /* cell name lookup or mutex initialization failed */
297                 free(localHostName);
298                 free(cfg_host);
299             }
300         }
301     }
302
303     if (tst == 0) {
304         /* success; return host config handle to user */
305         *hostHandleP = cfg_host;
306     } else {
307         /* indicate failure */
308         rc = 0;
309     }
310     if (st != NULL) {
311         *st = tst;
312     }
313     return rc;
314 }
315
316
317 /*
318  * cfg_HostClose() -- Release host configuration handle.
319  */
320 int ADMINAPI
321 cfg_HostClose(void *hostHandle,   /* host config handle */
322               afs_status_p st)    /* completion status */
323 {
324     int rc = 1;
325     afs_status_t tst2, tst = 0;
326     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
327
328     /* validate parameters */
329
330     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
331         tst = tst2;
332     }
333
334     /* free handle; can assume no other thread using this handle */
335
336     if (tst == 0) {
337         /* mark cfg handle invalid in case use after free (bug catcher) */
338         cfg_host->is_valid = 0;
339
340         if (cfg_host->bosHandle != NULL) {
341             if (!bos_ServerClose(cfg_host->bosHandle, &tst2)) {
342                 tst = tst2;
343             }
344         }
345         free(cfg_host->hostName);
346         (void)pthread_mutex_destroy(&cfg_host->mutex);
347         free(cfg_host);
348     }
349
350     if (tst != 0) {
351         rc = 0;
352     }
353     if (st != NULL) {
354         *st = tst;
355     }
356     return rc;
357 }
358
359
360 /*
361  * cfg_HostSetCell() -- Define server cell membership for host.
362  *
363  *     The cellDbHosts argument is a multistring containing the names of
364  *     the existing database servers already configured in the cell; this
365  *     multistring list can be obtained via cfg_CellServDbEnumerate().
366  *     If configuring the first server in a new cell then the cellDbHosts
367  *     list contains only the name of that host.
368  *
369  *     Note: The names in cellDbHosts MUST exactly match those in the
370  *           cell-wide server CellServDB; using cfg_CellServDbEnumerate()
371  *           is highly recommended.
372  */
373 int ADMINAPI
374 cfg_HostSetCell(void *hostHandle,        /* host config handle */
375                 const char *cellName,    /* cell name */
376                 const char *cellDbHosts, /* cell database hosts */
377                 afs_status_p st)         /* completion status */
378 {
379     int rc = 1;
380     afs_status_t tst2, tst = 0;
381     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
382
383     /* validate parameters */
384
385     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
386         tst = tst2;
387     } else if (cellName == NULL || *cellName == '\0') {
388         tst = ADMCFGCELLNAMENULL;
389     } else if (strlen(cellName) > (MAXCELLCHARS - 1)) {
390         tst = ADMCFGCELLNAMETOOLONG;
391     } else if (!cfgutil_HostHandleCellNameCompatible(cfg_host, cellName)) {
392         tst = ADMCFGCELLNAMECONFLICT;
393     } else if (cellDbHosts == NULL || *cellDbHosts == '\0') {
394         tst = ADMCFGCELLDBHOSTSNULL;
395     }
396
397     /* remote configuration not yet supported in this function */
398
399     if (tst == 0) {
400         if (!cfg_host->is_local) {
401             tst = ADMCFGNOTSUPPORTED;
402         }
403     }
404
405     /* define server cell and cell database hosts */
406
407     if (tst == 0) {
408         const char *dbHost = cellDbHosts;
409         struct afsconf_cell hostCell;
410         memset(&hostCell, 0, sizeof(hostCell));
411
412         strcpy(hostCell.name, cellName);
413         hostCell.numServers = 0;
414
415         while (*dbHost != '\0' && tst == 0) {
416             /* fill in each database host */
417             size_t dbHostLen = strlen(dbHost);
418
419             if (dbHostLen > (MAXHOSTCHARS - 1)) {
420                 tst = ADMCFGHOSTNAMETOOLONG;
421             } else if (hostCell.numServers >= MAXHOSTSPERCELL) {
422                 tst = ADMCFGCELLDBHOSTCOUNTTOOLARGE;
423             } else {
424                 strcpy(hostCell.hostName[hostCell.numServers++], dbHost);
425                 dbHost += dbHostLen + 1;
426             }
427         }
428
429         if (tst == 0) {
430             /* create server ThisCell/CellServDB dir if it does not exist */
431 #ifdef AFS_NT40_ENV
432             (void) mkdir(AFSDIR_USR_DIRPATH);
433             (void) mkdir(AFSDIR_SERVER_AFS_DIRPATH);
434             (void) mkdir(AFSDIR_SERVER_ETC_DIRPATH);
435 #else
436             (void) mkdir(AFSDIR_USR_DIRPATH, 0755);
437             (void) mkdir(AFSDIR_SERVER_AFS_DIRPATH, 0755);
438             (void) mkdir(AFSDIR_SERVER_ETC_DIRPATH, 0755);
439 #endif
440             if (afsconf_SetCellInfo(NULL,
441                                     AFSDIR_SERVER_ETC_DIRPATH, &hostCell)) {
442                 /* failed; most likely cause is bad host name */
443                 tst = ADMCFGSERVERSETCELLFAILED;
444             }
445         }
446     }
447
448     if (tst != 0) {
449         rc = 0;
450     }
451     if (st != NULL) {
452         *st = tst;
453     }
454     return rc;
455 }
456
457
458 /*
459  * cfg_HostSetAfsPrincipal() -- Put AFS server principal (afs) key in
460  *     host's KeyFile; principal is created if it does not exist.
461  *
462  *     If first server host in cell, passwd must be initial password for
463  *     the afs principal; the afs principal is created.
464  *
465  *     If additional server host, passwd can be specified or NULL; the
466  *     afs principal must already exist by definition.  If passwd is NULL
467  *     then an attempt is made to fetch the afs key.  If the key fetch fails
468  *     because pre 3.5 database servers are in use (which will only return a
469  *     key checksum) then the function fails with a return status of
470  *     ADMCFGAFSKEYNOTAVAILABLE; in this case the function should be called
471  *     again with passwd specified.  If passwd is specified (not NULL) but the
472  *     password key fails a checksum comparison with the current afs key
473  *     then the function fails with a return status of ADMCFGAFSPASSWDINVALID.
474  *
475  * ASSUMPTIONS: Client configured and BOS server started; if first host in
476  *     cell then Authentication server must be started as well.
477  */
478 int ADMINAPI
479 cfg_HostSetAfsPrincipal(void *hostHandle,    /* host config handle */
480                         short isFirst,       /* first server in cell flag */
481                         const char *passwd,  /* afs initial password */
482                         afs_status_p st)     /* completion status */
483 {
484     int rc = 1;
485     afs_status_t tst2, tst = 0;
486     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
487
488     /* validate parameters */
489
490     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
491         tst = tst2;
492     } else if ((isFirst && passwd == NULL) ||
493                (passwd != NULL && *passwd == '\0')) {
494         tst = ADMCFGPASSWDNULL;
495     }
496
497     /* put afs key in host's KeyFile */
498
499     if (tst == 0) {
500         kas_identity_t afsIdentity;
501         kas_encryptionKey_t afsKey;
502         int afsKvno;
503
504         strcpy(afsIdentity.principal, "afs");
505         afsIdentity.instance[0] = '\0';
506
507         if (isFirst) {
508             /* create afs principal */
509             if (!kas_PrincipalCreate(cfg_host->cellHandle,
510                                      NULL,
511                                      &afsIdentity,
512                                      passwd,
513                                      &tst2) &&
514                 tst2 != KAEXIST) {
515                 /* failed to create principal (and not because existed) */
516                 tst = tst2;
517             }
518         }
519
520         if (tst == 0) {
521             /* retrive afs principal information to verify or obtain key */
522             kas_principalEntry_t afsEntry;
523
524             if (!kas_PrincipalGet(cfg_host->cellHandle,
525                                   NULL,
526                                   &afsIdentity,
527                                   &afsEntry,
528                                   &tst2)) {
529                 tst = tst2;
530             } else {
531                 if (passwd != NULL) {
532                     /* password given; form key and verify as most recent */
533                     kas_encryptionKey_t passwdKey;
534                     unsigned int passwdKeyCksum;
535
536                     if (!kas_StringToKey(cfg_host->cellName,
537                                          passwd,
538                                          &passwdKey,
539                                          &tst2) ||
540
541                         !kas_KeyCheckSum(&passwdKey, &passwdKeyCksum, &tst2)) {
542                         /* failed to form key or key checksum */
543                         tst = tst2;
544
545                     } else if (passwdKeyCksum != afsEntry.keyCheckSum) {
546                         /* passwd string does not generate most recent key;
547                          * check if passwd string embeds key directly.
548                          */
549                         if (KasKeyEmbeddedInString(passwd, &passwdKey)) {
550                             /* passwd string embeds kas key */
551                             if (!kas_KeyCheckSum(&passwdKey,
552                                                  &passwdKeyCksum, &tst2)) {
553                                 tst = tst2;
554                             } else if (passwdKeyCksum !=
555                                        afsEntry.keyCheckSum) {
556                                 /* passwd string does not embed valid key */
557                                 tst = ADMCFGAFSPASSWDINVALID;
558                             }
559                         } else {
560                             /* passwd string does NOT embed key */
561                             tst = ADMCFGAFSPASSWDINVALID;
562                         }
563                     }
564
565                     if (tst == 0) {
566                         /* passwd seems to generate/embed most recent key */
567                         afsKey = passwdKey;
568                         afsKvno = afsEntry.keyVersion;
569                     }
570
571                 } else {
572                     /* password NOT given; check if key retrieved since
573                      * pre 3.5 database servers only return key checksum
574                      */
575                     if (KasKeyIsZero(&afsEntry.key)) {
576                         tst = ADMCFGAFSKEYNOTAVAILABLE;
577                     } else {
578                         afsKey = afsEntry.key;
579                         afsKvno = afsEntry.keyVersion;
580                     }
581                 }
582             }
583         }
584
585         if (tst == 0) {
586             /* add key to host's KeyFile; RPC must be unauthenticated;
587              * bosserver is presumed to be in noauth mode.
588              */
589             void *cellHandle, *bosHandle;
590
591             if (!afsclient_NullCellOpen(&cellHandle, &tst2)) {
592                 tst = tst2;
593             } else {
594                 if (!bos_ServerOpen(cellHandle,
595                                     cfg_host->hostName, &bosHandle, &tst2)) {
596                     tst = tst2;
597                 } else {
598                     if (!bos_KeyCreate(bosHandle, afsKvno, &afsKey, &tst2) &&
599                         tst2 != BZKEYINUSE) {
600                         /* failed to add key (and not because existed) */
601                         tst = tst2;
602                     }
603
604                     if (!bos_ServerClose(bosHandle, &tst2)) {
605                         tst = tst2;
606                     }
607                 }
608
609                 if (!afsclient_CellClose(cellHandle, &tst2)) {
610                     tst = tst2;
611                 }
612             }
613         }
614     }
615
616     if (tst != 0) {
617         rc = 0;
618     }
619     if (st != NULL) {
620         *st = tst;
621     }
622     return rc;
623 }
624
625
626 /*
627  * cfg_HostSetAdminPrincipal() -- Put generic administrator principal in
628  *     host's UserList; principal is created if it does not exist.
629  *
630  *     If first server host in cell, passwd and afsUid must be the initial
631  *     password and the AFS UID for the admin principal; the admin principal
632  *     is created.
633  *
634  *     If additional server host, passwd and afsUid are ignored; the admin
635  *     principal is assumed to exist.
636  *
637  * ASSUMPTIONS: Client configured and BOS server started; if first host in
638  *     cell then Authentication and Protection servers must be started as well.
639  */
640 int ADMINAPI
641 cfg_HostSetAdminPrincipal(void *hostHandle,    /* host config handle */
642                           short isFirst,       /* first server in cell flag */
643                           const char *admin,   /* admin principal name */
644                           const char *passwd,  /* admin initial password */
645                           unsigned int afsUid, /* admin AFS UID */
646                           afs_status_p st)     /* completion status */
647 {
648     int rc = 1;
649     afs_status_t tst2, tst = 0;
650     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
651
652     /* validate parameters and prepare host handle for bos functions */
653
654     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
655         tst = tst2;
656     } else if (admin == NULL || *admin == '\0') {
657         tst = ADMCFGADMINPRINCIPALNULL;
658     } else if (strlen(admin) > (KAS_MAX_NAME_LEN - 1)) {
659         tst = ADMCFGADMINPRINCIPALTOOLONG;
660     } else if (isFirst && (passwd == NULL || *passwd == '\0')) {
661         tst = ADMCFGPASSWDNULL;
662     } else if (!cfgutil_HostHandleBosInit(cfg_host, &tst2)) {
663         tst = tst2;
664     }
665
666     /* put admin in host's UserList */
667
668     if (tst == 0) {
669         if (isFirst) {
670             /* first server host in cell; create admin principal */
671             kas_identity_t adminIdentity;
672             int adminUid = afsUid;
673             kas_admin_t adminFlag = KAS_ADMIN;
674
675             strcpy(adminIdentity.principal, admin);
676             adminIdentity.instance[0] = '\0';
677
678             if (!kas_PrincipalCreate(cfg_host->cellHandle,
679                                      NULL,
680                                      &adminIdentity,
681                                      passwd,
682                                      &tst2) &&
683                 tst2 != KAEXIST) {
684                 /* failed to create principal (and not because existed) */
685                 tst = tst2;
686
687             } else if (!kas_PrincipalFieldsSet(cfg_host->cellHandle,
688                                                NULL,
689                                                &adminIdentity,
690                                                &adminFlag,
691                                                NULL, NULL, NULL, NULL,
692                                                NULL, NULL, NULL, NULL, NULL,
693                                                &tst2)) {
694                 /* failed to set admin attributes */
695                 tst = tst2;
696
697             } else if (!pts_UserCreate(cfg_host->cellHandle,
698                                        admin,
699                                        &adminUid,
700                                        &tst2) &&
701                        tst2 != PREXIST) {
702                 /* failed to create user (and not because existed) */
703                 tst = tst2;
704
705             } else if (!pts_GroupMemberAdd(cfg_host->cellHandle,
706                                            admin,
707                                            "system:administrators",
708                                            &tst2) &&
709                        tst2 != PRIDEXIST) {
710                 /* failed to add to group (not because already there) */
711                 tst = tst2;
712             }
713         }
714
715         if (tst == 0) {
716             /* add admin to host's UserList */
717             if (!bos_AdminCreate(cfg_host->bosHandle, admin, &tst2) &&
718                 tst2 != EEXIST) {
719                 /* failed to add admin (and not because existed) */
720                 /* DANGER: platform-specific errno values being returned */
721                 tst = tst2;
722             }
723         }
724     }
725
726     if (tst != 0) {
727         rc = 0;
728     }
729     if (st != NULL) {
730         *st = tst;
731     }
732     return rc;
733 }
734
735
736 /*
737  * cfg_HostInvalidate() -- Invalidate static server configuration on host.
738  *
739  *     Server configuration invalidated only if BOS server is not running.
740  */
741 int ADMINAPI
742 cfg_HostInvalidate(void *hostHandle,   /* host config handle */
743                    afs_status_p st)    /* completion status */
744 {
745     int rc = 1;
746     afs_status_t tst2, tst = 0;
747     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
748
749     /* validate parameters */
750
751     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
752         tst = tst2;
753     }
754
755     /* remote configuration not yet supported in this function */
756
757     if (tst == 0) {
758         if (!cfg_host->is_local) {
759             tst = ADMCFGNOTSUPPORTED;
760         }
761     }
762
763     /* make sure bosserver is not running on host */
764
765 #ifdef AFS_NT40_ENV
766     /* Windows - bosserver is controlled via the BOS control service */
767     if (tst == 0) {
768         DWORD svcState;
769
770         if (!cfgutil_WindowsServiceQuery(AFSREG_SVR_SVC_NAME,
771                                          &svcState,
772                                          &tst2)) {
773             tst = tst2;
774         } else if (svcState != SERVICE_STOPPED) {
775             tst = ADMCFGBOSSERVERACTIVE;
776         }
777     }
778 #else
779     if (tst == 0) {
780         /* function not yet implemented for Unix */
781         tst = ADMCFGNOTSUPPORTED;
782     }
783 #endif /* AFS_NT40_ENV */
784
785
786     /* remove server state files */
787
788     if (tst == 0) {
789         int i;
790         const char *cfgdir[3];
791
792         cfgdir[0] = AFSDIR_SERVER_ETC_DIRPATH;
793         cfgdir[1] = AFSDIR_SERVER_DB_DIRPATH;
794         cfgdir[2] = AFSDIR_SERVER_LOCAL_DIRPATH;
795
796         for (i = 0; i < 3 && tst == 0; i++) {
797             if (!cfgutil_CleanDirectory(cfgdir[i], &tst2)) {
798                 tst = tst2;
799             }
800         }
801     }
802
803     /* remove all vice partition table entries */
804
805 #ifdef AFS_NT40_ENV
806     if (tst == 0) {
807         struct vpt_iter vpiter;
808         struct vptab vpentry;
809
810         /* note: ignore errors except from removal attempts */
811
812         if (!vpt_Start(&vpiter)) {
813             while (!vpt_NextEntry(&vpiter, &vpentry)) {
814                 if (vpt_RemoveEntry(vpentry.vp_name)) {
815                     /* ENOENT implies entry does not exist; consider removed */
816                     if (errno != ENOENT) {
817                         if (errno == EACCES) {
818                             tst = ADMNOPRIV;
819                         } else {
820                             tst = ADMCFGVPTABLEWRITEFAILED;
821                         }
822                     }
823                 }
824             }
825             (void)vpt_Finish(&vpiter);
826         }
827     }
828 #else
829     /* function not yet implemented for unix */
830     if (tst == 0) {
831         tst = ADMCFGNOTSUPPORTED;
832     }
833 #endif /* AFS_NT40_ENV */
834
835     if (tst != 0) {
836         rc = 0;
837     }
838     if (st != NULL) {
839         *st = tst;
840     }
841     return rc;
842 }
843
844
845
846 /*
847  * cfg_HostPartitionTableEnumerate() -- Enumerate AFS partition table entries.
848  *
849  *     If the partition table is empty (or does not exist) then *tablePP
850  *     is set to NULL and *nEntriesP is set to zero (0).
851  *
852  *     Partitions in table are not necessarily those being exported; a table
853  *     entry may have been added or removed since the fileserver last started.
854  */
855 int ADMINAPI
856 cfg_HostPartitionTableEnumerate(void *hostHandle,   /* host config handle */
857                                 cfg_partitionEntry_t **tablePP, /* table */
858                                 int *nEntriesP,      /* table entry count */
859                                 afs_status_p st)     /* completion status */
860 {
861     int rc = 1;
862     afs_status_t tst2, tst = 0;
863     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
864
865     /* validate parameters */
866
867     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
868         tst = tst2;
869     } else if (tablePP == NULL) {
870         tst = ADMCFGVPTABLEPNULL;
871     } else if (nEntriesP == NULL) {
872         tst = ADMCFGVPTABLECOUNTPNULL;
873     }
874
875     /* remote configuration not yet supported in this function */
876
877     if (tst == 0) {
878         if (!cfg_host->is_local) {
879             tst = ADMCFGNOTSUPPORTED;
880         }
881     }
882
883     /* enumerate the vice partition table */
884
885 #ifdef AFS_NT40_ENV
886     if (tst == 0) {
887         struct vpt_iter vpiter;
888         struct vptab vpentry;
889         int vpentryCountMax = 0;
890
891         /* count table entries */
892
893         if (vpt_Start(&vpiter)) {
894             /* ENOENT implies table does not exist (which is OK) */
895             if (errno != ENOENT) {
896                 if (errno == EACCES) {
897                     tst = ADMNOPRIV;
898                 } else {
899                     tst = ADMCFGVPTABLEREADFAILED;
900                 }
901             }
902         } else {
903             while (!vpt_NextEntry(&vpiter, &vpentry)) {
904                 vpentryCountMax++;
905             }
906             if (errno != ENOENT) {
907                 tst = ADMCFGVPTABLEREADFAILED;
908             }
909             (void)vpt_Finish(&vpiter);
910         }
911
912         /* alloc storage for table entries; handle any entry count change */
913
914         if (tst == 0) {
915             if (vpentryCountMax == 0) {
916                 *nEntriesP = 0;
917                 *tablePP = NULL;
918             } else {
919                 /* return a two-part table; first points into second */
920                 void *metaTablep;
921                 size_t metaTableSize;
922
923                 metaTableSize =
924                     vpentryCountMax * (sizeof(cfg_partitionEntry_t) +
925                                        sizeof(struct vptab));
926
927                 if ((metaTablep = (void *)malloc(metaTableSize)) == NULL) {
928                     tst = ADMNOMEM;
929                 } else {
930                     int i;
931                     cfg_partitionEntry_t *cpePart;
932                     struct vptab *vptPart;
933                     int vpentryCount = 0;
934
935                     cpePart = (cfg_partitionEntry_t *)metaTablep;
936                     vptPart = (struct vptab *)(&cpePart[vpentryCountMax]);
937
938                     for (i = 0; i < vpentryCountMax; i++) {
939                         cpePart[i].partitionName = vptPart[i].vp_name;
940                         cpePart[i].deviceName = vptPart[i].vp_dev;
941                     }
942
943                     if (vpt_Start(&vpiter)) {
944                         /* ENOENT implies table does not exist (which is OK) */
945                         if (errno != ENOENT) {
946                             if (errno == EACCES) {
947                                 tst = ADMNOPRIV;
948                             } else {
949                                 tst = ADMCFGVPTABLEREADFAILED;
950                             }
951                         }
952                     } else {
953                         for (i = 0; i < vpentryCountMax; i++) {
954                             if (vpt_NextEntry(&vpiter, &vptPart[i])) {
955                                 break;
956                             }
957                         }
958
959                         if (i < vpentryCountMax && errno != ENOENT) {
960                             tst = ADMCFGVPTABLEREADFAILED;
961                         } else {
962                             vpentryCount = i;
963                         }
964                         (void)vpt_Finish(&vpiter);
965                     }
966
967                     if (tst == 0) {
968                         *nEntriesP = vpentryCount;
969
970                         if (vpentryCount != 0) {
971                             *tablePP = (cfg_partitionEntry_t *)metaTablep;
972                         } else {
973                             *tablePP = NULL;
974                             free(metaTablep);
975                         }
976                     } else {
977                         free(metaTablep);
978                     }
979                 }
980             }
981         }
982     }
983
984 #else
985     /* function not yet implemented for Unix */
986     if (tst == 0) {
987         tst = ADMCFGNOTSUPPORTED;
988     }
989 #endif  /* AFS_NT40_ENV */
990
991     if (tst != 0) {
992         rc = 0;
993     }
994     if (st != NULL) {
995         *st = tst;
996     }
997     return rc;
998 }
999
1000
1001 /*
1002  * cfg_HostPartitionTableAddEntry() -- Add or update AFS partition table entry.
1003  */
1004 int ADMINAPI
1005 cfg_HostPartitionTableAddEntry(void *hostHandle,      /* host config handle */
1006                                const char *partName,  /* partition name */
1007                                const char *devName,   /* device name */
1008                                afs_status_p st)       /* completion status */
1009 {
1010     int rc = 1;
1011     afs_status_t tst2, tst = 0;
1012     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1013
1014 #ifdef AFS_NT40_ENV
1015     /* validate parameters */
1016
1017     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1018         tst = tst2;
1019     } else if (partName == NULL) {
1020         tst = ADMCFGPARTITIONNAMENULL;
1021     } else if (!vpt_PartitionNameValid(partName)) {
1022         tst = ADMCFGPARTITIONNAMEBAD;
1023     } else if (devName == NULL) {
1024         tst = ADMCFGDEVICENAMENULL;
1025     } else if (!vpt_DeviceNameValid(devName)) {
1026         tst = ADMCFGDEVICENAMEBAD;
1027     }
1028
1029     /* remote configuration not yet supported in this function */
1030
1031     if (tst == 0) {
1032         if (!cfg_host->is_local) {
1033             tst = ADMCFGNOTSUPPORTED;
1034         }
1035     }
1036
1037     /* add entry to table */
1038
1039     if (tst == 0) {
1040         struct vptab vpentry;
1041
1042         strcpy(vpentry.vp_name, partName);
1043         strcpy(vpentry.vp_dev, devName);
1044
1045         if (vpt_AddEntry(&vpentry)) {
1046             if (errno == EACCES) {
1047                 tst = ADMNOPRIV;
1048             } else if (errno == EINVAL) {
1049                 /* shouldn't happen since checked partition/dev names */
1050                 tst = ADMCFGVPTABLEENTRYBAD;
1051             } else {
1052                 tst = ADMCFGVPTABLEWRITEFAILED;
1053             }
1054         }
1055     }
1056
1057 #else
1058     /* function not yet implemented for unix */
1059     if (tst == 0) {
1060         tst = ADMCFGNOTSUPPORTED;
1061     }
1062 #endif /* AFS_NT40_ENV */
1063
1064     if (tst != 0) {
1065         rc = 0;
1066     }
1067     if (st != NULL) {
1068         *st = tst;
1069     }
1070     return rc;
1071 }
1072
1073
1074 /*
1075  * cfg_HostPartitionTableRemoveEntry() -- Remove AFS partition table entry.
1076  */
1077 int ADMINAPI
1078 cfg_HostPartitionTableRemoveEntry(void *hostHandle,  /* host config handle */
1079                                   const char *partName, /* partition name */
1080                                   afs_status_p st)   /* completion status */
1081 {
1082     int rc = 1;
1083     afs_status_t tst2, tst = 0;
1084     cfg_host_p cfg_host = (cfg_host_p)hostHandle;
1085
1086 #ifdef AFS_NT40_ENV
1087     /* validate parameters */
1088
1089     if (!cfgutil_HostHandleValidate(cfg_host, &tst2)) {
1090         tst = tst2;
1091     } else if (partName == NULL) {
1092         tst = ADMCFGPARTITIONNAMENULL;
1093     } else if (!vpt_PartitionNameValid(partName)) {
1094         tst = ADMCFGPARTITIONNAMEBAD;
1095     }
1096
1097     /* remote configuration not yet supported in this function */
1098
1099     if (tst == 0) {
1100         if (!cfg_host->is_local) {
1101             tst = ADMCFGNOTSUPPORTED;
1102         }
1103     }
1104
1105     /* remove entry from table */
1106
1107     if (tst == 0) {
1108         if (vpt_RemoveEntry(partName)) {
1109             /* ENOENT implies entry does not exist; consider to be removed */
1110             if (errno != ENOENT) {
1111                 if (errno == EACCES) {
1112                     tst = ADMNOPRIV;
1113                 } else if (errno == EINVAL) {
1114                     /* shouldn't happen since checked partition/dev names */
1115                     tst = ADMCFGPARTITIONNAMEBAD;
1116                 } else {
1117                     tst = ADMCFGVPTABLEWRITEFAILED;
1118                 }
1119             }
1120         }
1121     }
1122
1123 #else
1124     /* function not yet implemented for unix */
1125     if (tst == 0) {
1126         tst = ADMCFGNOTSUPPORTED;
1127     }
1128 #endif /* AFS_NT40_ENV */
1129
1130     if (tst != 0) {
1131         rc = 0;
1132     }
1133     if (st != NULL) {
1134         *st = tst;
1135     }
1136     return rc;
1137 }
1138
1139
1140 /*
1141  * cfg_HostPartitionNameValid() -- check partition name syntax.
1142  */
1143 int ADMINAPI
1144 cfg_HostPartitionNameValid(const char *partName, /* partition name */
1145                            short *isValidP,      /* syntax is valid */
1146                            afs_status_p st)      /* completion status */
1147 {
1148     int rc = 1;
1149     afs_status_t tst = 0;
1150
1151     /* validate parameters */
1152
1153     if (partName == NULL) {
1154         tst = ADMCFGPARTITIONNAMENULL;
1155     } else if (isValidP == NULL) {
1156         tst = ADMCFGVALIDFLAGPNULL;
1157     }
1158
1159     /* check name syntax */
1160
1161 #ifdef AFS_NT40_ENV
1162     if (tst == 0) {
1163         *isValidP = vpt_PartitionNameValid(partName);
1164     }
1165 #else
1166     /* function not yet implemented for Unix */
1167     if (tst == 0) {
1168         tst = ADMCFGNOTSUPPORTED;
1169     }
1170 #endif
1171
1172     if (tst != 0) {
1173         rc = 0;
1174     }
1175     if (st != NULL) {
1176         *st = tst;
1177     }
1178     return rc;
1179 }
1180
1181
1182
1183 /*
1184  * cfg_HostDeviceNameValid() -- check device name syntax.
1185  */
1186 int ADMINAPI
1187 cfg_HostDeviceNameValid(const char *devName,  /* device name */
1188                         short *isValidP,      /* syntax is valid */
1189                         afs_status_p st)      /* completion status */
1190 {
1191     int rc = 1;
1192     afs_status_t tst = 0;
1193
1194     /* validate parameters */
1195
1196     if (devName == NULL) {
1197         tst = ADMCFGDEVICENAMENULL;
1198     } else if (isValidP == NULL) {
1199         tst = ADMCFGVALIDFLAGPNULL;
1200     }
1201
1202     /* check name syntax */
1203
1204 #ifdef AFS_NT40_ENV
1205     if (tst == 0) {
1206         *isValidP = vpt_DeviceNameValid(devName);
1207     }
1208 #else
1209     /* function not yet implemented for Unix */
1210     if (tst == 0) {
1211         tst = ADMCFGNOTSUPPORTED;
1212     }
1213 #endif
1214
1215     if (tst != 0) {
1216         rc = 0;
1217     }
1218     if (st != NULL) {
1219         *st = tst;
1220     }
1221     return rc;
1222 }
1223
1224
1225
1226 /* ---------------- Exported Utility functions ------------------ */
1227
1228
1229 /*
1230  * cfg_StringDeallocate() -- Deallocate (multi)string returned by library.
1231  */
1232 int ADMINAPI
1233 cfg_StringDeallocate(char *stringDataP,   /* (multi)string to deallocate */
1234                      afs_status_p st)     /* completion status */
1235 {
1236     free((void *)stringDataP);
1237     if (st != NULL) {
1238         *st = 0;
1239     }
1240     return 1;
1241 }
1242
1243
1244 /*
1245  * cfg_PartitionListDeallocate() -- Deallocate partition table enumeration
1246  *     returned by library.
1247  */
1248 int ADMINAPI
1249 cfg_PartitionListDeallocate(cfg_partitionEntry_t *partitionListDataP,
1250                             afs_status_p st)
1251 {
1252     free((void *)partitionListDataP);
1253     if (st != NULL) {
1254         *st = 0;
1255     }
1256     return 1;
1257 }
1258
1259
1260
1261
1262 /* ---------------- Local functions ------------------ */
1263
1264
1265 /*
1266  * KasKeyIsZero() -- determine if kas key is zero
1267  *
1268  * RETURN CODES: 1 if zero, 0 otherwise
1269  */
1270 static int
1271 KasKeyIsZero(kas_encryptionKey_t *kasKey)
1272 {
1273     char *keyp = (char *)kasKey;
1274     int i;
1275
1276     for (i = 0; i < sizeof(*kasKey); i++) {
1277         if (*keyp++ != 0) {
1278             return 0;
1279         }
1280     }
1281     return 1;
1282 }
1283
1284
1285 /*
1286  * KasKeyEmbeddedInString() -- determine if kas key is embedded in string
1287  *     and return key if extant.
1288  *
1289  * RETURN CODES: 1 if embedded key found, 0 otherwise
1290  */
1291 static int
1292 KasKeyEmbeddedInString(const char *keyString, kas_encryptionKey_t *kasKey)
1293 {
1294     char *octalDigits = "01234567";
1295
1296     /* keyString format is exactly 24 octal digits if embeds kas key */
1297     if (strlen(keyString) == 24 &&
1298         strspn(keyString, octalDigits) == 24) {
1299         /* kas key is embedded in keyString; extract it */
1300         int i;
1301
1302         for (i = 0; i < 24; i += 3) {
1303             char keyPiece[4];
1304             unsigned char keyPieceVal;
1305
1306             keyPiece[0] = keyString[i];
1307             keyPiece[1] = keyString[i + 1];
1308             keyPiece[2] = keyString[i + 2];
1309             keyPiece[3] = '\0';
1310
1311             keyPieceVal = (unsigned char)strtoul(keyPiece, NULL, 8);
1312
1313             *((unsigned char *)kasKey + (i / 3)) = keyPieceVal;
1314         }
1315         return 1;
1316     } else {
1317         /* key NOT embedded in keyString */
1318         return 0;
1319     }
1320 }