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