diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/nss/nsssrv.c | 252 | ||||
-rw-r--r-- | server/nss/nsssrv.h | 48 | ||||
-rw-r--r-- | server/nss/nsssrv_packet.c | 185 | ||||
-rw-r--r-- | server/server.c | 8 | ||||
-rw-r--r-- | server/server.mk | 4 | ||||
-rw-r--r-- | server/util/util.h | 1 |
6 files changed, 494 insertions, 4 deletions
diff --git a/server/nss/nsssrv.c b/server/nss/nsssrv.c new file mode 100644 index 00000000..c2826d27 --- /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 00000000..5e6c9aa5 --- /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 00000000..e33568a3 --- /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 a6958a0f..27fe09b7 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 1a451ae7..f796ec69 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 012fa1ff..495e3a24 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" |