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.2.1 2004/02/13 14:01:07 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"
42 #include "client_util.h"
44 #define RECV_TIMEOUT 30
45 #define ACK_TIMEOUT 10 /* XXX should be configurable */
49 * Here are the services that we understand.
55 # define IS_INTERNAL 1 /* service is internal */
56 # define NEED_KEYPIPE 2 /* pass kerberos key in pipe */
57 # define NO_AUTH 4 /* doesn't need authentication */
59 { "noop", IS_INTERNAL },
61 { "sendbackup", NEED_KEYPIPE },
62 { "sendfsinfo", NONE },
63 { "selfcheck", NONE },
68 int max_retry_count = MAX_RETRIES;
69 int ack_timeout = ACK_TIMEOUT;
72 # include "amandad-krb4.c"
76 int main P((int argc, char **argv));
77 void sendack P((pkt_t *hdr, pkt_t *msg));
78 void sendnak P((pkt_t *hdr, pkt_t *msg, char *str));
79 void setup_rep P((pkt_t *hdr, pkt_t *msg, int partial_rep));
80 char *strlower P((char *str));
89 unsigned long malloc_hist_1, malloc_size_1;
90 unsigned long malloc_hist_2, malloc_size_2;
91 char *pgm = "amandad"; /* in case argv[0] is not set */
93 /* in_msg: The first incoming request.
94 dup_msg: Any other incoming message.
95 out_msg: Standard, i.e. non-repeated, ACK and REP.
96 rej_msg: Any other outgoing message.
98 pkt_t in_msg, out_msg, out_pmsg, rej_msg, dup_msg;
99 char *cmd = NULL, *base = NULL;
100 char *noop_file = NULL;
104 int retry_count, rc, reqlen;
105 int req_pipe[2], rep_pipe[2];
107 char number[NUM_STR_SIZE];
108 am_feature_t *our_features = NULL;
109 char *our_feature_string = NULL;
110 int send_partial_reply = 0;
112 struct service_s *servp;
115 for(fd = 3; fd < FD_SETSIZE; fd++) {
117 * Make sure nobody spoofs us with a lot of extra open files
118 * that would cause an open we do to get a very high file
119 * descriptor, which in turn might be used as an index into
120 * an array (e.g. an fd_set).
128 * When called via inetd, it is not uncommon to forget to put the
129 * argv[0] value on the config line. On some systems (e.g. Solaris)
130 * this causes argv and/or argv[0] to be NULL, so we have to be
131 * careful getting our name.
133 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
134 if((pgm = strrchr(argv[0], '/')) != NULL) {
143 malloc_size_1 = malloc_inuse(&malloc_hist_1);
145 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
149 /* we'd rather not run as root */
152 if(client_uid == (uid_t) -1) {
153 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
157 * if we're using kerberos security, we'll need to be root in
158 * order to get at the machine's srvtab entry, so we hang on to
159 * some root privledges for now. We give them up entirely later.
164 initgroups(CLIENT_LOGIN, client_gid);
167 #endif /* KRB4_SECURITY */
169 #endif /* FORCE_USERID */
184 dbprintf(("%s: version %s\n", get_pname(), version()));
185 for(vp = version_info; *vp != NULL; vp++)
186 dbprintf(("%s: %s", debug_prefix(NULL), *vp));
188 if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
189 dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
190 debug_prefix(NULL)));
193 our_features = am_init_feature_set();
194 our_feature_string = am_feature_to_string(our_features);
196 dgram_zero(&in_msg.dgram);
197 dgram_socket(&in_msg.dgram, 0);
199 dgram_zero(&dup_msg.dgram);
200 dgram_socket(&dup_msg.dgram, 0);
202 dgram_zero(&out_msg.dgram);
203 dgram_socket(&out_msg.dgram, 0);
205 dgram_zero(&out_pmsg.dgram);
206 dgram_socket(&out_pmsg.dgram, 0);
208 dgram_zero(&rej_msg.dgram);
209 dgram_socket(&rej_msg.dgram, 0);
211 dgram_zero(&rej_msg.dgram);
212 dgram_socket(&rej_msg.dgram, 0);
214 /* set up input and response pipes */
217 if(argc >= 2 && strcmp(argv[1], "-krb4") == 0) {
219 dbprintf(("%s: using krb4 security\n", debug_prefix(NULL)));
222 dbprintf(("%s: using bsd security\n", debug_prefix(NULL)));
227 /* get request packet and attempt to parse it */
229 if((n = dgram_recv(&in_msg.dgram, RECV_TIMEOUT, &in_msg.peer)) <= 0) {
237 error("error receiving message: %s", s);
240 dbprintf(("%s: got packet:\n--------\n%s--------\n\n",
241 debug_prefix_time(NULL), in_msg.dgram.cur));
243 parse_pkt_header(&in_msg);
244 if(in_msg.type != P_REQ && in_msg.type != P_NAK && in_msg.type != P_ACK) {
246 dbprintf(("%s: this is a %s packet, nak'ing it\n",
247 debug_prefix_time(NULL),
248 in_msg.type == P_BOGUS? "bogus" : "unexpected"));
249 if(in_msg.type != P_BOGUS) {
250 parse_errmsg = newvstralloc(parse_errmsg,"unexpected ",
251 in_msg.type == P_ACK? "ack ":
252 in_msg.type == P_REP? "rep ": "",
255 sendnak(&in_msg, &rej_msg, parse_errmsg);
259 if(in_msg.type != P_REQ) {
260 dbprintf(("%s: strange, this is not a request packet\n",
261 debug_prefix_time(NULL)));
268 for(servp = service_table; servp->name != NULL; servp++)
269 if(strcmp(servp->name, in_msg.service) == 0) break;
271 if(servp->name == NULL) {
272 errstr = newstralloc2(errstr, "unknown service: ", in_msg.service);
273 sendnak(&in_msg, &rej_msg, errstr);
278 if((servp->flags & IS_INTERNAL) != 0) {
279 cmd = stralloc(servp->name);
281 base = newstralloc(base, servp->name);
282 cmd = newvstralloc(cmd, libexecdir, "/", base, versionsuffix(), NULL);
284 if(access(cmd, X_OK) == -1) {
285 dbprintf(("%s: execute access to \"%s\" denied\n",
286 debug_prefix_time(NULL), cmd));
287 errstr = newvstralloc(errstr,
288 "service ", base, " unavailable",
291 sendnak(&in_msg, &rej_msg, errstr);
298 /* everything looks ok initially, send ACK */
300 sendack(&in_msg, &out_msg);
303 * handle security check: this could take a long time, so it is
304 * done after the initial ack.
307 #if defined(KRB4_SECURITY)
309 * we need to be root to access the srvtab file, but only if we started
314 #endif /* KRB4_SECURITY */
317 if(!(servp->flags & NO_AUTH)
318 && !security_ok(&in_msg.peer, in_msg.security, in_msg.cksum, &errstr)) {
319 /* XXX log on authlog? */
320 setup_rep(&in_msg, &out_msg, 0);
321 ap_snprintf(out_msg.dgram.cur,
322 sizeof(out_msg.dgram.data)-out_msg.dgram.len,
323 "ERROR %s\n", errstr);
324 out_msg.dgram.len = strlen(out_msg.dgram.data);
328 #if defined(KRB4_SECURITY) && defined(FORCE_USERID)
331 * we held on to a root uid earlier for accessing files; since we're
332 * done doing anything requiring root, we can completely give it up.
336 if(client_uid == (uid_t) -1) {
337 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
339 initgroups(CLIENT_LOGIN, client_gid);
344 #endif /* KRB4_SECURITY && FORCE_USERID */
346 dbprintf(("%s: running service \"%s\"\n", debug_prefix_time(NULL), cmd));
348 if(strcmp(servp->name, "noop") == 0) {
349 ap_snprintf(number, sizeof(number), "%ld", (long)getpid());
350 noop_file = vstralloc(AMANDA_TMPDIR,
356 rep_pipe[0] = open(noop_file, O_RDWR|O_EXCL|O_CREAT);
357 if(rep_pipe[0] < 0) {
358 error("cannot open \"%s\": %s", noop_file, strerror(errno));
360 (void)unlink(noop_file);
361 s = vstralloc("OPTIONS features=", our_feature_string, ";\n", NULL);
363 if(fullwrite(rep_pipe[0], s, s_len) != s_len) {
364 error("cannot write %d bytes to %s", s_len, noop_file);
368 (void)lseek(rep_pipe[0], (off_t)0, SEEK_SET);
370 if(strcmp(servp->name, "sendsize") == 0) {
371 if(strncmp(in_msg.dgram.cur,"OPTIONS ",8) == 0) {
372 g_option_t *g_options;
373 char *option_str, *p;
375 option_str = stralloc(in_msg.dgram.cur+8);
376 p = strchr(option_str,'\n');
379 g_options = parse_g_options(option_str, 0);
380 if(am_has_feature(g_options->features, fe_partial_estimate)) {
381 send_partial_reply = 1;
386 if(pipe(req_pipe) == -1 || pipe(rep_pipe) == -1)
387 error("pipe: %s", strerror(errno));
389 /* spawn first child to handle the request */
392 case -1: error("could not fork for %s: %s", cmd, strerror(errno));
394 default: /* parent */
403 dup2(req_pipe[0], 0);
404 dup2(rep_pipe[1], 1);
406 /* modification by BIS@BBN 4/25/2003:
407 * close these file descriptors BEFORE doing pipe magic
408 * for transferring session key; inside transfer_session_key
409 * is a dup2 to move a pipe to KEY_PIPE, which collided
410 * with req_pipe[0]; when req_pipe[0] was closed after the
411 * call to transfer_session_key, then KEY_PIPE ended up
417 transfer_session_key();
422 execle(cmd, cmd, NULL, safe_env());
423 error("could not exec %s: %s", cmd, strerror(errno));
430 /* spawn second child to handle writing the packet to the first child */
433 case -1: error("could not fork for %s: %s", cmd, strerror(errno));
435 default: /* parent */
442 reqlen = strlen(in_msg.dgram.cur);
443 if((rc = fullwrite(req_pipe[1], in_msg.dgram.cur, reqlen)) != reqlen) {
445 error("write to child pipe: %s", strerror(errno));
447 error("write to child pipe: %d instead of %d", rc, reqlen);
457 setup_rep(&in_msg, &out_msg, 0);
458 if(send_partial_reply) {
459 setup_rep(&in_msg, &out_pmsg, 1);
462 add_mutual_authenticator(&out_msg.dgram);
463 add_mutual_authenticator(&out_pmsg.dgram);
469 FD_SET(rep_pipe[0], &insock);
471 if((servp->flags & IS_INTERNAL) != 0) {
475 n = select(rep_pipe[0] + 1,
476 (SELECT_ARG_TYPE *)&insock,
482 error("select failed: %s", strerror(errno));
485 if(FD_ISSET(rep_pipe[0], &insock)) {
486 if(dglen >= MAX_DGRAM) {
487 error("more than %d bytes received from child", MAX_DGRAM);
489 rc = read(rep_pipe[0], out_msg.dgram.cur+dglen, MAX_DGRAM-dglen);
492 error("reading response pipe: %s", strerror(errno));
497 if(send_partial_reply) {
498 strncpy(out_pmsg.dgram.cur+dglen, out_msg.dgram.cur+dglen, rc);
499 out_pmsg.dgram.len += rc;
500 out_pmsg.dgram.data[out_pmsg.dgram.len] = '\0';
501 dbprintf(("%s: sending PREP packet:\n----\n%s----\n\n",
502 debug_prefix_time(NULL), out_pmsg.dgram.data));
503 dgram_send_addr(in_msg.peer, &out_pmsg.dgram);
508 if(!FD_ISSET(0,&insock))
511 if((n = dgram_recv(&dup_msg.dgram, RECV_TIMEOUT, &dup_msg.peer)) <= 0) {
519 error("error receiving message: %s", s);
523 * Under normal conditions, the master will resend the REQ packet
524 * to be sure we are still alive. It expects an ACK back right away.
526 * XXX- Arguably we should parse and security check the new packet,
527 * only sending an ACK if it passes and the request is identical to
528 * the original one. However, that's too much work for now. :-)
530 * It should suffice to ACK whenever the sender is identical.
532 dbprintf(("%s: got packet:\n----\n%s----\n\n",
533 debug_prefix_time(NULL), dup_msg.dgram.data));
534 parse_pkt_header(&dup_msg);
535 if(dup_msg.peer.sin_addr.s_addr == in_msg.peer.sin_addr.s_addr &&
536 dup_msg.peer.sin_port == in_msg.peer.sin_port) {
537 if(dup_msg.type == P_REQ) {
538 dbprintf(("%s: received dup P_REQ packet, ACKing it\n",
539 debug_prefix_time(NULL)));
540 sendack(&in_msg, &rej_msg);
543 dbprintf(("%s: it is not a P_REQ, ignoring it\n",
544 debug_prefix_time(NULL)));
548 dbprintf(("%s: received other packet, NAKing it\n",
549 debug_prefix_time(NULL)));
550 dbprintf((" addr: peer %s dup %s, port: peer %d dup %d\n",
551 inet_ntoa(in_msg.peer.sin_addr),
552 inet_ntoa(dup_msg.peer.sin_addr),
553 (int)ntohs(in_msg.peer.sin_port),
554 (int)ntohs(dup_msg.peer.sin_port)));
555 /* XXX dup_msg filled in? */
556 sendnak(&dup_msg, &rej_msg, "amandad busy");
561 /* XXX reap child? log if non-zero status? don't respond if non zero? */
562 /* setup header for out_msg */
564 out_msg.dgram.len += dglen;
565 out_msg.dgram.data[out_msg.dgram.len] = '\0';
572 while(retry_count < max_retry_count) {
574 dbprintf(("%s: sending REP packet:\n----\n%s----\n\n",
575 debug_prefix_time(NULL), out_msg.dgram.data));
576 dgram_send_addr(in_msg.peer, &out_msg.dgram);
577 if((n = dgram_recv(&dup_msg.dgram, ack_timeout, &dup_msg.peer)) <= 0) {
586 /* timed out or error, try again */
589 dbprintf(("%s: waiting for ack: %s", debug_prefix_time(NULL), s));
590 if(retry_count < max_retry_count)
591 dbprintf((", retrying\n"));
593 dbprintf((", giving up!\n"));
597 dbprintf(("%s: got packet:\n----\n%s----\n\n",
598 debug_prefix_time(NULL), dup_msg.dgram.data));
599 parse_pkt_header(&dup_msg);
602 if(dup_msg.peer.sin_addr.s_addr == in_msg.peer.sin_addr.s_addr &&
603 dup_msg.peer.sin_port == in_msg.peer.sin_port) {
604 if(dup_msg.type == P_ACK)
607 dbprintf(("%s: it is not an ack\n", debug_prefix_time(NULL)));
610 dbprintf(("%s: weird, it is not a proper ack\n",
611 debug_prefix_time(NULL)));
612 dbprintf((" addr: peer %s dup %s, port: peer %d dup %d\n",
613 inet_ntoa(in_msg.peer.sin_addr),
614 inet_ntoa(dup_msg.peer.sin_addr),
615 (int)ntohs(in_msg.peer.sin_port),
616 (int)ntohs(dup_msg.peer.sin_port)));
619 /* XXX log if retry count exceeded */
623 amfree(our_feature_string);
624 am_release_feature_set(our_features);
626 malloc_size_2 = malloc_inuse(&malloc_hist_2);
628 if(malloc_size_1 != malloc_size_2) {
629 #if defined(USE_DBMALLOC)
630 malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
641 void sendack(hdr, msg)
645 /* XXX this isn't very safe either: handle could be bogus */
646 ap_snprintf(msg->dgram.data, sizeof(msg->dgram.data),
647 "Amanda %d.%d ACK HANDLE %s SEQ %d\n",
648 VERSION_MAJOR, VERSION_MINOR,
649 hdr->handle ? hdr->handle : "",
651 msg->dgram.len = strlen(msg->dgram.data);
652 dbprintf(("%s: sending ack:\n----\n%s----\n\n",
653 debug_prefix_time(NULL), msg->dgram.data));
654 dgram_send_addr(hdr->peer, &msg->dgram);
657 void sendnak(hdr, msg, str)
662 /* XXX this isn't very safe either: handle could be bogus */
663 ap_snprintf(msg->dgram.data, sizeof(msg->dgram.data),
664 "Amanda %d.%d NAK HANDLE %s SEQ %d\nERROR %s\n",
665 VERSION_MAJOR, VERSION_MINOR,
666 hdr->handle ? hdr->handle : "",
667 hdr->sequence, str ? str : "UNKNOWN");
669 msg->dgram.len = strlen(msg->dgram.data);
670 dbprintf(("%s: sending nack:\n----\n%s----\n\n",
671 debug_prefix_time(NULL), msg->dgram.data));
672 dgram_send_addr(hdr->peer, &msg->dgram);
675 void setup_rep(hdr, msg, partial_rep)
680 /* XXX this isn't very safe either: handle could be bogus */
681 ap_snprintf(msg->dgram.data, sizeof(msg->dgram.data),
682 "Amanda %d.%d %s HANDLE %s SEQ %d\n",
683 VERSION_MAJOR, VERSION_MINOR,
684 partial_rep == 0 ? "REP" : "PREP",
685 hdr->handle ? hdr->handle : "",
688 msg->dgram.len = strlen(msg->dgram.data);
689 msg->dgram.cur = msg->dgram.data + msg->dgram.len;
700 if(isupper((int)*s)) *s = tolower(*s);