warn when starting without keys
[openafs.git] / src / update / server.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <afs/procmgmt.h>
15 #include <roken.h>
16 #include <afs/opr.h>
17
18 #ifdef AFS_NT40_ENV
19 #include <WINNT/afsevent.h>
20 #endif
21
22 #ifdef  AFS_AIX_ENV
23 #include <sys/statfs.h>
24 #endif
25
26 #include <rx/xdr.h>
27 #include <rx/rx.h>
28 #include <rx/rxkad.h>
29 #include <afs/cellconfig.h>
30 #include <afs/afsutil.h>
31 #include <afs/fileutil.h>
32 #include <afs/com_err.h>
33
34 #include "update.h"
35 #include "global.h"
36
37 static int AddObject(char **expPath, char *dir);
38 static int PathInDirectory(char *dir, char *path);
39 int update_SendFile(int, struct rx_call *, struct stat *);
40 int update_SendDirInfo(char *, struct rx_call *, struct stat *,
41                        char *origDir);
42
43 struct afsconf_dir *cdir;
44 int nDirs;
45 char *dirName[MAXENTRIES];
46 int dirLevel[MAXENTRIES];
47 char *whoami;
48
49 static int Quit(char *);
50
51 int rxBind = 0;
52
53 #define ADDRSPERSITE 16         /* Same global is in rx/rx_user.c */
54 afs_uint32 SHostAddrs[ADDRSPERSITE];
55
56 /* check whether caller is authorized to manage RX statistics */
57 int
58 update_rxstat_userok(struct rx_call *call)
59 {
60     return afsconf_SuperUser(cdir, call, NULL);
61 }
62
63 /*
64  * PathInDirectory() -- determine if path is in directory (or is directory)
65  * Returns 1 if yes, 0 if no, -1 on error.
66  */
67 static int
68 PathInDirectory(char *dir, char *path)
69 {
70     int inDir = 0, code;
71     size_t dirLen;
72     char *dirNorm, *pathNorm;
73
74 #ifdef AFS_NT40_ENV
75     /* case-insensitive comparison of normalized, same-flavor (short) paths */
76     DWORD status;
77
78     dirNorm = malloc(AFSDIR_PATH_MAX);
79     if (dirNorm == NULL)
80         return -1;
81     status = GetShortPathName(dir, dirNorm, AFSDIR_PATH_MAX);
82     if (status == 0 || status > AFSDIR_PATH_MAX) {
83         /* can't convert path to short version; just use long version */
84         free(dirNorm);
85         dirNorm = strdup(dir);
86         if (dirNorm == NULL)
87             return -1;
88     }
89     FilepathNormalize(dirNorm);
90
91     pathNorm = malloc(AFSDIR_PATH_MAX);
92     if (pathNorm == NULL) {
93         code = -1;
94         goto out;
95     }
96     status = GetShortPathName(path, pathNorm, AFSDIR_PATH_MAX);
97     if (status == 0 || status > AFSDIR_PATH_MAX) {
98         /* can't convert path to short version; just use long version */
99         free(pathNorm);
100         pathNorm = strdup(path);
101         if (pathNorm == NULL) {
102             code = -1;
103             goto out;
104         }
105     }
106     FilepathNormalize(pathNorm);
107
108     dirLen = strlen(dirNorm);
109
110     if (_strnicmp(dirNorm, pathNorm, dirLen) == 0) {
111         /* substrings match; path must match dir or be subdirectory */
112         if (pathNorm[dirLen] == '\0' || pathNorm[dirLen] == '/') {
113             inDir = 1;
114         }
115     }
116 #else
117     /* case-sensitive comparison of normalized paths */
118     dirNorm = strdup(dir);
119     if (dirNorm == NULL)
120         return -1;
121     FilepathNormalize(dirNorm);
122
123     pathNorm = strdup(path);
124     if (pathNorm == NULL) {
125         code = -1;
126         goto out;
127     }
128     FilepathNormalize(pathNorm);
129
130     dirLen = strlen(dirNorm);
131
132     if (strncmp(dirNorm, pathNorm, dirLen) == 0) {
133         /* substrings match; path must match dir or be subdirectory */
134         if (pathNorm[dirLen] == '\0' || pathNorm[dirLen] == '/') {
135             inDir = 1;
136         }
137     }
138 #endif /* AFS_NT40_ENV */
139     code = 0;
140 out:
141     free(dirNorm);
142     free(pathNorm);
143     return (code != 0) ? code : inDir;
144 }
145
146 int
147 AuthOkay(struct rx_call *call, char *name)
148 {
149     int i, r;
150     rxkad_level level;
151     afs_int32 code;
152     int matches;
153
154     /* Must be in 'UserList' to use */
155     if (!afsconf_SuperUser(cdir, call, NULL))
156         return 0;
157
158     if (rx_SecurityClassOf(rx_ConnectionOf(call)) == RX_SECIDX_KAD) {
159         code = rxkad_GetServerInfo(rx_ConnectionOf(call), &level, 0, 0, 0, 0, 0);
160         if (code)
161             return 0;
162     } else
163         level = 0;
164
165     matches = 0;
166     for (i = 0; i < nDirs; i++) {
167         r = PathInDirectory(dirName[i], name);
168         if (r < 0)
169             return 0;
170         if (r) {
171             if (dirLevel[i] > level)
172                 return 0;
173             matches++;
174             /* keep searching in case there's a more restrictive subtree
175              * specified later. */
176         }
177     }
178     if (nDirs && !matches)
179         return 0;               /* if dirs spec., name must match */
180     return 1;                   /* okay or no dirs */
181 }
182
183 int
184 osi_audit(void)
185 {
186 /* this sucks but it works for now.
187 */
188     return 0;
189 }
190
191 #ifndef AFS_NT40_ENV
192 #include "AFS_component_version_number.c"
193 #endif
194
195 int
196 main(int argc, char *argv[])
197 {
198     struct rx_securityClass **securityClasses;
199     afs_int32 numClasses;
200     struct rx_service *service;
201     char hoststr[16];
202     afs_uint32 host = htonl(INADDR_ANY);
203
204     int a = 0;
205     rxkad_level level;
206     rxkad_level newLevel;
207
208 #ifdef  AFS_AIX32_ENV
209     /*
210      * The following signal action for AIX is necessary so that in case of a
211      * crash (i.e. core is generated) we can include the user's data section
212      * in the core dump. Unfortunately, by default, only a partial core is
213      * generated which, in many cases, isn't too useful.
214      */
215     struct sigaction nsa;
216
217     sigemptyset(&nsa.sa_mask);
218     nsa.sa_handler = SIG_DFL;
219     nsa.sa_flags = SA_FULLDUMP;
220     sigaction(SIGABRT, &nsa, NULL);
221     sigaction(SIGSEGV, &nsa, NULL);
222 #endif
223
224     whoami = argv[0];
225
226 #ifdef AFS_NT40_ENV
227     /* dummy signal call to force afsprocmgmt.dll to load on NT */
228     signal(SIGUSR1, SIG_DFL);
229 #endif
230
231     /* Initialize dirpaths */
232     if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
233 #ifdef AFS_NT40_ENV
234         ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
235 #endif
236         fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
237                 argv[0]);
238         exit(2);
239     }
240     nDirs = 0;
241     level = rxkad_clear;
242
243     if (argc == 1)              /* no arguments */
244         goto usage;
245
246     /* support help flag */
247     if (strcmp("-help", argv[1]) == 0)
248         goto usage;
249     if (strcmp("help", argv[1]) == 0)
250         goto usage;
251
252     for (a = 1; a < argc; a++) {
253         if (argv[a][0] == '-') {        /* parse options */
254             if (strcmp(argv[a], "-rxbind") == 0) {
255                 rxBind = 1;
256                 continue;
257             } else {
258                 char arg[256];
259                 lcstring(arg, argv[a], sizeof(arg));
260                 newLevel = rxkad_StringToLevel(&argv[a][1]);
261                 if (newLevel != -1) {
262                     level = newLevel;   /* set new level */
263                     continue;
264                 }
265             }
266           usage:
267             Quit("Usage: upserver [<directory>+] [-crypt <directory>+] [-clear <directory>+] [-auth <directory>+] [-rxbind] [-help]\n");
268         } else {
269             if (nDirs >= sizeof(dirName) / sizeof(dirName[0]))
270                 Quit("Too many dirs");
271             if (AddObject(&dirName[nDirs], argv[a])) {
272                 printf("%s: Unable to export dir %s. Skipping\n", whoami,
273                        argv[a]);
274                 continue;
275             }
276             dirLevel[nDirs] = level;    /* remember current level */
277             nDirs++;
278         }
279     }
280
281     if (nDirs == 0) {           /* Didn't find any directories to export */
282         printf("%s: No directories to export. Quitting\n", whoami);
283         exit(1);
284     }
285
286     cdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
287     if (cdir == 0) {
288         fprintf(stderr, "Can't get server configuration info (%s)\n",
289                 AFSDIR_SERVER_ETC_DIRPATH);
290         exit(1);
291     }
292
293     if (afsconf_CountKeys(cdir) == 0) {
294         fprintf(stderr, "WARNING: No encryption keys found! "
295                         "All authenticated accesses will fail."
296                         "Run akeyconvert or asetkey to import encryption keys.\n");
297     }
298
299     if (rxBind) {
300         afs_int32 ccode;
301         if (AFSDIR_SERVER_NETRESTRICT_FILEPATH ||
302             AFSDIR_SERVER_NETINFO_FILEPATH) {
303             char reason[1024];
304             ccode = afsconf_ParseNetFiles(SHostAddrs, NULL, NULL,
305                                           ADDRSPERSITE, reason,
306                                           AFSDIR_SERVER_NETINFO_FILEPATH,
307                                           AFSDIR_SERVER_NETRESTRICT_FILEPATH);
308         } else
309         {
310             ccode = rx_getAllAddr(SHostAddrs, ADDRSPERSITE);
311         }
312         if (ccode == 1)
313             host = SHostAddrs[0];
314     }
315
316     /* Initialize Rx, telling it port number this server will use for its
317      * single service */
318     fprintf(stderr, "upserver binding rx to %s:%d\n",
319             afs_inet_ntoa_r(host, hoststr), AFSCONF_UPDATEPORT);
320     if (rx_InitHost(host, htons(AFSCONF_UPDATEPORT)) < 0)
321         Quit("rx_init");
322
323     afsconf_BuildServerSecurityObjects(cdir, &securityClasses, &numClasses);
324
325     if (securityClasses[2] == NULL)
326         Quit("rxkad_NewServerSecurityObject");
327
328     /* Instantiate a single UPDATE service.  The rxgen-generated procedure
329      * which is called to decode requests is passed in here
330      * (UPDATE_ExecuteRequest). */
331     service =
332         rx_NewServiceHost(host, 0, UPDATE_SERVICEID, "UPDATE", securityClasses,
333                           numClasses, UPDATE_ExecuteRequest);
334     if (service == (struct rx_service *)0)
335         Quit("rx_NewService");
336     rx_SetMaxProcs(service, 2);
337
338     /* allow super users to manage RX statistics */
339     rx_SetRxStatUserOk(update_rxstat_userok);
340
341     rx_StartServer(1);          /* Donate this process to the server process pool */
342     Quit("StartServer returned?");
343     return 0;
344 }
345
346 /* fetch the file name and send it to the remote requester specified by call */
347
348 int
349 UPDATE_FetchFile(struct rx_call *call, char *name)
350 {
351     int fd = -1;
352     int error = 0;
353     struct stat status;
354     char *reqObject;
355
356     /* construct a local path from a canonical (wire-format) path */
357     if ((error = ConstructLocalPath(name, "/", &reqObject))) {
358         afs_com_err(whoami, error, "Unable to construct local path");
359         return UPDATE_ERROR;
360     }
361
362     if (!AuthOkay(call, reqObject)) {
363         error = UPDATE_ERROR;
364     } else {
365         fd = open(reqObject, O_RDONLY, 0);
366         if (fd < 0 || fstat(fd, &status) < 0) {
367             printf("Failed to open %s\n", reqObject);
368             error = UPDATE_ERROR;
369         }
370         if (!error)
371             error = update_SendFile(fd, call, &status);
372         if (fd >= 0)
373             close(fd);
374     }
375     free(reqObject);
376     return error;
377 }
378
379 /* fetch dir info about directory name and send it to remote host associated
380   with call. */
381 int
382 UPDATE_FetchInfo(struct rx_call *call, char *name)
383 {
384     int error = 0;
385     struct stat status;
386     char *reqObject;
387
388     /* construct a local path from a canonical (wire-format) path */
389     if ((error = ConstructLocalPath(name, "/", &reqObject))) {
390         afs_com_err(whoami, error, "Unable to construct local path");
391         return UPDATE_ERROR;
392     }
393
394     if (!AuthOkay(call, reqObject)) {
395         error = UPDATE_ERROR;
396     } else {
397         /* we only need to stat the obj, not open it. */
398         if (stat(reqObject, &status) < 0) {
399             printf("Failed to open %s\n", reqObject);
400             error = UPDATE_ERROR;
401         }
402         if ((status.st_mode & S_IFMT) != S_IFDIR) {
403             printf(" file %s is not a directory \n", reqObject);
404             error = -1;
405         }
406
407         if (!error)
408             error = update_SendDirInfo(reqObject, call, &status, name);
409     }
410     free(reqObject);
411     return error;
412 }
413
414 static int
415 Quit(char *msg)
416 {
417     fprintf(stderr, "%s", msg);
418     exit(1);
419 }
420
421 int
422 update_SendFile(int fd, struct rx_call *call, struct stat *status)
423 {
424     char *buffer = (char *)0;
425     int blockSize;
426     afs_int32 length, tlen;
427 #ifdef  AFS_AIX_ENV
428     struct statfs tstatfs;
429 #endif
430
431     afs_int32 error = 0;
432 #ifdef  AFS_AIX_ENV
433     /* Unfortunately in AIX valuable fields such as st_blksize are gone from the stat structure!! */
434     fstatfs(fd, &tstatfs);
435     blockSize = tstatfs.f_bsize;
436 #elif AFS_NT40_ENV
437     blockSize = 4096;
438 #else
439     blockSize = status->st_blksize;
440 #endif
441     length = status->st_size;
442     buffer = malloc(blockSize);
443     if (!buffer) {
444         printf("malloc failed\n");
445         return UPDATE_ERROR;
446     }
447     tlen = htonl(length);
448     rx_Write(call, (char *)&tlen, sizeof(afs_int32));   /* send length on fetch */
449     while (!error && length) {
450         int nbytes = (length > blockSize ? blockSize : length);
451         nbytes = read(fd, buffer, nbytes);
452         if (nbytes <= 0) {
453             fprintf(stderr, "File system read failed\n");
454             break;
455         }
456         if (rx_Write(call, buffer, nbytes) != nbytes)
457             break;
458         length -= nbytes;
459     }
460     if (buffer)
461         free(buffer);
462     if (length)
463         error = UPDATE_ERROR;
464     return error;
465 }
466
467 /* Enumerate dir (name) and write dir entry info into temp file.
468  */
469 int
470 update_SendDirInfo(char *name,          /* Name of dir to enumerate */
471      struct rx_call *call,      /* rx call */
472      struct stat *status,       /* stat struct for dir */
473      char *origDir)             /* orig name of dir before being localized */
474 {
475     DIR *dirp;
476     struct dirent *dp;
477     FILE *stream;
478     struct stat tstatus;
479     char filename[MAXFNSIZE], dirInfoFile[MAXFNSIZE];
480     int fd, tfd, errcode, error, err;
481
482     error = 0;
483     dirp = opendir(name);
484     sprintf(dirInfoFile, "%s/upserver.tmp", gettmpdir());
485     stream = fopen(dirInfoFile, "w");
486     if (!stream) {
487         error = EIO;
488     } else {
489         while ((dp = readdir(dirp))) {
490             strcpy(filename, name);
491             strcat(filename, "/");
492             strcat(filename, dp->d_name);
493
494             tfd = open(filename, O_RDONLY, 0);
495             if (tfd < 0 || fstat(tfd, &tstatus) < 0) {
496                 printf("Failed to open %s\n", name);
497                 error = UPDATE_ERROR;
498                 goto fail;
499             }
500             if ((tstatus.st_mode & S_IFMT) != S_IFDIR) {        /* not a directory */
501                 char dirEntry[MAXFNSIZE];
502
503                 strcpy(dirEntry, origDir);
504                 strcat(dirEntry, "/");
505                 strcat(dirEntry, dp->d_name);
506                 err =
507                     fprintf(stream, "\"%s\" %u %u %u %u %u %u\n", dirEntry,
508                             (unsigned int)tstatus.st_mtime,
509                             (unsigned int)tstatus.st_size, tstatus.st_mode,
510                             tstatus.st_uid, tstatus.st_gid,
511                             (unsigned int)tstatus.st_atime);
512                 if (err < 0)
513                     error = EIO;
514             }
515             err = close(tfd);
516             if (err) {
517                 printf("could not close file %s \n", filename);
518                 error = UPDATE_ERROR;
519                 goto fail;
520             }
521         }
522     }
523   fail:
524     if (dirp)
525         closedir(dirp);
526     if (stream) {
527         if (ferror(stream))
528             if (!error)
529                 error = UPDATE_ERROR;
530         fclose(stream);
531     }
532     if (error == 0) {
533         fd = open(dirInfoFile, O_RDONLY, 0);
534         if (fd >= 0) {
535             fstat(fd, &tstatus);
536             errcode = update_SendFile(fd, call, &tstatus);
537             if (errcode)
538                 if (!error)
539                     error = UPDATE_ERROR;
540             close(fd);
541         }
542     }
543     unlink(dirInfoFile);
544     return error;
545 }
546
547
548 /* AddObject() - Adds the object to the list of exported objects after
549  *     converting to a local path.
550  *
551  * expPath : points to allocated storage in which the exportable path is
552  *           passed back.
553  * dir     : dir name passed in for export
554  *
555  */
556 static int
557 AddObject(char **expPath, char *dir)
558 {
559     int error;
560     struct stat statbuf;
561
562     /* construct a local path from a canonical (wire-format) path */
563     if ((error = ConstructLocalPath(dir, "/", expPath))) {
564         afs_com_err(whoami, error, "Unable to construct local path");
565         return error;
566     }
567
568     /* stat the object */
569     error = stat(*expPath, &statbuf);
570     if (error) {
571         afs_com_err(whoami, error, ";Can't stat object.");
572         return error;
573     }
574     /* now check if the object has an exportable (file/dir)  type */
575     if (!(statbuf.st_mode & S_IFDIR)) {
576         fprintf(stderr, "%s: Unacceptable object type for %s\n", whoami,
577                 *expPath);
578         return -1;
579     }
580
581     return 0;
582 }