diff options
-rw-r--r-- | utils/gssd/gssd_proc.c | 138 |
1 files changed, 86 insertions, 52 deletions
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c index 6a68cdf..4efc088 100644 --- a/utils/gssd/gssd_proc.c +++ b/utils/gssd/gssd_proc.c @@ -72,6 +72,7 @@ #include "gss_util.h" #include "krb5_util.h" #include "context.h" +#include "nfsrpc.h" /* * pollarray: @@ -541,6 +542,74 @@ out_err: } /* + * If the port isn't already set, do an rpcbind query to the remote server + * using the program and version and get the port. + * + * Newer kernels send the value of the port= mount option in the "info" + * file for the upcall or '0' for NFSv2/3. For NFSv4 it sends the value + * of the port= option or '2049'. The port field in a new sockaddr should + * reflect the value that was sent by the kernel. + */ +static int +populate_port(struct sockaddr *sa, const socklen_t salen, + const rpcprog_t program, const rpcvers_t version, + const unsigned short protocol) +{ + struct sockaddr_in *s4 = (struct sockaddr_in *) sa; + unsigned short port; + + /* + * Newer kernels send the port in the upcall. If we already have + * the port, there's no need to look it up. + */ + switch (sa->sa_family) { + case AF_INET: + if (s4->sin_port != 0) { + printerr(2, "DEBUG: port already set to %d\n", + ntohs(s4->sin_port)); + return 1; + } + break; + default: + printerr(0, "ERROR: unsupported address family %d\n", + sa->sa_family); + return 0; + } + + /* + * Newer kernels that send the port in the upcall set the value to + * 2049 for NFSv4 mounts when one isn't specified. The check below is + * only for kernels that don't send the port in the upcall. For those + * we either have to do an rpcbind query or set it to the standard + * port. Doing a query could be problematic (firewalls, etc), so take + * the latter approach. + */ + if (program == 100003 && version == 4) { + port = 2049; + goto set_port; + } + + port = nfs_getport(sa, salen, program, version, protocol); + if (!port) { + printerr(0, "ERROR: unable to obtain port for prog %ld " + "vers %ld\n", program, version); + return 0; + } + +set_port: + printerr(2, "DEBUG: setting port to %hu for prog %lu vers %lu\n", port, + program, version); + + switch (sa->sa_family) { + case AF_INET: + s4->sin_port = htons(port); + break; + } + + return 1; +} + +/* * Create an RPC connection and establish an authenticated * gss context with a server. */ @@ -555,15 +624,13 @@ int create_auth_rpc_client(struct clnt_info *clp, AUTH *auth = NULL; uid_t save_uid = -1; int retval = -1; - int errcode; OM_uint32 min_stat; char rpc_errmsg[1024]; int sockp = RPC_ANYSOCK; int sendsz = 32768, recvsz = 32768; - struct addrinfo ai_hints, *a = NULL; - char service[64]; - char *at_sign; - struct sockaddr_in *addr = (struct sockaddr_in *) &clp->addr; + int protocol; + struct sockaddr *addr = (struct sockaddr *) &clp->addr; + socklen_t salen; /* Create the context as the user (not as root) */ save_uid = geteuid(); @@ -617,15 +684,10 @@ int create_auth_rpc_client(struct clnt_info *clp, printerr(2, "creating %s client for server %s\n", clp->protocol, clp->servername); - memset(&ai_hints, '\0', sizeof(ai_hints)); - ai_hints.ai_family = PF_INET; - ai_hints.ai_flags |= AI_CANONNAME; if ((strcmp(clp->protocol, "tcp")) == 0) { - ai_hints.ai_socktype = SOCK_STREAM; - ai_hints.ai_protocol = IPPROTO_TCP; + protocol = IPPROTO_TCP; } else if ((strcmp(clp->protocol, "udp")) == 0) { - ai_hints.ai_socktype = SOCK_DGRAM; - ai_hints.ai_protocol = IPPROTO_UDP; + protocol = IPPROTO_UDP; } else { printerr(0, "WARNING: unrecognized protocol, '%s', requested " "for connection to server %s for user with uid %d\n", @@ -633,40 +695,22 @@ int create_auth_rpc_client(struct clnt_info *clp, goto out_fail; } - /* extract the service name from clp->servicename */ - if ((at_sign = strchr(clp->servicename, '@')) == NULL) { - printerr(0, "WARNING: servicename (%s) not formatted as " - "expected with service@host\n", clp->servicename); - goto out_fail; - } - if ((at_sign - clp->servicename) >= sizeof(service)) { - printerr(0, "WARNING: service portion of servicename (%s) " - "is too long!\n", clp->servicename); - goto out_fail; - } - strncpy(service, clp->servicename, at_sign - clp->servicename); - service[at_sign - clp->servicename] = '\0'; - - errcode = getaddrinfo(clp->servername, service, &ai_hints, &a); - if (errcode) { - printerr(0, "WARNING: Error from getaddrinfo for server " - "'%s': %s\n", clp->servername, gai_strerror(errcode)); + switch (addr->sa_family) { + case AF_INET: + salen = sizeof(struct sockaddr_in); + break; + default: + printerr(1, "ERROR: Unknown address family %d\n", + addr->sa_family); goto out_fail; } - if (a == NULL) { - printerr(0, "WARNING: No address information found for " - "connection to server %s for user with uid %d\n", - clp->servername, uid); + if (!populate_port(addr, salen, clp->prog, clp->vers, protocol)) goto out_fail; - } - - if (addr->sin_port != 0) - ((struct sockaddr_in *) a->ai_addr)->sin_port = addr->sin_port; - if (a->ai_protocol == IPPROTO_TCP) { + if (protocol == IPPROTO_TCP) { if ((rpc_clnt = clnttcp_create( - (struct sockaddr_in *) a->ai_addr, + (struct sockaddr_in *) addr, clp->prog, clp->vers, &sockp, sendsz, recvsz)) == NULL) { snprintf(rpc_errmsg, sizeof(rpc_errmsg), @@ -677,10 +721,10 @@ int create_auth_rpc_client(struct clnt_info *clp, clnt_spcreateerror(rpc_errmsg)); goto out_fail; } - } else if (a->ai_protocol == IPPROTO_UDP) { + } else if (protocol == IPPROTO_UDP) { const struct timeval timeout = {5, 0}; if ((rpc_clnt = clntudp_bufcreate( - (struct sockaddr_in *) a->ai_addr, + (struct sockaddr_in *) addr, clp->prog, clp->vers, timeout, &sockp, sendsz, recvsz)) == NULL) { snprintf(rpc_errmsg, sizeof(rpc_errmsg), @@ -691,16 +735,7 @@ int create_auth_rpc_client(struct clnt_info *clp, clnt_spcreateerror(rpc_errmsg)); goto out_fail; } - } else { - /* Shouldn't happen! */ - printerr(0, "ERROR: requested protocol '%s', but " - "got addrinfo with protocol %d\n", - clp->protocol, a->ai_protocol); - goto out_fail; } - /* We're done with this */ - freeaddrinfo(a); - a = NULL; printerr(2, "creating context with server %s\n", clp->servicename); auth = authgss_create_default(rpc_clnt, clp->servicename, &sec); @@ -722,7 +757,6 @@ int create_auth_rpc_client(struct clnt_info *clp, out: if (sec.cred != GSS_C_NO_CREDENTIAL) gss_release_cred(&min_stat, &sec.cred); - if (a != NULL) freeaddrinfo(a); /* Restore euid to original value */ if ((save_uid != -1) && (setfsuid(save_uid) != uid)) { printerr(0, "WARNING: Failed to restore fsuid" |