From fcef1231eea30ee9cdc75f3f39f4b9207a84ea1d Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 24 Feb 2009 19:37:42 -0500 Subject: Add PAM client Also rename nss_client to sss_client and reuse the same pipe protocol for both the NSS and PAM client libraries. Signed-off-by: Simo Sorce --- nss_client/common.c | 588 ---------------------------------------------------- 1 file changed, 588 deletions(-) delete mode 100644 nss_client/common.c (limited to 'nss_client/common.c') diff --git a/nss_client/common.c b/nss_client/common.c deleted file mode 100644 index a79dc660..00000000 --- a/nss_client/common.c +++ /dev/null @@ -1,588 +0,0 @@ -/* - * System Security Services Daemon. NSS client interface - * - * Copyright (C) Simo Sorce 2007 - * - * Winbind derived code: - * Copyright (C) Tim Potter 2000 - * Copyright (C) Andrew Tridgell 2000 - * Copyright (C) Andrew Bartlett 2002 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sss_nss.h" - -/* common functions */ - -int sss_nss_sd = -1; /* the nss socket descriptor */ - -static void sss_nss_close_socket(void) -{ - if (sss_nss_sd != -1) { - close(sss_nss_sd); - sss_nss_sd = -1; - } -} - -/* Requests: - * - * byte 0-3: 32bit unsigned with length (the complete packet length: 0 to X) - * byte 4-7: 32bit unsigned with command code - * byte 8-11: 32bit unsigned (reserved) - * byte 12-15: 32bit unsigned (reserved) - * byte 16-X: (optional) request structure associated to the command code used - */ -static enum nss_status sss_nss_send_req(enum sss_nss_command cmd, - struct sss_nss_req_data *rd, - int *errnop) -{ - uint32_t header[4]; - size_t datasent; - - header[0] = SSS_NSS_HEADER_SIZE + (rd?rd->len:0); - header[1] = cmd; - header[2] = 0; - header[3] = 0; - - datasent = 0; - - while (datasent < header[0]) { - struct pollfd pfd; - int rdsent; - int res; - - *errnop = 0; - pfd.fd = sss_nss_sd; - pfd.events = POLLOUT; - - res = poll(&pfd, 1, SSS_NSS_SOCKET_TIMEOUT); - switch (res) { - case -1: - *errnop = errno; - break; - case 0: - *errnop = ETIME; - break; - case 1: - if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { - *errnop = EPIPE; - } - if (!(pfd.revents & POLLOUT)) { - *errnop = EBUSY; - } - break; - default: /* more than one avail ?? */ - *errnop = EBADF; - break; - } - if (*errnop) { - sss_nss_close_socket(); - return NSS_STATUS_UNAVAIL; - } - - if (datasent < SSS_NSS_HEADER_SIZE) { - res = write(sss_nss_sd, - (char *)header + datasent, - SSS_NSS_HEADER_SIZE - datasent); - } else { - rdsent = datasent - SSS_NSS_HEADER_SIZE; - res = write(sss_nss_sd, - (const char *)rd->data + rdsent, - rd->len - rdsent); - } - - if ((res == -1) || (res == 0)) { - - /* Write failed */ - sss_nss_close_socket(); - *errnop = errno; - return NSS_STATUS_UNAVAIL; - } - - datasent += res; - } - - return NSS_STATUS_SUCCESS; -} - -/* Replies: - * - * byte 0-3: 32bit unsigned with length (the complete packet length: 0 to X) - * byte 4-7: 32bit unsigned with command code - * byte 8-11: 32bit unsigned with the request status (server errno) - * byte 12-15: 32bit unsigned (reserved) - * byte 16-X: (optional) reply structure associated to the command code used - */ - -static enum nss_status sss_nss_recv_rep(enum sss_nss_command cmd, - uint8_t **buf, int *len, - int *errnop) -{ - uint32_t header[4]; - size_t datarecv; - - header[0] = SSS_NSS_HEADER_SIZE; /* unitl we know the real lenght */ - header[1] = 0; - header[2] = 0; - header[3] = 0; - - datarecv = 0; - *buf = NULL; - *len = 0; - - while (datarecv < header[0]) { - struct pollfd pfd; - int bufrecv; - int res; - - *errnop = 0; - pfd.fd = sss_nss_sd; - pfd.events = POLLIN; - - res = poll(&pfd, 1, SSS_NSS_SOCKET_TIMEOUT); - switch (res) { - case -1: - *errnop = errno; - break; - case 0: - *errnop = ETIME; - break; - case 1: - if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { - *errnop = EPIPE; - } - if (!(pfd.revents & POLLIN)) { - *errnop = EBUSY; - } - break; - default: /* more than one avail ?? */ - *errnop = EBADF; - break; - } - if (*errnop) { - sss_nss_close_socket(); - return NSS_STATUS_UNAVAIL; - } - - if (datarecv < SSS_NSS_HEADER_SIZE) { - res = read(sss_nss_sd, - (char *)header + datarecv, - SSS_NSS_HEADER_SIZE - datarecv); - } else { - bufrecv = datarecv - SSS_NSS_HEADER_SIZE; - res = read(sss_nss_sd, - (char *)(*buf) + bufrecv, - header[0] - datarecv); - } - - if ((res == -1) || (res == 0)) { - - /* Read failed. I think the only useful thing - * we can do here is just return -1 and fail - * since the transaction has failed half way - * through. */ - - sss_nss_close_socket(); - *errnop = errno; - return NSS_STATUS_UNAVAIL; - } - - datarecv += res; - - if (datarecv == SSS_NSS_HEADER_SIZE && *len == 0) { - /* at this point recv buf is not yet - * allocated and the header has just - * been read, do checks and proceed */ - if (header[2] != 0) { - /* server side error */ - sss_nss_close_socket(); - *errnop = header[2]; - if (*errnop == EAGAIN) { - return NSS_STATUS_TRYAGAIN; - } else { - return NSS_STATUS_UNAVAIL; - } - } - if (header[1] != cmd) { - /* wrong command id */ - sss_nss_close_socket(); - *errnop = EBADMSG; - return NSS_STATUS_UNAVAIL; - } - if (header[0] > SSS_NSS_HEADER_SIZE) { - *len = header[0] - SSS_NSS_HEADER_SIZE; - *buf = malloc(*len); - if (!*buf) { - sss_nss_close_socket(); - *errnop = ENOMEM; - return NSS_STATUS_UNAVAIL; - } - } - } - } - - return NSS_STATUS_SUCCESS; -} - -/* this function will check command codes match and returned length is ok */ -/* repbuf and replen report only the data section not the header */ -static enum nss_status sss_nss_make_request_nochecks( - enum sss_nss_command cmd, - struct sss_nss_req_data *rd, - uint8_t **repbuf, size_t *replen, - int *errnop) -{ - enum nss_status ret; - uint8_t *buf = NULL; - int len = 0; - - /* send data */ - ret = sss_nss_send_req(cmd, rd, errnop); - if (ret != NSS_STATUS_SUCCESS) { - return ret; - } - - /* data sent, now get reply */ - ret = sss_nss_recv_rep(cmd, &buf, &len, errnop); - if (ret != NSS_STATUS_SUCCESS) { - return ret; - } - - /* we got through, now we have the custom data in buf if any, - * return it if requested */ - if (repbuf && buf) { - *repbuf = buf; - if (replen) { - *replen = len; - } - } else { - free(buf); - if (replen) { - *replen = 0; - } - } - - return NSS_STATUS_SUCCESS; -} - -/* GET_VERSION Reply: - * 0-3: 32bit unsigned version number - */ - -static int sss_nss_check_version(void) -{ - uint8_t *repbuf; - size_t replen; - enum nss_status nret; - int errnop; - int res = NSS_STATUS_UNAVAIL; - - nret = sss_nss_make_request_nochecks(SSS_NSS_GET_VERSION, NULL, - &repbuf, &replen, &errnop); - if (nret != NSS_STATUS_SUCCESS) { - return nret; - } - - if (!repbuf) { - return res; - } - - if (((uint32_t *)repbuf)[0] == SSS_NSS_VERSION) { - res = NSS_STATUS_SUCCESS; - } - - free(repbuf); - return res; -} - -/* this 2 functions are adapted from samba3 winbinbd's wb_common.c */ - -/* Make sure socket handle isn't stdin (0), stdout(1) or stderr(2) by setting - * the limit to 3 */ -#define RECURSION_LIMIT 3 - -static int make_nonstd_fd_internals(int fd, int limit) -{ - int new_fd; - if (fd >= 0 && fd <= 2) { -#ifdef F_DUPFD - if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) { - return -1; - } - /* Paranoia */ - if (new_fd < 3) { - close(new_fd); - return -1; - } - close(fd); - return new_fd; -#else - if (limit <= 0) - return -1; - - new_fd = dup(fd); - if (new_fd == -1) - return -1; - - /* use the program stack to hold our list of FDs to close */ - new_fd = make_nonstd_fd_internals(new_fd, limit - 1); - close(fd); - return new_fd; -#endif - } - return fd; -} - -/**************************************************************************** - Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, - else - if SYSV use O_NDELAY - if BSD use FNDELAY - Set close on exec also. -****************************************************************************/ - -static int make_safe_fd(int fd) -{ - int result, flags; - int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT); - if (new_fd == -1) { - close(fd); - return -1; - } - - /* Socket should be nonblocking. */ -#ifdef O_NONBLOCK -#define FLAG_TO_SET O_NONBLOCK -#else -#ifdef SYSV -#define FLAG_TO_SET O_NDELAY -#else /* BSD */ -#define FLAG_TO_SET FNDELAY -#endif -#endif - - if ((flags = fcntl(new_fd, F_GETFL)) == -1) { - close(new_fd); - return -1; - } - - flags |= FLAG_TO_SET; - if (fcntl(new_fd, F_SETFL, flags) == -1) { - close(new_fd); - return -1; - } - -#undef FLAG_TO_SET - - /* Socket should be closed on exec() */ -#ifdef FD_CLOEXEC - result = flags = fcntl(new_fd, F_GETFD, 0); - if (flags >= 0) { - flags |= FD_CLOEXEC; - result = fcntl( new_fd, F_SETFD, flags ); - } - if (result < 0) { - close(new_fd); - return -1; - } -#endif - return new_fd; -} - -static int sss_nss_open_socket(int *errnop) -{ - struct sockaddr_un nssaddr; - int inprogress = 1; - int wait_time, sleep_time; - int sd; - - memset(&nssaddr, 0, sizeof(struct sockaddr_un)); - nssaddr.sun_family = AF_UNIX; - strncpy(nssaddr.sun_path, SSS_NSS_SOCKET_NAME, - strlen(SSS_NSS_SOCKET_NAME) + 1); - - sd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sd == -1) { - *errnop = errno; - return -1; - } - - /* set as non-blocking, close on exec, and make sure standard - * descriptors are not used */ - sd = make_safe_fd(sd); - if (sd == -1) { - *errnop = errno; - return -1; - } - - /* this piece is adapted from winbind client code */ - wait_time = 0; - sleep_time = 0; - while(inprogress) { - int connect_errno = 0; - socklen_t errnosize; - struct timeval tv; - fd_set w_fds; - int ret; - - wait_time += sleep_time; - - ret = connect(sd, (struct sockaddr *)&nssaddr, - sizeof(nssaddr)); - if (ret == 0) { - return sd; - } - - switch(errno) { - case EINPROGRESS: - FD_ZERO(&w_fds); - FD_SET(sd, &w_fds); - tv.tv_sec = SSS_NSS_SOCKET_TIMEOUT - wait_time; - tv.tv_usec = 0; - - ret = select(sd + 1, NULL, &w_fds, NULL, &tv); - - if (ret > 0) { - errnosize = sizeof(connect_errno); - ret = getsockopt(sd, SOL_SOCKET, SO_ERROR, - &connect_errno, &errnosize); - if (ret >= 0 && connect_errno == 0) { - return sd; - } - } - wait_time += SSS_NSS_SOCKET_TIMEOUT; - break; - case EAGAIN: - if (wait_time < SSS_NSS_SOCKET_TIMEOUT) { - sleep_time = rand() % 2 + 1; - sleep(sleep_time); - } - break; - default: - *errnop = errno; - inprogress = 0; - break; - } - - if (wait_time >= SSS_NSS_SOCKET_TIMEOUT) { - inprogress = 0; - } - } - - /* if we get here connect() failed or we timed out */ - - close(sd); - return -1; -} - -static enum nss_status sss_nss_check_socket(int *errnop) -{ - static pid_t mypid; - int mysd; - - if (getpid() != mypid) { - sss_nss_close_socket(); - mypid = getpid(); - } - - /* check if the socket has been closed on the other side */ - if (sss_nss_sd != -1) { - struct pollfd pfd; - int res; - - *errnop = 0; - pfd.fd = sss_nss_sd; - pfd.events = POLLIN | POLLOUT; - - res = poll(&pfd, 1, SSS_NSS_SOCKET_TIMEOUT); - switch (res) { - case -1: - *errnop = errno; - break; - case 0: - *errnop = ETIME; - break; - case 1: - if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { - *errnop = EPIPE; - } - if (!(pfd.revents & (POLLIN | POLLOUT))) { - *errnop = EBUSY; - } - break; - default: /* more than one avail ?? */ - *errnop = EBADF; - break; - } - if (*errnop) { - sss_nss_close_socket(); - return NSS_STATUS_UNAVAIL; - } - - return NSS_STATUS_SUCCESS; - } - - mysd = sss_nss_open_socket(errnop); - if (mysd == -1) { - return NSS_STATUS_UNAVAIL; - } - - sss_nss_sd = mysd; - - if (sss_nss_check_version()) { - return NSS_STATUS_SUCCESS; - } - - sss_nss_close_socket(); - *errnop = EFAULT; - return NSS_STATUS_UNAVAIL; -} - -/* this function will check command codes match and returned length is ok */ -/* repbuf and replen report only the data section not the header */ -enum nss_status sss_nss_make_request(enum sss_nss_command cmd, - struct sss_nss_req_data *rd, - uint8_t **repbuf, size_t *replen, - int *errnop) -{ - enum nss_status ret; - char *envval; - - /* avoid looping in the nss daemon */ - envval = getenv("_SSS_LOOPS"); - if (envval && strcmp(envval, "NO") == 0) { - return NSS_STATUS_NOTFOUND; - } - - ret = sss_nss_check_socket(errnop); - if (ret != NSS_STATUS_SUCCESS) { - return ret; - } - - return sss_nss_make_request_nochecks(cmd, rd, repbuf, replen, errnop); -} - -- cgit