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