From: Greg Hudson Date: Sat, 26 Jun 2010 03:32:55 +0000 (+0000) Subject: Make kadmin work over IPv6 X-Git-Tag: krb5-1.9-beta1~180 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=0080380b3b3e945c5eb84504771d9d01ee76a611;p=krb5.git Make kadmin work over IPv6 Make gssrpc work over IPv6 TCP sockets provided that the client creates and connects/binds the sockets and doesn't query their addresses or use bindresvport(). Make kadmin work within those constraints and handle IPv6. Specific changes: * Make svctcp_create() able to extract the port from an IPv6 socket, using a new helper function getport(). * Make clnttcp_create() handle a null raddr value if *sockp is set. * Make kadm5_get_service_name() use getaddrinfo() to canonicalize the admin server name. * Make libkadm5clnt's init_any() responsible for connecting its socket using a new helper function connect_to_server(), which uses getaddrinfo instead of gethostbyname. Pass a null address to clnttcp_create(). * Make libapputil's net-server.c set up IPv6 as well as IPv4 listener ports for RPC connections. * Adjust the error code expected in a libkadm5 unit test. ticket: 6746 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24147 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/kadmin/testing/util/tcl_kadm5.c b/src/kadmin/testing/util/tcl_kadm5.c index 3a679996e..b33cf0a0f 100644 --- a/src/kadmin/testing/util/tcl_kadm5.c +++ b/src/kadmin/testing/util/tcl_kadm5.c @@ -351,6 +351,8 @@ static Tcl_DString *unparse_err(kadm5_ret_t code) code_string = "KADM5_BAD_SERVER_NAME"; break; case KADM5_MISSING_KRB5_CONF_PARAMS: code_string = "KADM5_MISSING_KRB5_CONF_PARAMS"; break; + case KADM5_XDR_FAILURE: code_string = "KADM5_XDR_FAILURE"; break; + case KADM5_CANT_RESOLVE: code_string = "KADM5_CANT_RESOLVE"; break; case OSA_ADB_DUP: code_string = "OSA_ADB_DUP"; break; diff --git a/src/lib/apputils/net-server.c b/src/lib/apputils/net-server.c index 18582e272..dfae9d1f5 100644 --- a/src/lib/apputils/net-server.c +++ b/src/lib/apputils/net-server.c @@ -575,6 +575,20 @@ setup_a_rpc_listener(struct socksetup *data, struct sockaddr *addr) if (setreuseaddr(sock, 1) < 0) com_err(data->prog, errno, "Cannot enable SO_REUSEADDR on fd %d", sock); +#ifdef KRB5_USE_INET6 + if (addr->sa_family == AF_INET6) { +#ifdef IPV6_V6ONLY + if (setv6only(sock, 1)) + com_err(data->prog, errno, + "setsockopt(%d,IPV6_V6ONLY,1) failed", sock); + else + com_err(data->prog, 0, "setsockopt(%d,IPV6_V6ONLY,1) worked", + sock); +#else + krb5_klog_syslog(LOG_INFO, "no IPV6_V6ONLY socket option support"); +#endif /* IPV6_V6ONLY */ + } +#endif /* KRB5_USE_INET6 */ if (bind(sock, addr, socklen(addr)) == -1) { com_err(data->prog, errno, "Cannot bind RPC server socket on %s", paddr(addr)); @@ -671,6 +685,9 @@ static int setup_rpc_listener_ports(struct socksetup *data) { struct sockaddr_in sin4; +#ifdef KRB5_USE_INET6 + struct sockaddr_in6 sin6; +#endif int i; struct rpc_svc_data svc; @@ -681,24 +698,54 @@ setup_rpc_listener_ports(struct socksetup *data) #endif sin4.sin_addr.s_addr = INADDR_ANY; +#ifdef KRB5_USE_INET6 + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; +#ifdef HAVE_SA_LEN + sin6.sin6_len = sizeof(sin6); +#endif + sin6.sin6_addr = in6addr_any; +#endif + FOREACH_ELT (rpc_svc_data, i, svc) { int s4; +#ifdef KRB5_USE_INET6 + int s6; +#endif set_sa_port((struct sockaddr *)&sin4, htons(svc.port)); s4 = setup_a_rpc_listener(data, (struct sockaddr *)&sin4); if (s4 < 0) return -1; + + if (add_rpc_listener_fd(data, &svc, s4) == NULL) + close(s4); else { - if (add_rpc_listener_fd(data, &svc, s4) == NULL) - close(s4); + FD_SET(s4, &sstate.rfds); + if (s4 >= sstate.max) + sstate.max = s4 + 1; + krb5_klog_syslog(LOG_INFO, "listening on fd %d: rpc %s", + s4, paddr((struct sockaddr *)&sin4)); + } + +#ifdef KRB5_USE_INET6 + if (ipv6_enabled()) { + set_sa_port((struct sockaddr *)&sin6, htons(svc.port)); + s6 = setup_a_tcp_listener(data, (struct sockaddr *)&sin6); + if (s6 < 0) + return -1; + + if (add_rpc_listener_fd(data, &svc, s6) == NULL) + close(s6); else { - FD_SET(s4, &sstate.rfds); - if (s4 >= sstate.max) - sstate.max = s4 + 1; + FD_SET(s6, &sstate.rfds); + if (s6 >= sstate.max) + sstate.max = s6 + 1; krb5_klog_syslog(LOG_INFO, "listening on fd %d: rpc %s", - s4, paddr((struct sockaddr *)&sin4)); + s6, paddr((struct sockaddr *)&sin6)); } } +#endif } FD_ZERO(&rpc_listenfds); rpc_listenfds = svc_fdset; diff --git a/src/lib/kadm5/alt_prof.c b/src/lib/kadm5/alt_prof.c index bc5eb2314..6a7965512 100644 --- a/src/lib/kadm5/alt_prof.c +++ b/src/lib/kadm5/alt_prof.c @@ -33,6 +33,7 @@ /* * alt_prof.c - Implement alternate profile file handling. */ +#include "fake-addrinfo.h" #include "k5-int.h" #include #include "adm_proto.h" @@ -882,7 +883,8 @@ kadm5_get_admin_service_name(krb5_context ctx, { krb5_error_code ret; kadm5_config_params params_in, params_out; - struct hostent *hp; + struct addrinfo hint, *ai = NULL; + int err; memset(¶ms_in, 0, sizeof(params_in)); memset(¶ms_out, 0, sizeof(params_out)); @@ -898,8 +900,10 @@ kadm5_get_admin_service_name(krb5_context ctx, goto err_params; } - hp = gethostbyname(params_out.admin_server); - if (hp == NULL) { + memset(&hint, 0, sizeof(hint)); + hint.ai_flags = AI_CANONNAME; + err = getaddrinfo(params_out.admin_server, NULL, &hint, &ai); + if (err != 0) { ret = KADM5_CANT_RESOLVE; krb5_set_error_message(ctx, ret, "Cannot resolve address of admin server \"%s\" " @@ -907,13 +911,15 @@ kadm5_get_admin_service_name(krb5_context ctx, realm_in); goto err_params; } - if (strlen(hp->h_name) + sizeof("kadmin/") > maxlen) { + if (strlen(ai->ai_canonname) + sizeof("kadmin/") > maxlen) { ret = ENOMEM; goto err_params; } - snprintf(admin_name, maxlen, "kadmin/%s", hp->h_name); + snprintf(admin_name, maxlen, "kadmin/%s", ai->ai_canonname); err_params: + if (ai != NULL) + freeaddrinfo(ai); kadm5_free_config_params(ctx, ¶ms_out); return ret; } diff --git a/src/lib/kadm5/clnt/client_init.c b/src/lib/kadm5/clnt/client_init.c index 9301b4bfc..cc9664f4d 100644 --- a/src/lib/kadm5/clnt/client_init.c +++ b/src/lib/kadm5/clnt/client_init.c @@ -40,6 +40,7 @@ #include #include #include +#include #include /* for KRB5_ADM_DEFAULT_PORT */ #include #ifdef __STDC__ @@ -79,6 +80,9 @@ gic_iter(kadm5_server_handle_t handle, enum init_type init_type, char *svcname, char *realm, char *full_svcname, unsigned int full_svcname_len); +static kadm5_ret_t +connect_to_server(const char *hostname, int port, int *fd); + static kadm5_ret_t setup_gss(kadm5_server_handle_t handle, kadm5_config_params *params_in, char *client_name, char *full_svcname); @@ -151,8 +155,6 @@ init_any(krb5_context context, char *client_name, enum init_type init_type, kadm5_config_params *params_in, krb5_ui_4 struct_version, krb5_ui_4 api_version, char **db_args, void **server_handle) { - struct sockaddr_in addr; - struct hostent *hp; int fd; krb5_boolean iprop_enable; @@ -271,20 +273,9 @@ init_any(krb5_context context, char *client_name, enum init_type init_type, sizeof(full_svcname)); if (code) goto error; - /* - * We have ticket; open the RPC connection. - */ - - hp = gethostbyname(handle->params.admin_server); - if (hp == (struct hostent *) NULL) { - code = KADM5_BAD_SERVER_NAME; - goto cleanup; - } - /* - * If the service_name and client_name are iprop-centric, - * we need to clnttcp_create to the appropriate RPC prog. - */ + /* If the service_name and client_name are iprop-centric, use the iprop + * port and RPC identifiers. */ iprop_enable = (service_name != NULL && strstr(service_name, KIPROP_SVC_NAME) != NULL && strstr(client_name, KIPROP_SVC_NAME) != NULL); @@ -298,13 +289,11 @@ init_any(krb5_context context, char *client_name, enum init_type init_type, rpc_vers = KADMVERS; } - memset(&addr, 0, sizeof(addr)); - addr.sin_family = hp->h_addrtype; - (void) memcpy(&addr.sin_addr, hp->h_addr, sizeof(addr.sin_addr)); - addr.sin_port = htons((u_short) port); + code = connect_to_server(handle->params.admin_server, port, &fd); + if (code) + goto error; - fd = RPC_ANYSOCK; - handle->clnt = clnttcp_create(&addr, rpc_prog, rpc_vers, &fd, 0, 0); + handle->clnt = clnttcp_create(NULL, rpc_prog, rpc_vers, &fd, 0, 0); if (handle->clnt == NULL) { code = KADM5_RPC_ERROR; #ifdef DEBUG @@ -562,6 +551,49 @@ error: return code; } +/* Set *fd to a socket connected to hostname and port. */ +static kadm5_ret_t +connect_to_server(const char *hostname, int port, int *fd) +{ + struct addrinfo hint, *addrs, *a; + char portbuf[32]; + int err, s; + kadm5_ret_t code; + + /* Look up the server's addresses. */ + (void) snprintf(portbuf, sizeof(portbuf), "%d", port); + memset(&hint, 0, sizeof(hint)); + hint.ai_socktype = SOCK_STREAM; +#ifdef AI_NUMERICSERV + hint.ai_flags = AI_NUMERICSERV; +#endif + err = getaddrinfo(hostname, portbuf, &hint, &addrs); + if (err != 0) + return KADM5_CANT_RESOLVE; + + /* Try to connect to each address until we succeed. */ + for (a = addrs; a != NULL; a = a->ai_next) { + s = socket(a->ai_family, a->ai_socktype, 0); + if (s == -1) { + code = KADM5_FAILURE; + goto cleanup; + } + err = connect(s, a->ai_addr, a->ai_addrlen); + if (err == 0) { + *fd = s; + code = 0; + goto cleanup; + } + close(s); + } + + /* We didn't succeed on any address. */ + code = KADM5_RPC_ERROR; +cleanup: + freeaddrinfo(addrs); + return code; +} + /* Acquire GSSAPI credentials and set up RPC auth flavor. */ static kadm5_ret_t setup_gss(kadm5_server_handle_t handle, kadm5_config_params *params_in, diff --git a/src/lib/kadm5/unit-test/api.current/init-v2.exp b/src/lib/kadm5/unit-test/api.current/init-v2.exp index a364b9c14..7a353d4e9 100644 --- a/src/lib/kadm5/unit-test/api.current/init-v2.exp +++ b/src/lib/kadm5/unit-test/api.current/init-v2.exp @@ -70,7 +70,7 @@ proc test102 {} { [config_params {KADM5_CONFIG_ADMIN_SERVER} does.not.exist] \ $KADM5_STRUCT_VERSION $KADM5_API_VERSION_3 \ server_handle - } "BAD_SERVER_NAME" + } "CANT_RESOLVE" } if {$RPC} test102 diff --git a/src/lib/rpc/clnt_tcp.c b/src/lib/rpc/clnt_tcp.c index 0eb8f45dd..cfa44c0e2 100644 --- a/src/lib/rpc/clnt_tcp.c +++ b/src/lib/rpc/clnt_tcp.c @@ -150,7 +150,7 @@ clnttcp_create( /* * If no port number given ask the pmap for one */ - if (raddr->sin_port == 0) { + if (raddr != NULL && raddr->sin_port == 0) { u_short port; if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) { mem_free((caddr_t)ct, sizeof(struct ct_data)); @@ -185,7 +185,10 @@ clnttcp_create( ct->ct_sock = *sockp; ct->ct_wait.tv_usec = 0; ct->ct_waitset = FALSE; - ct->ct_addr = *raddr; + if (raddr == NULL) + memset(&ct->ct_addr, 0, sizeof(ct->ct_addr)); + else + ct->ct_addr = *raddr; /* * Initialize call message diff --git a/src/lib/rpc/svc_tcp.c b/src/lib/rpc/svc_tcp.c index 796627f2f..b9aee070f 100644 --- a/src/lib/rpc/svc_tcp.c +++ b/src/lib/rpc/svc_tcp.c @@ -116,6 +116,17 @@ struct tcp_conn { /* kept in xprt->xp_p1 */ char verf_body[MAX_AUTH_BYTES]; }; +static u_short +getport(struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET) + return ntohs(((struct sockaddr_in *) addr)->sin_port); + else if (addr->sa_family == AF_INET6) + return ntohs(((struct sockaddr_in6 *) addr)->sin6_port); + else + return 0; +} + /* * Usage: * xprt = svctcp_create(sock, send_buf_size, recv_buf_size); @@ -145,8 +156,9 @@ svctcp_create( bool_t madesock = FALSE; register SVCXPRT *xprt; register struct tcp_rendezvous *r; - struct sockaddr_in addr; - int len = sizeof(struct sockaddr_in); + struct sockaddr_in sin; + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); if (sock == RPC_ANYSOCK) { if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { @@ -156,14 +168,14 @@ svctcp_create( set_cloexec_fd(sock); madesock = TRUE; } - memset(&addr, 0, sizeof (addr)); + memset(&sin, 0, sizeof(sin)); #if HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - addr.sin_len = sizeof(addr); + sin.sin_len = sizeof(sin); #endif - addr.sin_family = AF_INET; - if (bindresvport(sock, &addr)) { - addr.sin_port = 0; - (void)bind(sock, (struct sockaddr *)&addr, len); + sin.sin_family = AF_INET; + if (bindresvport(sock, &sin)) { + sin.sin_port = 0; + (void)bind(sock, (struct sockaddr *)&sin, len); } if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) { perror("svc_tcp.c - cannot getsockname"); @@ -194,7 +206,7 @@ svctcp_create( xprt->xp_auth = NULL; xprt->xp_verf = gssrpc__null_auth; xprt->xp_ops = &svctcp_rendezvous_op; - xprt->xp_port = ntohs(addr.sin_port); + xprt->xp_port = getport((struct sockaddr *) &addr); xprt->xp_sock = sock; xprt->xp_laddrlen = 0; xprt_register(xprt); @@ -274,7 +286,7 @@ rendezvous_request( SOCKET sock; struct tcp_rendezvous *r; struct sockaddr_in addr, laddr; - int len, llen; + socklen_t len, llen; r = (struct tcp_rendezvous *)xprt->xp_p1; again: