X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Fbsd-security.c;h=82348ed98241b0c849658696826267eb0188bc95;hb=refs%2Fheads%2Fmaster;hp=303c4d040f598b922a73baad0391f8348faa1931;hpb=34197d9f46a5f4e944378cbb65fca32ee0eec7b9;p=debian%2Famanda diff --git a/common-src/bsd-security.c b/common-src/bsd-security.c index 303c4d0..82348ed 100644 --- a/common-src/bsd-security.c +++ b/common-src/bsd-security.c @@ -1,6 +1,7 @@ /* * Amanda, The Advanced Maryland Automatic Network Disk Archiver * Copyright (c) 1991-1999 University of Maryland at College Park + * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved. * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its @@ -24,7 +25,7 @@ * file named AUTHORS, in the root directory of this distribution. */ /* - * $Id: bsd-security.c,v 1.75.2.1 2006/09/28 18:46:08 martinea Exp $ + * $Id: bsd-security.c,v 1.75 2006/07/19 17:41:14 martinea Exp $ * * "BSD" security module */ @@ -37,43 +38,24 @@ #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 /* } */ - /* * Interface functions */ 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 *, int, int, - void (*)(security_handle_t *, pkt_t *)); +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 *); @@ -92,6 +74,7 @@ const security_driver_t bsd_security_driver = { "BSD", bsd_connect, bsd_accept, + sec_get_authenticated_peer_name_hostname, bsd_close, udpbsd_sendpkt, udp_recvpkt, @@ -107,14 +90,18 @@ const security_driver_t bsd_security_driver = { 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 udp_handle_t netfd; -static int not_init = 1; +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; @@ -138,75 +125,181 @@ bsd_connect( void * datap) { struct sec_handle *bh; - struct servent *se; - struct hostent *he; 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); (void)conf_fn; /* Quiet unused parameter warning */ (void)datap; /* Quiet unused parameter warning */ - bh = alloc(SIZEOF(*bh)); + bh = g_new0(struct sec_handle, 1); bh->proto_handle=NULL; - bh->udp = &netfd; security_handleinit(&bh->sech, &bsd_security_driver); - /* - * Only init the socket once - */ - if (not_init == 1) { - uid_t euid; - dgram_zero(&netfd.dgram); - - euid = geteuid(); - seteuid((uid_t)0); - dgram_bind(&netfd.dgram, &port); - seteuid(euid); - netfd.handle = NULL; - netfd.pkt.body = NULL; - netfd.recv_security_ok = &bsd_recv_security_ok; - netfd.prefix_packet = &bsd_prefix_packet; + 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; + } + + 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; + } + /* + * Only init the IPv6 socket once + */ + 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 + + /* IPv4 socket already bound */ + if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 0) { + break; + } + /* - * We must have a reserved port. Bomb if we didn't get one. + * Only init the IPv4 socket once */ - 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); - return; + 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; } - not_init = 0; } - if ((he = gethostbyname(hostname)) == NULL) { + if (res_addr == NULL) { + dbprintf(_("Can't bind a socket to connect to %s\n"), hostname); security_seterror(&bh->sech, - "%s: could not resolve hostname", hostname); + _("Can't bind a socket to connect to %s\n"), hostname); (*fn)(arg, &bh->sech, S_ERROR); - return; + amfree(canonname); + return; } - bsdprintf(("Resolved hostname=%s\n", hostname)); - if ((se = getservbyname(AMANDA_SERVICE_NAME, "udp")) == NULL) - port = (in_port_t)htons(AMANDA_SERVICE_DEFAULT); + +#ifdef WORKING_IPV6 + if (res_addr->ai_addr->sa_family == AF_INET6) + bh->udp = &netfd6; else - port = (in_port_t)se->s_port; - amanda_gettimeofday(&sequence_time, &dontcare); +#endif + bh->udp = &netfd4; + + auth_debug(1, _("Resolved hostname=%s\n"), canonname); + + if (conf_fn) { + service = conf_fn("client_port", datap); + if (!service || strlen(service) <= 1) + service = "amanda"; + } else { + service = "amanda"; + } + 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; + } + + amanda_gettimeofday(&sequence_time); sequence = (int)sequence_time.tv_sec ^ (int)sequence_time.tv_usec; handle=alloc(15); - snprintf(handle, 14, "000-%08x", (unsigned)newhandle++); - if (udp_inithandle(&netfd, bh, he, port, handle, sequence) < 0) { + 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); } - else + else { (*fn)(arg, &bh->sech, S_OK); + } amfree(handle); + amfree(canonname); + + freeaddrinfo(res); } /* @@ -215,34 +308,46 @@ bsd_connect( 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 (*fn)(security_handle_t *, pkt_t *), + void *datap) { + struct stat sbuf; 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; /* * We assume in and out point to the same socket, and just use * in. */ - dgram_socket(&netfd.dgram, in); + dgram_socket(&netfd4.dgram, in); + dgram_socket(&netfd6.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 */ - netfd.accept_fn = fn; - netfd.recv_security_ok = &bsd_recv_security_ok; - netfd.prefix_packet = &bsd_prefix_packet; - netfd.driver = &bsd_security_driver; - - udp_addref(&netfd, &udp_netfd_read_callback); + netfd4.accept_fn = fn; + netfd4.recv_security_ok = &bsd_recv_security_ok; + netfd4.prefix_packet = &bsd_prefix_packet; + netfd4.driver = &bsd_security_driver; + + /* 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"); + } } /* @@ -258,21 +363,26 @@ bsd_close( return; } - bsdprintf(("%s: bsd: close handle '%s'\n", - debug_prefix_time(NULL), bh->proto_handle)); + auth_debug(1, _("bsd: close handle '%s'\n"), bh->proto_handle); udp_recvpkt_cancel(bh); if(bh->next) { bh->next->prev = bh->prev; } else { - netfd.bh_last = bh->prev; + if (!not_init6 && netfd6.bh_last == bh) + netfd6.bh_last = bh->prev; + else + netfd4.bh_last = bh->prev; } if(bh->prev) { bh->prev->next = bh->next; } else { - netfd.bh_first = bh->next; + if (!not_init6 && netfd6.bh_first == bh) + netfd6.bh_first = bh->next; + else + netfd4.bh_first = bh->next; } amfree(bh->proto_handle); @@ -288,28 +398,25 @@ static void * bsd_stream_server( void * h) { -#ifndef TEST /* { */ 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, (size_t)STREAM_BUFSIZE, - (size_t)STREAM_BUFSIZE, 0); + 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; return (bs); -#else - return (NULL); -#endif /* !TEST */ /* } */ } /* @@ -320,7 +427,6 @@ static int bsd_stream_accept( void * s) { -#ifndef TEST /* { */ struct sec_stream *bs = s; assert(bs != NULL); @@ -330,10 +436,9 @@ bsd_stream_accept( 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); } @@ -346,21 +451,20 @@ bsd_stream_client( int id) { struct sec_stream *bs = NULL; -#ifndef TEST /* { */ struct sec_handle *bh = h; #ifdef DUMPER_SOCKET_BUFFERING - size_t rcvbuf = SIZEOF(bs->databuf) * 2; + int rcvbuf = SIZEOF(bs->databuf) * 2; #endif assert(bh != 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, (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); @@ -370,7 +474,6 @@ bsd_stream_client( #ifdef DUMPER_SOCKET_BUFFERING setsockopt(bs->fd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf, SIZEOF(rcvbuf)); #endif -#endif /* !TEST */ /* } */ return (bs); } @@ -442,6 +545,10 @@ bsd_stream_read( bs->arg = arg; } +/* buffer for bsd_stream_read_sync function */ +static ssize_t sync_pktlen; +static void *sync_pkt; + /* * Read a chunk of data to a stream. Blocks until completion. */ @@ -460,11 +567,13 @@ bsd_stream_read_sync( 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 = bs->databuf; - return (bs->len); + *buf = sync_pkt; + return (sync_pktlen); } @@ -480,8 +589,7 @@ stream_read_sync_callback( assert(bs != NULL); - bsdprintf(("%s: bsd: stream_read_callback_sync: fd %d\n", - debug_prefix_time(NULL), bs->fd)); + 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. @@ -491,8 +599,15 @@ stream_read_sync_callback( 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)); + security_stream_seterror(&bs->secstr, "%s", strerror(errno)); bs->len = n; + sync_pktlen = bs->len; + if (sync_pktlen > 0) { + sync_pkt = malloc(sync_pktlen); + memcpy(sync_pkt, bs->databuf, sync_pktlen); + } else { + sync_pkt = NULL; + } } /* @@ -524,213 +639,14 @@ stream_read_callback( assert(bs != NULL); - /* - * Remove the event first, in case they reschedule it in the callback. - */ - bsd_stream_read_cancel(bs); do { n = read(bs->fd, bs->databuf, SIZEOF(bs->databuf)); } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN))); + if (n <= 0) + bsd_stream_read_cancel(bs); if (n < 0) - security_stream_seterror(&bs->secstr, strerror(errno)); + security_stream_seterror(&bs->secstr, "%s", strerror(errno)); (*bs->fn)(bs->arg, bs->databuf, n); } - -#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( - int s, - struct sockaddr_in *addrp, - in_port_t first_port, - in_port_t last_port, - char * proto) -{ - (void)s; /* Quiet unused parameter warning */ - (void)addrp; /* Quiet unused parameter warning */ - (void)first_port; /* Quiet unused parameter warning */ - (void)last_port; /* Quiet unused parameter warning */ - (void)proto; /* Quiet unused parameter warning */ - - return 0; -} - -/* - * Construct a datestamp (YYYYMMDD) from a time_t. - */ -char * -construct_datestamp( - 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); -} - -/* - * Construct a timestamp (YYYYMMDDHHMMSS) from a time_t. - */ -char * -construct_timestamp( - time_t * t) -{ - struct tm *tm; - char timestamp[6*NUM_STR_SIZE]; - time_t when; - - if(t == NULL) { - when = time((time_t *)NULL); - } else { - when = *t; - } - 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 = {}; -const security_driver_t ssh_security_driver = {}; -const security_driver_t bsdtcp_security_driver = {}; -const security_driver_t bsdudp_security_driver = {}; - -/* - * This function will be called to accept the connection and is used - * to report success or failure. - */ -static void fake_accept_function( - 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 ( - int argc, - char ** argv) -{ - char *remoteuser; - char *remotehost; - struct hostent *hp; - struct sec_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); - /*NOTREACHED*/ - } - 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); - } - do { - amfree(remoteuser); - remoteuser = agets(stdin); - if (remoteuser == NULL) - return 0; - } while (remoteuser[0] == '\0'); - - if (isatty(0)) { - fputs("Remote host: ", stdout); - fflush(stdout); - } - - do { - amfree(remotehost); - remotehost = agets(stdin); - if (remotehost == NULL) - return 0; - } while (remotehost[0] == '\0'); - - set_pname("security"); - dbopen(NULL); - - startclock(); - - 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. - */ - netfd.peer.sin_port = htons(IPPORT_RESERVED - 1); - - bh = alloc(SIZEOF(*bh)); - bh->proto_handle=NULL; - bh->udp = &netfd; - 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 */ - - netfd.accept_fn = fake_accept_function; - netfd.recv_security_ok = &bsd_recv_security_ok; - netfd.prefix_packet = &bsd_prefix_packet; - udp_netfd_read_callback(&netfd); - - return 0; -} - -#endif /* } */