summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/nss/nsssrv.c252
-rw-r--r--server/nss/nsssrv.h48
-rw-r--r--server/nss/nsssrv_packet.c185
-rw-r--r--server/server.c8
-rw-r--r--server/server.mk4
-rw-r--r--server/util/util.h1
6 files changed, 494 insertions, 4 deletions
diff --git a/server/nss/nsssrv.c b/server/nss/nsssrv.c
new file mode 100644
index 000000000..c2826d279
--- /dev/null
+++ b/server/nss/nsssrv.c
@@ -0,0 +1,252 @@
+/*
+ SSSD
+
+ NSS Responder
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+#include <sys/time.h>
+#include "../events/events.h"
+#include "../talloc/talloc.h"
+#include "util/util.h"
+#include "service.h"
+#include "nss/nsssrv.h"
+
+struct nss_ctx {
+ struct task_server *task;
+ struct fd_event *lfde;
+ int lfd;
+};
+
+struct cli_ctx {
+ int cfd;
+ struct fd_event *cfde;
+ struct sockaddr_un addr;
+ struct cli_request *creq;
+};
+
+static void set_nonblocking(int fd)
+{
+ unsigned v;
+ v = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, v | O_NONBLOCK);
+}
+
+static void set_close_on_exec(int fd)
+{
+ unsigned v;
+ v = fcntl(fd, F_GETFD, 0);
+ fcntl(fd, F_SETFD, v | FD_CLOEXEC);
+}
+
+static int client_destructor(struct cli_ctx *ctx)
+{
+ if (ctx->cfd > 0) close(ctx->cfd);
+ return 0;
+}
+
+static void client_send(struct event_context *ev, struct cli_ctx *ctx)
+{
+ int ret;
+
+ ret = nss_packet_send(ctx->creq->out, ctx->cfd);
+ if (ret == RES_RETRY) {
+ /* not all data was sent, loop again */
+ return;
+ }
+ if (ret != RES_SUCCESS) {
+ DEBUG(0, ("Failed to read request, aborting client!\n"));
+ talloc_free(ctx);
+ return;
+ }
+
+ /* ok all sent */
+ EVENT_FD_NOT_WRITEABLE(ctx->cfde);
+ EVENT_FD_READABLE(ctx->cfde);
+ talloc_free(ctx->creq);
+ ctx->creq = NULL;
+ return;
+}
+
+static void client_recv(struct event_context *ev, struct cli_ctx *ctx)
+{
+ int ret;
+
+ if (!ctx->creq) {
+ ctx->creq = talloc_zero(ctx, struct cli_request);
+ if (!ctx->creq) {
+ DEBUG(0, ("Failed to alloc request, aborting client!\n"));
+ talloc_free(ctx);
+ return;
+ }
+ }
+
+ if (!ctx->creq->in) {
+ ret = nss_packet_new(ctx->creq, 0, &ctx->creq->in);
+ if (ret != RES_SUCCESS) {
+ DEBUG(0, ("Failed to alloc request, aborting client!\n"));
+ talloc_free(ctx);
+ return;
+ }
+ }
+
+ ret = nss_packet_recv(ctx->creq->in, ctx->cfd);
+ switch (ret) {
+ case RES_SUCCESS:
+ /* do not read anymore */
+ EVENT_FD_NOT_READABLE(ctx->cfde);
+ /* execute command */
+ /* nss_cmd_execute(ctx); */
+ break;
+
+ case RES_RETRY:
+ /* need to read still some data, loop again */
+ break;
+
+ default:
+ DEBUG(0, ("Failed to read request, aborting client!\n"));
+ talloc_free(ctx);
+ }
+
+ return;
+}
+
+static void client_fd_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags, void *ptr)
+{
+ struct cli_ctx *ctx = talloc_get_type(ptr, struct cli_ctx);
+
+ if (flags & EVENT_FD_READ) {
+ client_recv(ev, ctx);
+ return;
+ }
+ if (flags & EVENT_FD_WRITE) {
+ client_send(ev, ctx);
+ return;
+ }
+}
+
+static void accept_fd_handler(struct event_context *ev,
+ struct fd_event *fde,
+ uint16_t flags, void *ptr)
+{
+ /* accept and attach new event handler */
+ struct nss_ctx *ctx = talloc_get_type(ptr, struct nss_ctx);
+ struct cli_ctx *cctx;
+ socklen_t len;
+
+ cctx = talloc_zero(ctx, struct cli_ctx);
+ if (!cctx) {
+ struct sockaddr_un addr;
+ int fd;
+ DEBUG(0, ("Out of memory trying to setup client context!\n"));
+ /* accept and close to signal the client we have a problem */
+ memset(&addr, 0, sizeof(addr));
+ len = sizeof(addr);
+ fd = accept(ctx->lfd, (struct sockaddr *)&addr, &len);
+ if (fd == -1) {
+ return;
+ }
+ close(fd);
+ return;
+ }
+
+ len = sizeof(cctx->addr);
+ cctx->cfd = accept(ctx->lfd, (struct sockaddr *)&cctx->addr, &len);
+ if (cctx->cfd == -1) {
+ DEBUG(1, ("Accept failed [%s]", strerror(errno)));
+ talloc_free(cctx);
+ return;
+ }
+
+ cctx->cfde = event_add_fd(ev, cctx, cctx->cfd,
+ EVENT_FD_READ, client_fd_handler, cctx);
+ if (!cctx->cfde) {
+ close(cctx->cfd);
+ talloc_free(cctx);
+ DEBUG(1, ("Failed to queue client handler\n"));
+ }
+
+ talloc_set_destructor(cctx, client_destructor);
+
+ return;
+}
+
+/* create a unix socket and listen to it */
+static void set_unix_socket(struct event_context *ev,
+ struct nss_ctx *ctx,
+ const char *sock_name)
+{
+ struct sockaddr_un addr;
+
+ /* make sure we have no old sockets around */
+ unlink(sock_name);
+
+ ctx->lfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ctx->lfd == -1) {
+ return;
+ }
+
+ set_nonblocking(ctx->lfd);
+ set_close_on_exec(ctx->lfd);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, sock_name, sizeof(addr.sun_path));
+
+ if (bind(ctx->lfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ DEBUG(0,("Unable to bind on socket '%s'\n", sock_name));
+ goto failed;
+ }
+ if (listen(ctx->lfd, 10) != 0) {
+ DEBUG(0,("Unable to listen on socket '%s'\n", sock_name));
+ goto failed;
+ }
+
+ ctx->lfde = event_add_fd(ev, ctx, ctx->lfd,
+ EVENT_FD_READ, accept_fd_handler, ctx);
+
+ return;
+
+failed:
+ close(ctx->lfd);
+}
+
+void nss_task_init(struct task_server *task)
+{
+ struct nss_ctx *ctx;
+
+ task_server_set_title(task, "sssd[nsssrv]");
+
+ ctx = talloc_zero(task, struct nss_ctx);
+ if (!ctx) {
+ task_server_terminate(task, "fatal error initializing nss_ctx\n");
+ return;
+ }
+ ctx->task = task;
+
+ set_unix_socket(task->event_ctx, ctx, SSS_NSS_SOCKET_NAME);
+
+}
diff --git a/server/nss/nsssrv.h b/server/nss/nsssrv.h
new file mode 100644
index 000000000..5e6c9aa55
--- /dev/null
+++ b/server/nss/nsssrv.h
@@ -0,0 +1,48 @@
+/*
+ SSSD
+
+ NSS Responder, header file
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __NSSSRV_H__
+#define __NSSSRV_H__
+
+#include <stdint.h>
+#include "../nss_client/sss_nss.h"
+
+struct nss_packet;
+
+struct cli_request {
+ enum sss_nss_command cmd;
+ void *cmd_req;
+
+ /* original request from the wire */
+ struct nss_packet *in;
+
+ /* reply data */
+ struct nss_packet *out;
+};
+
+/* from nsssrv_packet.c */
+int nss_packet_new(TALLOC_CTX *mem_ctx, size_t size,
+ struct nss_packet **rpacket);
+int nss_packet_grow(struct nss_packet *packet, size_t size);
+int nss_packet_recv(struct nss_packet *packet, int fd);
+int nss_packet_send(struct nss_packet *packet, int fd);
+
+#endif /* __NSSSRV_H__ */
diff --git a/server/nss/nsssrv_packet.c b/server/nss/nsssrv_packet.c
new file mode 100644
index 000000000..e33568a32
--- /dev/null
+++ b/server/nss/nsssrv_packet.c
@@ -0,0 +1,185 @@
+/*
+ SSSD
+
+ NSS Responder, command parser
+
+ Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <errno.h>
+#include "../talloc/talloc.h"
+#include "util/util.h"
+#include "nss/nsssrv.h"
+
+#define NSSSRV_PACKET_MEM_SIZE 512
+
+struct nss_packet {
+ size_t memsize;
+ uint8_t *buffer;
+
+ /* header */
+ uint32_t *len;
+ uint32_t *cmd;
+ uint32_t *status;
+ uint32_t *reserved;
+
+ uint8_t *body;
+
+ /* io pointer */
+ size_t iop;
+};
+
+/*
+ * Allocate a new packet structure
+ *
+ * - if size is defined use it otherwise the default packet will be
+ * NSSSRV_PACKET_MEM_SIZE bytes.
+ * - if buf is provided also give back the pointer to the base of
+ * the buffer (the header), so that a packet can be written into
+ * firecgtly from the wire
+ */
+int nss_packet_new(TALLOC_CTX *mem_ctx, size_t size,
+ struct nss_packet **rpacket)
+{
+ struct nss_packet *packet;
+
+ packet = talloc(mem_ctx, struct nss_packet);
+ if (!packet) return RES_NOMEM;
+
+ if (size) {
+ int n = size % NSSSRV_PACKET_MEM_SIZE;
+ packet->memsize = (n + 1) * NSSSRV_PACKET_MEM_SIZE;
+ } else {
+ packet->memsize = NSSSRV_PACKET_MEM_SIZE;
+ }
+
+ packet->buffer = talloc_size(packet, packet->memsize);
+ if (!packet->buffer) {
+ talloc_free(packet);
+ return RES_NOMEM;
+ }
+ memset(packet->buffer, 0, SSS_NSS_HEADER_SIZE);
+
+ packet->len = &((uint32_t *)packet->buffer)[0];
+ packet->cmd = &((uint32_t *)packet->buffer)[1];
+ packet->status = &((uint32_t *)packet->buffer)[2];
+ packet->reserved = &((uint32_t *)packet->buffer)[3];
+ packet->body = (uint8_t *)&((uint32_t *)packet->buffer)[4];
+
+ *(packet->len) = size?size:SSS_NSS_HEADER_SIZE;
+
+ *rpacket = packet;
+
+ return RES_SUCCESS;
+}
+
+/* grows a packet size only in NSSSRV_PACKET_MEM_SIZE chunks */
+int nss_packet_grow(struct nss_packet *packet, size_t size)
+{
+ size_t totlen, len;
+ uint8_t *newmem;
+
+ if (size == 0) {
+ return RES_SUCCESS;
+ }
+
+ totlen = packet->memsize;
+ len = *packet->len + size;
+
+ /* make sure we do not overflow */
+ if (totlen < len) {
+ int n = len % NSSSRV_PACKET_MEM_SIZE + 1;
+ totlen += n * NSSSRV_PACKET_MEM_SIZE;
+ if (totlen < len) {
+ return RES_INVALID_DATA;
+ }
+ }
+
+ if (totlen > packet->memsize) {
+ newmem = talloc_realloc_size(packet, packet->buffer, totlen);
+ if (!newmem) {
+ return RES_NOMEM;
+ }
+
+ packet->memsize = totlen;
+ packet->buffer = newmem;
+ packet->len = &((uint32_t *)packet->buffer)[0];
+ packet->cmd = &((uint32_t *)packet->buffer)[1];
+ packet->status = &((uint32_t *)packet->buffer)[2];
+ packet->reserved = &((uint32_t *)packet->buffer)[3];
+ packet->body = (uint8_t *)&((uint32_t *)packet->buffer)[4];
+ }
+
+ *(packet->len) += size;
+
+ return 0;
+}
+
+int nss_packet_recv(struct nss_packet *packet, int fd)
+{
+ size_t rb;
+ size_t len;
+ void *buf;
+
+ buf = packet->buffer + packet->iop;
+ if (packet->iop > 4) len = *packet->len;
+ else len = packet->memsize;
+
+ errno = 0;
+ rb = recv(fd, buf, len, 0);
+
+ if (rb == -1 && errno == EAGAIN) {
+ return RES_RETRY;
+ }
+
+ packet->iop += rb;
+ if (packet->iop < 4) {
+ return RES_RETRY;
+ }
+
+ if (packet->iop < *packet->len) {
+ return RES_RETRY;
+ }
+
+ return RES_SUCCESS;
+}
+
+int nss_packet_send(struct nss_packet *packet, int fd)
+{
+ size_t rb;
+ size_t len;
+ void *buf;
+
+ buf = packet->buffer + packet->iop;
+ len = *packet->len - packet->iop;
+
+ errno = 0;
+ rb = send(fd, buf, len, 0);
+
+ if (rb == -1 && errno == EAGAIN) {
+ return RES_RETRY;
+ }
+
+ packet->iop += rb;
+ if (packet->iop < *packet->len) {
+ return RES_RETRY;
+ }
+
+ return RES_SUCCESS;
+}
diff --git a/server/server.c b/server/server.c
index a6958a0fd..27fe09b75 100644
--- a/server/server.c
+++ b/server/server.c
@@ -32,7 +32,9 @@
#include "../events/events.h"
#include "../ldb/include/ldb.h"
#include "service.h"
-#include "monitor.h"
+
+extern void monitor_task_init(struct task_server *task);
+extern void nss_task_init(struct task_server *task);
static void sig_hup(int sig)
{
@@ -198,9 +200,11 @@ int main(int argc, const char *argv[])
/* Services */
register_server_service("monitor", monitor_task_init);
+ register_server_service("nss", nss_task_init);
- services = calloc(2, sizeof(char *));
+ services = calloc(3, sizeof(char *));
services[0] = "monitor";
+ services[1] = "nss";
status = server_service_startup(event_ctx, services);
if (status != RES_SUCCESS) {
diff --git a/server/server.mk b/server/server.mk
index 1a451ae72..f796ec690 100644
--- a/server/server.mk
+++ b/server/server.mk
@@ -1,6 +1,6 @@
-SERVER_OBJ = server.o monitor.o process.o service.o service_task.o util/signal.o util/become_daemon.o
+SERVER_OBJ = server.o monitor.o process.o service.o service_task.o util/signal.o util/become_daemon.o nss/nsssrv.o nss/nsssrv_packet.o
-install:: all
+install:: all
${INSTALLCMD} -d $(DESTDIR)$(sbindir)
${INSTALLCMD} -m 755 sssd $(DESTDIR)$(sbindir)
diff --git a/server/util/util.h b/server/util/util.h
index 012fa1ffc..495e3a241 100644
--- a/server/util/util.h
+++ b/server/util/util.h
@@ -22,6 +22,7 @@
#define RES_ERROR 1
#define RES_NOMEM 2
#define RES_INVALID_DATA 3
+#define RES_RETRY 4
#include "util/dlinklist.h"