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