diff options
-rw-r--r-- | src/Makefile | 2 | ||||
-rw-r--r-- | src/plugin.c | 318 | ||||
-rw-r--r-- | src/plugin.ldif | 2 | ||||
-rw-r--r-- | src/portmap.c | 211 | ||||
-rw-r--r-- | src/portmap.h | 10 | ||||
-rw-r--r-- | src/schema.c | 43 | ||||
-rw-r--r-- | src/schema.h | 10 |
7 files changed, 433 insertions, 163 deletions
diff --git a/src/Makefile b/src/Makefile index 0454c12..53efd4d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ CFLAGS = -g3 -Wall -Wimplicit -Wextra -Wno-unused -fPIC -D_REENTRANT $(shell pkg-config --cflags nspr nss) LDFLAGS = -lnsl -lpthread -plugin.so: plugin.c +plugin.so: plugin.c portmap.c schema.c $(CC) $(CFLAGS) -shared -o $@ $^ $(LDFLAGS) diff --git a/src/plugin.c b/src/plugin.c index 885ccde..333dbee 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -22,155 +22,37 @@ #include <plarenas.h> #include <dirsrv/slapi-plugin.h> +#include "portmap.h" +#include "schema.h" + #define PACKAGE_VERSION "0.0" #define PORT 388 /* the module initialization function */ static Slapi_PluginDesc plugin_description = { - .spd_id = "my-plugin", + .spd_id = "nis-plugin", .spd_vendor = "hamdingers.org", .spd_version = PACKAGE_VERSION, - .spd_description = "sample plugin", -}; -struct domain { - char *domain; - struct map { - char *name; - char *key; - char *format; - } *maps; - int n_maps; + .spd_description = "NIS Service Plugin", }; -struct state { + +struct plugin_state { + PLArenaPool *arena; pthread_t tid; Slapi_ComponentId *plugin_identity; - PLArenaPool *arena; - int listeners; + int resvport; + int n_listeners; int listenfd[4]; int sock_pf[4]; int sock_type[4]; }; + struct search_stream_data { - struct state *state; + struct plugin_state *state; int client; }; -struct map default_maps[] = { - {"passwd.byname", "uid", "uid"}, - {"passwd.bynumber", "uidNumber", "uid"}, -}; -struct domain local_domains = { - ".local", - default_maps, - sizeof(default_maps) / sizeof(default_maps[0]), -}; - -static int -setup_listeners(struct state **lstate) -{ - int sockfd = -1, err, i; - struct sockaddr_in addr4; - struct sockaddr_in6 addr6; - struct state *state; - PLArenaPool *arena = NULL; - - arena = PORT_NewArena(sizeof(double)); - if (arena == NULL) { - goto failed; - } - state = PORT_ArenaZAlloc(arena, sizeof(*state)); - if (state == NULL) { - goto failed; - } - state->arena = arena; - - for (i = 0; i < 4; i++) { - int sock_pf, sock_type, one = 1; - struct sockaddr *addr; - socklen_t addrlen; - switch (i) { - case 0: - sock_pf = PF_INET; - sock_type = SOCK_DGRAM; - addr = (struct sockaddr *) &addr4; - addrlen = sizeof(addr4); - break; - case 1: - sock_pf = PF_INET; - sock_type = SOCK_STREAM; - addr = (struct sockaddr *) &addr4; - addrlen = sizeof(addr4); - break; - case 2: - sock_pf = PF_INET6; - sock_type = SOCK_DGRAM; - addr = (struct sockaddr *) &addr6; - addrlen = sizeof(addr6); - break; - case 3: - sock_pf = PF_INET6; - sock_type = SOCK_STREAM; - addr = (struct sockaddr *) &addr6; - addrlen = sizeof(addr6); - break; - } - memset(&addr4, 0, sizeof(addr4)); - addr4.sin_port = htons(PORT); - memset(&addr6, 0, sizeof(addr6)); - addr6.sin6_port = htons(PORT); - sockfd = socket(sock_pf, sock_type, 0); - if (sockfd == -1) { - slapi_log_error(SLAPI_LOG_PLUGIN, - plugin_description.spd_id, - "error creating a listening socket\n"); - continue; - } - if (setsockopt(sockfd, IPPROTO_IP, SO_REUSEADDR, - &one, sizeof(one)) != 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, - plugin_description.spd_id, - "error marking socket for reuse\n"); - } - if (bind(sockfd, addr, addrlen) != 0) { - slapi_log_error(SLAPI_LOG_PLUGIN, - plugin_description.spd_id, - "error binding to listening port\n"); - close(sockfd); - continue; - } - if ((sock_type == SOCK_STREAM) && (listen(sockfd, 128) == -1)) { - slapi_log_error(SLAPI_LOG_PLUGIN, - plugin_description.spd_id, - "error marking socket for listening\n"); - close(sockfd); - continue; - } - state->listenfd[state->listeners] = sockfd; - state->sock_pf[state->listeners] = sock_pf; - state->sock_type[state->listeners] = sock_type; - slapi_log_error(SLAPI_LOG_PLUGIN, - plugin_description.spd_id, - "listening on port %d for %s%s clients\n", - PORT, - sock_type == SOCK_STREAM ? "tcp" : "udp", - sock_pf == PF_INET6 ? "6" : ""); - state->listeners++; - } - slapi_log_error(SLAPI_LOG_PLUGIN, - plugin_description.spd_id, - "set up %d listening sockets\n", state->listeners); - *lstate = state; - return 0; -failed: - err = errno; - if (arena != NULL) { - PORT_FreeArena(arena, PR_TRUE); - } - errno = err; - return -1; -} - static void cb_stream_result(int rc, void *callback_data) { @@ -231,7 +113,7 @@ cb_stream_referral(char *referral, void *callback_data) } static void -handle_stream_client(struct state *state, int client) +handle_stream_client(struct plugin_state *state, int client) { Slapi_PBlock *pblock; int i; @@ -262,7 +144,7 @@ handle_stream_client(struct state *state, int client) } static void -handle_dgram_client(struct state *state, int sockfd, +handle_dgram_client(struct plugin_state *state, int sockfd, char *dgram, int dgram_size, struct sockaddr *client_addr, socklen_t client_addrlen) { @@ -375,7 +257,7 @@ handle_dgram_client(struct state *state, int sockfd, "domain(%s)? ", p); reply.rm_reply.rp_stat = MSG_ACCEPTED; reply.rm_reply.rp_acpt.ar_stat = SUCCESS; - bool_ret = strcmp(p, ".local") == 0; + bool_ret = schema_supports_domain(p); reply.rm_reply.rp_acpt.ar_results.where = &bool_ret; reply.rm_reply.rp_acpt.ar_results.proc = xdr_bool; if ((request.rm_call.cb_proc == YPPROC_DOMAIN_NONACK) && @@ -580,7 +462,7 @@ handle_dgram_client(struct state *state, int sockfd, } static void -handle_stream_client_new(struct state *state, int client) +handle_stream_client_new(struct plugin_state *state, int client) { int i, last, ret; int32_t len; @@ -623,7 +505,7 @@ handle_stream_client_new(struct state *state, int client) static void * process_requests(void *p) { - struct state *state = p; + struct plugin_state *state = p; struct pollfd fds[4]; int client, i; char dgram[65536]; @@ -631,18 +513,18 @@ process_requests(void *p) socklen_t client_addrlen; for (;;) { memset(&fds, 0, sizeof(fds)); - for (i = 0; i < state->listeners; i++) { + for (i = 0; i < state->n_listeners; i++) { fds[i].fd = state->listenfd[i]; fds[i].events = POLLIN; } - switch (poll(fds, state->listeners, -1) != -1) { + switch (poll(fds, state->n_listeners, -1) != -1) { case -1: return NULL; break; case 0: continue; default: - for (i = 0; i < state->listeners; i++) { + for (i = 0; i < state->n_listeners; i++) { if ((fds[i].revents & POLLIN) == 0) { continue; } @@ -683,14 +565,24 @@ process_requests(void *p) return state; } -/* Set up the plugin. */ +/* Start the plugin's work thread. */ static int plugin_start(Slapi_PBlock *pb) { - struct state *state; + struct plugin_state *state; slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); slapi_log_error(SLAPI_LOG_PLUGIN, "plugin_start", "plugin starting\n"); + if (portmap_register(state->resvport, YPPROG, YPVERS, + IPPROTO_TCP, PORT)) { + slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, + "error registering with portmap\n"); + } + if (portmap_register(state->resvport, YPPROG, YPVERS, + IPPROTO_UDP, PORT)) { + slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, + "error registering with portmap\n"); + } if (pthread_create(&state->tid, NULL, &process_requests, state) != 0) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error starting listener thread\n"); @@ -700,23 +592,139 @@ plugin_start(Slapi_PBlock *pb) "plugin started\n"); return 0; } -/* Prepare for shutdown. */ + +/* Stop the plugin's work thread. */ static int plugin_close(Slapi_PBlock *pb) { - if (pmap_unset(YPPROG, YPVERS) != 1) { - slapi_log_error(SLAPI_LOG_PLUGIN, "plugin_start", - "error unregistering ports, continuing\n"); + struct plugin_state *state; + slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state); + portmap_unregister(state->resvport, YPPROG, YPVERS); + return 0; +} + +static int +setup_listeners(struct plugin_state **lstate) +{ + int sockfd = -1, err, i; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + struct plugin_state *state; + PLArenaPool *arena = NULL; + + arena = PORT_NewArena(sizeof(double)); + if (arena == NULL) { + goto failed; } - slapi_log_error(SLAPI_LOG_PLUGIN, "my_init_function", - "plugin closing\n"); + state = PORT_ArenaZAlloc(arena, sizeof(*state)); + if (state == NULL) { + goto failed; + } + state->arena = arena; + state->resvport = -1; + + for (i = 0; i < 4; i++) { + int sock_pf, sock_type, one = 1; + struct sockaddr *addr; + socklen_t addrlen; + const char *sock_desc; + switch (i) { + case 0: + sock_pf = PF_INET; + sock_type = SOCK_DGRAM; + addr = (struct sockaddr *) &addr4; + addrlen = sizeof(addr4); + sock_desc = "udp"; + break; + case 1: + sock_pf = PF_INET; + sock_type = SOCK_STREAM; + addr = (struct sockaddr *) &addr4; + addrlen = sizeof(addr4); + sock_desc = "tcp"; + break; + case 2: + sock_pf = PF_INET6; + sock_type = SOCK_DGRAM; + addr = (struct sockaddr *) &addr6; + addrlen = sizeof(addr6); + sock_desc = "udp6"; + break; + case 3: + sock_pf = PF_INET6; + sock_type = SOCK_STREAM; + addr = (struct sockaddr *) &addr6; + addrlen = sizeof(addr6); + sock_desc = "tcp6"; + break; + } + memset(&addr4, 0, sizeof(addr4)); + addr4.sin_port = htons(PORT); + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_port = htons(PORT); + sockfd = socket(sock_pf, sock_type, 0); + if (sockfd == -1) { + slapi_log_error(SLAPI_LOG_PLUGIN, + plugin_description.spd_id, + "error creating a %s socket\n", + sock_desc); + continue; + } + 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\n", + sock_desc); + } + if (bind(sockfd, addr, addrlen) != 0) { + slapi_log_error(SLAPI_LOG_PLUGIN, + plugin_description.spd_id, + "error binding %s socket to port\n", + sock_desc); + close(sockfd); + continue; + } + if ((sock_type == SOCK_STREAM) && (listen(sockfd, 128) == -1)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + plugin_description.spd_id, + "error marking %s socket for " + "listening\n", sock_desc); + close(sockfd); + continue; + } + if (i == 0) { + state->resvport = sockfd; + } + state->listenfd[state->n_listeners] = sockfd; + state->sock_pf[state->n_listeners] = sock_pf; + state->sock_type[state->n_listeners] = sock_type; + slapi_log_error(SLAPI_LOG_PLUGIN, + plugin_description.spd_id, + "listening on port %d for %s%s clients\n", + PORT, + sock_type == SOCK_STREAM ? "tcp" : "udp", + sock_pf == PF_INET6 ? "6" : ""); + 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; + if (arena != NULL) { + PORT_FreeArena(arena, PR_TRUE); + } + errno = err; + return -1; } int -my_init_function(Slapi_PBlock *pb) +nis_plugin_init(Slapi_PBlock *pb) { - struct state *state; + struct plugin_state *state = NULL; if (setup_listeners(&state) == -1) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, "error setting up listening sockets\n"); @@ -728,19 +736,7 @@ my_init_function(Slapi_PBlock *pb) slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, &plugin_close); slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &state->plugin_identity); slapi_pblock_set(pb, SLAPI_PLUGIN_PRIVATE, state); - if (pmap_unset(YPPROG, YPVERS) != 1) { - slapi_log_error(SLAPI_LOG_PLUGIN, "plugin_start", - "error clearing registrations, continuing\n"); - } - if (pmap_set(YPPROG, YPVERS, IPPROTO_TCP, PORT) != 1) { - slapi_log_error(SLAPI_LOG_PLUGIN, "plugin_start", - "error registering TCP port, continuing\n"); - } - if (pmap_set(YPPROG, YPVERS, IPPROTO_UDP, PORT) != 1) { - slapi_log_error(SLAPI_LOG_PLUGIN, "plugin_start", - "error registering UDP port, continuing\n"); - } slapi_log_error(SLAPI_LOG_PLUGIN, plugin_description.spd_id, - "registering plugin hooks\n"); + "registered plugin hooks\n"); return 0; } diff --git a/src/plugin.ldif b/src/plugin.ldif index 082ed11..09931dd 100644 --- a/src/plugin.ldif +++ b/src/plugin.ldif @@ -4,7 +4,7 @@ objectclass: nsSlapdPlugin objectclass: extensibleObject cn: My Plugin nsslapd-pluginpath: /usr/src/local/sn/src/plugin.so -nsslapd-plugininitfunc: my_init_function +nsslapd-plugininitfunc: nis_plugin_init nsslapd-plugintype: object nsslapd-pluginenabled: on nsslapd-pluginid: my-plugin diff --git a/src/portmap.c b/src/portmap.c new file mode 100644 index 0000000..ff0aa62 --- /dev/null +++ b/src/portmap.c @@ -0,0 +1,211 @@ +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#include <rpc/xdr.h> +#include <rpc/rpc_msg.h> +#include <rpc/pmap_prot.h> +#include <errno.h> +#include <poll.h> +#include <time.h> + +#include <dirsrv/slapi-plugin.h> + +static bool_t +portmap_register_work(int resv_sock, + int program, int version, int protocol, int port, + int proc) +{ + char portmap_buf[4000], auth_buf[4000], reply_buf[8000]; + uint32_t length; + int portmap_length, reply_length; + AUTH *auth; + XDR portmap_xdrs, auth_xdrs; + struct rpc_msg msg; + struct pmap map; + bool_t ret; + struct sockaddr addr; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + union { + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + } client_addr; + socklen_t client_addrlen; + struct pollfd pollfd; + int i; + u_long xid; + gid_t gid = 0; + + memset(&addr, 0, sizeof(addr)); + memset(&addr4, 0, sizeof(addr4)); + memset(&addr6, 0, sizeof(addr6)); + addr.sa_family = AF_UNSPEC; + addr4.sin_family = AF_INET; + addr4.sin_port = ntohs(PMAPPORT); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = ntohs(PMAPPORT); + + memset(&msg, 0, sizeof(msg)); + msg.rm_xid = xid = time(NULL); + 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; + + xdrmem_create(&auth_xdrs, auth_buf, sizeof(auth_buf), XDR_ENCODE); + auth = authunix_create("localhost", 0, 0, 1, &gid); + auth_marshall(auth, &auth_xdrs); + msg.rm_call.cb_cred.oa_flavor = AUTH_SYS; + msg.rm_call.cb_cred.oa_base = auth_buf; + msg.rm_call.cb_cred.oa_length = xdr_getpos(&auth_xdrs); + auth_destroy(auth); + xdr_destroy(&auth_xdrs); + + map.pm_prog = program; + map.pm_vers = version; + map.pm_prot = protocol; + map.pm_port = port; + + 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); + xdr_destroy(&portmap_xdrs); + + client_addrlen = 0; + connect(resv_sock, (struct sockaddr*) &addr4, sizeof(addr4)); + for (i = 1; i < 32; i *= 2) { + if (send(resv_sock, &portmap_buf, portmap_length, + 0) != portmap_length) { + slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", + "error sending request to portmap\n"); + continue; + } + pollfd.fd = resv_sock; + pollfd.events = POLLIN | POLLERR; + if ((poll(&pollfd, 1, i * 1000) > 0) && + (pollfd.revents & POLLIN)) { + client_addrlen = sizeof(client_addr); + reply_length = recvfrom(resv_sock, + reply_buf, sizeof(reply_buf), 0, + (struct sockaddr *)&client_addr, + &client_addrlen); + if (reply_length > 0) { + xdrmem_create(&portmap_xdrs, + reply_buf, reply_length, + XDR_DECODE); + if (xdr_replymsg(&portmap_xdrs, &msg)) { + if ((msg.rm_direction == REPLY) && + (msg.rm_xid == xid)) { + break; + } + } + xdr_destroy(&portmap_xdrs); + } + } + } + connect(resv_sock, &addr, sizeof(addr)); + if (i == 32) { + slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", + "timeout registering with portmap service\n"); + return FALSE; + } + + if (msg.rm_reply.rp_stat != MSG_ACCEPTED) { + slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", + "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, "XXX", + "portmap request rejected: " + "authentication failed: %s\n", + auth_status); + break; + case RPC_MISMATCH: + slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", + "portmap request rejected: " + "RPC mismatch\n"); + break; + } + xdr_destroy(&portmap_xdrs); + return FALSE; + } + + auth = authunix_create_default(); + if (auth_validate(auth, &msg.rm_reply.rp_acpt.ar_verf)) { + slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", + "portmap reply authenticated\n"); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", + "portmap reply failed authentication\n"); + } + auth_destroy(auth); + + if (msg.rm_reply.rp_acpt.ar_stat != SUCCESS) { + slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", + "portmap request not processed\n"); + xdr_destroy(&portmap_xdrs); + return FALSE; + } + + if (xdr_bool(&portmap_xdrs, &ret)) { + slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", + "portmap request succeeded\n"); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, "XXX", + "portmap response did not include a reply\n"); + ret = FALSE; + } + + xdr_destroy(&portmap_xdrs); + + return ret; +} + +bool_t +portmap_register(int resv_sock, + int program, int version, int protocol, int port) +{ + return portmap_register_work(resv_sock, program, version, + protocol, port, PMAPPROC_UNSET); +} + +bool_t +portmap_unregister(int resv_sock, int program, int version) +{ + return portmap_register_work(resv_sock, program, version, + 0, 0, PMAPPROC_UNSET); +} diff --git a/src/portmap.h b/src/portmap.h new file mode 100644 index 0000000..9161095 --- /dev/null +++ b/src/portmap.h @@ -0,0 +1,10 @@ +#ifndef portmap_h +#define portmap_h + +#include <rpc/xdr.h> + +bool_t portmap_register(int resv_sock, + int program, int version, int protocol, int port); +bool_t portmap_unregister(int resv_sock, int program, int version); + +#endif diff --git a/src/schema.c b/src/schema.c new file mode 100644 index 0000000..cee39f9 --- /dev/null +++ b/src/schema.c @@ -0,0 +1,43 @@ +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#include <dirsrv/slapi-plugin.h> +#include "schema.h" + +/* Replace this with a cache of dynamically-read data that gets refreshed if + * it's more than X seconds old. */ +PRBool +schema_supports_domain(const char *domain) +{ + if (strcmp(domain, ".local") == 0) { + return PR_TRUE; + } + return PR_FALSE; +} + +void +schema_get(const char *domain, const char *map, + char **base, int *scope, char **key, char **format) +{ + *base = NULL; + *scope = 0; + *key = NULL; + *format = NULL; + if (strcmp(domain, ".local") != 0) { + return; + } + if (strcmp(map, "passwd.byname") == 0) { + *base = strdup("dc=boston,dc=redhat,dc=com"); + *scope = LDAP_SCOPE_SUBTREE; + *key = strdup("uid"); + *format = strdup("${uid}:${gecos}"); + } + if (strcmp(map, "passwd.bynumber") == 0) { + *base = strdup("dc=boston,dc=redhat,dc=com"); + *scope = LDAP_SCOPE_SUBTREE; + *key = strdup("uidNumber"); + *format = strdup("${uid}:${gecos}"); + } + return; +} diff --git a/src/schema.h b/src/schema.h new file mode 100644 index 0000000..e90e137 --- /dev/null +++ b/src/schema.h @@ -0,0 +1,10 @@ + + +#ifndef schema_h +#define schema_h + +PRBool schema_supports_domain(const char *domain); +void schema_get(const char *domain, const char *map, + char **base, int *scope, char **key, char **format); + +#endif |