/* * Copyright 2008 Red Hat, Inc. * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This Program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this Program; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include #include #include #ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H #include #include #include #else #include #endif #include #include #include #include "portmap.h" #ifdef PORTMAP_MAIN #include #include int slapi_log_error(int i, char *f, char *fmt, ...) { va_list va; va_start(va, fmt); vfprintf(stderr, fmt, va); va_end(va); return 0; } int main(int argc, char **argv) { int s; struct sockaddr_in sin; 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"); } setreuid(2510, 2510); portmap_unregister("portmap", s, YPPROG, YPVERS); portmap_register("portmap", s, YPPROG, YPVERS, IPPROTO_UDP, ntohs(sin.sin_port)); return 0; } #endif static bool_t portmap_register_work(const char *module, int resv_sock, int program, int version, int protocol, int port, int proc) { 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; struct pmap map; bool_t ret = FALSE; struct sockaddr addr; struct sockaddr_in addr4; 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; /* Build the RPC header. */ memset(&msg, 0, sizeof(msg)); msg.rm_xid = xid = (time(NULL) ^ port ^ protocol ^ proc); msg.rm_direction = CALL; msg.rm_call.cb_rpcvers = 2; msg.rm_call.cb_prog = PMAPPROG; msg.rm_call.cb_vers = PMAPVERS; msg.rm_call.cb_proc = proc; /* Build an authenticator. */ xdrmem_create(&auth_xdrs, auth_buf, sizeof(auth_buf), XDR_ENCODE); auth = authnone_create(); auth_marshall(auth, &auth_xdrs); msg.rm_call.cb_cred = auth->ah_cred; msg.rm_call.cb_verf = auth->ah_verf; /* Populate the arguments for this request. */ map.pm_prog = program; map.pm_vers = version; map.pm_prot = protocol; map.pm_port = port; /* Encode the header and the arguments, then clean up temporaries. */ xdrmem_create(&portmap_xdrs, portmap_buf, sizeof(portmap_buf), XDR_ENCODE); xdr_callmsg(&portmap_xdrs, &msg); xdr_pmap(&portmap_xdrs, &map); portmap_length = xdr_getpos(&portmap_xdrs); auth_destroy(auth); xdr_destroy(&auth_xdrs); xdr_destroy(&portmap_xdrs); memset(&portmap_xdrs, 0, sizeof(portmap_xdrs)); /* 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); connect(resv_sock, (struct sockaddr*) &addr4, sizeof(addr4)); /* Transmit our request. Retry a few times if it doesn't go through. */ 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, 0) != portmap_length) { slapi_log_error(SLAPI_LOG_PLUGIN, log_id, "error sending request to portmap\n"); continue; } /* Wait for a response. */ pollfd.fd = resv_sock; pollfd.events = POLLIN | POLLERR; if ((poll(&pollfd, 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); /* Decode the response. */ if (reply_length > 0) { /* Decode an RPC header and the returned * boolean from the buffer. */ memset(&msg, 0, sizeof(msg)); xdrmem_create(&portmap_xdrs, reply_buf, reply_length, XDR_DECODE); msg.rm_reply.rp_acpt.ar_results.where = (caddr_t) &ret; msg.rm_reply.rp_acpt.ar_results.proc = (xdrproc_t) xdr_bool; if (xdr_replymsg(&portmap_xdrs, &msg)) { if ((msg.rm_direction == REPLY) && (msg.rm_xid == xid)) { xdr_destroy(&portmap_xdrs); break; } } xdr_destroy(&portmap_xdrs); memset(&portmap_xdrs, 0, sizeof(portmap_xdrs)); } } } /* 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; } /* Check that the portmapper didn't just reject the request out of * hand. */ if (msg.rm_reply.rp_stat != MSG_ACCEPTED) { slapi_log_error(SLAPI_LOG_PLUGIN, log_id, "portmap request not accepted\n"); switch (msg.rm_reply.rp_rjct.rj_stat) { const char *auth_status; case AUTH_ERROR: switch (msg.rm_reply.rp_rjct.rj_why) { case AUTH_OK: auth_status = "ok"; break; case AUTH_BADCRED: auth_status = "bad credentials"; break; case AUTH_REJECTEDCRED: auth_status = "rejected credentials"; break; case AUTH_BADVERF: auth_status = "bad verifier"; break; case AUTH_REJECTEDVERF: auth_status = "rejected verifier"; break; case AUTH_TOOWEAK: auth_status = "too weak"; break; case AUTH_INVALIDRESP: auth_status = "invalid response"; break; case AUTH_FAILED: default: auth_status = "unknown error"; break; } slapi_log_error(SLAPI_LOG_PLUGIN, log_id, "portmap request rejected: " "authentication failed: %s\n", auth_status); break; case RPC_MISMATCH: slapi_log_error(SLAPI_LOG_PLUGIN, log_id, "portmap request rejected: " "RPC mismatch\n"); break; } return FALSE; xdr_destroy(&portmap_xdrs); } /* Validate the portmapper's credentials. */ auth = authunix_create_default(); if (auth_validate(auth, &msg.rm_reply.rp_acpt.ar_verf)) { slapi_log_error(SLAPI_LOG_PLUGIN, log_id, "portmap reply authenticated\n"); } else { slapi_log_error(SLAPI_LOG_PLUGIN, log_id, "portmap reply failed authentication\n"); } auth_destroy(auth); /* Check if we the portmapper gave us a reply argument. */ if (msg.rm_reply.rp_acpt.ar_stat != SUCCESS) { slapi_log_error(SLAPI_LOG_PLUGIN, log_id, "portmap request not processed\n"); xdr_destroy(&portmap_xdrs); return FALSE; } /* Check what happened. */ if (ret) { slapi_log_error(SLAPI_LOG_PLUGIN, log_id, "portmap request succeeded\n"); } else { slapi_log_error(SLAPI_LOG_PLUGIN, log_id, "portmap response did not include a reply\n"); } xdr_destroy(&portmap_xdrs); return ret; } bool_t portmap_register(const char *log_id, int resv_sock, int program, int version, int protocol, int port) { return portmap_register_work(log_id, resv_sock, program, version, protocol, port, PMAPPROC_SET); } bool_t portmap_unregister(const char *log_id, int resv_sock, int program, int version) { return portmap_register_work(log_id, resv_sock, program, version, 0, 0, PMAPPROC_UNSET); }