2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: amandad.c,v 1.32.2.4.4.1.2.6 2003/12/16 22:36:45 martinea Exp $
30 * handle client-host side of Amanda network communications, including
31 * security checks, execution of the proper service, and acking the
38 #include "amfeatures.h"
43 #define RECV_TIMEOUT 30
44 #define ACK_TIMEOUT 10 /* XXX should be configurable */
48 * Here are the services that we understand.
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 */
58 { "noop", IS_INTERNAL },
60 { "sendbackup", NEED_KEYPIPE },
61 { "sendfsinfo", NONE },
62 { "selfcheck", NONE },
67 int max_retry_count = MAX_RETRIES;
68 int ack_timeout = ACK_TIMEOUT;
71 # include "amandad-krb4.c"
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));
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 */
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.
97 pkt_t in_msg, out_msg, rej_msg, dup_msg;
98 char *cmd = NULL, *base = NULL;
99 char *noop_file = NULL;
103 int retry_count, rc, reqlen;
104 int req_pipe[2], rep_pipe[2];
106 char number[NUM_STR_SIZE];
107 am_feature_t *our_features = NULL;
108 char *our_feature_string = NULL;
110 struct service_s *servp;
113 for(fd = 3; fd < FD_SETSIZE; fd++) {
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).
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.
131 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
132 if((pgm = strrchr(argv[0], '/')) != NULL) {
141 malloc_size_1 = malloc_inuse(&malloc_hist_1);
143 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
147 /* we'd rather not run as root */
150 if(client_uid == (uid_t) -1) {
151 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
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.
162 initgroups(CLIENT_LOGIN, client_gid);
165 #endif /* KRB4_SECURITY */
167 #endif /* FORCE_USERID */
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));
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)));
191 our_features = am_init_feature_set();
192 our_feature_string = am_feature_to_string(our_features);
194 dgram_zero(&in_msg.dgram);
195 dgram_socket(&in_msg.dgram, 0);
197 dgram_zero(&dup_msg.dgram);
198 dgram_socket(&dup_msg.dgram, 0);
200 dgram_zero(&out_msg.dgram);
201 dgram_socket(&out_msg.dgram, 0);
203 dgram_zero(&rej_msg.dgram);
204 dgram_socket(&rej_msg.dgram, 0);
206 dgram_zero(&rej_msg.dgram);
207 dgram_socket(&rej_msg.dgram, 0);
209 /* set up input and response pipes */
212 if(argc >= 2 && strcmp(argv[1], "-krb4") == 0) {
214 dbprintf(("%s: using krb4 security\n", debug_prefix(NULL)));
217 dbprintf(("%s: using bsd security\n", debug_prefix(NULL)));
222 /* get request packet and attempt to parse it */
224 if((n = dgram_recv(&in_msg.dgram, RECV_TIMEOUT, &in_msg.peer)) <= 0) {
232 error("error receiving message: %s", s);
235 dbprintf(("%s: got packet:\n--------\n%s--------\n\n",
236 debug_prefix_time(NULL), in_msg.dgram.cur));
238 parse_pkt_header(&in_msg);
239 if(in_msg.type != P_REQ && in_msg.type != P_NAK && in_msg.type != P_ACK) {
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 ": "",
250 sendnak(&in_msg, &rej_msg, parse_errmsg);
254 if(in_msg.type != P_REQ) {
255 dbprintf(("%s: strange, this is not a request packet\n",
256 debug_prefix_time(NULL)));
263 for(servp = service_table; servp->name != NULL; servp++)
264 if(strcmp(servp->name, in_msg.service) == 0) break;
266 if(servp->name == NULL) {
267 errstr = newstralloc2(errstr, "unknown service: ", in_msg.service);
268 sendnak(&in_msg, &rej_msg, errstr);
273 if((servp->flags & IS_INTERNAL) != 0) {
274 cmd = stralloc(servp->name);
276 base = newstralloc(base, servp->name);
277 cmd = newvstralloc(cmd, libexecdir, "/", base, versionsuffix(), NULL);
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",
286 sendnak(&in_msg, &rej_msg, errstr);
293 /* everything looks ok initially, send ACK */
295 sendack(&in_msg, &out_msg);
298 * handle security check: this could take a long time, so it is
299 * done after the initial ack.
302 #if defined(KRB4_SECURITY)
304 * we need to be root to access the srvtab file, but only if we started
309 #endif /* KRB4_SECURITY */
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);
323 #if defined(KRB4_SECURITY) && defined(FORCE_USERID)
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.
331 if(client_uid == (uid_t) -1) {
332 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
334 initgroups(CLIENT_LOGIN, client_gid);
339 #endif /* KRB4_SECURITY && FORCE_USERID */
341 dbprintf(("%s: running service \"%s\"\n", debug_prefix_time(NULL), cmd));
343 if(strcmp(servp->name, "noop") == 0) {
344 ap_snprintf(number, sizeof(number), "%ld", (long)getpid());
345 noop_file = vstralloc(AMANDA_TMPDIR,
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));
355 (void)unlink(noop_file);
356 s = vstralloc("OPTIONS features=", our_feature_string, ";\n", NULL);
358 if(fullwrite(rep_pipe[0], s, s_len) != s_len) {
359 error("cannot write %d bytes to %s", s_len, noop_file);
363 (void)lseek(rep_pipe[0], (off_t)0, SEEK_SET);
365 if(pipe(req_pipe) == -1 || pipe(rep_pipe) == -1)
366 error("pipe: %s", strerror(errno));
368 /* spawn first child to handle the request */
371 case -1: error("could not fork for %s: %s", cmd, strerror(errno));
373 default: /* parent */
382 dup2(req_pipe[0], 0);
383 dup2(rep_pipe[1], 1);
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
396 transfer_session_key();
401 execle(cmd, cmd, NULL, safe_env());
402 error("could not exec %s: %s", cmd, strerror(errno));
409 /* spawn second child to handle writing the packet to the first child */
412 case -1: error("could not fork for %s: %s", cmd, strerror(errno));
414 default: /* parent */
421 reqlen = strlen(in_msg.dgram.cur);
422 if((rc = fullwrite(req_pipe[1], in_msg.dgram.cur, reqlen)) != reqlen) {
424 error("write to child pipe: %s", strerror(errno));
426 error("write to child pipe: %d instead of %d", rc, reqlen);
436 setup_rep(&in_msg, &out_msg);
438 add_mutual_authenticator(&out_msg.dgram);
444 FD_SET(rep_pipe[0], &insock);
446 if((servp->flags & IS_INTERNAL) != 0) {
450 n = select(rep_pipe[0] + 1,
451 (SELECT_ARG_TYPE *)&insock,
457 error("select failed: %s", strerror(errno));
460 if(FD_ISSET(rep_pipe[0], &insock)) {
461 if(dglen >= MAX_DGRAM) {
462 error("more than %d bytes received from child", MAX_DGRAM);
464 rc = read(rep_pipe[0], out_msg.dgram.cur+dglen, MAX_DGRAM-dglen);
467 error("reading response pipe: %s", strerror(errno));
473 if(!FD_ISSET(0,&insock))
476 if((n = dgram_recv(&dup_msg.dgram, RECV_TIMEOUT, &dup_msg.peer)) <= 0) {
484 error("error receiving message: %s", s);
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.
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. :-)
495 * It should suffice to ACK whenever the sender is identical.
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);
508 dbprintf(("%s: it is not a P_REQ, ignoring it\n",
509 debug_prefix_time(NULL)));
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");
526 /* XXX reap child? log if non-zero status? don't respond if non zero? */
527 /* setup header for out_msg */
529 out_msg.dgram.len += dglen;
530 out_msg.dgram.data[out_msg.dgram.len] = '\0';
537 while(retry_count < max_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) {
551 /* timed out or error, try again */
554 dbprintf(("%s: waiting for ack: %s", debug_prefix_time(NULL), s));
555 if(retry_count < max_retry_count)
556 dbprintf((", retrying\n"));
558 dbprintf((", giving up!\n"));
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);
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)
572 dbprintf(("%s: it is not an ack\n", debug_prefix_time(NULL)));
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)));
584 /* XXX log if retry count exceeded */
588 amfree(our_feature_string);
589 am_release_feature_set(our_features);
591 malloc_size_2 = malloc_inuse(&malloc_hist_2);
593 if(malloc_size_1 != malloc_size_2) {
594 #if defined(USE_DBMALLOC)
595 malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
606 void sendack(hdr, msg)
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 : "",
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);
622 void sendnak(hdr, msg, str)
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");
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);
640 void setup_rep(hdr, msg)
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 : "",
651 msg->dgram.len = strlen(msg->dgram.data);
652 msg->dgram.cur = msg->dgram.data + msg->dgram.len;
663 if(isupper((int)*s)) *s = tolower(*s);