Imported Upstream version 2.4.4p3
[debian/amanda] / client-src / amandad.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27 /*
28  * $Id: amandad.c,v 1.32.2.4.4.1.2.6 2003/12/16 22:36:45 martinea Exp $
29  *
30  * handle client-host side of Amanda network communications, including
31  * security checks, execution of the proper service, and acking the
32  * master side
33  */
34
35 #include "amanda.h"
36 #include "clock.h"
37 #include "dgram.h"
38 #include "amfeatures.h"
39 #include "version.h"
40 #include "protocol.h"
41 #include "util.h"
42
43 #define RECV_TIMEOUT 30
44 #define ACK_TIMEOUT  10         /* XXX should be configurable */
45 #define MAX_RETRIES   5
46
47 /* 
48  * Here are the services that we understand.
49  */
50 struct service_s {
51     char *name;
52     int flags;
53 #       define NONE             0
54 #       define IS_INTERNAL      1       /* service is internal */
55 #       define NEED_KEYPIPE     2       /* pass kerberos key in pipe */
56 #       define NO_AUTH          4       /* doesn't need authentication */
57 } service_table[] = {
58     { "noop",           IS_INTERNAL },
59     { "sendsize",       NONE },
60     { "sendbackup",     NEED_KEYPIPE },
61     { "sendfsinfo",     NONE },
62     { "selfcheck",      NONE },
63     { NULL, NONE }
64 };
65
66
67 int max_retry_count = MAX_RETRIES;
68 int ack_timeout     = ACK_TIMEOUT;
69
70 #ifdef KRB4_SECURITY
71 #  include "amandad-krb4.c"
72 #endif
73
74 /* local functions */
75 int main P((int argc, char **argv));
76 void sendack P((pkt_t *hdr, pkt_t *msg));
77 void sendnak P((pkt_t *hdr, pkt_t *msg, char *str));
78 void setup_rep P((pkt_t *hdr, pkt_t *msg));
79 char *strlower P((char *str));
80
81 int main(argc, argv)
82 int argc;
83 char **argv;
84 {
85     int n;
86     int fd;
87     char *errstr = NULL;
88     unsigned long malloc_hist_1, malloc_size_1;
89     unsigned long malloc_hist_2, malloc_size_2;
90     char *pgm = "amandad";              /* in case argv[0] is not set */
91
92     /* in_msg: The first incoming request.
93        dup_msg: Any other incoming message.
94        out_msg: Standard, i.e. non-repeated, ACK and REP.
95        rej_msg: Any other outgoing message.
96      */
97     pkt_t in_msg, out_msg, rej_msg, dup_msg;
98     char *cmd = NULL, *base = NULL;
99     char *noop_file = NULL;
100     char **vp;
101     char *s;
102     ssize_t s_len;
103     int retry_count, rc, reqlen;
104     int req_pipe[2], rep_pipe[2];
105     int dglen = 0;
106     char number[NUM_STR_SIZE];
107     am_feature_t *our_features = NULL;
108     char *our_feature_string = NULL;
109
110     struct service_s *servp;
111     fd_set insock;
112
113     for(fd = 3; fd < FD_SETSIZE; fd++) {
114         /*
115          * Make sure nobody spoofs us with a lot of extra open files
116          * that would cause an open we do to get a very high file
117          * descriptor, which in turn might be used as an index into
118          * an array (e.g. an fd_set).
119          */
120         close(fd);
121     }
122
123     safe_cd();
124
125     /*
126      * When called via inetd, it is not uncommon to forget to put the
127      * argv[0] value on the config line.  On some systems (e.g. Solaris)
128      * this causes argv and/or argv[0] to be NULL, so we have to be
129      * careful getting our name.
130      */
131     if (argc >= 1 && argv != NULL && argv[0] != NULL) {
132         if((pgm = strrchr(argv[0], '/')) != NULL) {
133             pgm++;
134         } else {
135             pgm = argv[0];
136         }
137     }
138
139     set_pname(pgm);
140
141     malloc_size_1 = malloc_inuse(&malloc_hist_1);
142
143     erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
144
145 #ifdef FORCE_USERID
146
147     /* we'd rather not run as root */
148     if(geteuid() == 0) {
149 #ifdef KRB4_SECURITY
150         if(client_uid == (uid_t) -1) {
151             error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
152         }
153
154         /*
155          * if we're using kerberos security, we'll need to be root in
156          * order to get at the machine's srvtab entry, so we hang on to
157          * some root privledges for now.  We give them up entirely later.
158          */
159         setegid(client_gid);
160         seteuid(client_uid);
161 #else
162         initgroups(CLIENT_LOGIN, client_gid);
163         setgid(client_gid);
164         setuid(client_uid);
165 #endif  /* KRB4_SECURITY */
166     }
167 #endif  /* FORCE_USERID */
168
169     /* initialize */
170
171     dbopen();
172     {
173         int db_fd = dbfd();
174         if(db_fd != -1) {
175             dup2(db_fd, 1);
176             dup2(db_fd, 2);
177         }
178     }
179
180     startclock();
181
182     dbprintf(("%s: version %s\n", get_pname(), version()));
183     for(vp = version_info; *vp != NULL; vp++)
184         dbprintf(("%s: %s", debug_prefix(NULL), *vp));
185
186     if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
187         dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
188                   debug_prefix(NULL)));
189     }
190
191     our_features = am_init_feature_set();
192     our_feature_string = am_feature_to_string(our_features);
193
194     dgram_zero(&in_msg.dgram); 
195     dgram_socket(&in_msg.dgram, 0);
196
197     dgram_zero(&dup_msg.dgram);
198     dgram_socket(&dup_msg.dgram, 0);
199
200     dgram_zero(&out_msg.dgram);
201     dgram_socket(&out_msg.dgram, 0);
202
203     dgram_zero(&rej_msg.dgram);
204     dgram_socket(&rej_msg.dgram, 0);
205
206     dgram_zero(&rej_msg.dgram);
207     dgram_socket(&rej_msg.dgram, 0);
208
209     /* set up input and response pipes */
210
211 #ifdef KRB4_SECURITY
212     if(argc >= 2 && strcmp(argv[1], "-krb4") == 0) {
213         krb4_auth = 1;
214         dbprintf(("%s: using krb4 security\n", debug_prefix(NULL)));
215     }
216     else {
217         dbprintf(("%s: using bsd security\n", debug_prefix(NULL)));
218         krb4_auth = 0;
219     }
220 #endif
221
222     /* get request packet and attempt to parse it */
223
224     if((n = dgram_recv(&in_msg.dgram, RECV_TIMEOUT, &in_msg.peer)) <= 0) {
225         char *s;
226
227         if (n == 0) {
228             s = "timeout";
229         } else {
230             s = strerror(errno);
231         }
232         error("error receiving message: %s", s);
233     }
234
235     dbprintf(("%s: got packet:\n--------\n%s--------\n\n",
236               debug_prefix_time(NULL), in_msg.dgram.cur));
237
238     parse_pkt_header(&in_msg);
239     if(in_msg.type != P_REQ && in_msg.type != P_NAK && in_msg.type != P_ACK) {
240         /* XXX */
241         dbprintf(("%s: this is a %s packet, nak'ing it\n", 
242                   debug_prefix_time(NULL),
243                   in_msg.type == P_BOGUS? "bogus" : "unexpected"));
244         if(in_msg.type != P_BOGUS) {
245             parse_errmsg = newvstralloc(parse_errmsg,"unexpected ",
246                 in_msg.type == P_ACK? "ack ":
247                 in_msg.type == P_REP? "rep ": "",
248                 "packet", NULL);
249         }
250         sendnak(&in_msg, &rej_msg, parse_errmsg);
251         dbclose();
252         return 1;
253     }
254     if(in_msg.type != P_REQ) {
255         dbprintf(("%s: strange, this is not a request packet\n",
256                   debug_prefix_time(NULL)));
257         dbclose();
258         return 1;
259     }
260
261     /* lookup service */
262
263     for(servp = service_table; servp->name != NULL; servp++)
264         if(strcmp(servp->name, in_msg.service) == 0) break;
265
266     if(servp->name == NULL) {
267         errstr = newstralloc2(errstr, "unknown service: ", in_msg.service);
268         sendnak(&in_msg, &rej_msg, errstr);
269         dbclose();
270         return 1;
271     }
272
273     if((servp->flags & IS_INTERNAL) != 0) {
274         cmd = stralloc(servp->name);
275     } else {
276         base = newstralloc(base, servp->name);
277         cmd = newvstralloc(cmd, libexecdir, "/", base, versionsuffix(), NULL);
278
279         if(access(cmd, X_OK) == -1) {
280             dbprintf(("%s: execute access to \"%s\" denied\n",
281                       debug_prefix_time(NULL), cmd));
282             errstr = newvstralloc(errstr,
283                                   "service ", base, " unavailable",
284                                   NULL);
285             amfree(base);
286             sendnak(&in_msg, &rej_msg, errstr);
287             dbclose();
288             return 1;
289         }
290         amfree(base);
291     }
292
293     /* everything looks ok initially, send ACK */
294
295     sendack(&in_msg, &out_msg);
296
297     /* 
298      * handle security check: this could take a long time, so it is 
299      * done after the initial ack.
300      */
301
302 #if defined(KRB4_SECURITY)
303     /*
304      * we need to be root to access the srvtab file, but only if we started
305      * out that way.
306      */
307     setegid(getgid());
308     seteuid(getuid());
309 #endif /* KRB4_SECURITY */
310
311     amfree(errstr);
312     if(!(servp->flags & NO_AUTH)
313        && !security_ok(&in_msg.peer, in_msg.security, in_msg.cksum, &errstr)) {
314         /* XXX log on authlog? */
315         setup_rep(&in_msg, &out_msg);
316         ap_snprintf(out_msg.dgram.cur,
317                     sizeof(out_msg.dgram.data)-out_msg.dgram.len,
318                     "ERROR %s\n", errstr);
319         out_msg.dgram.len = strlen(out_msg.dgram.data);
320         goto send_response;
321     }
322
323 #if defined(KRB4_SECURITY) && defined(FORCE_USERID)
324
325     /*
326      * we held on to a root uid earlier for accessing files; since we're
327      * done doing anything requiring root, we can completely give it up.
328      */
329
330     if(geteuid() == 0) {
331         if(client_uid == (uid_t) -1) {
332             error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
333         }
334         initgroups(CLIENT_LOGIN, client_gid);
335         setgid(client_gid);
336         setuid(client_uid);
337     }
338
339 #endif  /* KRB4_SECURITY && FORCE_USERID */
340
341     dbprintf(("%s: running service \"%s\"\n", debug_prefix_time(NULL), cmd));
342
343     if(strcmp(servp->name, "noop") == 0) {
344         ap_snprintf(number, sizeof(number), "%ld", (long)getpid());
345         noop_file = vstralloc(AMANDA_TMPDIR,
346                               "/",
347                               get_pname(),
348                               ".noop.",
349                               number,
350                               NULL);
351         rep_pipe[0] = open(noop_file, O_RDWR|O_EXCL|O_CREAT);
352         if(rep_pipe[0] < 0) {
353             error("cannot open \"%s\": %s", noop_file, strerror(errno));
354         }
355         (void)unlink(noop_file);
356         s = vstralloc("OPTIONS features=", our_feature_string, ";\n", NULL);
357         s_len = strlen(s);
358         if(fullwrite(rep_pipe[0], s, s_len) != s_len) {
359             error("cannot write %d bytes to %s", s_len, noop_file);
360         }
361         amfree(noop_file);
362         amfree(s);
363         (void)lseek(rep_pipe[0], (off_t)0, SEEK_SET);
364     } else {
365         if(pipe(req_pipe) == -1 || pipe(rep_pipe) == -1)
366             error("pipe: %s", strerror(errno));
367
368         /* spawn first child to handle the request */
369
370         switch(fork()) {
371         case -1: error("could not fork for %s: %s", cmd, strerror(errno));
372
373         default:                /* parent */
374
375             break; 
376
377         case 0:         /* child */
378
379             aclose(req_pipe[1]); 
380             aclose(rep_pipe[0]);
381
382             dup2(req_pipe[0], 0);
383             dup2(rep_pipe[1], 1);
384
385             /* modification by BIS@BBN 4/25/2003:
386              * close these file descriptors BEFORE doing pipe magic
387              * for transferring session key; inside transfer_session_key
388              * is a dup2 to move a pipe to KEY_PIPE, which collided
389              * with req_pipe[0]; when req_pipe[0] was closed after the
390              * call to transfer_session_key, then KEY_PIPE ended up
391              * being closed. */
392             aclose(req_pipe[0]);
393             aclose(rep_pipe[1]);
394
395 #ifdef  KRB4_SECURITY
396             transfer_session_key();
397 #endif
398
399             /* run service */
400
401             execle(cmd, cmd, NULL, safe_env());
402             error("could not exec %s: %s", cmd, strerror(errno));
403         }
404         amfree(cmd);
405
406         aclose(req_pipe[0]);
407         aclose(rep_pipe[1]);
408
409         /* spawn second child to handle writing the packet to the first child */
410
411         switch(fork()) {
412         case -1: error("could not fork for %s: %s", cmd, strerror(errno));
413
414         default:                /* parent */
415
416             break;
417
418         case 0:         /* child */
419
420             aclose(rep_pipe[0]);
421             reqlen = strlen(in_msg.dgram.cur);
422             if((rc = fullwrite(req_pipe[1], in_msg.dgram.cur, reqlen)) != reqlen) {
423                 if(rc < 0) {
424                     error("write to child pipe: %s", strerror(errno));
425                 } else {
426                     error("write to child pipe: %d instead of %d", rc, reqlen);
427                 }
428             }
429             aclose(req_pipe[1]);
430             exit(0);
431         }
432
433         aclose(req_pipe[1]);
434     }
435
436     setup_rep(&in_msg, &out_msg);
437 #ifdef KRB4_SECURITY
438     add_mutual_authenticator(&out_msg.dgram);
439 #endif
440
441     while(1) {
442
443         FD_ZERO(&insock);
444         FD_SET(rep_pipe[0], &insock);
445
446         if((servp->flags & IS_INTERNAL) != 0) {
447             n = 0;
448         } else {
449             FD_SET(0, &insock);
450             n = select(rep_pipe[0] + 1,
451                        (SELECT_ARG_TYPE *)&insock,
452                        NULL,
453                        NULL,
454                        NULL);
455         }
456         if(n < 0) {
457             error("select failed: %s", strerror(errno));
458         }
459
460         if(FD_ISSET(rep_pipe[0], &insock)) {
461             if(dglen >= MAX_DGRAM) {
462                 error("more than %d bytes received from child", MAX_DGRAM);
463             }
464             rc = read(rep_pipe[0], out_msg.dgram.cur+dglen, MAX_DGRAM-dglen);
465             if(rc <= 0) {
466                 if (rc < 0) {
467                     error("reading response pipe: %s", strerror(errno));
468                 }
469                 break;
470             }
471             dglen += rc;
472         }
473         if(!FD_ISSET(0,&insock))
474             continue;
475
476         if((n = dgram_recv(&dup_msg.dgram, RECV_TIMEOUT, &dup_msg.peer)) <= 0) {
477             char *s;
478
479             if (n == 0) {
480                 s = "timeout";
481             } else {
482                 s = strerror(errno);
483             }
484             error("error receiving message: %s", s);
485         }
486
487         /* 
488          * Under normal conditions, the master will resend the REQ packet
489          * to be sure we are still alive.  It expects an ACK back right away.
490          *
491          * XXX- Arguably we should parse and security check the new packet, 
492          * only sending an ACK if it passes and the request is identical to
493          * the original one.  However, that's too much work for now. :-) 
494          *
495          * It should suffice to ACK whenever the sender is identical.
496          */
497         dbprintf(("%s: got packet:\n----\n%s----\n\n",
498                   debug_prefix_time(NULL), dup_msg.dgram.data));
499         parse_pkt_header(&dup_msg);
500         if(dup_msg.peer.sin_addr.s_addr == in_msg.peer.sin_addr.s_addr &&
501            dup_msg.peer.sin_port == in_msg.peer.sin_port) {
502             if(dup_msg.type == P_REQ) {
503                 dbprintf(("%s: received dup P_REQ packet, ACKing it\n",
504                           debug_prefix_time(NULL)));
505                 sendack(&in_msg, &rej_msg);
506             }
507             else {
508                 dbprintf(("%s: it is not a P_REQ, ignoring it\n",
509                           debug_prefix_time(NULL)));
510             }
511         }
512         else {
513             dbprintf(("%s: received other packet, NAKing it\n",
514                       debug_prefix_time(NULL)));
515             dbprintf(("  addr: peer %s dup %s, port: peer %d dup %d\n",
516                       inet_ntoa(in_msg.peer.sin_addr),
517                       inet_ntoa(dup_msg.peer.sin_addr),
518                       (int)ntohs(in_msg.peer.sin_port),
519                       (int)ntohs(dup_msg.peer.sin_port)));
520             /* XXX dup_msg filled in? */
521             sendnak(&dup_msg, &rej_msg, "amandad busy");
522         }
523
524     }
525
526     /* XXX reap child?  log if non-zero status?  don't respond if non zero? */
527     /* setup header for out_msg */
528
529     out_msg.dgram.len += dglen;
530     out_msg.dgram.data[out_msg.dgram.len] = '\0';
531     aclose(rep_pipe[0]);
532
533 send_response:
534
535     retry_count = 0;
536
537     while(retry_count < max_retry_count) {
538         if(!retry_count)
539             dbprintf(("%s: sending REP packet:\n----\n%s----\n\n",
540                       debug_prefix_time(NULL), out_msg.dgram.data));
541         dgram_send_addr(in_msg.peer, &out_msg.dgram);
542         if((n = dgram_recv(&dup_msg.dgram, ack_timeout, &dup_msg.peer)) <= 0) {
543             char *s;
544
545             if (n == 0) {
546                 s = "timeout";
547             } else {
548                 s = strerror(errno);
549             }
550
551             /* timed out or error, try again */
552             retry_count++;
553
554             dbprintf(("%s: waiting for ack: %s", debug_prefix_time(NULL), s));
555             if(retry_count < max_retry_count) 
556                 dbprintf((", retrying\n"));
557             else 
558                 dbprintf((", giving up!\n"));
559
560             continue;
561         }
562         dbprintf(("%s: got packet:\n----\n%s----\n\n",
563                   debug_prefix_time(NULL), dup_msg.dgram.data));
564         parse_pkt_header(&dup_msg);
565
566         
567         if(dup_msg.peer.sin_addr.s_addr == in_msg.peer.sin_addr.s_addr &&
568            dup_msg.peer.sin_port == in_msg.peer.sin_port) {
569             if(dup_msg.type == P_ACK)
570                 break;
571             else
572                 dbprintf(("%s: it is not an ack\n", debug_prefix_time(NULL)));
573         }
574         else {
575             dbprintf(("%s: weird, it is not a proper ack\n",
576                       debug_prefix_time(NULL)));
577             dbprintf(("  addr: peer %s dup %s, port: peer %d dup %d\n",
578                       inet_ntoa(in_msg.peer.sin_addr),
579                       inet_ntoa(dup_msg.peer.sin_addr),
580                       (int)ntohs(in_msg.peer.sin_port),
581                       (int)ntohs(dup_msg.peer.sin_port)));
582         }               
583     }
584     /* XXX log if retry count exceeded */
585
586     amfree(cmd);
587     amfree(noop_file);
588     amfree(our_feature_string);
589     am_release_feature_set(our_features);
590     our_features = NULL;
591     malloc_size_2 = malloc_inuse(&malloc_hist_2);
592
593     if(malloc_size_1 != malloc_size_2) {
594 #if defined(USE_DBMALLOC)
595         malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
596 #endif
597     }
598
599     dbclose();
600     return 0;
601 }
602
603
604 /* -------- */
605
606 void sendack(hdr, msg)
607 pkt_t *hdr;
608 pkt_t *msg;
609 {
610     /* XXX this isn't very safe either: handle could be bogus */
611     ap_snprintf(msg->dgram.data, sizeof(msg->dgram.data),
612                 "Amanda %d.%d ACK HANDLE %s SEQ %d\n",
613                 VERSION_MAJOR, VERSION_MINOR,
614                 hdr->handle ? hdr->handle : "",
615                 hdr->sequence);
616     msg->dgram.len = strlen(msg->dgram.data);
617     dbprintf(("%s: sending ack:\n----\n%s----\n\n",
618               debug_prefix_time(NULL), msg->dgram.data));
619     dgram_send_addr(hdr->peer, &msg->dgram);
620 }
621
622 void sendnak(hdr, msg, str)
623 pkt_t *hdr;
624 pkt_t *msg;
625 char *str;
626 {
627     /* XXX this isn't very safe either: handle could be bogus */
628     ap_snprintf(msg->dgram.data, sizeof(msg->dgram.data),
629                 "Amanda %d.%d NAK HANDLE %s SEQ %d\nERROR %s\n",
630                 VERSION_MAJOR, VERSION_MINOR,
631                 hdr->handle ? hdr->handle : "",
632                 hdr->sequence, str ? str : "UNKNOWN");
633
634     msg->dgram.len = strlen(msg->dgram.data);
635     dbprintf(("%s: sending nack:\n----\n%s----\n\n",
636               debug_prefix_time(NULL), msg->dgram.data));
637     dgram_send_addr(hdr->peer, &msg->dgram);
638 }
639
640 void setup_rep(hdr, msg)
641 pkt_t *hdr;
642 pkt_t *msg;
643 {
644     /* XXX this isn't very safe either: handle could be bogus */
645     ap_snprintf(msg->dgram.data, sizeof(msg->dgram.data),
646                 "Amanda %d.%d REP HANDLE %s SEQ %d\n",
647                 VERSION_MAJOR, VERSION_MINOR,
648                 hdr->handle ? hdr->handle : "",
649                 hdr->sequence);
650
651     msg->dgram.len = strlen(msg->dgram.data);
652     msg->dgram.cur = msg->dgram.data + msg->dgram.len;
653
654 }
655
656 /* -------- */
657
658 char *strlower(str)
659 char *str;
660 {
661     char *s;
662     for(s=str; *s; s++)
663         if(isupper((int)*s)) *s = tolower(*s);
664     return str;
665 }