X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Fbsd-security.c;h=2d4304e64212f9909e13316c8697e2f2da33fcfb;hb=c4f19a4c1ce2b200d4e18c880257a4389bd5368c;hp=60e63d3eecf49868d0f494838892fe2a8c15bfc4;hpb=eefb15c5c04acb3c75f0c704ea664feb1bbae75c;p=debian%2Famanda diff --git a/common-src/bsd-security.c b/common-src/bsd-security.c index 60e63d3..2d4304e 100644 --- a/common-src/bsd-security.c +++ b/common-src/bsd-security.c @@ -24,7 +24,7 @@ * file named AUTHORS, in the root directory of this distribution. */ /* - * $Id: bsd-security.c,v 1.54 2006/03/09 16:51:41 martinea Exp $ + * $Id: bsd-security.c,v 1.75 2006/07/19 17:41:14 martinea Exp $ * * "BSD" security module */ @@ -36,159 +36,35 @@ #include "event.h" #include "packet.h" #include "security.h" +#include "security-util.h" +#include "sockaddr-util.h" #include "stream.h" -#include "version.h" - -/*#define BSD_DEBUG*/ - -#ifdef BSD_DEBUG -#define bsdprintf(x) dbprintf(x) -#else -#define bsdprintf(x) -#endif #ifndef SO_RCVBUF #undef DUMPER_SOCKET_BUFFERING #endif -#ifdef BSD_SECURITY /* { */ - -/* - * Change the following from #undef to #define to cause detailed logging - * of the security steps, e.g. into /tmp/amanda/amandad*debug. - */ -#undef SHOW_SECURITY_DETAIL - -#if defined(TEST) /* { */ -#define SHOW_SECURITY_DETAIL -#undef bsdprintf -#define bsdprintf(p) printf p -#endif /* } */ - -/* - * This is the private handle data - */ -struct bsd_handle { - /* - * This must be first. Instances of bsd_handle will be cast to - * security_handle_t's. - */ - security_handle_t sech; - - /* - * protocol handle for this request. Each request gets its own - * handle, so we differentiate packets for them with a "handle" header - * in each packet. - * - */ - int event_id; /* unique event_id */ - char *proto_handle; - - /* - * sequence number. - */ - int sequence; - - /* - * The remote host we're transmitting to - */ - char hostname[256]; - struct sockaddr_in peer; - - /* - * Function to call when recvpkt detects new incoming data for this - * handle - */ - void (*fn) P((void *, pkt_t *, security_status_t)); - - /* - * Argument for previous function - */ - void *arg; - - /* - * read (EV_WAIT) handle for a recv - */ - event_handle_t *ev_read; - - /* - * Timeout handle for a recv - */ - event_handle_t *ev_timeout; - - struct bsd_handle *prev, *next; -}; - -struct bsd_handle *bh_first=NULL, *bh_last=NULL; - -/* - * This is the internal security_stream data - */ -struct bsd_stream { - /* - * This must be first, because instances of this will be cast - * to security_stream_t's outside of this module. - */ - security_stream_t secstr; - - /* - * This is the file descriptor which we will do io on - */ - int fd; - - /* - * This is the file descriptor which we will listen for incoming - * connections on (for streams which receive connections) - */ - int socket; - - /* - * This is the local port this stream is bound to - */ - int port; - - /* - * This is the read event handle for this data stream - */ - event_handle_t *ev_read; - - /* - * This is the function and argument that is called when this stream - * is readable. It is passed a buffer of data read. - */ - void (*fn) P((void *, void *, ssize_t)); - void *arg; - - /* - * This is the buffer that we read data into that will be passed - * to the read callback. - */ - char databuf[NETWORK_BLOCK_BYTES]; -}; - /* * Interface functions */ -static void bsd_connect P((const char *, - char *(*)(char *, void *), - void (*)(void *, security_handle_t *, security_status_t), void *)); -static void bsd_accept P((int, int, void (*)(security_handle_t *, pkt_t *))); -static void bsd_close P((void *)); -static int bsd_sendpkt P((void *, pkt_t *)); -static void bsd_recvpkt P((void *, - void (*)(void *, pkt_t *, security_status_t), void *, int)); -static void bsd_recvpkt_cancel P((void *)); - -static void *bsd_stream_server P((void *)); -static int bsd_stream_accept P((void *)); -static void *bsd_stream_client P((void *, int)); -static void bsd_stream_close P((void *)); -static int bsd_stream_auth P((void *)); -static int bsd_stream_id P((void *)); -static int bsd_stream_write P((void *, const void *, size_t)); -static void bsd_stream_read P((void *, void (*)(void *, void *, ssize_t), - void *)); -static void bsd_stream_read_cancel P((void *)); +static void bsd_connect(const char *, char *(*)(char *, void *), + void (*)(void *, security_handle_t *, security_status_t), + void *, void *); +static void bsd_accept(const struct security_driver *, + char *(*)(char *, void *), + int, int, + void (*)(security_handle_t *, pkt_t *), + void *); +static void bsd_close(void *); +static void * bsd_stream_server(void *); +static int bsd_stream_accept(void *); +static void * bsd_stream_client(void *, int); +static void bsd_stream_close(void *); +static int bsd_stream_auth(void *); +static int bsd_stream_id(void *); +static void bsd_stream_read(void *, void (*)(void *, void *, ssize_t), void *); +static ssize_t bsd_stream_read_sync(void *, void **); +static void bsd_stream_read_cancel(void *); /* * This is our interface to the outside world @@ -197,1206 +73,348 @@ const security_driver_t bsd_security_driver = { "BSD", bsd_connect, bsd_accept, + sec_get_authenticated_peer_name_hostname, bsd_close, - bsd_sendpkt, - bsd_recvpkt, - bsd_recvpkt_cancel, + udpbsd_sendpkt, + udp_recvpkt, + udp_recvpkt_cancel, bsd_stream_server, bsd_stream_accept, bsd_stream_client, bsd_stream_close, bsd_stream_auth, bsd_stream_id, - bsd_stream_write, + tcp_stream_write, bsd_stream_read, + bsd_stream_read_sync, bsd_stream_read_cancel, + sec_close_connection_none, + NULL, + NULL }; /* * This is data local to the datagram socket. We have one datagram * per process, so it is global. */ -static struct { - dgram_t dgram; /* datagram to read/write from */ - struct sockaddr_in peer; /* who sent it to us */ - pkt_t pkt; /* parsed form of dgram */ - char *handle; /* handle from recvd packet */ - int sequence; /* seq no of packet */ - event_handle_t *ev_read; /* read event handle from dgram */ - int refcnt; /* number of handles blocked for reading */ -} netfd; +static udp_handle_t netfd4; +static udp_handle_t netfd6; +static int not_init4 = 1; +static int not_init6 = 1; /* generate new handles from here */ static int newhandle = 0; -static int newevent = 0; - -/* - * We register one event handler for our network fd which takes - * care of all of our async requests. When all async requests - * have either been satisfied or cancelled, we unregister our - * network event handler. - */ -#define netfd_addref() do { \ - if (netfd.refcnt++ == 0) { \ - assert(netfd.ev_read == NULL); \ - netfd.ev_read = event_register(netfd.dgram.socket, EV_READFD, \ - netfd_read_callback, NULL); \ - } \ - assert(netfd.refcnt > 0); \ -} while (0) - -/* - * If this is the last request to be removed, then remove the - * reader event from the netfd. - */ -#define netfd_delref() do { \ - assert(netfd.refcnt > 0); \ - if (--netfd.refcnt == 0) { \ - assert(netfd.ev_read != NULL); \ - event_release(netfd.ev_read); \ - netfd.ev_read = NULL; \ - } \ -} while (0) - -/* - * This is the function and argument that is called when new requests - * arrive on the netfd. - */ -static void (*accept_fn) P((security_handle_t *, pkt_t *)); /* * These are the internal helper functions */ -static char *check_user P((struct bsd_handle *, const char *)); -static int inithandle P((struct bsd_handle *, struct hostent *, - int, char *, int)); -static const char *pkthdr2str P((const struct bsd_handle *, const pkt_t *)); -static int str2pkthdr P((void)); -static void netfd_read_callback P((void *)); -static void recvpkt_callback P((void *)); -static void recvpkt_timeout P((void *)); -static int recv_security_ok P((struct bsd_handle *)); -static void stream_read_callback P((void *)); - - -#if defined(SHOW_SECURITY_DETAIL) /* { */ -/* - * Display stat() information about a file. - */ -void show_stat_info(a, b) - char *a, *b; -{ - char *name = vstralloc(a, b, NULL); - struct stat sbuf; - struct passwd *pwptr; - char *owner; - struct group *grptr; - char *group; - - if (stat(name, &sbuf) != 0) { - bsdprintf(("%s: cannot stat %s: %s\n", - debug_prefix_time(NULL), name, strerror(errno))); - amfree(name); - return; - } - if ((pwptr = getpwuid(sbuf.st_uid)) == NULL) { - owner = alloc(NUM_STR_SIZE + 1); - snprintf(owner, NUM_STR_SIZE, "%ld", (long)sbuf.st_uid); - } else { - owner = stralloc(pwptr->pw_name); - } - if ((grptr = getgrgid(sbuf.st_gid)) == NULL) { - group = alloc(NUM_STR_SIZE + 1); - snprintf(owner, NUM_STR_SIZE, "%ld", (long)sbuf.st_gid); - } else { - group = stralloc(grptr->gr_name); - } - bsdprintf(("%s: processing file: %s\n", debug_prefix(NULL), name)); - bsdprintf(("%s: owner=%s group=%s mode=%03o\n", - debug_prefix(NULL), owner, group, (int) (sbuf.st_mode & 0777))); - amfree(name); - amfree(owner); - amfree(group); -} -#endif /* } */ +static void stream_read_callback(void *); +static void stream_read_sync_callback(void *); /* * Setup and return a handle outgoing to a client */ + static void -bsd_connect(hostname, conf_fn, fn, arg) - const char *hostname; - char *(*conf_fn) P((char *, void *)); - void (*fn) P((void *, security_handle_t *, security_status_t)); - void *arg; -{ - struct bsd_handle *bh; - struct servent *se; - struct hostent *he; - int port; +bsd_connect( + const char * hostname, + char * (*conf_fn)(char *, void *), + void (*fn)(void *, security_handle_t *, security_status_t), + void * arg, + void * datap) +{ + struct sec_handle *bh; + in_port_t port = 0; struct timeval sequence_time; - amanda_timezone dontcare; int sequence; char *handle; + int result; + struct addrinfo *res, *res_addr; + char *canonname; + int result_bind; + char *service; assert(hostname != NULL); - bh = alloc(sizeof(*bh)); - bh->proto_handle=NULL; - security_handleinit(&bh->sech, &bsd_security_driver); - - /* - * Only init the socket once - */ - if (netfd.dgram.socket == 0) { - uid_t euid; - dgram_zero(&netfd.dgram); - - euid = geteuid(); - seteuid(0); - dgram_bind(&netfd.dgram, &port); - seteuid(euid); - /* - * We must have a reserved port. Bomb if we didn't get one. - */ - if (port >= IPPORT_RESERVED) { - security_seterror(&bh->sech, - "unable to bind to a reserved port (got port %d)", - port); - (*fn)(arg, &bh->sech, S_ERROR); - return; - } - } - - if ((he = gethostbyname(hostname)) == NULL) { - security_seterror(&bh->sech, - "%s: could not resolve hostname", hostname); - (*fn)(arg, &bh->sech, S_ERROR); - return; - } - if ((se = getservbyname(AMANDA_SERVICE_NAME, "udp")) == NULL) - port = htons(AMANDA_SERVICE_DEFAULT); - else - port = se->s_port; - amanda_gettimeofday(&sequence_time, &dontcare); - sequence = (int)sequence_time.tv_sec ^ (int)sequence_time.tv_usec; - handle=malloc(15); - snprintf(handle,14,"000-%08x", newhandle++); - if (inithandle(bh, he, port, handle, sequence) < 0) - (*fn)(arg, &bh->sech, S_ERROR); - else - (*fn)(arg, &bh->sech, S_OK); -} - -/* - * Setup to accept new incoming connections - */ -static void -bsd_accept(in, out, fn) - int in, out; - void (*fn) P((security_handle_t *, pkt_t *)); -{ - - assert(in >= 0 && out >= 0); - assert(fn != NULL); - - /* - * We assume in and out point to the same socket, and just use - * in. - */ - dgram_socket(&netfd.dgram, in); - - /* - * Assign the function and return. When they call recvpkt later, - * the recvpkt callback will call this function when it discovers - * new incoming connections - */ - accept_fn = fn; - - netfd_addref(); -} - -/* - * Given a hostname and a port, setup a bsd_handle - */ -static int -inithandle(bh, he, port, handle, sequence) - struct bsd_handle *bh; - struct hostent *he; - int port; - char *handle; - int sequence; -{ - int i; - - assert(he != NULL); - assert(port > 0); - - /* - * Save the hostname and port info - */ - strncpy(bh->hostname, he->h_name, sizeof(bh->hostname) - 1); - bh->hostname[sizeof(bh->hostname) - 1] = '\0'; - bh->peer.sin_addr = *(struct in_addr *)he->h_addr; - bh->peer.sin_port = port; - bh->peer.sin_family = AF_INET; - - bh->prev = bh_last; - if(bh_last) {bh->prev->next = bh;} - if(!bh_first) {bh_first = bh;} - bh->next = NULL; - bh_last = bh; - - /* - * Do a forward lookup of the hostname. This is unnecessary if we - * are initiating the connection, but is very serious if we are - * receiving. We want to make sure the hostname - * resolves back to the remote ip for security reasons. - */ - if ((he = gethostbyname(bh->hostname)) == NULL) { - security_seterror(&bh->sech, - "%s: could not resolve hostname", bh->hostname); - return (-1); - } - /* - * Make sure the hostname matches. This should always work. - */ - if (strncasecmp(bh->hostname, he->h_name, strlen(bh->hostname)) != 0) { - security_seterror(&bh->sech, - "%s: did not resolve to %s", bh->hostname, bh->hostname); - return (-1); - } - - /* - * Now look for a matching ip address. - */ - for (i = 0; he->h_addr_list[i] != NULL; i++) { - if (memcmp(&bh->peer.sin_addr, he->h_addr_list[i], - sizeof(struct in_addr)) == 0) { - break; - } - } - - /* - * If we didn't find it, try the aliases. This is a workaround for - * Solaris if DNS goes over NIS. - */ - if (he->h_addr_list[i] == NULL) { - const char *ipstr = inet_ntoa(bh->peer.sin_addr); - for (i = 0; he->h_aliases[i] != NULL; i++) { - if (strcmp(he->h_aliases[i], ipstr) == 0) - break; - } - /* - * No aliases either. Failure. Someone is fooling with us or - * DNS is messed up. - */ - if (he->h_aliases[i] == NULL) { - security_seterror(&bh->sech, - "DNS check failed: no matching ip address for %s", - bh->hostname); - return (-1); - } - } - - bh->sequence = sequence; - bh->event_id = newevent++; - bh->proto_handle = handle; - bh->fn = NULL; - bh->arg = NULL; - bh->ev_read = NULL; - bh->ev_timeout = NULL; - - bsdprintf(("%s: adding handle '%s'\n", - debug_prefix_time(NULL), bh->proto_handle)); - - return(0); -} - -/* - * Frees a handle allocated by the above - */ -static void -bsd_close(cookie) - void *cookie; -{ - struct bsd_handle *bh = cookie; - - if(bh->proto_handle == NULL) { - return; - } - - bsdprintf(("%s: close handle '%s'\n", - debug_prefix_time(NULL), bh->proto_handle)); - - bsd_recvpkt_cancel(bh); - if(bh->next) { - bh->next->prev = bh->prev; - } - else { - bh_last = bh->prev; - } - if(bh->prev) { - bh->prev->next = bh->next; - } - else { - bh_first = bh->next; - } - - amfree(bh); -} - -/* - * Transmit a packet. Add security information first. - */ -static int -bsd_sendpkt(cookie, pkt) - void *cookie; - pkt_t *pkt; -{ - struct bsd_handle *bh = cookie; - struct passwd *pwd; - - assert(bh != NULL); - assert(pkt != NULL); - - /* - * Initialize this datagram, and add the header - */ - dgram_zero(&netfd.dgram); - dgram_cat(&netfd.dgram, pkthdr2str(bh, pkt)); - - /* - * Add the security info. This depends on which kind of packet we're - * sending. - */ - switch (pkt->type) { - case P_REQ: - /* - * Requests get sent with our username in the body - */ - if ((pwd = getpwuid(geteuid())) == NULL) { - security_seterror(&bh->sech, - "can't get login name for my uid %ld", (long)getuid()); - return (-1); - } - dgram_cat(&netfd.dgram, "SECURITY USER %s\n", pwd->pw_name); - break; - - default: - break; - } - - /* - * Add the body, and send it - */ - dgram_cat(&netfd.dgram, pkt->body); - if (dgram_send_addr(bh->peer, &netfd.dgram) != 0) { - security_seterror(&bh->sech, - "send %s to %s failed: %s", pkt_type2str(pkt->type), - bh->hostname, strerror(errno)); - return (-1); - } - return (0); -} - -/* - * Set up to receive a packet asynchronously, and call back when it has - * been read. - */ -static void -bsd_recvpkt(cookie, fn, arg, timeout) - void *cookie, *arg; - void (*fn) P((void *, pkt_t *, security_status_t)); - int timeout; -{ - struct bsd_handle *bh = cookie; - - assert(bh != NULL); - assert(fn != NULL); - - - /* - * Subsequent recvpkt calls override previous ones - */ - if (bh->ev_read == NULL) { - netfd_addref(); - bh->ev_read = event_register(bh->event_id, EV_WAIT, - recvpkt_callback, bh); - } - if (bh->ev_timeout != NULL) - event_release(bh->ev_timeout); - if (timeout < 0) - bh->ev_timeout = NULL; - else - bh->ev_timeout = event_register(timeout, EV_TIME, recvpkt_timeout, bh); - bh->fn = fn; - bh->arg = arg; -} - -/* - * Remove a async receive request on this handle from the queue. - * If it is the last one to be removed, then remove the event - * handler for our network fd - */ -static void -bsd_recvpkt_cancel(cookie) - void *cookie; -{ - struct bsd_handle *bh = cookie; - - assert(bh != NULL); + (void)conf_fn; /* Quiet unused parameter warning */ + (void)datap; /* Quiet unused parameter warning */ - if (bh->ev_read != NULL) { - netfd_delref(); - event_release(bh->ev_read); - bh->ev_read = NULL; - } - - if (bh->ev_timeout != NULL) { - event_release(bh->ev_timeout); - bh->ev_timeout = NULL; - } -} - -/* - * Callback for received packets. This is the function bsd_recvpkt - * registers with the event handler. It is called when the event handler - * realizes that data is waiting to be read on the network socket. - */ -static void -netfd_read_callback(cookie) - void *cookie; -{ - struct bsd_handle *bh; - struct hostent *he; - int a; - - assert(cookie == NULL); - -#ifndef TEST /* { */ - /* - * Receive the packet. - */ - dgram_zero(&netfd.dgram); - if (dgram_recv(&netfd.dgram, 0, &netfd.peer) < 0) - return; -#endif /* !TEST */ /* } */ - - /* - * Parse the packet. - */ - if (str2pkthdr() < 0) - return; - - /* - * If there are events waiting on this handle, we're done - */ - bh = bh_first; - while(bh != NULL && (strcmp(bh->proto_handle, netfd.handle) != 0 || - bh->sequence != netfd.sequence || - bh->peer.sin_addr.s_addr != netfd.peer.sin_addr.s_addr || - bh->peer.sin_port != netfd.peer.sin_port)) { - bh = bh->next; - } - if (bh && event_wakeup(bh->event_id) > 0) - return; - - /* - * If we didn't find a handle, then check for a new incoming packet. - * If no accept handler was setup, then just return. - */ - if (accept_fn == NULL) - return; - - he = gethostbyaddr((void *)&netfd.peer.sin_addr, - (int)sizeof(netfd.peer.sin_addr), AF_INET); - if (he == NULL) - return; - bh = alloc(sizeof(*bh)); + bh = g_new0(struct sec_handle, 1); bh->proto_handle=NULL; security_handleinit(&bh->sech, &bsd_security_driver); - a = inithandle(bh, - he, - netfd.peer.sin_port, - netfd.handle, - netfd.sequence); - if (a < 0) { - if(bh->next) { - bh->next->prev = bh->prev; - } - else { - bh_last = bh->prev; - } - if(bh->prev) { - bh->prev->next = bh->next; - } - else { - bh_first = bh->prev; - } - - bsdprintf(("%s: closeX handle '%s'\n", - debug_prefix_time(NULL), bh->proto_handle)); - - amfree(bh); - return; - } - /* - * Check the security of the packet. If it is bad, then pass NULL - * to the accept function instead of a packet. - */ - if (recv_security_ok(bh) < 0) - (*accept_fn)(&bh->sech, NULL); - else - (*accept_fn)(&bh->sech, &netfd.pkt); -} - -/* - * This is called when a handle is woken up because data read off of the - * net is for it. - */ -static void -recvpkt_callback(cookie) - void *cookie; -{ - struct bsd_handle *bh = cookie; - void (*fn) P((void *, pkt_t *, security_status_t)); - void *arg; - - assert(bh != NULL); - bsdprintf(("%s: receive handle '%s' netfd '%s'\n", - debug_prefix_time(NULL), bh->proto_handle,netfd.handle)); - - if(strcmp(bh->proto_handle,netfd.handle) != 0) assert(1); - - /* if it didn't come from the same host/port, forget it */ - if (memcmp(&bh->peer.sin_addr, &netfd.peer.sin_addr, - sizeof(netfd.peer.sin_addr)) != 0 || - bh->peer.sin_port != netfd.peer.sin_port) { - netfd.handle = NULL; - return; - } - - /* - * We need to cancel the recvpkt request before calling the callback - * because the callback may reschedule us. - */ - fn = bh->fn; - arg = bh->arg; - bsd_recvpkt_cancel(bh); - - /* - * Check the security of the packet. If it is bad, then pass NULL - * to the packet handling function instead of a packet. - */ - if (recv_security_ok(bh) < 0) - (*fn)(arg, NULL, S_ERROR); - else - (*fn)(arg, &netfd.pkt, S_OK); -} - -/* - * This is called when a handle times out before receiving a packet. - */ -static void -recvpkt_timeout(cookie) - void *cookie; -{ - struct bsd_handle *bh = cookie; - void (*fn) P((void *, pkt_t *, security_status_t)); - void *arg; - - assert(bh != NULL); - - assert(bh->ev_timeout != NULL); - fn = bh->fn; - arg = bh->arg; - bsd_recvpkt_cancel(bh); - (*fn)(arg, NULL, S_TIMEOUT); - -} - -/* - * Check the security of a received packet. Returns negative on security - * violation, or returns 0 if ok. Removes the security info from the pkt_t. - */ -static int -recv_security_ok(bh) - struct bsd_handle *bh; -{ - char *tok, *security, *body, *result; - pkt_t *pkt = &netfd.pkt; - - /* - * Set this preempively before we mangle the body. - */ - security_seterror(&bh->sech, - "bad SECURITY line: '%s'", pkt->body); - /* - * Now, find the SECURITY line in the body, and parse it out - * into an argv. - */ - if (strncmp(pkt->body, "SECURITY", sizeof("SECURITY") - 1) == 0) { - tok = strtok(pkt->body, " "); - assert(strcmp(tok, "SECURITY") == 0); - /* security info goes until the newline */ - security = strtok(NULL, "\n"); - body = strtok(NULL, ""); - /* - * If the body is f-ked, then try to recover - */ - if (body == NULL) { - if (security != NULL) - body = security + strlen(security) + 2; - else - body = pkt->body; - } - } else { - security = NULL; - body = pkt->body; + result = resolve_hostname(hostname, SOCK_DGRAM, &res, &canonname); + if(result != 0) { + dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result)); + security_seterror(&bh->sech, _("resolve_hostname(%s): %s\n"), hostname, + gai_strerror(result)); + (*fn)(arg, &bh->sech, S_ERROR); + return; + } + if (canonname == NULL) { + dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname); + security_seterror(&bh->sech, + _("resolve_hostname(%s) did not return a canonical name\n"), hostname); + (*fn)(arg, &bh->sech, S_ERROR); + if (res) freeaddrinfo(res); + return; + } + if (res == NULL) { + dbprintf(_("resolve_hostname(%s): no results\n"), hostname); + security_seterror(&bh->sech, + _("resolve_hostname(%s): no results\n"), hostname); + (*fn)(arg, &bh->sech, S_ERROR); + amfree(canonname); + return; } - /* - * We need to do different things depending on which type of packet - * this is. - */ - switch (pkt->type) { - case P_REQ: - /* - * Request packets must come from a reserved port - */ - if (ntohs(bh->peer.sin_port) >= IPPORT_RESERVED) { - security_seterror(&bh->sech, - "host %s: port %d not secure", bh->hostname, - ntohs(bh->peer.sin_port)); - return (-1); + for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) { +#ifdef WORKING_IPV6 + /* IPv6 socket already bound */ + if (res_addr->ai_addr->sa_family == AF_INET6 && not_init6 == 0) { + break; } - /* - * Request packets contain a remote username. We need to check - * that we allow it in. - * - * They will look like: - * SECURITY USER [username] + * Only init the IPv6 socket once */ - - /* there must be some security info */ - if (security == NULL) { - security_seterror(&bh->sech, - "no bsd SECURITY for P_REQ"); - return (-1); + if (res_addr->ai_addr->sa_family == AF_INET6 && not_init6 == 1) { + dgram_zero(&netfd6.dgram); + + set_root_privs(1); + result_bind = dgram_bind(&netfd6.dgram, + res_addr->ai_addr->sa_family, &port); + set_root_privs(0); + if (result_bind != 0) { + continue; + } + netfd6.handle = NULL; + netfd6.pkt.body = NULL; + netfd6.recv_security_ok = &bsd_recv_security_ok; + netfd6.prefix_packet = &bsd_prefix_packet; + /* + * We must have a reserved port. Bomb if we didn't get one. + */ + if (port >= IPPORT_RESERVED) { + security_seterror(&bh->sech, + _("unable to bind to a reserved port (got port %u)"), + (unsigned int)port); + (*fn)(arg, &bh->sech, S_ERROR); + freeaddrinfo(res); + amfree(canonname); + return; + } + not_init6 = 0; + bh->udp = &netfd6; + break; } +#endif - /* second word must be USER */ - if ((tok = strtok(security, " ")) == NULL) - return (-1); /* default errmsg */ - if (strcmp(tok, "USER") != 0) { - security_seterror(&bh->sech, - "REQ SECURITY line parse error, expecting USER, got %s", tok); - return (-1); + /* IPv4 socket already bound */ + if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 0) { + break; } - /* the third word is the username */ - if ((tok = strtok(NULL, "")) == NULL) - return (-1); /* default errmsg */ - if ((result = check_user(bh, tok)) != NULL) { - security_seterror(&bh->sech, "%s", result); - amfree(result); - return (-1); + /* + * Only init the IPv4 socket once + */ + if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 1) { + dgram_zero(&netfd4.dgram); + + set_root_privs(1); + result_bind = dgram_bind(&netfd4.dgram, + res_addr->ai_addr->sa_family, &port); + set_root_privs(0); + if (result_bind != 0) { + continue; + } + netfd4.handle = NULL; + netfd4.pkt.body = NULL; + netfd4.recv_security_ok = &bsd_recv_security_ok; + netfd4.prefix_packet = &bsd_prefix_packet; + /* + * We must have a reserved port. Bomb if we didn't get one. + */ + if (port >= IPPORT_RESERVED) { + security_seterror(&bh->sech, + "unable to bind to a reserved port (got port %u)", + (unsigned int)port); + (*fn)(arg, &bh->sech, S_ERROR); + freeaddrinfo(res); + amfree(canonname); + return; + } + not_init4 = 0; + bh->udp = &netfd4; + break; } - - /* we're good to go */ - break; - default: - break; } - /* - * If there is security info at the front of the packet, we need to - * shift the rest of the data up and nuke it. - */ - if (body != pkt->body) - memmove(pkt->body, body, strlen(body) + 1); - return (0); -} - -static char * -check_user(bh, remoteuser) - struct bsd_handle *bh; - const char *remoteuser; -{ - struct passwd *pwd; - char *r; - char *result = NULL; - char *localuser = NULL; - - /* lookup our local user name */ - if ((pwd = getpwnam(CLIENT_LOGIN)) == NULL) { - return vstralloc("getpwnam(", CLIENT_LOGIN, ") fails", NULL); + if (res_addr == NULL) { + dbprintf(_("Can't bind a socket to connect to %s\n"), hostname); + security_seterror(&bh->sech, + _("Can't bind a socket to connect to %s\n"), hostname); + (*fn)(arg, &bh->sech, S_ERROR); + amfree(canonname); + return; } - /* - * Make a copy of the user name in case getpw* is called by - * any of the lower level routines. - */ - localuser = stralloc(pwd->pw_name); - -#ifndef USE_AMANDAHOSTS - r = check_user_ruserok(bh->hostname, pwd, remoteuser); -#else - r = check_user_amandahosts(bh->hostname, pwd, remoteuser); +#ifdef WORKING_IPV6 + if (res_addr->ai_addr->sa_family == AF_INET6) + bh->udp = &netfd6; + else #endif - if (r != NULL) { - result = vstralloc("access as ", localuser, " not allowed", - " from ", remoteuser, "@", bh->hostname, - ": ", r, - NULL); - amfree(r); - } - amfree(localuser); - return result; -} - -/* - * See if a remote user is allowed in. This version uses ruserok() - * and friends. - * - * Returns 0 on success, or negative on error. - */ -char * -check_user_ruserok(host, pwd, remoteuser) - const char *host; - struct passwd *pwd; - const char *remoteuser; -{ - int saved_stderr; - int fd[2]; - FILE *fError; - amwait_t exitcode; - pid_t ruserok_pid; - pid_t pid; - char *es; - char *result; - int ok; - char number[NUM_STR_SIZE]; - uid_t myuid = getuid(); + bh->udp = &netfd4; - /* - * note that some versions of ruserok (eg SunOS 3.2) look in - * "./.rhosts" rather than "~CLIENT_LOGIN/.rhosts", so we have to - * chdir ourselves. Sigh. - * - * And, believe it or not, some ruserok()'s try an initgroup just - * for the hell of it. Since we probably aren't root at this point - * it'll fail, and initgroup "helpfully" will blatt "Setgroups: Not owner" - * into our stderr output even though the initgroup failure is not a - * problem and is expected. Thanks a lot. Not. - */ - if (pipe(fd) != 0) { - return stralloc2("pipe() fails: ", strerror(errno)); - } - if ((ruserok_pid = fork()) < 0) { - return stralloc2("fork() fails: ", strerror(errno)); - } else if (ruserok_pid == 0) { - int ec; - - close(fd[0]); - fError = fdopen(fd[1], "w"); - /* pamper braindead ruserok's */ - if (chdir(pwd->pw_dir) != 0) { - fprintf(fError, "chdir(%s) failed: %s", - pwd->pw_dir, strerror(errno)); - fclose(fError); - exit(1); - } + auth_debug(1, _("Resolved hostname=%s\n"), canonname); -#if defined(SHOW_SECURITY_DETAIL) /* { */ - { - char *dir = stralloc(pwd->pw_dir); - - bsdprintf(("%s: calling ruserok(%s, %d, %s, %s)\n", - debug_prefix_time(NULL), - host, myuid == 0, remoteuser, pwd->pw_name)); - if (myuid == 0) { - bsdprintf(("%s: because you are running as root, ", - debug_prefix(NULL))); - bsdprintf(("/etc/hosts.equiv will not be used\n")); - } else { - show_stat_info("/etc/hosts.equiv", NULL); - } - show_stat_info(dir, "/.rhosts"); - amfree(dir); - } -#endif /* } */ - - saved_stderr = dup(2); - close(2); - if (open("/dev/null", O_RDWR) == -1) { - dbprintf(("Could not open /dev/null: %s\n", - strerror(errno))); - ec = 1; - } else { - ok = ruserok(host, myuid == 0, remoteuser, CLIENT_LOGIN); - if (ok < 0) { - ec = 1; - } else { - ec = 0; - } - } - (void)dup2(saved_stderr,2); - close(saved_stderr); - exit(ec); + if (conf_fn) { + service = conf_fn("client_port", datap); + if (!service || strlen(service) <= 1) + service = "amanda"; + } else { + service = "amanda"; } - close(fd[1]); - fError = fdopen(fd[0], "r"); - - result = NULL; - while ((es = agets(fError)) != NULL) { - if (result == NULL) { - result = stralloc(""); - } else { - strappend(result, ": "); - } - strappend(result, es); + port = find_port_for_service(service, "udp"); + if (port == 0) { + security_seterror(&bh->sech, _("%s/udp unknown protocol"), service); + (*fn)(arg, &bh->sech, S_ERROR); + amfree(canonname); + return; } - close(fd[0]); - while (1) { - if ((pid = wait(&exitcode)) == (pid_t) -1) { - if (errno == EINTR) { - continue; - } - amfree(result); - return stralloc2("ruserok wait failed: %s", strerror(errno)); - } - if (pid == ruserok_pid) { - break; - } - } - if (WIFSIGNALED(exitcode)) { - amfree(result); - snprintf(number, sizeof(number), "%d", WTERMSIG(exitcode)); - return stralloc2("ruserok child got signal ", number); + amanda_gettimeofday(&sequence_time); + sequence = (int)sequence_time.tv_sec ^ (int)sequence_time.tv_usec; + handle=alloc(15); + g_snprintf(handle, 14, "000-%08x", (unsigned)newhandle++); + if (udp_inithandle(bh->udp, bh, canonname, + (sockaddr_union *)res_addr->ai_addr, port, handle, sequence) < 0) { + (*fn)(arg, &bh->sech, S_ERROR); + amfree(bh->hostname); + amfree(bh); } - if (WEXITSTATUS(exitcode) == 0) { - amfree(result); - } else if (result == NULL) { - result = stralloc("ruserok failed"); + else { + (*fn)(arg, &bh->sech, S_OK); } + amfree(handle); + amfree(canonname); - return result; + freeaddrinfo(res); } /* - * Check to see if a user is allowed in. This version uses .amandahosts - * Returns -1 on failure, or 0 on success. + * Setup to accept new incoming connections */ -char * -check_user_amandahosts(host, pwd, remoteuser) - const char *host; - struct passwd *pwd; - const char *remoteuser; +static void +bsd_accept( + const struct security_driver * driver, + char *(*conf_fn)(char *, void *), + int in, + int out, + void (*fn)(security_handle_t *, pkt_t *), + void *datap) { - char *line = NULL; - char *filehost; - const char *fileuser; - char *ptmp = NULL; - char *result = NULL; - FILE *fp = NULL; - int found; struct stat sbuf; - char n1[NUM_STR_SIZE]; - char n2[NUM_STR_SIZE]; - int hostmatch; - int usermatch; - uid_t localuid; - char *localuser = NULL; - /* - * Save copies of what we need from the passwd structure in case - * any other code calls getpw*. - */ - localuid = pwd->pw_uid; - localuser = stralloc(pwd->pw_name); - - ptmp = stralloc2(pwd->pw_dir, "/.amandahosts"); -#if defined(SHOW_SECURITY_DETAIL) /* { */ - show_stat_info(ptmp, "");; -#endif /* } */ - if ((fp = fopen(ptmp, "r")) == NULL) { - result = vstralloc("cannot open ", ptmp, ": ", strerror(errno), NULL); - amfree(ptmp); - amfree(localuser); - return result; - } + assert(in >= 0 && out >= 0); + assert(fn != NULL); + + (void)out; /* Quiet unused parameter warning */ + (void)driver; /* Quiet unused parameter warning */ + (void)conf_fn; + (void)datap; /* - * Make sure the file is owned by the Amanda user and does not - * have any group/other access allowed. + * We assume in and out point to the same socket, and just use + * in. */ - if (fstat(fileno(fp), &sbuf) != 0) { - result = vstralloc("cannot fstat ", ptmp, ": ", strerror(errno), NULL); - goto common_exit; - } - if (sbuf.st_uid != localuid) { - snprintf(n1, sizeof(n1), "%ld", (long)sbuf.st_uid); - snprintf(n2, sizeof(n2), "%ld", (long)localuid); - result = vstralloc(ptmp, ": ", - "owned by id ", n1, - ", should be ", n2, - NULL); - goto common_exit; - } - if ((sbuf.st_mode & 077) != 0) { - result = stralloc2(ptmp, - ": incorrect permissions; file must be accessible only by its owner"); - goto common_exit; - } + dgram_socket(&netfd4.dgram, in); + dgram_socket(&netfd6.dgram, in); /* - * Now, scan the file for the host/user. + * Assign the function and return. When they call recvpkt later, + * the recvpkt callback will call this function when it discovers + * new incoming connections */ - found = 0; - while ((line = agets(fp)) != NULL) { -#if defined(SHOW_SECURITY_DETAIL) /* { */ - bsdprintf(("%s: processing line: <%s>\n", debug_prefix(NULL), line)); -#endif /* } */ - /* get the host out of the file */ - if ((filehost = strtok(line, " \t")) == NULL) { - amfree(line); - continue; - } - - /* get the username. If no user specified, then use the local user */ - if ((fileuser = strtok(NULL, " \t")) == NULL) { - fileuser = localuser; - } + netfd4.accept_fn = fn; + netfd4.recv_security_ok = &bsd_recv_security_ok; + netfd4.prefix_packet = &bsd_prefix_packet; + netfd4.driver = &bsd_security_driver; - hostmatch = (strcasecmp(filehost, host) == 0); - usermatch = (strcasecmp(fileuser, remoteuser) == 0); -#if defined(SHOW_SECURITY_DETAIL) /* { */ - bsdprintf(("%s: comparing \"%s\" with\n", debug_prefix(NULL), filehost)); - bsdprintf(("%s: \"%s\" (%s)\n", host, - debug_prefix(NULL), hostmatch ? "match" : "no match")); - bsdprintf(("%s: and \"%s\" with\n", fileuser, debug_prefix(NULL))); - bsdprintf(("%s: \"%s\" (%s)\n", remoteuser, - debug_prefix(NULL), usermatch ? "match" : "no match")); -#endif /* } */ - amfree(line); - /* compare */ - if (hostmatch && usermatch) { - /* success */ - found = 1; - break; - } - } - if (! found) { - result = vstralloc(ptmp, ": ", - "\"", host, " ", remoteuser, "\"", - " entry not found", - NULL); + /* check if in is a socket */ + fstat(in, &sbuf); + if (S_ISSOCK(sbuf.st_mode)) { + udp_addref(&netfd4, &udp_netfd_read_callback); + } else { + g_warning("input file descriptor is not a socket; cannot use BSD auth"); } - -common_exit: - - afclose(fp); - amfree(ptmp); - amfree(line); - amfree(localuser); - - return result; } -/* return 1 on success, 0 on failure */ -int -check_security(addr, str, cksum, errstr) -struct sockaddr_in *addr; -char *str; -unsigned long cksum; -char **errstr; +/* + * Frees a handle allocated by the above + */ +static void +bsd_close( + void * cookie) { - char *remotehost = NULL, *remoteuser = NULL; - char *bad_bsd = NULL; - struct hostent *hp; - struct passwd *pwptr; - int myuid, i, j; - char *s, *fp; - int ch; - - *errstr = NULL; - - /* what host is making the request? */ - - hp = gethostbyaddr((char *)&addr->sin_addr, sizeof(addr->sin_addr), - AF_INET); - if(hp == NULL) { - /* XXX include remote address in message */ - *errstr = vstralloc("[", - "addr ", inet_ntoa(addr->sin_addr), ": ", - "hostname lookup failed", - "]", NULL); - return 0; - } - remotehost = stralloc(hp->h_name); - - /* Now let's get the hostent for that hostname */ - hp = gethostbyname( remotehost ); - if(hp == NULL) { - /* XXX include remote hostname in message */ - *errstr = vstralloc("[", - "host ", remotehost, ": ", - "hostname lookup failed", - "]", NULL); - amfree(remotehost); - return 0; - } - - /* Verify that the hostnames match -- they should theoretically */ - if( strncasecmp( remotehost, hp->h_name, strlen(remotehost)+1 ) != 0 ) { - *errstr = vstralloc("[", - "hostnames do not match: ", - remotehost, " ", hp->h_name, - "]", NULL); - amfree(remotehost); - return 0; - } + struct sec_handle *bh = cookie; - /* Now let's verify that the ip which gave us this hostname - * is really an ip for this hostname; or is someone trying to - * break in? (THIS IS THE CRUCIAL STEP) - */ - for (i = 0; hp->h_addr_list[i]; i++) { - if (memcmp(hp->h_addr_list[i], - (char *) &addr->sin_addr, sizeof(addr->sin_addr)) == 0) - break; /* name is good, keep it */ + if(bh->proto_handle == NULL) { + return; } - /* If we did not find it, your DNS is messed up or someone is trying - * to pull a fast one on you. :( - */ + auth_debug(1, _("bsd: close handle '%s'\n"), bh->proto_handle); - /* Check even the aliases list. Work around for Solaris if dns goes over NIS */ - - if( !hp->h_addr_list[i] ) { - for (j = 0; hp->h_aliases[j] !=0 ; j++) { - if ( strcmp(hp->h_aliases[j],inet_ntoa(addr->sin_addr)) == 0) - break; /* name is good, keep it */ - } - if( !hp->h_aliases[j] ) { - *errstr = vstralloc("[", - "ip address ", inet_ntoa(addr->sin_addr), - " is not in the ip list for ", remotehost, - "]", - NULL); - amfree(remotehost); - return 0; - } + udp_recvpkt_cancel(bh); + if(bh->next) { + bh->next->prev = bh->prev; } - - /* next, make sure the remote port is a "reserved" one */ - - if(ntohs(addr->sin_port) >= IPPORT_RESERVED) { - char number[NUM_STR_SIZE]; - - snprintf(number, sizeof(number), "%d", ntohs(addr->sin_port)); - *errstr = vstralloc("[", - "host ", remotehost, ": ", - "port ", number, " not secure", - "]", NULL); - amfree(remotehost); - return 0; + else { + if (!not_init6 && netfd6.bh_last == bh) + netfd6.bh_last = bh->prev; + else + netfd4.bh_last = bh->prev; } - - /* extract the remote user name from the message */ - - s = str; - ch = *s++; - - bad_bsd = vstralloc("[", - "host ", remotehost, ": ", - "bad bsd security line", - "]", NULL); - -#define sc "USER " - if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) { - *errstr = bad_bsd; - bad_bsd = NULL; - amfree(remotehost); - return 0; + if(bh->prev) { + bh->prev->next = bh->next; } - s += sizeof(sc)-1; - ch = s[-1]; -#undef sc - - skip_whitespace(s, ch); - if(ch == '\0') { - *errstr = bad_bsd; - bad_bsd = NULL; - amfree(remotehost); - return 0; + else { + if (!not_init6 && netfd6.bh_first == bh) + netfd6.bh_first = bh->next; + else + netfd4.bh_first = bh->next; } - fp = s - 1; - skip_non_whitespace(s, ch); - s[-1] = '\0'; - remoteuser = stralloc(fp); - s[-1] = ch; - amfree(bad_bsd); - - /* lookup our local user name */ - - myuid = getuid(); - if((pwptr = getpwuid(myuid)) == NULL) - error("error [getpwuid(%d) fails]", myuid); - - dbprintf(("bsd security: remote host %s user %s local user %s\n", - remotehost, remoteuser, pwptr->pw_name)); - -#ifndef USE_AMANDAHOSTS - s = check_user_ruserok(remotehost, pwptr, remoteuser); -#else - s = check_user_amandahosts(remotehost, pwptr, remoteuser); -#endif - if (s != NULL) { - *errstr = vstralloc("[", - "access as ", pwptr->pw_name, " not allowed", - " from ", remoteuser, "@", remotehost, - ": ", s, "]", NULL); - } - amfree(s); - amfree(remotehost); - amfree(remoteuser); - return *errstr == NULL; + amfree(bh->proto_handle); + amfree(bh->hostname); + amfree(bh); } - /* * Create the server end of a stream. For bsd, this means setup a tcp * socket for receiving a connection. */ static void * -bsd_stream_server(h) - void *h; +bsd_stream_server( + void * h) { - struct bsd_stream *bs = NULL; -#ifndef TEST /* { */ - struct bsd_handle *bh = h; + struct sec_stream *bs = NULL; + struct sec_handle *bh = h; assert(bh != NULL); - bs = alloc(sizeof(*bs)); + bs = g_new0(struct sec_stream, 1); security_streaminit(&bs->secstr, &bsd_security_driver); - bs->socket = stream_server(&bs->port, STREAM_BUFSIZE, STREAM_BUFSIZE); + bs->socket = stream_server(SU_GET_FAMILY(&bh->udp->peer), &bs->port, + (size_t)STREAM_BUFSIZE, (size_t)STREAM_BUFSIZE, + 0); if (bs->socket < 0) { security_seterror(&bh->sech, - "can't create server stream: %s", strerror(errno)); + _("can't create server stream: %s"), strerror(errno)); amfree(bs); return (NULL); } bs->fd = -1; bs->ev_read = NULL; -#endif /* !TEST */ /* } */ return (bs); } @@ -1405,23 +423,21 @@ bsd_stream_server(h) * block on accept() */ static int -bsd_stream_accept(s) - void *s; +bsd_stream_accept( + void * s) { -#ifndef TEST /* { */ - struct bsd_stream *bs = s; + struct sec_stream *bs = s; assert(bs != NULL); assert(bs->socket != -1); assert(bs->fd < 0); - bs->fd = stream_accept(bs->socket, 30, -1, -1); + bs->fd = stream_accept(bs->socket, 30, STREAM_BUFSIZE, STREAM_BUFSIZE); if (bs->fd < 0) { security_stream_seterror(&bs->secstr, - "can't accept new stream connection: %s", strerror(errno)); + _("can't accept new stream connection: %s"), strerror(errno)); return (-1); } -#endif /* !TEST */ /* } */ return (0); } @@ -1429,32 +445,25 @@ bsd_stream_accept(s) * Return a connected stream */ static void * -bsd_stream_client(h, id) - void *h; - int id; +bsd_stream_client( + void * h, + int id) { - struct bsd_stream *bs = NULL; -#ifndef TEST /* { */ - struct bsd_handle *bh = h; + struct sec_stream *bs = NULL; + struct sec_handle *bh = h; #ifdef DUMPER_SOCKET_BUFFERING - int rcvbuf = sizeof(bs->databuf) * 2; + int rcvbuf = SIZEOF(bs->databuf) * 2; #endif assert(bh != NULL); - if (id < 0) { - security_seterror(&bh->sech, - "%d: invalid security stream id", id); - return (NULL); - } - - bs = alloc(sizeof(*bs)); + bs = g_new0(struct sec_stream, 1); security_streaminit(&bs->secstr, &bsd_security_driver); - bs->fd = stream_client(bh->hostname, id, STREAM_BUFSIZE, STREAM_BUFSIZE, - &bs->port, 0); + bs->fd = stream_client(bh->hostname, (in_port_t)id, + STREAM_BUFSIZE, STREAM_BUFSIZE, &bs->port, 0); if (bs->fd < 0) { security_seterror(&bh->sech, - "can't connect stream to %s port %d: %s", bh->hostname, + _("can't connect stream to %s port %d: %s"), bh->hostname, id, strerror(errno)); amfree(bs); return (NULL); @@ -1462,9 +471,8 @@ bsd_stream_client(h, id) bs->socket = -1; /* we're a client */ bs->ev_read = NULL; #ifdef DUMPER_SOCKET_BUFFERING - setsockopt(bs->fd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf, sizeof(rcvbuf)); + setsockopt(bs->fd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf, SIZEOF(rcvbuf)); #endif -#endif /* !TEST */ /* } */ return (bs); } @@ -1472,10 +480,10 @@ bsd_stream_client(h, id) * Close and unallocate resources for a stream */ static void -bsd_stream_close(s) - void *s; +bsd_stream_close( + void * s) { - struct bsd_stream *bs = s; + struct sec_stream *bs = s; assert(bs != NULL); @@ -1491,9 +499,10 @@ bsd_stream_close(s) * Authenticate a stream. bsd streams have no authentication */ static int -bsd_stream_auth(s) - void *s; +bsd_stream_auth( + void * s) { + (void)s; /* Quiet unused parameter warning */ return (0); /* success */ } @@ -1502,37 +511,14 @@ bsd_stream_auth(s) * Returns the stream id for this stream. This is just the local port. */ static int -bsd_stream_id(s) - void *s; -{ - struct bsd_stream *bs = s; - - assert(bs != NULL); - - return (bs->port); -} - -/* - * Write a chunk of data to a stream. Blocks until completion. - */ -static int -bsd_stream_write(s, buf, size) - void *s; - const void *buf; - size_t size; +bsd_stream_id( + void * s) { -#ifndef TEST /* { */ - struct bsd_stream *bs = s; + struct sec_stream *bs = s; assert(bs != NULL); - if (fullwrite(bs->fd, buf, size) < 0) { - security_stream_seterror(&bs->secstr, - "write error on stream %d: %s", bs->port, strerror(errno)); - return (-1); - } -#endif /* !TEST */ /* } */ - return (0); + return ((int)bs->port); } /* @@ -1540,11 +526,12 @@ bsd_stream_write(s, buf, size) * and arg when completed. */ static void -bsd_stream_read(s, fn, arg) - void *s, *arg; - void (*fn) P((void *, void *, ssize_t)); +bsd_stream_read( + void * s, + void (*fn)(void *, void *, ssize_t), + void * arg) { - struct bsd_stream *bs = s; + struct sec_stream *bs = s; /* * Only one read request can be active per stream. @@ -1552,41 +539,57 @@ bsd_stream_read(s, fn, arg) if (bs->ev_read != NULL) event_release(bs->ev_read); - bs->ev_read = event_register(bs->fd, EV_READFD, stream_read_callback, bs); + bs->ev_read = event_register((event_id_t)bs->fd, EV_READFD, stream_read_callback, bs); bs->fn = fn; bs->arg = arg; } +/* buffer for bsd_stream_read_sync function */ +static ssize_t sync_pktlen; +static void *sync_pkt; + /* - * Cancel a previous stream read request. It's ok if we didn't - * have a read scheduled. + * Read a chunk of data to a stream. Blocks until completion. */ -static void -bsd_stream_read_cancel(s) - void *s; +static ssize_t +bsd_stream_read_sync( + void * s, + void ** buf) { - struct bsd_stream *bs = s; + struct sec_stream *bs = s; assert(bs != NULL); - if (bs->ev_read != NULL) { - event_release(bs->ev_read); - bs->ev_read = NULL; + /* + * Only one read request can be active per stream. + */ + if(bs->ev_read != NULL) { + return -1; } + sync_pktlen = 0; + sync_pkt = NULL; + bs->ev_read = event_register((event_id_t)bs->fd, EV_READFD, + stream_read_sync_callback, bs); + event_wait(bs->ev_read); + *buf = sync_pkt; + return (sync_pktlen); } + /* - * Callback for bsd_stream_read + * Callback for bsd_stream_read_sync */ static void -stream_read_callback(arg) - void *arg; +stream_read_sync_callback( + void * s) { - struct bsd_stream *bs = arg; + struct sec_stream *bs = s; ssize_t n; assert(bs != NULL); + auth_debug(1, _("bsd: stream_read_callback_sync: fd %d\n"), bs->fd); + /* * Remove the event first, in case they reschedule it in the callback. */ @@ -1595,271 +598,52 @@ stream_read_callback(arg) n = read(bs->fd, bs->databuf, sizeof(bs->databuf)); } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN))); if (n < 0) - security_stream_seterror(&bs->secstr, strerror(errno)); - (*bs->fn)(bs->arg, bs->databuf, n); -} - -/* - * Convert a packet header into a string - */ -static const char * -pkthdr2str(bh, pkt) - const struct bsd_handle *bh; - const pkt_t *pkt; -{ - static char retbuf[256]; - - assert(bh != NULL); - assert(pkt != NULL); - - snprintf(retbuf, sizeof(retbuf), "Amanda %d.%d %s HANDLE %s SEQ %d\n", - VERSION_MAJOR, VERSION_MINOR, pkt_type2str(pkt->type), - bh->proto_handle, bh->sequence); - - bsdprintf(("%s: pkthdr2str handle '%s'\n", - debug_prefix_time(NULL), bh->proto_handle)); - - /* check for truncation. If only we had asprintf()... */ - assert(retbuf[strlen(retbuf) - 1] == '\n'); - - return (retbuf); -} - -/* - * Parses out the header line in 'str' into the pkt and handle - * Returns negative on parse error. - */ -static int -str2pkthdr() -{ - char *str; - const char *tok; - pkt_t *pkt; - - pkt = &netfd.pkt; - - assert(netfd.dgram.cur != NULL); - str = stralloc(netfd.dgram.cur); - - /* "Amanda %d.%d HANDLE %s SEQ %d\n" */ - - /* Read in "Amanda" */ - if ((tok = strtok(str, " ")) == NULL || strcmp(tok, "Amanda") != 0) - goto parse_error; - - /* nothing is done with the major/minor numbers currently */ - if ((tok = strtok(NULL, " ")) == NULL || strchr(tok, '.') == NULL) - goto parse_error; - - /* Read in the packet type */ - if ((tok = strtok(NULL, " ")) == NULL) - goto parse_error; - pkt_init(pkt, pkt_str2type(tok), ""); - if (pkt->type == (pktype_t)-1) - goto parse_error; - - /* Read in "HANDLE" */ - if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "HANDLE") != 0) - goto parse_error; - - /* parse the handle */ - if ((tok = strtok(NULL, " ")) == NULL) - goto parse_error; - netfd.handle = stralloc(tok); - - /* Read in "SEQ" */ - if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "SEQ") != 0) - goto parse_error; - - /* parse the sequence number */ - if ((tok = strtok(NULL, "\n")) == NULL) - goto parse_error; - netfd.sequence = atoi(tok); - - /* Save the body, if any */ - if ((tok = strtok(NULL, "")) != NULL) - pkt_cat(pkt, "%s", tok); - - amfree(str); - return (0); - -parse_error: -#if 0 /* XXX we have no way of passing this back up */ - security_seterror(&bh->sech, - "parse error in packet header : '%s'", origstr); -#endif - amfree(str); - return (-1); -} - -#endif /* BSD_SECURITY */ /* } */ - -#if defined(TEST) /* { */ - -/* - * The following dummy bind_portrange function is so we do not need to - * drag in util.o just for the test program. - */ -int -bind_portrange(s, addrp, first_port, last_port, proto) - int s; - struct sockaddr_in *addrp; - int first_port, last_port; - char *proto; -{ - return 0; -} - -/* - * Construct a datestamp (YYYYMMDD) from a time_t. - */ -char * -construct_datestamp(t) - time_t *t; -{ - struct tm *tm; - char datestamp[3*NUM_STR_SIZE]; - time_t when; - - if(t == NULL) { - when = time((time_t *)NULL); - } else { - when = *t; - } - tm = localtime(&when); - snprintf(datestamp, sizeof(datestamp), - "%04d%02d%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday); - return stralloc(datestamp); + security_stream_seterror(&bs->secstr, "%s", strerror(errno)); + bs->len = n; + sync_pktlen = bs->len; + sync_pkt = malloc(sync_pktlen); + memcpy(sync_pkt, bs->databuf, sync_pktlen); } /* - * Construct a timestamp (YYYYMMDDHHMMSS) from a time_t. + * Cancel a previous stream read request. It's ok if we didn't + * have a read scheduled. */ -char * -construct_timestamp(t) - time_t *t; +static void +bsd_stream_read_cancel( + void * s) { - struct tm *tm; - char timestamp[6*NUM_STR_SIZE]; - time_t when; + struct sec_stream *bs = s; - if(t == NULL) { - when = time((time_t *)NULL); - } else { - when = *t; + assert(bs != NULL); + if (bs->ev_read != NULL) { + event_release(bs->ev_read); + bs->ev_read = NULL; } - tm = localtime(&when); - snprintf(timestamp, sizeof(timestamp), - "%04d%02d%02d%02d%02d%02d", - tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); - return stralloc(timestamp); } /* - * The following are so we can include security.o but not all the rest - * of the security modules. - */ -const security_driver_t krb4_security_driver = {}; -const security_driver_t krb5_security_driver = {}; -const security_driver_t rsh_security_driver = {}; - -/* - * This function will be called to accept the connection and is used - * to report success or failure. + * Callback for bsd_stream_read */ -static void fake_accept_function(handle, pkt) - security_handle_t *handle; - pkt_t *pkt; -{ - if (pkt == NULL) { - fputs(handle->error, stdout); - fputc('\n', stdout); - } else { - fputs("access is allowed\n", stdout); - } -} - -int -main (argc, argv) +static void +stream_read_callback( + void * arg) { - char *remoteuser; - char *remotehost; - struct hostent *hp; - struct bsd_handle *bh; - void *save_cur; - struct passwd *pwent; - - /* Don't die when child closes pipe */ - signal(SIGPIPE, SIG_IGN); - - /* - * The following is stolen from amandad to emulate what it would - * do on startup. - */ - if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) { - client_uid = pwent->pw_uid; - client_gid = pwent->pw_gid; - endpwent(); - } - -#ifdef FORCE_USERID - /* we'd rather not run as root */ - if (geteuid() == 0) { - if(client_uid == (uid_t) -1) { - error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN); - } - initgroups(CLIENT_LOGIN, client_gid); - setgid(client_gid); - setegid(client_gid); - seteuid(client_uid); - } -#endif /* FORCE_USERID */ - - if (isatty(0)) { - fputs("Remote user: ", stdout); - fflush(stdout); - } - if ((remoteuser = agets(stdin)) == NULL) { - return 0; - } - - if (isatty(0)) { - fputs("Remote host: ", stdout); - fflush(stdout); - } - if ((remotehost = agets(stdin)) == NULL) { - return 0; - } + struct sec_stream *bs = arg; + ssize_t n; - set_pname("security"); - startclock(); + assert(bs != NULL); - if ((hp = gethostbyname(remotehost)) == NULL) { - fprintf(stderr, "cannot look up remote host %s\n", remotehost); - return 1; - } - memcpy((char *)&netfd.peer.sin_addr, - (char *)hp->h_addr, - sizeof(hp->h_addr)); /* - * Fake that it is coming from a reserved port. + * Remove the event first, in case they reschedule it in the callback. */ - netfd.peer.sin_port = htons(IPPORT_RESERVED - 1); - - bh = alloc(sizeof(*bh)); - bh->proto_handle=NULL; - netfd.pkt.type = P_REQ; - dgram_zero(&netfd.dgram); - save_cur = netfd.dgram.cur; /* cheating */ - dgram_cat(&netfd.dgram, "%s", pkthdr2str(bh, &netfd.pkt)); - dgram_cat(&netfd.dgram, "SECURITY USER %s\n", remoteuser); - netfd.dgram.cur = save_cur; /* cheating */ + bsd_stream_read_cancel(bs); + do { + n = read(bs->fd, bs->databuf, SIZEOF(bs->databuf)); + } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN))); - accept_fn = fake_accept_function; - netfd_read_callback(NULL); + if (n < 0) + security_stream_seterror(&bs->secstr, "%s", strerror(errno)); - return 0; + (*bs->fn)(bs->arg, bs->databuf, n); } - -#endif /* } */