From b314b3233a7ade5e3f5878051327e9d97b94be7d Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Thu, 12 Feb 2009 18:22:39 -0500 Subject: - move client socket creation here, so that we don't have to know how to do it elsewhere - refactor the send/receive logic out so that it can be used for either rpcbind or portmap - try to connect to rpcbind over a local socket first, else assume we have to use portmap over IP - use our own rpcb encode/decode function to avoid mixing the one from libtirpc with the rest of libc's XDR functions -- THAT WOULD BE BAD --- src/portmap.c | 554 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 372 insertions(+), 182 deletions(-) (limited to 'src/portmap.c') diff --git a/src/portmap.c b/src/portmap.c index 3d56971..3213ac3 100644 --- a/src/portmap.c +++ b/src/portmap.c @@ -28,6 +28,7 @@ #include #include #include +#include #ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H #include @@ -39,14 +40,26 @@ #include #include -#ifdef HAVE_LIBTIRPC -#include -#else #include -#endif #include "portmap.h" +#ifndef RPCBPROG +#define RPCBPROG PMAPPROG +#endif +#ifndef RPCBVERS +#define RPCBVERS 3 +#endif +#ifndef RPCBPROC_SET +#define RPCBPROC_SET 1 +#endif +#ifndef RPCBPROC_UNSET +#define RPCBPROC_UNSET 2 +#endif +#ifndef _PATH_RPCBINDSOCK +#define _PATH_RPCBINDSOCK "/var/run/rpcbind.sock" +#endif + #ifdef PORTMAP_MAIN #include #include @@ -63,142 +76,72 @@ slapi_log_error(int i, char *f, char *fmt, ...) int main(int argc, char **argv) { - int s, ret; - struct sockaddr_in sin; + int s, ret, port; struct sockaddr_un sunaddr; s = socket(PF_INET, SOCK_DGRAM, 0); - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - if (bindresvport(s, &sin) != 0) { - printf("error binding to reserved port\n"); + if ((port = portmap_bind_resvport(s, AF_INET, 0)) < 0) { + printf("error binding to reserved port, using 541\n"); + port = 541; /* arbitrary */ } else { - printf("bound to reserved port %d\n", ntohs(sin.sin_port)); + printf("bound to reserved port %d\n", port); } -#ifdef HAVE_LIBTIRPC - s = socket(AF_LOCAL, SOCK_STREAM, 0); - memset(&sunaddr, 0, sizeof(sunaddr)); - sunaddr.sun_family = AF_LOCAL; - strcpy(sunaddr.sun_path, _PATH_RPCBINDSOCK); - connect(s, (struct sockaddr *) &sunaddr, sizeof(sunaddr)); -#endif + s = portmap_create_client_socket("portmap", port); + setregid(2516, 2516); setreuid(2510, 2510); + portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET, IPPROTO_TCP, + 0); + portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET, IPPROTO_UDP, + 0); + portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET6, IPPROTO_TCP, + 0); + portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET6, IPPROTO_UDP, + 0); portmap_register("portmap", s, YPPROG, YPVERS, AF_INET, IPPROTO_TCP, - ntohs(sin.sin_port)); + port); portmap_register("portmap", s, YPPROG, YPVERS, AF_INET, IPPROTO_UDP, - ntohs(sin.sin_port)); + port); portmap_register("portmap", s, YPPROG, YPVERS, AF_INET6, IPPROTO_TCP, - ntohs(sin.sin_port)); + port); portmap_register("portmap", s, YPPROG, YPVERS, AF_INET6, IPPROTO_UDP, - ntohs(sin.sin_port)); + port); ret = system("rpcinfo | grep ypserv"); ret = system("rpcinfo -p | grep ypserv"); - portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET, IPPROTO_TCP); - portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET, IPPROTO_UDP); - portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET6, IPPROTO_TCP); - portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET6, IPPROTO_UDP); + portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET, IPPROTO_TCP, + port); + portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET, IPPROTO_UDP, + port); + portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET6, IPPROTO_TCP, + port); + portmap_unregister("portmap", s, YPPROG, YPVERS, AF_INET6, IPPROTO_UDP, + port); return 0; } #endif static bool_t -portmap_register_work(const char *module, int resv_sock, - int program, int version, - int family, int protocol, int port, - bool_t create) +portmap_register_work(const char *module, int client_sock, + bool_t stream, + struct sockaddr *dgram_address, socklen_t addrlen, + int prog, int vers, int proc, + void *args, xdrproc_t args_xdr) { char portmap_buf[4000], auth_buf[4000], reply_buf[8000], *log_id; int portmap_length, reply_length; AUTH *auth; XDR portmap_xdrs, auth_xdrs; struct rpc_msg msg; -#ifdef HAVE_LIBTIRPC - char address_buf[32]; - struct rpcb rpcb; int fragment_length; -#else - struct pmap map; -#endif - int prog, vers, proc; bool_t ret = FALSE; struct sockaddr addr; - struct sockaddr_in addr4; - struct sockaddr_in6 addr6; - struct sockaddr_un addrun; - union { - struct sockaddr_in addr4; - } client_addr; - socklen_t client_addrlen; struct pollfd pollfd; int i; static u_long xid; log_id = (char *) module; - memset(&addr, 0, sizeof(addr)); - addr.sa_family = AF_UNSPEC; - - /* Populate the arguments for this request. */ -#ifdef HAVE_LIBTIRPC - rpcb.r_prog = program; - rpcb.r_vers = version; - rpcb.r_owner = module; - switch (family) { - case AF_INET: - snprintf(address_buf, sizeof(address_buf), "0.0.0.0.%d.%d", - (port >> 8) & 0xff, port & 0xff); - switch (protocol) { - case IPPROTO_TCP: - rpcb.r_netid = "tcp"; - rpcb.r_addr = address_buf; - break; - case IPPROTO_UDP: - rpcb.r_netid = "udp"; - rpcb.r_addr = address_buf; - break; - default: - rpcb.r_netid = NULL; - rpcb.r_addr = NULL; - break; - } - break; - case AF_INET6: - snprintf(address_buf, sizeof(address_buf), "::.%d.%d", - (port >> 8) & 0xff, port & 0xff); - switch (protocol) { - case IPPROTO_TCP: - rpcb.r_netid = "tcp6"; - rpcb.r_addr = address_buf; - break; - case IPPROTO_UDP: - rpcb.r_netid = "udp6"; - rpcb.r_addr = address_buf; - break; - default: - rpcb.r_netid = NULL; - rpcb.r_addr = NULL; - break; - } - break; - default: - rpcb.r_netid = NULL; - rpcb.r_addr = NULL; - break; - } - prog = RPCBPROG; - vers = RPCBVERS; - proc = create ? RPCBPROC_SET : RPCBPROC_UNSET; -#else - map.pm_prog = program; - map.pm_vers = version; - map.pm_prot = protocol; - map.pm_port = port; - prog = PMAPPROG; - vers = PMAPVERS; - proc = create ? PMAPPROC_SET : PMAPPROC_UNSET; -#endif /* Build the RPC header. */ memset(&msg, 0, sizeof(msg)); - msg.rm_xid = xid = (time(NULL) ^ port ^ protocol ^ proc); + msg.rm_xid = xid = (time(NULL) ^ prog ^ vers ^ proc ^ getpid()); msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = 2; msg.rm_call.cb_prog = prog; @@ -213,93 +156,102 @@ portmap_register_work(const char *module, int resv_sock, msg.rm_call.cb_verf = auth->ah_verf; /* Encode the header and the arguments, then clean up temporaries. */ -#ifdef HAVE_LIBTIRPC - xdrmem_create(&portmap_xdrs, portmap_buf + 4, sizeof(portmap_buf) - 4, - XDR_ENCODE); -#else - xdrmem_create(&portmap_xdrs, portmap_buf, sizeof(portmap_buf), - XDR_ENCODE); -#endif + if (stream) { + /* Leave room for the message length on a stream connection. */ + xdrmem_create(&portmap_xdrs, + portmap_buf + 4, sizeof(portmap_buf) - 4, + XDR_ENCODE); + } else { + /* Straight-up datagram. */ + xdrmem_create(&portmap_xdrs, portmap_buf, sizeof(portmap_buf), + XDR_ENCODE); + } + + /* Encode the message header and the call itself. */ xdr_callmsg(&portmap_xdrs, &msg); -#ifdef HAVE_LIBTIRPC - xdr_rpcb(&portmap_xdrs, &rpcb); -#else - xdr_pmap(&portmap_xdrs, &map); -#endif + portmap_length = xdr_getpos(&portmap_xdrs); + (*args_xdr)(&portmap_xdrs, args); + slapi_log_error(SLAPI_LOG_PLUGIN, log_id, + "encoded %d bytes of args\n", + xdr_getpos(&portmap_xdrs) - portmap_length); portmap_length = xdr_getpos(&portmap_xdrs); auth_destroy(auth); xdr_destroy(&auth_xdrs); xdr_destroy(&portmap_xdrs); memset(&portmap_xdrs, 0, sizeof(portmap_xdrs)); -#ifdef HAVE_LIBTIRPC - /* Compute the message length and prepend it. */ - fragment_length = portmap_length; - fragment_length |= 0x80000000; - portmap_buf[0] = (fragment_length >> 24) & 0xff; - portmap_buf[1] = (fragment_length >> 16) & 0xff; - portmap_buf[2] = (fragment_length >> 8) & 0xff; - portmap_buf[3] = (fragment_length >> 0) & 0xff; - portmap_length += 4; -#else - /* "Connect" to the local portmapper. */ - memset(&addr4, 0, sizeof(addr4)); - addr4.sin_family = AF_INET; - addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr4.sin_port = htons(PMAPPORT); - i = connect(resv_sock, (struct sockaddr*) &addr4, sizeof(addr4)); - if (i != 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, log_id, - "error targeting portmap with sock %d: %s\n", - resv_sock, strerror(errno)); - return FALSE; + if (stream) { + /* Compute the request message length and prepend it. */ + fragment_length = portmap_length; + fragment_length |= 0x80000000; + portmap_buf[0] = (fragment_length >> 24) & 0xff; + portmap_buf[1] = (fragment_length >> 16) & 0xff; + portmap_buf[2] = (fragment_length >> 8) & 0xff; + portmap_buf[3] = (fragment_length >> 0) & 0xff; + portmap_length += 4; + } else { + /* Point the datagram socket at the remote. */ + if (connect(client_sock, dgram_address, addrlen) != 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, log_id, + "error targeting portmap: %s\n", + strerror(errno)); + return FALSE; + } } -#endif - /* Transmit our request. Retry a few times if it doesn't go through. */ + /* Transmit our request. Be ready to retry a few times if it doesn't + * go through for datagram connections. */ for (i = 1; i < 32; i *= 2) { /* Try to send our request. If there's any problem, * immediately retry. */ - if (send(resv_sock, &portmap_buf, portmap_length, + if (send(client_sock, &portmap_buf, portmap_length, 0) != portmap_length) { slapi_log_error(SLAPI_LOG_PLUGIN, log_id, - "error sending request to portmap\n"); - continue; + "error sending request to portmap or " + "rpcbind on %d: %s\n", client_sock, + strerror(errno)); + if (stream) { + break; + } else { + continue; + } } /* Wait for a response. */ - pollfd.fd = resv_sock; + pollfd.fd = client_sock; pollfd.events = POLLIN | POLLERR; - if ((poll(&pollfd, 1, i * 1000) > 0) && + if ((poll(&pollfd, 1, stream ? -1 : i * 1000) > 0) && (pollfd.revents & POLLIN)) { /* Read the response. */ - client_addrlen = sizeof(client_addr); - reply_length = recvfrom(resv_sock, - reply_buf, sizeof(reply_buf), 0, - (struct sockaddr *)&client_addr, - &client_addrlen); + reply_length = recv(client_sock, + reply_buf, sizeof(reply_buf), 0); /* Decode the response. */ if (reply_length > 0) { /* Decode an RPC header and the returned * boolean from the buffer. */ memset(&msg, 0, sizeof(msg)); -#ifdef HAVE_LIBTIRPC - fragment_length = ((reply_buf[0] << 24) | - (reply_buf[1] << 16) | - (reply_buf[2] << 8) | - (reply_buf[3] << 0)) & - 0x7fffffff; - if (fragment_length != reply_length - 4) { - fprintf(stderr, "Bad reply length!\n"); + if (stream) { + /* Strip off the fragment length. */ + fragment_length = (reply_buf[0] << 24) | + (reply_buf[1] << 16) | + (reply_buf[2] << 8) | + (reply_buf[3] << 0); + if ((fragment_length & 0x80000000) == 0) { + /* XXX - if it's not the whole + * message, then we're screwed. + * */; + } + fragment_length &= 0x7fffffff; + xdrmem_create(&portmap_xdrs, + reply_buf + 4, + reply_length - 4, + XDR_DECODE); + } else { + /* Straight-up datagram. */ + xdrmem_create(&portmap_xdrs, + reply_buf, reply_length, + XDR_DECODE); } - xdrmem_create(&portmap_xdrs, - reply_buf + 4, reply_length - 4, - XDR_DECODE); -#else - xdrmem_create(&portmap_xdrs, - reply_buf, reply_length, - XDR_DECODE); -#endif msg.rm_reply.rp_acpt.ar_results.where = (caddr_t) &ret; msg.rm_reply.rp_acpt.ar_results.proc = @@ -317,15 +269,20 @@ portmap_register_work(const char *module, int resv_sock, } } -#ifndef HAVE_LIBTIRPC - /* Disconnect from the portmapper, but keep our socket around. */ - connect(resv_sock, &addr, sizeof(addr)); - if (i == 32) { - slapi_log_error(SLAPI_LOG_PLUGIN, log_id, - "timeout registering with portmap service\n"); - return FALSE; + if (!stream) { + /* "Disconnect" from a datagram service, but keep our client + * socket around. */ + memset(&addr, 0, sizeof(addr)); + addr.sa_family = AF_UNSPEC; + connect(client_sock, &addr, sizeof(addr)); + /* Check for a timeout. */ + if (i == 32) { + slapi_log_error(SLAPI_LOG_PLUGIN, log_id, + "timeout registering with portmap " + "service\n"); + return FALSE; + } } -#endif /* Check that the portmapper didn't just reject the request out of * hand. */ @@ -410,18 +367,251 @@ portmap_register_work(const char *module, int resv_sock, return ret; } +struct rpcbind_req { + int program; + int version; + char *network; + char *address; + char *owner; +}; + +static bool_t +portmap_xdr_rpcbind_req(XDR *xdrs, struct rpcbind_req *req) +{ + return xdr_int(xdrs, &req->program) && + xdr_int(xdrs, &req->version) && + xdr_wrapstring(xdrs, &req->network) && + xdr_wrapstring(xdrs, &req->address) && + xdr_wrapstring(xdrs, &req->owner); +} + +static bool_t +portmap_register_rpcbind(const char *module, int client_sock, + bool_t create, + int family, int protocol, int port, + int program, int version) +{ + char address_buf[64]; + struct rpcbind_req req; + req.program = program; + req.version = version; + req.owner = "superuser"; + switch (family) { + case AF_INET: + snprintf(address_buf, sizeof(address_buf), "0.0.0.0.%d.%d", + (port >> 8) & 0xff, port & 0xff); + switch (protocol) { + case IPPROTO_TCP: + req.network = "tcp"; + req.address = address_buf; + break; + case IPPROTO_UDP: + req.network = "udp"; + req.address = address_buf; + break; + default: + req.network = NULL; + req.address = NULL; + break; + } + break; + case AF_INET6: + snprintf(address_buf, sizeof(address_buf), "::.%d.%d", + (port >> 8) & 0xff, port & 0xff); + switch (protocol) { + case IPPROTO_TCP: + req.network = "tcp6"; + req.address = address_buf; + break; + case IPPROTO_UDP: + req.network = "udp6"; + req.address = address_buf; + break; + default: + req.network = NULL; + req.address = NULL; + break; + } + break; + default: + req.network = NULL; + req.address = NULL; + break; + } + return portmap_register_work(module, client_sock, TRUE, NULL, 0, + RPCBPROG, RPCBVERS, + create ? RPCBPROC_SET : RPCBPROC_UNSET, + &req, + (xdrproc_t) &portmap_xdr_rpcbind_req); +} + +static bool_t +portmap_register_portmap(const char *module, int client_sock, + bool_t create, + int protocol, int port, int program, int version) +{ + struct pmap map; + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_LOOPBACK; + sin.sin_port = htons(PMAPPORT); + map.pm_prog = program; + map.pm_vers = version; + map.pm_prot = protocol; + map.pm_port = port; + return portmap_register_work(module, client_sock, + FALSE, + (struct sockaddr *) &sin, sizeof(sin), + PMAPPROG, PMAPVERS, + create ? PMAPPROC_SET : PMAPPROC_UNSET, + &map, (xdrproc_t) &xdr_pmap); +} + +static bool_t +portmap_is_stream(int sd) +{ + int socktype; + socklen_t socklen = sizeof(socktype); + if (getsockopt(sd, SOL_SOCKET, SO_TYPE, &socktype, &socklen) == 0) { + return (socklen == sizeof(int)) && (socktype == SOCK_STREAM); + } + return FALSE; +} + bool_t portmap_register(const char *log_id, int resv_sock, - int program, int version, int family, int protocol, int port) + int program, int version, + int family, int protocol, int port) { - return portmap_register_work(log_id, resv_sock, program, version, - family, protocol, port, TRUE); + return portmap_is_stream(resv_sock) ? + portmap_register_rpcbind(log_id, resv_sock, + TRUE, + family, protocol, port, + program, version) : + portmap_register_portmap(log_id, resv_sock, + TRUE, + protocol, port, + program, version); } bool_t portmap_unregister(const char *log_id, int resv_sock, - int program, int version, int family, int protocol) + int program, int version, + int family, int protocol, int port) { - return portmap_register_work(log_id, resv_sock, program, version, - family, protocol, 0, FALSE); + return portmap_is_stream(resv_sock) ? + portmap_register_rpcbind(log_id, resv_sock, + FALSE, + family, protocol, port, + program, version) : + portmap_register_portmap(log_id, resv_sock, + FALSE, + protocol, port, + program, version); +} + +int +portmap_bind_resvport(int fd, int family, int but_not) +{ + int i, offset, port; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + switch (family) { + case AF_INET: + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + break; + case AF_INET6: + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + break; + default: + return -1; + } + offset = getpid() % 512; + for (i = 512; i < 1024; i++) { + port = ((offset + i) % 512) + 512; + if (port == but_not) { + continue; + } + switch (family) { + case AF_INET: + sin.sin_port = htons(port); + if (bind(fd, (struct sockaddr*) &sin, + sizeof(sin)) == 0) { + return port; + } + break; + case AF_INET6: + sin6.sin6_port = htons(port); + if (bind(fd, (struct sockaddr*) &sin6, + sizeof(sin6)) == 0) { + return port; + } + break; + default: + return -1; + } + } + return -1; +} + +static int +portmap_create_rpcbind_client_socket(char *module) +{ + int sockfd; + struct sockaddr_un sockun; + + sockfd = socket(PF_LOCAL, SOCK_STREAM, 0); + if (sockfd == -1) { + slapi_log_error(SLAPI_LOG_FATAL, module, + "error allocating portmap client socket\n"); + return -1; + } + memset(&sockun, 0, sizeof(sockun)); + sockun.sun_family = AF_LOCAL; + strcpy(sockun.sun_path, _PATH_RPCBINDSOCK); + if (connect(sockfd, (struct sockaddr *) &sockun, sizeof(sockun)) != 0) { + slapi_log_error(SLAPI_LOG_FATAL, module, + "error connecting rpcbind client " + "socket to the service\n"); + close(sockfd); + return -1; + } + return sockfd; +} + +static int +portmap_create_portmap_client_socket(char *module, int but_not) +{ + int sockfd; + sockfd = socket(PF_INET, SOCK_DGRAM, 0); + if (sockfd == -1) { + slapi_log_error(SLAPI_LOG_FATAL, module, + "error allocating portmap client socket\n"); + return -1; + } + if (portmap_bind_resvport(sockfd, AF_INET, but_not) <= 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, module, + "unable to bind portmap client socket to a " + "privileged port\n"); + close(sockfd); + return -1; + } + return sockfd; +} + +int +portmap_create_client_socket(char *module, int but_not) +{ + int sock; + sock = portmap_create_rpcbind_client_socket(module); + if (sock == -1) { + sock = portmap_create_portmap_client_socket(module, but_not); + } + slapi_log_error(SLAPI_LOG_PLUGIN, module, + "created client socket %d for portmap client\n", + sock); + return sock; } -- cgit