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.2 2005/09/20 21:31:52 jrjackson 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));
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, out_pmsg, 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;
109 int send_partial_reply = 0;
111 struct service_s *servp;
118 * When called via inetd, it is not uncommon to forget to put the
119 * argv[0] value on the config line. On some systems (e.g. Solaris)
120 * this causes argv and/or argv[0] to be NULL, so we have to be
121 * careful getting our name.
123 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
124 if((pgm = strrchr(argv[0], '/')) != NULL) {
133 malloc_size_1 = malloc_inuse(&malloc_hist_1);
135 erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
139 /* we'd rather not run as root */
142 if(client_uid == (uid_t) -1) {
143 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
147 * if we're using kerberos security, we'll need to be root in
148 * order to get at the machine's srvtab entry, so we hang on to
149 * some root privledges for now. We give them up entirely later.
154 initgroups(CLIENT_LOGIN, client_gid);
157 #endif /* KRB4_SECURITY */
159 #endif /* FORCE_USERID */
174 dbprintf(("%s: version %s\n", get_pname(), version()));
175 for(vp = version_info; *vp != NULL; vp++)
176 dbprintf(("%s: %s", debug_prefix(NULL), *vp));
178 if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
179 dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
180 debug_prefix(NULL)));
183 our_features = am_init_feature_set();
184 our_feature_string = am_feature_to_string(our_features);
186 dgram_zero(&in_msg.dgram);
187 dgram_socket(&in_msg.dgram, 0);
189 dgram_zero(&dup_msg.dgram);
190 dgram_socket(&dup_msg.dgram, 0);
192 dgram_zero(&out_msg.dgram);
193 dgram_socket(&out_msg.dgram, 0);
195 dgram_zero(&out_pmsg.dgram);
196 dgram_socket(&out_pmsg.dgram, 0);
198 dgram_zero(&rej_msg.dgram);
199 dgram_socket(&rej_msg.dgram, 0);
201 dgram_zero(&rej_msg.dgram);
202 dgram_socket(&rej_msg.dgram, 0);
204 /* set up input and response pipes */
207 if(argc >= 2 && strcmp(argv[1], "-krb4") == 0) {
209 dbprintf(("%s: using krb4 security\n", debug_prefix(NULL)));
212 dbprintf(("%s: using bsd security\n", debug_prefix(NULL)));
217 /* get request packet and attempt to parse it */
219 if((n = dgram_recv(&in_msg.dgram, RECV_TIMEOUT, &in_msg.peer)) <= 0) {
227 error("error receiving message: %s", s);
230 dbprintf(("%s: got packet:\n--------\n%s--------\n\n",
231 debug_prefix_time(NULL), in_msg.dgram.cur));
233 parse_pkt_header(&in_msg);
234 if(in_msg.type != P_REQ && in_msg.type != P_NAK && in_msg.type != P_ACK) {
236 dbprintf(("%s: this is a %s packet, nak'ing it\n",
237 debug_prefix_time(NULL),
238 in_msg.type == P_BOGUS? "bogus" : "unexpected"));
239 if(in_msg.type != P_BOGUS) {
240 parse_errmsg = newvstralloc(parse_errmsg,"unexpected ",
241 in_msg.type == P_ACK? "ack ":
242 in_msg.type == P_REP? "rep ": "",
245 sendnak(&in_msg, &rej_msg, parse_errmsg);
249 if(in_msg.type != P_REQ) {
250 dbprintf(("%s: strange, this is not a request packet\n",
251 debug_prefix_time(NULL)));
258 for(servp = service_table; servp->name != NULL; servp++)
259 if(strcmp(servp->name, in_msg.service) == 0) break;
261 if(servp->name == NULL) {
262 errstr = newstralloc2(errstr, "unknown service: ", in_msg.service);
263 sendnak(&in_msg, &rej_msg, errstr);
268 if((servp->flags & IS_INTERNAL) != 0) {
269 cmd = stralloc(servp->name);
271 base = newstralloc(base, servp->name);
272 cmd = newvstralloc(cmd, libexecdir, "/", base, versionsuffix(), NULL);
274 if(access(cmd, X_OK) == -1) {
275 dbprintf(("%s: execute access to \"%s\" denied\n",
276 debug_prefix_time(NULL), cmd));
277 errstr = newvstralloc(errstr,
278 "service ", base, " unavailable",
281 sendnak(&in_msg, &rej_msg, errstr);
288 /* everything looks ok initially, send ACK */
290 sendack(&in_msg, &out_msg);
293 * handle security check: this could take a long time, so it is
294 * done after the initial ack.
297 #if defined(KRB4_SECURITY)
299 * we need to be root to access the srvtab file, but only if we started
304 #endif /* KRB4_SECURITY */
307 if(!(servp->flags & NO_AUTH)
308 && !security_ok(&in_msg.peer, in_msg.security, in_msg.cksum, &errstr)) {
309 /* XXX log on authlog? */
310 setup_rep(&in_msg, &out_msg, 0);
311 ap_snprintf(out_msg.dgram.cur,
312 sizeof(out_msg.dgram.data)-out_msg.dgram.len,
313 "ERROR %s\n", errstr);
314 out_msg.dgram.len = strlen(out_msg.dgram.data);
318 #if defined(KRB4_SECURITY) && defined(FORCE_USERID)
321 * we held on to a root uid earlier for accessing files; since we're
322 * done doing anything requiring root, we can completely give it up.
326 if(client_uid == (uid_t) -1) {
327 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
329 initgroups(CLIENT_LOGIN, client_gid);
334 #endif /* KRB4_SECURITY && FORCE_USERID */
336 dbprintf(("%s: running service \"%s\"\n", debug_prefix_time(NULL), cmd));
338 if(strcmp(servp->name, "noop") == 0) {
339 ap_snprintf(number, sizeof(number), "%ld", (long)getpid());
340 noop_file = vstralloc(AMANDA_TMPDIR,
346 rep_pipe[0] = open(noop_file, O_RDWR|O_EXCL|O_CREAT);
347 if(rep_pipe[0] < 0) {
348 error("cannot open \"%s\": %s", noop_file, strerror(errno));
350 (void)unlink(noop_file);
351 s = vstralloc("OPTIONS features=", our_feature_string, ";\n", NULL);
353 if(fullwrite(rep_pipe[0], s, s_len) != s_len) {
354 error("cannot write %d bytes to %s", s_len, noop_file);
358 (void)lseek(rep_pipe[0], (off_t)0, SEEK_SET);
360 if(strcmp(servp->name, "sendsize") == 0) {
361 if(strncmp(in_msg.dgram.cur,"OPTIONS ",8) == 0) {
362 g_option_t *g_options;
363 char *option_str, *p;
365 option_str = stralloc(in_msg.dgram.cur+8);
366 p = strchr(option_str,'\n');
369 g_options = parse_g_options(option_str, 0);
370 if(am_has_feature(g_options->features, fe_partial_estimate)) {
371 send_partial_reply = 1;
376 if(pipe(req_pipe) == -1 || pipe(rep_pipe) == -1)
377 error("pipe: %s", strerror(errno));
379 /* spawn first child to handle the request */
382 case -1: error("could not fork for %s: %s", cmd, strerror(errno));
384 default: /* parent */
393 dup2(req_pipe[0], 0);
394 dup2(rep_pipe[1], 1);
396 /* modification by BIS@BBN 4/25/2003:
397 * close these file descriptors BEFORE doing pipe magic
398 * for transferring session key; inside transfer_session_key
399 * is a dup2 to move a pipe to KEY_PIPE, which collided
400 * with req_pipe[0]; when req_pipe[0] was closed after the
401 * call to transfer_session_key, then KEY_PIPE ended up
407 transfer_session_key();
412 execle(cmd, cmd, NULL, safe_env());
413 error("could not exec %s: %s", cmd, strerror(errno));
420 /* spawn second child to handle writing the packet to the first child */
423 case -1: error("could not fork for %s: %s", cmd, strerror(errno));
425 default: /* parent */
432 reqlen = strlen(in_msg.dgram.cur);
433 if((rc = fullwrite(req_pipe[1], in_msg.dgram.cur, reqlen)) != reqlen) {
435 error("write to child pipe: %s", strerror(errno));
437 error("write to child pipe: %d instead of %d", rc, reqlen);
447 setup_rep(&in_msg, &out_msg, 0);
448 if(send_partial_reply) {
449 setup_rep(&in_msg, &out_pmsg, 1);
452 add_mutual_authenticator(&out_msg.dgram);
453 add_mutual_authenticator(&out_pmsg.dgram);
459 FD_SET(rep_pipe[0], &insock);
461 if((servp->flags & IS_INTERNAL) != 0) {
465 n = select(rep_pipe[0] + 1,
466 (SELECT_ARG_TYPE *)&insock,
472 error("select failed: %s", strerror(errno));
475 if(FD_ISSET(rep_pipe[0], &insock)) {
476 if(dglen >= MAX_DGRAM) {
477 error("more than %d bytes received from child", MAX_DGRAM);
479 rc = read(rep_pipe[0], out_msg.dgram.cur+dglen, MAX_DGRAM-dglen);
482 error("reading response pipe: %s", strerror(errno));
487 if(send_partial_reply) {
488 strncpy(out_pmsg.dgram.cur+dglen, out_msg.dgram.cur+dglen, rc);
489 out_pmsg.dgram.len += rc;
490 out_pmsg.dgram.data[out_pmsg.dgram.len] = '\0';
491 dbprintf(("%s: sending PREP packet:\n----\n%s----\n\n",
492 debug_prefix_time(NULL), out_pmsg.dgram.data));
493 dgram_send_addr(in_msg.peer, &out_pmsg.dgram);
498 if(!FD_ISSET(0,&insock))
501 if((n = dgram_recv(&dup_msg.dgram, RECV_TIMEOUT, &dup_msg.peer)) <= 0) {
509 error("error receiving message: %s", s);
513 * Under normal conditions, the master will resend the REQ packet
514 * to be sure we are still alive. It expects an ACK back right away.
516 * XXX- Arguably we should parse and security check the new packet,
517 * only sending an ACK if it passes and the request is identical to
518 * the original one. However, that's too much work for now. :-)
520 * It should suffice to ACK whenever the sender is identical.
522 dbprintf(("%s: got packet:\n----\n%s----\n\n",
523 debug_prefix_time(NULL), dup_msg.dgram.data));
524 parse_pkt_header(&dup_msg);
525 if(dup_msg.peer.sin_addr.s_addr == in_msg.peer.sin_addr.s_addr &&
526 dup_msg.peer.sin_port == in_msg.peer.sin_port) {
527 if(dup_msg.type == P_REQ) {
528 dbprintf(("%s: received dup P_REQ packet, ACKing it\n",
529 debug_prefix_time(NULL)));
530 sendack(&in_msg, &rej_msg);
533 dbprintf(("%s: it is not a P_REQ, ignoring it\n",
534 debug_prefix_time(NULL)));
538 dbprintf(("%s: received other packet, NAKing it\n",
539 debug_prefix_time(NULL)));
540 dbprintf((" addr: peer %s dup %s, port: peer %d dup %d\n",
541 inet_ntoa(in_msg.peer.sin_addr),
542 inet_ntoa(dup_msg.peer.sin_addr),
543 (int)ntohs(in_msg.peer.sin_port),
544 (int)ntohs(dup_msg.peer.sin_port)));
545 /* XXX dup_msg filled in? */
546 sendnak(&dup_msg, &rej_msg, "amandad busy");
551 /* XXX reap child? log if non-zero status? don't respond if non zero? */
552 /* setup header for out_msg */
554 out_msg.dgram.len += dglen;
555 out_msg.dgram.data[out_msg.dgram.len] = '\0';
562 while(retry_count < max_retry_count) {
564 dbprintf(("%s: sending REP packet:\n----\n%s----\n\n",
565 debug_prefix_time(NULL), out_msg.dgram.data));
566 dgram_send_addr(in_msg.peer, &out_msg.dgram);
567 if((n = dgram_recv(&dup_msg.dgram, ack_timeout, &dup_msg.peer)) <= 0) {
576 /* timed out or error, try again */
579 dbprintf(("%s: waiting for ack: %s", debug_prefix_time(NULL), s));
580 if(retry_count < max_retry_count)
581 dbprintf((", retrying\n"));
583 dbprintf((", giving up!\n"));
587 dbprintf(("%s: got packet:\n----\n%s----\n\n",
588 debug_prefix_time(NULL), dup_msg.dgram.data));
589 parse_pkt_header(&dup_msg);
592 if(dup_msg.peer.sin_addr.s_addr == in_msg.peer.sin_addr.s_addr &&
593 dup_msg.peer.sin_port == in_msg.peer.sin_port) {
594 if(dup_msg.type == P_ACK)
597 dbprintf(("%s: it is not an ack\n", debug_prefix_time(NULL)));
600 dbprintf(("%s: weird, it is not a proper ack\n",
601 debug_prefix_time(NULL)));
602 dbprintf((" addr: peer %s dup %s, port: peer %d dup %d\n",
603 inet_ntoa(in_msg.peer.sin_addr),
604 inet_ntoa(dup_msg.peer.sin_addr),
605 (int)ntohs(in_msg.peer.sin_port),
606 (int)ntohs(dup_msg.peer.sin_port)));
609 /* XXX log if retry count exceeded */
613 amfree(our_feature_string);
614 am_release_feature_set(our_features);
616 malloc_size_2 = malloc_inuse(&malloc_hist_2);
618 if(malloc_size_1 != malloc_size_2) {
619 #if defined(USE_DBMALLOC)
620 malloc_list(dbfd(), malloc_hist_1, malloc_hist_2);
631 void sendack(hdr, msg)
635 /* XXX this isn't very safe either: handle could be bogus */
636 ap_snprintf(msg->dgram.data, sizeof(msg->dgram.data),
637 "Amanda %d.%d ACK HANDLE %s SEQ %d\n",
638 VERSION_MAJOR, VERSION_MINOR,
639 hdr->handle ? hdr->handle : "",
641 msg->dgram.len = strlen(msg->dgram.data);
642 dbprintf(("%s: sending ack:\n----\n%s----\n\n",
643 debug_prefix_time(NULL), msg->dgram.data));
644 dgram_send_addr(hdr->peer, &msg->dgram);
647 void sendnak(hdr, msg, str)
652 /* XXX this isn't very safe either: handle could be bogus */
653 ap_snprintf(msg->dgram.data, sizeof(msg->dgram.data),
654 "Amanda %d.%d NAK HANDLE %s SEQ %d\nERROR %s\n",
655 VERSION_MAJOR, VERSION_MINOR,
656 hdr->handle ? hdr->handle : "",
657 hdr->sequence, str ? str : "UNKNOWN");
659 msg->dgram.len = strlen(msg->dgram.data);
660 dbprintf(("%s: sending nack:\n----\n%s----\n\n",
661 debug_prefix_time(NULL), msg->dgram.data));
662 dgram_send_addr(hdr->peer, &msg->dgram);
665 void setup_rep(hdr, msg, partial_rep)
670 /* XXX this isn't very safe either: handle could be bogus */
671 ap_snprintf(msg->dgram.data, sizeof(msg->dgram.data),
672 "Amanda %d.%d %s HANDLE %s SEQ %d\n",
673 VERSION_MAJOR, VERSION_MINOR,
674 partial_rep == 0 ? "REP" : "PREP",
675 hdr->handle ? hdr->handle : "",
678 msg->dgram.len = strlen(msg->dgram.data);
679 msg->dgram.cur = msg->dgram.data + msg->dgram.len;
690 if(isupper((int)*s)) *s = tolower(*s);