/* * 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 #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_TCPD_H #include #endif #ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H #include #include #include #else #include #endif #include "wrap.h" #include "disp-nis.h" #include "map.h" #include "nis.h" #include "plugin.h" #include "portmap.h" /* the module initialization function */ static Slapi_PluginDesc plugin_description = { .spd_id = "nis-plugin", .spd_vendor = "redhat.com", .spd_version = PACKAGE_VERSION, .spd_description = "NIS Server Plugin", }; /* Start the plugin's work thread. */ static int plugin_startup(Slapi_PBlock *pb) { struct plugin_state *state; const char *pname; int i, protocol; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); slapi_pblock_get(pb, SLAPI_TARGET_DN, &state->plugin_base); /* Populate the maps and data. */ map_startup(state); /* Register the listener sockets with the portmapper. */ if (state->pmap_client_socket != -1) { /* kick off any other NIS servers */ portmap_unregister(plugin_description.spd_id, state->pmap_client_socket, YPPROG, YPVERS); /* register our ports */ for (i = 0; i < state->n_listeners; i++) { switch (state->listener[i].type) { case SOCK_DGRAM: protocol = IPPROTO_UDP; pname = "UDP"; break; case SOCK_STREAM: protocol = IPPROTO_TCP; pname = "TCP"; break; default: /* never reached */ assert(0); break; } if (protocol == IPPROTO_IP) { continue; } if (!portmap_register(plugin_description.spd_id, state->pmap_client_socket, YPPROG, YPVERS, protocol, state->listener[i].port)) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error registering %s service " "with portmap\n", pname); } } } /* Start a new listening thread to handle incoming traffic. */ state->tid = wrap_start_thread(&dispatch_thread, state); if (state->tid == NULL) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error starting listener thread\n"); return -1; } slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "plugin startup completed\n"); return 0; } /* Stop the plugin's work thread. */ static int plugin_shutdown(Slapi_PBlock *pb) { struct plugin_state *state; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); wrap_stop_thread(state->tid); if (state->pmap_client_socket != -1) { /* Clear our registration with the portmapper. */ portmap_unregister(plugin_description.spd_id, state->pmap_client_socket, YPPROG, YPVERS); } free(state); return 0; } /* Read the parameters which we need at initialization-time. */ static void plugin_read_config(Slapi_PBlock *plugin_pb, int *port) { Slapi_ComponentId *id; const char *dn, **argv = NULL; int argc = 0, i; *port = 0; slapi_pblock_get(plugin_pb, SLAPI_PLUGIN_IDENTITY, &id); slapi_pblock_get(plugin_pb, SLAPI_TARGET_DN, &dn); slapi_pblock_get(plugin_pb, SLAPI_PLUGIN_ARGC, &argc); slapi_pblock_get(plugin_pb, SLAPI_PLUGIN_ARGV, &argv); for (i = 0; (i < argc) && (argv != NULL) && (argv[i] != NULL); i++) { switch (i) { case 0: *port = atoi(argv[i]); slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "argument 0 (port) = %d\n", *port); break; } } } /* Handle the part of startup that needs to be done before we drop privileges: * bind to listening ports. */ static int plugin_state_init(Slapi_PBlock *pb, struct plugin_state **lstate) { int port, sockfd = -1, err, i; struct plugin_state *state = NULL; struct sockaddr_in sin; struct sockaddr_in6 sin6; state = malloc(sizeof(*state)); if (state == NULL) { goto failed; } memset(state, 0, sizeof(*state)); state->plugin_base = NULL; state->plugin_desc = &plugin_description; state->max_value_size = DEFAULT_MAX_VALUE_SIZE; state->max_dgram_size = DEFAULT_MAX_DGRAM_SIZE; slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &state->plugin_identity); slapi_pblock_get(pb, SLAPI_TARGET_DN, &state->plugin_base); slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "init: target-dn is %s%s%s\n", state->plugin_base ? "\"" : "", state->plugin_base ? state->plugin_base : "NULL", state->plugin_base ? "\"" : ""); plugin_read_config(pb, &port); #ifdef HAVE_TCPD_H state->request_info = malloc(sizeof(*(state->request_info))); if ((state->request_info == NULL) || (request_init(state->request_info, 0) != state->request_info) || (request_set(state->request_info, RQ_DAEMON, DEFAULT_TCPWRAP_NAME, 0) != state->request_info)) { slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "error initializing tcp_wrappers for \"%s\"\n", plugin_description.spd_id); return -1; } #else state->request_info = NULL; #endif /* Create a socket for use in communicating with the portmapper. */ sockfd = socket(PF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { goto failed; } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; if (bindresvport(sockfd, &sin) != 0) { close(sockfd); goto failed; } state->pmap_client_socket = sockfd; /* We need to bind on privileged ports for both datagram and connected * listeners, over both IPv4 and IPv6. */ state->n_listeners = 0; for (i = 0; i < 2; i++) { int pf, type, one, flags, ret; const char *sock_desc; /* Before we do anything else, on our second trip through, make * sure that the first socket was created, because we'll need * it for communicating with the portmapper. */ if ((i > 0) && (state->n_listeners == 0)) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "no socket available to use for " "communicating with portmapper\n"); continue; } /* Figure out what kind of socket we need, and a textual * term to use in log messages. */ pf = (i & 2) ? PF_INET6 : PF_INET; type = (i & 1) ? SOCK_STREAM : SOCK_DGRAM; sock_desc = (i & 2) ? ((i & 1) ? "tcp6" : "udp6") : ((i & 1) ? "tcp" : "udp"); /* Allocate the socket. */ sockfd = socket(pf, type, 0); if (sockfd == -1) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error creating a %s socket\n", sock_desc); continue; } /* Mark the socket as reusable and non-blocking. */ one = 1; if (setsockopt(sockfd, IPPROTO_IP, SO_REUSEADDR, &one, sizeof(one)) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error marking %s socket for reuse, " "continuing\n", sock_desc); } flags = fcntl(sockfd, F_GETFL); if ((flags & O_NONBLOCK) == 0) { fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); } /* Bind to the server port. */ memset(&sin, 0, sizeof(sin)); memset(&sin6, 0, sizeof(sin6)); sin.sin_family = AF_INET; sin6.sin6_family = AF_INET6; if (port == 0) { ret = (pf == PF_INET6) ? bindresvport(sockfd, (struct sockaddr_in*) &sin6) : bindresvport(sockfd, &sin); } else { sin.sin_port = htons(port); sin6.sin6_port = htons(port); ret = (pf == PF_INET6) ? bind(sockfd, (struct sockaddr*) &sin6, sizeof(sin6)) : bind(sockfd, (struct sockaddr*) &sin, sizeof(sin)); } if (ret != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error binding %s socket to a " "privileged port: %s\n", sock_desc, strerror(errno)); close(sockfd); continue; } /* Pick out the port number that we got back, in case we used * bindresvport[6]. */ port = (pf == PF_INET6) ? ntohs(sin6.sin6_port) : ntohs(sin.sin_port); /* If it's a listening socket, let the kernel know that we're * ready to accept client connections. */ if (type == SOCK_STREAM) { if (listen(sockfd, 128) == -1) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error marking %s socket for " "listening: %s\n", sock_desc, strerror(errno)); close(sockfd); continue; } } /* Save the other info. */ state->listener[state->n_listeners].fd = sockfd; state->listener[state->n_listeners].port = port; state->listener[state->n_listeners].pf = pf; state->listener[state->n_listeners].type = type; slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "listening on port %d for %s clients\n", state->listener[state->n_listeners].port, sock_desc); state->n_listeners++; } slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "set up %d listening sockets\n", state->n_listeners); *lstate = state; return 0; failed: err = errno; free(state); errno = err; return -1; } int nis_plugin_init(Slapi_PBlock *pb) { struct plugin_state *state = NULL; /* Allocate a memory pool and start listening for connections. */ if (plugin_state_init(pb, &state) == -1) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error setting up plugin\n"); return -1; } /* Register the plugin with the server. */ slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03); slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, &plugin_description); slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, &plugin_startup); slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, &plugin_shutdown); slapi_pblock_set(pb, SLAPI_PLUGIN_PRIVATE, state); /* Let the backend do its registration. */ map_init(pb, state); slapi_log_error(SLAPI_LOG_PLUGIN, state->plugin_desc->spd_id, "registered plugin hooks\n"); return 0; }