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