diff options
author | Stephen Gallagher <sgallagh@redhat.com> | 2010-02-18 07:49:04 -0500 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2010-02-18 13:48:45 -0500 |
commit | 1c48b5a62f73234ed26bb20f0ab345ab61cda0ab (patch) | |
tree | 0b6cddd567a862e1a7b5df23764869782a62ca78 /src/sss_client | |
parent | 8c56df3176f528fe0260974b3bf934173c4651ea (diff) | |
download | sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.tar.gz sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.tar.bz2 sssd-1c48b5a62f73234ed26bb20f0ab345ab61cda0ab.zip |
Rename server/ directory to src/
Also update BUILD.txt
Diffstat (limited to 'src/sss_client')
-rw-r--r-- | src/sss_client/common.c | 669 | ||||
-rw-r--r-- | src/sss_client/group.c | 435 | ||||
-rw-r--r-- | src/sss_client/man/pam_sss.8.xml | 97 | ||||
-rw-r--r-- | src/sss_client/pam_sss.c | 1166 | ||||
-rw-r--r-- | src/sss_client/pam_test_client.c | 95 | ||||
-rw-r--r-- | src/sss_client/passwd.c | 373 | ||||
-rw-r--r-- | src/sss_client/protos.h | 137 | ||||
-rw-r--r-- | src/sss_client/sss_cli.h | 220 | ||||
-rw-r--r-- | src/sss_client/sss_nss.exports | 73 | ||||
-rw-r--r-- | src/sss_client/sss_pam.exports | 4 | ||||
-rw-r--r-- | src/sss_client/sss_pam_macros.h | 30 |
11 files changed, 3299 insertions, 0 deletions
diff --git a/src/sss_client/common.c b/src/sss_client/common.c new file mode 100644 index 00000000..6732c24f --- /dev/null +++ b/src/sss_client/common.c @@ -0,0 +1,669 @@ +/* + * 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 <nss.h> +#include <security/pam_modules.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <fcntl.h> +#include <poll.h> +#include "sss_cli.h" + +/* common functions */ + +int sss_cli_sd = -1; /* the sss client socket descriptor */ + +static void sss_cli_close_socket(void) +{ + if (sss_cli_sd != -1) { + close(sss_cli_sd); + sss_cli_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_cli_command cmd, + struct sss_cli_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, error; + + *errnop = 0; + pfd.fd = sss_cli_sd; + pfd.events = POLLOUT; + + do { + errno = 0; + res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT); + error = errno; + + /* If error is EINTR here, we'll try again + * If it's any other error, we'll catch it + * below. + */ + } while (error == EINTR); + + switch (res) { + case -1: + *errnop = error; + 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_cli_close_socket(); + return NSS_STATUS_UNAVAIL; + } + + if (datasent < SSS_NSS_HEADER_SIZE) { + res = write(sss_cli_sd, + (char *)header + datasent, + SSS_NSS_HEADER_SIZE - datasent); + } else { + rdsent = datasent - SSS_NSS_HEADER_SIZE; + res = write(sss_cli_sd, + (const char *)rd->data + rdsent, + rd->len - rdsent); + } + + if ((res == -1) || (res == 0)) { + + /* Write failed */ + sss_cli_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_cli_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; + *errnop = 0; + + while (datarecv < header[0]) { + struct pollfd pfd; + int bufrecv; + int res, error; + + pfd.fd = sss_cli_sd; + pfd.events = POLLIN; + + do { + errno = 0; + res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT); + error = errno; + + /* If error is EINTR here, we'll try again + * If it's any other error, we'll catch it + * below. + */ + } while (error == EINTR); + + switch (res) { + case -1: + *errnop = error; + 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_cli_close_socket(); + return NSS_STATUS_UNAVAIL; + } + + if (datarecv < SSS_NSS_HEADER_SIZE) { + res = read(sss_cli_sd, + (char *)header + datarecv, + SSS_NSS_HEADER_SIZE - datarecv); + } else { + bufrecv = datarecv - SSS_NSS_HEADER_SIZE; + res = read(sss_cli_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_cli_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_cli_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_cli_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_cli_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_cli_command cmd, + struct sss_cli_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(const char *socket_name) +{ + uint8_t *repbuf; + size_t replen; + enum nss_status nret; + int errnop; + int res = NSS_STATUS_UNAVAIL; + uint32_t expected_version; + struct sss_cli_req_data req; + + if (strcmp(socket_name, SSS_NSS_SOCKET_NAME) == 0) { + expected_version = SSS_NSS_PROTOCOL_VERSION; + } else if (strcmp(socket_name, SSS_PAM_SOCKET_NAME) == 0 || + strcmp(socket_name, SSS_PAM_PRIV_SOCKET_NAME) == 0) { + expected_version = SSS_PAM_PROTOCOL_VERSION; + } else { + return NSS_STATUS_UNAVAIL; + } + + req.len = sizeof(expected_version); + req.data = &expected_version; + + nret = sss_nss_make_request_nochecks(SSS_GET_VERSION, &req, + &repbuf, &replen, &errnop); + if (nret != NSS_STATUS_SUCCESS) { + return nret; + } + + if (!repbuf) { + return res; + } + + if (((uint32_t *)repbuf)[0] == expected_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, const char *socket_name) +{ + 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, socket_name, + strlen(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_CLI_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_CLI_SOCKET_TIMEOUT; + break; + case EAGAIN: + if (wait_time < SSS_CLI_SOCKET_TIMEOUT) { + sleep_time = rand() % 2 + 1; + sleep(sleep_time); + } + break; + default: + *errnop = errno; + inprogress = 0; + break; + } + + if (wait_time >= SSS_CLI_SOCKET_TIMEOUT) { + inprogress = 0; + } + } + + /* if we get here connect() failed or we timed out */ + + close(sd); + return -1; +} + +static enum sss_status sss_cli_check_socket(int *errnop, const char *socket_name) +{ + static pid_t mypid; + int mysd; + + if (getpid() != mypid) { + sss_cli_close_socket(); + mypid = getpid(); + } + + /* check if the socket has been closed on the other side */ + if (sss_cli_sd != -1) { + struct pollfd pfd; + int res, error; + + *errnop = 0; + pfd.fd = sss_cli_sd; + pfd.events = POLLIN | POLLOUT; + + do { + errno = 0; + res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT); + error = errno; + + /* If error is EINTR here, we'll try again + * If it's any other error, we'll catch it + * below. + */ + } while (error == EINTR); + + switch (res) { + case -1: + *errnop = error; + 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_cli_close_socket(); + return SSS_STATUS_UNAVAIL; + } + + return SSS_STATUS_SUCCESS; + } + + mysd = sss_nss_open_socket(errnop, socket_name); + if (mysd == -1) { + return SSS_STATUS_UNAVAIL; + } + + sss_cli_sd = mysd; + + if (sss_nss_check_version(socket_name) == NSS_STATUS_SUCCESS) { + return SSS_STATUS_SUCCESS; + } + + sss_cli_close_socket(); + *errnop = EFAULT; + return SSS_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_cli_command cmd, + struct sss_cli_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_cli_check_socket(errnop, SSS_NSS_SOCKET_NAME); + if (ret != SSS_STATUS_SUCCESS) { + return NSS_STATUS_UNAVAIL; + } + + return sss_nss_make_request_nochecks(cmd, rd, repbuf, replen, errnop); +} + +int sss_pam_make_request(enum sss_cli_command cmd, + struct sss_cli_req_data *rd, + uint8_t **repbuf, size_t *replen, + int *errnop) +{ + int ret; + char *envval; + struct stat stat_buf; + + /* avoid looping in the pam daemon */ + envval = getenv("_SSS_LOOPS"); + if (envval && strcmp(envval, "NO") == 0) { + return PAM_SERVICE_ERR; + } + + /* only root shall use the privileged pipe */ + if (getuid() == 0 && getgid() == 0) { + ret = stat(SSS_PAM_PRIV_SOCKET_NAME, &stat_buf); + if (ret != 0) return PAM_SERVICE_ERR; + if ( ! (stat_buf.st_uid == 0 && + stat_buf.st_gid == 0 && + (stat_buf.st_mode&(S_IFSOCK|S_IRUSR|S_IWUSR)) == stat_buf.st_mode)) { + return PAM_SERVICE_ERR; + } + + ret = sss_cli_check_socket(errnop, SSS_PAM_PRIV_SOCKET_NAME); + } else { + ret = sss_cli_check_socket(errnop, SSS_PAM_SOCKET_NAME); + } + if (ret != NSS_STATUS_SUCCESS) { + return PAM_SERVICE_ERR; + } + + return sss_nss_make_request_nochecks(cmd, rd, repbuf, replen, errnop); +} diff --git a/src/sss_client/group.c b/src/sss_client/group.c new file mode 100644 index 00000000..675b8b71 --- /dev/null +++ b/src/sss_client/group.c @@ -0,0 +1,435 @@ +/* + * System Security Services Daemon. NSS client interface + * + * Copyright (C) Simo Sorce 2007 + * + * 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. + */ + +/* GROUP database NSS interface */ + +#include <nss.h> +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include "sss_cli.h" + +static struct sss_nss_getgrent_data { + size_t len; + size_t ptr; + uint8_t *data; +} sss_nss_getgrent_data; + +static void sss_nss_getgrent_data_clean(void) { + + if (sss_nss_getgrent_data.data != NULL) { + free(sss_nss_getgrent_data.data); + sss_nss_getgrent_data.data = NULL; + } + sss_nss_getgrent_data.len = 0; + sss_nss_getgrent_data.ptr = 0; +} + +/* GETGRNAM Request: + * + * 0-X: string with name + * + * GERTGRGID Request: + * + * 0-7: 32bit number with gid + * + * INITGROUPS Request: + * + * 0-3: 32bit number with gid + * 4-7: 32bit unsigned with max num of entries + * + * Replies: + * + * 0-3: 32bit unsigned number of results + * 4-7: 32bit unsigned (reserved/padding) + * For each result (64bit padded ?): + * 0-3: 32bit number gid + * 4-7: 32bit unsigned number of members + * 8-X: sequence of 0 terminated strings (name, passwd, mem..) + * + * FIXME: do we need to pad so that each result is 32 bit aligned ? + */ +struct sss_nss_gr_rep { + struct group *result; + char *buffer; + size_t buflen; +}; + +static int sss_nss_getgr_readrep(struct sss_nss_gr_rep *pr, + uint8_t *buf, size_t *len) +{ + size_t i, l, slen, ptmem; + ssize_t dlen; + char *sbuf; + uint32_t mem_num; + + if (*len < 11) { /* not enough space for data, bad packet */ + return EBADMSG; + } + + pr->result->gr_gid = ((uint32_t *)buf)[0]; + mem_num = ((uint32_t *)buf)[1]; + + sbuf = (char *)&buf[8]; + slen = *len - 8; + dlen = pr->buflen; + + pr->result->gr_name = &(pr->buffer[0]); + i = 0; + while (slen > i && dlen > 0) { + pr->buffer[i] = sbuf[i]; + if (pr->buffer[i] == '\0') break; + i++; + dlen--; + } + if (slen <= i) { /* premature end of buf */ + return EBADMSG; + } + if (dlen <= 0) { /* not enough memory */ + return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ + } + i++; + dlen--; + + pr->result->gr_passwd = &(pr->buffer[i]); + while (slen > i && dlen > 0) { + pr->buffer[i] = sbuf[i]; + if (pr->buffer[i] == '\0') break; + i++; + dlen--; + } + if (slen <= i) { /* premature end of buf */ + return EBADMSG; + } + if (dlen <= 0) { /* not enough memory */ + return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ + } + i++; + dlen--; + + /* now members */ + pr->result->gr_mem = (char **)&(pr->buffer[i]); + ptmem = sizeof(char *) * (mem_num + 1); + if (ptmem > dlen) { + return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ + } + dlen -= ptmem; + ptmem += i; + pr->result->gr_mem[mem_num] = NULL; /* terminate array */ + + for (l = 0; l < mem_num; l++) { + pr->result->gr_mem[l] = &(pr->buffer[ptmem]); + while ((slen > i) && (dlen > 0)) { + pr->buffer[ptmem] = sbuf[i]; + if (pr->buffer[ptmem] == '\0') break; + i++; + dlen--; + ptmem++; + } + if (slen <= i) { /* premature end of buf */ + return EBADMSG; + } + if (dlen <= 0) { /* not enough memory */ + return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ + } + i++; + dlen--; + ptmem++; + } + + *len = slen -i; + return 0; +} + +/* INITGROUP Reply: + * + * 0-3: 32bit unsigned number of results + * 4-7: 32bit unsigned (reserved/padding) + * For each result: + * 0-4: 32bit number with gid + */ + + +enum nss_status _nss_sss_initgroups_dyn(const char *user, gid_t group, + long int *start, long int *size, + gid_t **groups, long int limit, + int *errnop) +{ + struct sss_cli_req_data rd; + uint8_t *repbuf; + size_t replen; + enum nss_status nret; + uint32_t *rbuf; + uint32_t num_ret; + long int l, max_ret; + + rd.len = strlen(user) +1; + rd.data = user; + + nret = sss_nss_make_request(SSS_NSS_INITGR, &rd, + &repbuf, &replen, errnop); + if (nret != NSS_STATUS_SUCCESS) { + return nret; + } + + /* no results if not found */ + num_ret = ((uint32_t *)repbuf)[0]; + if (num_ret == 0) { + free(repbuf); + return NSS_STATUS_NOTFOUND; + } + max_ret = num_ret; + + /* check we have enough space in the buffer */ + if ((*size - *start) < num_ret) { + long int newsize; + gid_t *newgroups; + + newsize = *size + num_ret; + if ((limit > 0) && (newsize > limit)) { + newsize = limit; + max_ret = newsize - *start; + } + + newgroups = (gid_t *)realloc((*groups), newsize * sizeof(**groups)); + if (!newgroups) { + *errnop = ENOMEM; + free(repbuf); + return NSS_STATUS_TRYAGAIN; + } + *groups = newgroups; + *size = newsize; + } + + rbuf = &((uint32_t *)repbuf)[2]; + for (l = 0; l < max_ret; l++) { + (*groups)[*start] = rbuf[l]; + *start += 1; + } + + return NSS_STATUS_SUCCESS; +} + + +enum nss_status _nss_sss_getgrnam_r(const char *name, struct group *result, + char *buffer, size_t buflen, int *errnop) +{ + struct sss_cli_req_data rd; + struct sss_nss_gr_rep grrep; + uint8_t *repbuf; + size_t replen, len; + enum nss_status nret; + int ret; + + /* Caught once glibc passing in buffer == 0x0 */ + if (!buffer || !buflen) return ERANGE; + + rd.len = strlen(name) + 1; + rd.data = name; + + nret = sss_nss_make_request(SSS_NSS_GETGRNAM, &rd, + &repbuf, &replen, errnop); + if (nret != NSS_STATUS_SUCCESS) { + return nret; + } + + grrep.result = result; + grrep.buffer = buffer; + grrep.buflen = buflen; + + /* no results if not found */ + if (((uint32_t *)repbuf)[0] == 0) { + free(repbuf); + return NSS_STATUS_NOTFOUND; + } + + /* only 1 result is accepted for this function */ + if (((uint32_t *)repbuf)[0] != 1) { + *errnop = EBADMSG; + return NSS_STATUS_TRYAGAIN; + } + + len = replen - 8; + ret = sss_nss_getgr_readrep(&grrep, repbuf+8, &len); + free(repbuf); + if (ret) { + *errnop = ret; + return NSS_STATUS_TRYAGAIN; + } + + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_sss_getgrgid_r(gid_t gid, struct group *result, + char *buffer, size_t buflen, int *errnop) +{ + struct sss_cli_req_data rd; + struct sss_nss_gr_rep grrep; + uint8_t *repbuf; + size_t replen, len; + enum nss_status nret; + uint32_t group_gid; + int ret; + + /* Caught once glibc passing in buffer == 0x0 */ + if (!buffer || !buflen) return ERANGE; + + group_gid = gid; + rd.len = sizeof(uint32_t); + rd.data = &group_gid; + + nret = sss_nss_make_request(SSS_NSS_GETGRGID, &rd, + &repbuf, &replen, errnop); + if (nret != NSS_STATUS_SUCCESS) { + return nret; + } + + grrep.result = result; + grrep.buffer = buffer; + grrep.buflen = buflen; + + /* no results if not found */ + if (((uint32_t *)repbuf)[0] == 0) { + free(repbuf); + return NSS_STATUS_NOTFOUND; + } + + /* only 1 result is accepted for this function */ + if (((uint32_t *)repbuf)[0] != 1) { + *errnop = EBADMSG; + return NSS_STATUS_TRYAGAIN; + } + + len = replen - 8; + ret = sss_nss_getgr_readrep(&grrep, repbuf+8, &len); + free(repbuf); + if (ret) { + *errnop = ret; + return NSS_STATUS_TRYAGAIN; + } + + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_sss_setgrent(void) +{ + enum nss_status nret; + int errnop; + + /* make sure we do not have leftovers, and release memory */ + sss_nss_getgrent_data_clean(); + + nret = sss_nss_make_request(SSS_NSS_SETGRENT, + NULL, NULL, NULL, &errnop); + if (nret != NSS_STATUS_SUCCESS) { + errno = errnop; + return nret; + } + + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_sss_getgrent_r(struct group *result, + char *buffer, size_t buflen, int *errnop) +{ + struct sss_cli_req_data rd; + struct sss_nss_gr_rep grrep; + uint8_t *repbuf; + size_t replen; + enum nss_status nret; + uint32_t num_entries; + int ret; + + /* Caught once glibc passing in buffer == 0x0 */ + if (!buffer || !buflen) return ERANGE; + + /* if there are leftovers return the next one */ + if (sss_nss_getgrent_data.data != NULL && + sss_nss_getgrent_data.ptr < sss_nss_getgrent_data.len) { + + repbuf = (uint8_t *)sss_nss_getgrent_data.data + + sss_nss_getgrent_data.ptr; + replen = sss_nss_getgrent_data.len - + sss_nss_getgrent_data.ptr; + + grrep.result = result; + grrep.buffer = buffer; + grrep.buflen = buflen; + + ret = sss_nss_getgr_readrep(&grrep, repbuf, &replen); + if (ret) { + *errnop = ret; + return NSS_STATUS_TRYAGAIN; + } + + /* advance buffer pointer */ + sss_nss_getgrent_data.ptr = sss_nss_getgrent_data.len - replen; + + return NSS_STATUS_SUCCESS; + } + + /* release memory if any */ + sss_nss_getgrent_data_clean(); + + /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */ + num_entries = SSS_NSS_MAX_ENTRIES; + rd.len = sizeof(uint32_t); + rd.data = &num_entries; + + nret = sss_nss_make_request(SSS_NSS_GETGRENT, &rd, + &repbuf, &replen, errnop); + if (nret != NSS_STATUS_SUCCESS) { + return nret; + } + + /* no results if not found */ + if ((((uint32_t *)repbuf)[0] == 0) || (replen - 8 == 0)) { + free(repbuf); + return NSS_STATUS_NOTFOUND; + } + + sss_nss_getgrent_data.data = repbuf; + sss_nss_getgrent_data.len = replen; + sss_nss_getgrent_data.ptr = 8; /* skip metadata fields */ + + /* call again ourselves, this will return the first result */ + return _nss_sss_getgrent_r(result, buffer, buflen, errnop); +} + +enum nss_status _nss_sss_endgrent(void) +{ + enum nss_status nret; + int errnop; + + /* make sure we do not have leftovers, and release memory */ + sss_nss_getgrent_data_clean(); + + nret = sss_nss_make_request(SSS_NSS_ENDGRENT, + NULL, NULL, NULL, &errnop); + if (nret != NSS_STATUS_SUCCESS) { + errno = errnop; + return nret; + } + + return NSS_STATUS_SUCCESS; +} diff --git a/src/sss_client/man/pam_sss.8.xml b/src/sss_client/man/pam_sss.8.xml new file mode 100644 index 00000000..f6ac9f47 --- /dev/null +++ b/src/sss_client/man/pam_sss.8.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.4//EN" +"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> +<reference> +<title>SSSD Manual pages</title> +<refentry> + <refentryinfo> + <productname>SSSD</productname> + <orgname>The SSSD upstream - http://fedorahosted.org/sssd</orgname> + </refentryinfo> + + <refmeta> + <refentrytitle>pam_sss</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + + <refnamediv id='name'> + <refname>pam_sss</refname> + <refpurpose>PAM module for SSSD</refpurpose> + </refnamediv> + + <refsynopsisdiv id='synopsis'> + <cmdsynopsis> + <command>pam_sss.so</command> + <arg choice='opt'> + <replaceable>forward_pass</replaceable> + </arg> + <arg choice='opt'> + <replaceable>use_first_pass</replaceable> + </arg> + <arg choice='opt'> + <replaceable>use_authtok</replaceable> + </arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1 id='description'> + <title>DESCRIPTION</title> + <para><command>pam_sss.so</command> is the PAM interface to the System + Security Services daemon (SSSD). Errors and results are logged through + <command>syslog(3)</command> with the LOG_AUTHPRIV facility.</para> + </refsect1> + + <refsect1 id='options'> + <title>OPTIONS</title> + <variablelist remap='IP'> + <varlistentry> + <term> + <option>forward_pass</option> + </term> + <listitem> + <para>If <option>forward_pass</option> is set the entered + password is put on the stack for other PAM modules to use. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>use_first_pass</option> + </term> + <listitem> + <para>The argument use_first_pass forces the module to use + a previous stacked modules password and will never prompt + the user - if no password is available or the password is + not appropriate, the user will be denied access.</para> + </listitem> + </varlistentry> + <varlistentry> + <term> + <option>use_authtok</option> + </term> + <listitem> + <para>When password changing enforce the module to set the + new password to the one provided by a previously stacked + password module.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1 id='module_types_provides'> + <title>MODULE TYPES PROVIDED</title> + <para>All module types (<option>account</option>, <option>auth</option>, + <option>password</option> and <option>session</option>) are provided. + </para> + </refsect1> + + <refsect1 id='see_also'> + <title>SEE ALSO</title> + <para> + <citerefentry> + <refentrytitle>sssd.conf</refentrytitle><manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> +</refentry> +</reference> diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c new file mode 100644 index 00000000..8a1e3129 --- /dev/null +++ b/src/sss_client/pam_sss.c @@ -0,0 +1,1166 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 Red Hat + + 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/>. +*/ + +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#define PAM_SM_SESSION +#define PAM_SM_PASSWORD + +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <syslog.h> +#include <time.h> + +#include <security/pam_modules.h> +#include <security/pam_ext.h> +#include <security/pam_modutil.h> +#include "sss_pam_macros.h" + +#include "sss_cli.h" + +#include <libintl.h> +#define _(STRING) dgettext (PACKAGE, STRING) +#include "config.h" + +#define FLAGS_USE_FIRST_PASS (1 << 0) +#define FLAGS_FORWARD_PASS (1 << 1) +#define FLAGS_USE_AUTHTOK (1 << 2) + +struct pam_items { + const char* pam_service; + const char* pam_user; + const char* pam_tty; + const char* pam_ruser; + const char* pam_rhost; + char* pam_authtok; + char* pam_newauthtok; + const char* pamstack_authtok; + const char* pamstack_oldauthtok; + size_t pam_service_size; + size_t pam_user_size; + size_t pam_tty_size; + size_t pam_ruser_size; + size_t pam_rhost_size; + int pam_authtok_type; + size_t pam_authtok_size; + int pam_newauthtok_type; + size_t pam_newauthtok_size; + pid_t cli_pid; + const char *login_name; +}; + +#define DEBUG_MGS_LEN 1024 +#define MAX_AUTHTOK_SIZE (1024*1024) +#define CHECK_AND_RETURN_PI_STRING(s) ((s != NULL && *s != '\0')? s : "(not available)") + +static void logger(pam_handle_t *pamh, int level, const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + +#ifdef DEBUG + va_list apd; + char debug_msg[DEBUG_MGS_LEN]; + int ret; + va_copy(apd, ap); + + ret = vsnprintf(debug_msg, DEBUG_MGS_LEN, fmt, apd); + if (ret >= DEBUG_MGS_LEN) { + D(("the following message is truncated: %s", debug_msg)); + } else if (ret < 0) { + D(("vsnprintf failed to format debug message!")); + } else { + D((debug_msg)); + } + + va_end(apd); +#endif + + pam_vsyslog(pamh, LOG_AUTHPRIV|level, fmt, ap); + + va_end(ap); +} + + +static size_t add_authtok_item(enum pam_item_type type, + enum sss_authtok_type authtok_type, + const char *tok, const size_t size, + uint8_t *buf) { + size_t rp=0; + uint32_t c; + + if (tok == NULL) return 0; + + c = type; + memcpy(&buf[rp], &c, sizeof(uint32_t)); + rp += sizeof(uint32_t); + + c = size + sizeof(uint32_t); + memcpy(&buf[rp], &c, sizeof(uint32_t)); + rp += sizeof(uint32_t); + + c = authtok_type; + memcpy(&buf[rp], &c, sizeof(uint32_t)); + rp += sizeof(uint32_t); + + memcpy(&buf[rp], tok, size); + rp += size; + + return rp; +} + + +static size_t add_uint32_t_item(enum pam_item_type type, const uint32_t val, + uint8_t *buf) { + size_t rp=0; + uint32_t c; + + c = type; + memcpy(&buf[rp], &c, sizeof(uint32_t)); + rp += sizeof(uint32_t); + + c = sizeof(uint32_t); + memcpy(&buf[rp], &c, sizeof(uint32_t)); + rp += sizeof(uint32_t); + + c = val; + memcpy(&buf[rp], &c, sizeof(uint32_t)); + rp += sizeof(uint32_t); + + return rp; +} + +static size_t add_string_item(enum pam_item_type type, const char *str, + const size_t size, uint8_t *buf) { + size_t rp=0; + uint32_t c; + + if (str == NULL || *str == '\0') return 0; + + c = type; + memcpy(&buf[rp], &c, sizeof(uint32_t)); + rp += sizeof(uint32_t); + + c = size; + memcpy(&buf[rp], &c, sizeof(uint32_t)); + rp += sizeof(uint32_t); + + memcpy(&buf[rp], str, size); + rp += size; + + return rp; +} + +static void overwrite_and_free_authtoks(struct pam_items *pi) +{ + if (pi->pam_authtok != NULL) { + _pam_overwrite_n((void *)pi->pam_authtok, pi->pam_authtok_size); + free((void *)pi->pam_authtok); + pi->pam_authtok = NULL; + } + + if (pi->pam_newauthtok != NULL) { + _pam_overwrite_n((void *)pi->pam_newauthtok, pi->pam_newauthtok_size); + free((void *)pi->pam_newauthtok); + pi->pam_newauthtok = NULL; + } +} + +static int pack_message_v3(struct pam_items *pi, size_t *size, + uint8_t **buffer) { + int len; + uint8_t *buf; + int rp; + uint32_t terminator = SSS_END_OF_PAM_REQUEST; + + len = sizeof(uint32_t) + + 2*sizeof(uint32_t) + pi->pam_user_size + + sizeof(uint32_t); + len += *pi->pam_service != '\0' ? + 2*sizeof(uint32_t) + pi->pam_service_size : 0; + len += *pi->pam_tty != '\0' ? + 2*sizeof(uint32_t) + pi->pam_tty_size : 0; + len += *pi->pam_ruser != '\0' ? + 2*sizeof(uint32_t) + pi->pam_ruser_size : 0; + len += *pi->pam_rhost != '\0' ? + 2*sizeof(uint32_t) + pi->pam_rhost_size : 0; + len += pi->pam_authtok != NULL ? + 3*sizeof(uint32_t) + pi->pam_authtok_size : 0; + len += pi->pam_newauthtok != NULL ? + 3*sizeof(uint32_t) + pi->pam_newauthtok_size : 0; + len += 3*sizeof(uint32_t); /* cli_pid */ + + buf = malloc(len); + if (buf == NULL) { + D(("malloc failed.")); + return PAM_BUF_ERR; + } + + rp = 0; + ((uint32_t *)(&buf[rp]))[0] = SSS_START_OF_PAM_REQUEST; + rp += sizeof(uint32_t); + + rp += add_string_item(SSS_PAM_ITEM_USER, pi->pam_user, pi->pam_user_size, + &buf[rp]); + + rp += add_string_item(SSS_PAM_ITEM_SERVICE, pi->pam_service, + pi->pam_service_size, &buf[rp]); + + rp += add_string_item(SSS_PAM_ITEM_TTY, pi->pam_tty, pi->pam_tty_size, + &buf[rp]); + + rp += add_string_item(SSS_PAM_ITEM_RUSER, pi->pam_ruser, pi->pam_ruser_size, + &buf[rp]); + + rp += add_string_item(SSS_PAM_ITEM_RHOST, pi->pam_rhost, pi->pam_rhost_size, + &buf[rp]); + + rp += add_uint32_t_item(SSS_PAM_ITEM_CLI_PID, (uint32_t) pi->cli_pid, + &buf[rp]); + + rp += add_authtok_item(SSS_PAM_ITEM_AUTHTOK, pi->pam_authtok_type, + pi->pam_authtok, pi->pam_authtok_size, &buf[rp]); + + rp += add_authtok_item(SSS_PAM_ITEM_NEWAUTHTOK, pi->pam_newauthtok_type, + pi->pam_newauthtok, pi->pam_newauthtok_size, + &buf[rp]); + + memcpy(&buf[rp], &terminator, sizeof(uint32_t)); + rp += sizeof(uint32_t); + + if (rp != len) { + D(("error during packet creation.")); + return PAM_BUF_ERR; + } + + *size = len; + *buffer = buf; + + return 0; +} + +static int null_strcmp(const char *s1, const char *s2) { + if (s1 == NULL && s2 == NULL) return 0; + if (s1 == NULL && s2 != NULL) return -1; + if (s1 != NULL && s2 == NULL) return 1; + return strcmp(s1, s2); +} + +enum { + SSS_PAM_CONV_DONE = 0, + SSS_PAM_CONV_STD, + SSS_PAM_CONV_REENTER, +}; + +static int do_pam_conversation(pam_handle_t *pamh, const int msg_style, + const char *msg, + const char *reenter_msg, + char **answer) +{ + int ret; + int state = SSS_PAM_CONV_STD; + struct pam_conv *conv; + struct pam_message *mesg[1]; + struct pam_response *resp=NULL; + + if ((msg_style == PAM_TEXT_INFO || msg_style == PAM_ERROR_MSG) && + msg == NULL) return PAM_SYSTEM_ERR; + + if ((msg_style == PAM_PROMPT_ECHO_OFF || + msg_style == PAM_PROMPT_ECHO_ON) && + (msg == NULL || answer == NULL)) return PAM_SYSTEM_ERR; + + if (msg_style == PAM_TEXT_INFO || msg_style == PAM_ERROR_MSG) { + logger(pamh, LOG_INFO, "User %s message: %s", + msg_style == PAM_TEXT_INFO ? "info" : "error", + msg); + } + + ret=pam_get_item(pamh, PAM_CONV, (const void **) &conv); + if (ret != PAM_SUCCESS) return ret; + + do { + mesg[0] = malloc(sizeof(struct pam_message)); + if (mesg[0] == NULL) { + D(("Malloc failed.")); + return PAM_SYSTEM_ERR; + } + + mesg[0]->msg_style = msg_style; + if (state == SSS_PAM_CONV_REENTER) { + mesg[0]->msg = reenter_msg; + } else { + mesg[0]->msg = msg; + } + + ret=conv->conv(1, (const struct pam_message **) mesg, &resp, + conv->appdata_ptr); + free(mesg[0]); + if (ret != PAM_SUCCESS) { + D(("Conversation failure: %s.", pam_strerror(pamh,ret))); + return ret; + } + + if (msg_style == PAM_PROMPT_ECHO_OFF || + msg_style == PAM_PROMPT_ECHO_ON) { + if (resp == NULL) { + D(("response expected, but resp==NULL")); + return PAM_SYSTEM_ERR; + } + + if (state == SSS_PAM_CONV_REENTER) { + if (null_strcmp(*answer, resp[0].resp) != 0) { + logger(pamh, LOG_NOTICE, "Passwords do not match."); + _pam_overwrite((void *)resp[0].resp); + free(resp[0].resp); + if (*answer != NULL) { + _pam_overwrite((void *)*answer); + free(*answer); + *answer = NULL; + } + ret = do_pam_conversation(pamh, PAM_ERROR_MSG, + _("Passwords do not match"), + NULL, NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return PAM_SYSTEM_ERR; + } + return PAM_CRED_ERR; + } + _pam_overwrite((void *)resp[0].resp); + free(resp[0].resp); + } else { + if (resp[0].resp == NULL) { + D(("Empty password")); + *answer = NULL; + } else { + *answer = strndup(resp[0].resp, MAX_AUTHTOK_SIZE); + _pam_overwrite((void *)resp[0].resp); + free(resp[0].resp); + if(*answer == NULL) { + D(("strndup failed")); + return PAM_BUF_ERR; + } + } + } + free(resp); + resp = NULL; + } + + if (reenter_msg != NULL && state == SSS_PAM_CONV_STD) { + state = SSS_PAM_CONV_REENTER; + } else { + state = SSS_PAM_CONV_DONE; + } + } while (state != SSS_PAM_CONV_DONE); + + return PAM_SUCCESS; +} + +static int user_info_offline_auth(pam_handle_t *pamh, size_t buflen, + uint8_t *buf) +{ + int ret; + long long expire_date; + struct tm tm; + char expire_str[128]; + char user_msg[256]; + + expire_str[0] = '\0'; + + if (buflen != sizeof(uint32_t) + sizeof(long long)) { + D(("User info response data has the wrong size")); + return PAM_BUF_ERR; + } + + memcpy(&expire_date, buf + sizeof(uint32_t), sizeof(long long)); + + if (expire_date > 0) { + if (localtime_r((time_t *) &expire_date, &tm) != NULL) { + ret = strftime(expire_str, sizeof(expire_str), "%c", &tm); + if (ret == 0) { + D(("strftime failed.")); + expire_str[0] = '\0'; + } + } else { + D(("localtime_r failed")); + } + } + + ret = snprintf(user_msg, sizeof(user_msg), "%s%s%s.", + _("Offline authentication"), + expire_str[0] ? _(", your cached password will expire at: ") : "", + expire_str[0] ? expire_str : ""); + if (ret < 0 || ret >= sizeof(user_msg)) { + D(("snprintf failed.")); + return PAM_SYSTEM_ERR; + } + + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, user_msg, NULL, NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return PAM_SYSTEM_ERR; + } + + return PAM_SUCCESS; +} + +static int user_info_offline_auth_delayed(pam_handle_t *pamh, size_t buflen, + uint8_t *buf) +{ + int ret; + long long delayed_until; + struct tm tm; + char delay_str[128]; + char user_msg[256]; + + delay_str[0] = '\0'; + + if (buflen != sizeof(uint32_t) + sizeof(long long)) { + D(("User info response data has the wrong size")); + return PAM_BUF_ERR; + } + + memcpy(&delayed_until, buf + sizeof(uint32_t), sizeof(long long)); + + if (delayed_until <= 0) { + D(("User info response data has an invalid value")); + return PAM_BUF_ERR; + } + + if (localtime_r((time_t *) &delayed_until, &tm) != NULL) { + ret = strftime(delay_str, sizeof(delay_str), "%c", &tm); + if (ret == 0) { + D(("strftime failed.")); + delay_str[0] = '\0'; + } + } else { + D(("localtime_r failed")); + } + + ret = snprintf(user_msg, sizeof(user_msg), "%s%s.", + _("Offline authentication, authentication is denied until: "), + delay_str); + if (ret < 0 || ret >= sizeof(user_msg)) { + D(("snprintf failed.")); + return PAM_SYSTEM_ERR; + } + + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, user_msg, NULL, NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return PAM_SYSTEM_ERR; + } + + return PAM_SUCCESS; +} + +static int user_info_offline_chpass(pam_handle_t *pamh, size_t buflen, + uint8_t *buf) +{ + int ret; + + if (buflen != sizeof(uint32_t)) { + D(("User info response data has the wrong size")); + return PAM_BUF_ERR; + } + + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, + _("System is offline, password change not possible"), + NULL, NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return PAM_SYSTEM_ERR; + } + + return PAM_SUCCESS; +} + +static int user_info_chpass_error(pam_handle_t *pamh, size_t buflen, + uint8_t *buf) +{ + int ret; + uint32_t msg_len; + char user_msg[256]; + + if (buflen < 2* sizeof(uint32_t)) { + D(("User info response data is too short")); + return PAM_BUF_ERR; + } + + memcpy(&msg_len, buf + sizeof(uint32_t), sizeof(uint32_t)); + + if (buflen != 2* sizeof(uint32_t) + msg_len) { + D(("User info response data has the wrong size")); + return PAM_BUF_ERR; + } + + ret = snprintf(user_msg, sizeof(user_msg), "%s%s%.*s", + _("Password change failed. "), + msg_len > 0 ? _("Server message: ") : "", + msg_len, + msg_len > 0 ? (char *)(buf + 2 * sizeof(uint32_t)) : "" ); + if (ret < 0 || ret >= sizeof(user_msg)) { + D(("snprintf failed.")); + return PAM_SYSTEM_ERR; + } + + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, user_msg, NULL, NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return PAM_SYSTEM_ERR; + } + + return PAM_SUCCESS; +} + + +static int eval_user_info_response(pam_handle_t *pamh, size_t buflen, + uint8_t *buf) +{ + int ret; + uint32_t type; + + if (buflen < sizeof(uint32_t)) { + D(("User info response data is too short")); + return PAM_BUF_ERR; + } + + memcpy(&type, buf, sizeof(uint32_t)); + + switch(type) { + case SSS_PAM_USER_INFO_OFFLINE_AUTH: + ret = user_info_offline_auth(pamh, buflen, buf); + break; + case SSS_PAM_USER_INFO_OFFLINE_AUTH_DELAYED: + ret = user_info_offline_auth_delayed(pamh, buflen, buf); + break; + case SSS_PAM_USER_INFO_OFFLINE_CHPASS: + ret = user_info_offline_chpass(pamh, buflen, buf); + break; + case SSS_PAM_USER_INFO_CHPASS_ERROR: + ret = user_info_chpass_error(pamh, buflen, buf); + break; + default: + D(("Unknown user info type [%d]", type)); + ret = PAM_SYSTEM_ERR; + } + + return ret; +} + +static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf) +{ + int ret; + size_t p=0; + char *env_item; + int32_t c; + int32_t type; + int32_t len; + int32_t pam_status; + + if (buflen < (2*sizeof(int32_t))) { + D(("response buffer is too small")); + return PAM_BUF_ERR; + } + + memcpy(&pam_status, buf+p, sizeof(int32_t)); + p += sizeof(int32_t); + + + memcpy(&c, buf+p, sizeof(int32_t)); + p += sizeof(int32_t); + + while(c>0) { + if (buflen < (p+2*sizeof(int32_t))) { + D(("response buffer is too small")); + return PAM_BUF_ERR; + } + + memcpy(&type, buf+p, sizeof(int32_t)); + p += sizeof(int32_t); + + memcpy(&len, buf+p, sizeof(int32_t)); + p += sizeof(int32_t); + + if (buflen < (p + len)) { + D(("response buffer is too small")); + return PAM_BUF_ERR; + } + + switch(type) { + case SSS_PAM_SYSTEM_INFO: + if (buf[p + (len -1)] != '\0') { + D(("user info does not end with \\0.")); + break; + } + logger(pamh, LOG_INFO, "system info: [%s]", &buf[p]); + break; + case SSS_PAM_DOMAIN_NAME: + D(("domain name: [%s]", &buf[p])); + break; + case SSS_ENV_ITEM: + case SSS_PAM_ENV_ITEM: + case SSS_ALL_ENV_ITEM: + if (buf[p + (len -1)] != '\0') { + D(("env item does not end with \\0.")); + break; + } + + D(("env item: [%s]", &buf[p])); + if (type == SSS_PAM_ENV_ITEM || type == SSS_ALL_ENV_ITEM) { + ret = pam_putenv(pamh, (char *)&buf[p]); + if (ret != PAM_SUCCESS) { + D(("pam_putenv failed.")); + break; + } + } + + if (type == SSS_ENV_ITEM || type == SSS_ALL_ENV_ITEM) { + env_item = strdup((char *)&buf[p]); + if (env_item == NULL) { + D(("strdup failed")); + break; + } + ret = putenv(env_item); + if (ret == -1) { + D(("putenv failed.")); + break; + } + } + break; + case SSS_PAM_USER_INFO: + ret = eval_user_info_response(pamh, len, &buf[p]); + if (ret != PAM_SUCCESS) { + D(("eval_user_info_response failed")); + } + break; + default: + D(("Unknown response type [%d]", type)); + } + p += len; + + --c; + } + + return PAM_SUCCESS; +} + +static int get_pam_items(pam_handle_t *pamh, struct pam_items *pi) +{ + int ret; + + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi->pam_authtok = NULL; + pi->pam_authtok_size = 0; + pi->pam_newauthtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi->pam_newauthtok = NULL; + pi->pam_newauthtok_size = 0; + + ret = pam_get_item(pamh, PAM_SERVICE, (const void **) &(pi->pam_service)); + if (ret != PAM_SUCCESS) return ret; + if (pi->pam_service == NULL) pi->pam_service=""; + pi->pam_service_size=strlen(pi->pam_service)+1; + + ret = pam_get_item(pamh, PAM_USER, (const void **) &(pi->pam_user)); + if (ret != PAM_SUCCESS) return ret; + if (pi->pam_user == NULL) { + D(("No user found, aborting.")); + return PAM_BAD_ITEM; + } + if (strcmp(pi->pam_user, "root") == 0) { + D(("pam_sss will not handle root.")); + return PAM_USER_UNKNOWN; + } + pi->pam_user_size=strlen(pi->pam_user)+1; + + + ret = pam_get_item(pamh, PAM_TTY, (const void **) &(pi->pam_tty)); + if (ret != PAM_SUCCESS) return ret; + if (pi->pam_tty == NULL) pi->pam_tty=""; + pi->pam_tty_size=strlen(pi->pam_tty)+1; + + ret = pam_get_item(pamh, PAM_RUSER, (const void **) &(pi->pam_ruser)); + if (ret != PAM_SUCCESS) return ret; + if (pi->pam_ruser == NULL) pi->pam_ruser=""; + pi->pam_ruser_size=strlen(pi->pam_ruser)+1; + + ret = pam_get_item(pamh, PAM_RHOST, (const void **) &(pi->pam_rhost)); + if (ret != PAM_SUCCESS) return ret; + if (pi->pam_rhost == NULL) pi->pam_rhost=""; + pi->pam_rhost_size=strlen(pi->pam_rhost)+1; + + ret = pam_get_item(pamh, PAM_AUTHTOK, + (const void **) &(pi->pamstack_authtok)); + if (ret != PAM_SUCCESS) return ret; + if (pi->pamstack_authtok == NULL) pi->pamstack_authtok=""; + + ret = pam_get_item(pamh, PAM_OLDAUTHTOK, + (const void **) &(pi->pamstack_oldauthtok)); + if (ret != PAM_SUCCESS) return ret; + if (pi->pamstack_oldauthtok == NULL) pi->pamstack_oldauthtok=""; + + pi->cli_pid = getpid(); + + pi->login_name = pam_modutil_getlogin(pamh); + if (pi->login_name == NULL) pi->login_name=""; + + return PAM_SUCCESS; +} + +static void print_pam_items(struct pam_items *pi) +{ + if (pi == NULL) return; + + D(("Service: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_service))); + D(("User: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_user))); + D(("Tty: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_tty))); + D(("Ruser: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_ruser))); + D(("Rhost: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_rhost))); + D(("Pamstack_Authtok: %s", + CHECK_AND_RETURN_PI_STRING(pi->pamstack_authtok))); + D(("Pamstack_Oldauthtok: %s", + CHECK_AND_RETURN_PI_STRING(pi->pamstack_oldauthtok))); + D(("Authtok: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_authtok))); + D(("Newauthtok: %s", CHECK_AND_RETURN_PI_STRING(pi->pam_newauthtok))); + D(("Cli_PID: %d", pi->cli_pid)); +} + +static int send_and_receive(pam_handle_t *pamh, struct pam_items *pi, + enum sss_cli_command task) +{ + int ret; + int errnop; + struct sss_cli_req_data rd; + uint8_t *buf = NULL; + uint8_t *repbuf = NULL; + size_t replen; + int pam_status = PAM_SYSTEM_ERR; + + print_pam_items(pi); + + ret = pack_message_v3(pi, &rd.len, &buf); + if (ret != 0) { + D(("pack_message failed.")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + rd.data = buf; + + ret = sss_pam_make_request(task, &rd, &repbuf, &replen, &errnop); + + if (ret != NSS_STATUS_SUCCESS) { + logger(pamh, LOG_ERR, "Request to sssd failed."); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + +/* FIXME: add an end signature */ + if (replen < (2*sizeof(int32_t))) { + D(("response not in expected format.")); + pam_status = PAM_SYSTEM_ERR; + goto done; + } + + pam_status = ((int32_t *)repbuf)[0]; + ret = eval_response(pamh, replen, repbuf); + if (ret != PAM_SUCCESS) { + D(("eval_response failed.")); + pam_status = ret; + goto done; + } + + switch (task) { + case SSS_PAM_AUTHENTICATE: + logger(pamh, (pam_status == PAM_SUCCESS ? LOG_INFO : LOG_NOTICE), + "authentication %s; logname=%s uid=%lu euid=%d tty=%s " + "ruser=%s rhost=%s user=%s", + pam_status == PAM_SUCCESS ? "success" : "failure", + pi->login_name, getuid(), (unsigned long) geteuid(), + pi->pam_tty, pi->pam_ruser, pi->pam_rhost, pi->pam_user); + if (pam_status != PAM_SUCCESS) { + logger(pamh, LOG_NOTICE, "received for user %s: %d (%s)", + pi->pam_user, pam_status, + pam_strerror(pamh,pam_status)); + } + break; + case SSS_PAM_CHAUTHTOK_PRELIM: + if (pam_status != PAM_SUCCESS) { + logger(pamh, LOG_NOTICE, + "Authentication failed for user %s: %d (%s)", + pi->pam_user, pam_status, + pam_strerror(pamh,pam_status)); + } + break; + case SSS_PAM_CHAUTHTOK: + if (pam_status != PAM_SUCCESS) { + logger(pamh, LOG_NOTICE, + "Password change failed for user %s: %d (%s)", + pi->pam_user, pam_status, + pam_strerror(pamh,pam_status)); + } + break; + case SSS_PAM_ACCT_MGMT: + if (pam_status != PAM_SUCCESS) { + logger(pamh, LOG_NOTICE, + "Access denied for user %s: %d (%s)", + pi->pam_user, pam_status, + pam_strerror(pamh,pam_status)); + } + break; + case SSS_PAM_SETCRED: + case SSS_PAM_OPEN_SESSION: + case SSS_PAM_CLOSE_SESSION: + break; + default: + D(("Illegal task [%d]", task)); + return PAM_SYSTEM_ERR; + } + +done: + if (buf != NULL ) { + _pam_overwrite_n((void *)buf, rd.len); + free(buf); + } + free(repbuf); + + return pam_status; +} + +static int prompt_password(pam_handle_t *pamh, struct pam_items *pi) +{ + int ret; + char *answer = NULL; + + ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, _("Password: "), + NULL, &answer); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return ret; + } + + if (answer == NULL) { + pi->pam_authtok = NULL; + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi->pam_authtok_size=0; + } else { + pi->pam_authtok = strdup(answer); + _pam_overwrite((void *)answer); + free(answer); + answer=NULL; + if (pi->pam_authtok == NULL) { + return PAM_BUF_ERR; + } + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi->pam_authtok_size=strlen(pi->pam_authtok); + } + + return PAM_SUCCESS; +} + +static int prompt_new_password(pam_handle_t *pamh, struct pam_items *pi) +{ + int ret; + char *answer = NULL; + + ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, + _("New Password: "), + _("Reenter new Password: "), + &answer); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return ret; + } + if (answer == NULL) { + pi->pam_newauthtok = NULL; + pi->pam_newauthtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi->pam_newauthtok_size=0; + } else { + pi->pam_newauthtok = strdup(answer); + _pam_overwrite((void *)answer); + free(answer); + answer=NULL; + if (pi->pam_newauthtok == NULL) { + return PAM_BUF_ERR; + } + pi->pam_newauthtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi->pam_newauthtok_size=strlen(pi->pam_newauthtok); + } + + return PAM_SUCCESS; +} + +static void eval_argv(pam_handle_t *pamh, int argc, const char **argv, + uint32_t *flags) +{ + for (; argc-- > 0; ++argv) { + if (strcmp(*argv, "forward_pass") == 0) { + *flags |= FLAGS_FORWARD_PASS; + } else if (strcmp(*argv, "use_first_pass") == 0) { + *flags |= FLAGS_USE_FIRST_PASS; + } else if (strcmp(*argv, "use_authtok") == 0) { + *flags |= FLAGS_USE_AUTHTOK; + } else { + logger(pamh, LOG_WARNING, "unknown option: %s", *argv); + } + } + + return; +} + +static int get_authtok_for_authentication(pam_handle_t *pamh, + struct pam_items *pi, + uint32_t flags) +{ + int ret; + + if (flags & FLAGS_USE_FIRST_PASS) { + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi->pam_authtok = strdup(pi->pamstack_authtok); + if (pi->pam_authtok == NULL) { + D(("option use_first_pass set, but no password found")); + return PAM_BUF_ERR; + } + pi->pam_authtok_size = strlen(pi->pam_authtok); + } else { + ret = prompt_password(pamh, pi); + if (ret != PAM_SUCCESS) { + D(("failed to get password from user")); + return ret; + } + + if (flags & FLAGS_FORWARD_PASS) { + ret = pam_set_item(pamh, PAM_AUTHTOK, pi->pam_authtok); + if (ret != PAM_SUCCESS) { + D(("Failed to set PAM_AUTHTOK [%s], " + "authtok may not be available for other modules", + pam_strerror(pamh,ret))); + } + } + } + + return PAM_SUCCESS; +} + +static int get_authtok_for_password_change(pam_handle_t *pamh, + struct pam_items *pi, + uint32_t flags, + int pam_flags) +{ + int ret; + + /* we query for the old password during PAM_PRELIM_CHECK to make + * pam_sss work e.g. with pam_cracklib */ + if (pam_flags & PAM_PRELIM_CHECK) { + if (getuid() != 0 && !(flags & FLAGS_USE_FIRST_PASS)) { + ret = prompt_password(pamh, pi); + if (ret != PAM_SUCCESS) { + D(("failed to get password from user")); + return ret; + } + + ret = pam_set_item(pamh, PAM_OLDAUTHTOK, pi->pam_authtok); + if (ret != PAM_SUCCESS) { + D(("Failed to set PAM_OLDAUTHTOK [%s], " + "oldauthtok may not be available", + pam_strerror(pamh,ret))); + return ret; + } + } + + return PAM_SUCCESS; + } + + if (pi->pamstack_oldauthtok == NULL) { + if (getuid() != 0) { + D(("no password found for chauthtok")); + return PAM_BUF_ERR; + } else { + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi->pam_authtok = NULL; + pi->pam_authtok_size = 0; + } + } else { + pi->pam_authtok = strdup(pi->pamstack_oldauthtok); + pi->pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi->pam_authtok_size = strlen(pi->pam_authtok); + } + + if (flags & FLAGS_USE_AUTHTOK) { + pi->pam_newauthtok_type = SSS_AUTHTOK_TYPE_PASSWORD; + pi->pam_newauthtok = strdup(pi->pamstack_authtok); + if (pi->pam_newauthtok == NULL) { + D(("option use_authtok set, but no new password found")); + return PAM_BUF_ERR; + } + pi->pam_newauthtok_size = strlen(pi->pam_newauthtok); + } else { + ret = prompt_new_password(pamh, pi); + if (ret != PAM_SUCCESS) { + D(("failed to get new password from user")); + return ret; + } + + if (flags & FLAGS_FORWARD_PASS) { + ret = pam_set_item(pamh, PAM_AUTHTOK, pi->pam_newauthtok); + if (ret != PAM_SUCCESS) { + D(("Failed to set PAM_AUTHTOK [%s], " + "oldauthtok may not be available", + pam_strerror(pamh,ret))); + } + } + } + + return PAM_SUCCESS; +} + +static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, + int pam_flags, int argc, const char **argv) +{ + int ret; + struct pam_items pi; + uint32_t flags = 0; + + bindtextdomain(PACKAGE, LOCALEDIR); + + D(("Hello pam_sssd: %d", task)); + + eval_argv(pamh, argc, argv, &flags); + + ret = get_pam_items(pamh, &pi); + if (ret != PAM_SUCCESS) { + D(("get items returned error: %s", pam_strerror(pamh,ret))); + return ret; + } + + + switch(task) { + case SSS_PAM_AUTHENTICATE: + ret = get_authtok_for_authentication(pamh, &pi, flags); + if (ret != PAM_SUCCESS) { + D(("failed to get authentication token: %s", + pam_strerror(pamh, ret))); + return ret; + } + break; + case SSS_PAM_CHAUTHTOK: + ret = get_authtok_for_password_change(pamh, &pi, flags, pam_flags); + if (ret != PAM_SUCCESS) { + D(("failed to get tokens for password change: %s", + pam_strerror(pamh, ret))); + return ret; + } + if (pam_flags & PAM_PRELIM_CHECK) { + task = SSS_PAM_CHAUTHTOK_PRELIM; + } + break; + case SSS_PAM_ACCT_MGMT: + case SSS_PAM_SETCRED: + case SSS_PAM_OPEN_SESSION: + case SSS_PAM_CLOSE_SESSION: + break; + default: + D(("Illegal task [%d]", task)); + return PAM_SYSTEM_ERR; + } + + ret = send_and_receive(pamh, &pi, task); + + if (ret == PAM_AUTHTOK_EXPIRED && task == SSS_PAM_AUTHENTICATE) { + D(("Authtoken expired, trying to change it")); + ret = do_pam_conversation(pamh, PAM_ERROR_MSG, + _("Password has expired."), NULL, NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return PAM_SYSTEM_ERR; + } + + pi.pamstack_oldauthtok = pi.pam_authtok; + ret = get_authtok_for_password_change(pamh, &pi, flags, pam_flags); + if (ret != PAM_SUCCESS) { + D(("failed to get tokens for password change: %s", + pam_strerror(pamh, ret))); + return ret; + } + + ret = send_and_receive(pamh, &pi, SSS_PAM_CHAUTHTOK); + } + + overwrite_and_free_authtoks(&pi); + + return ret; +} + +PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, + const char **argv ) +{ + return pam_sss(SSS_PAM_AUTHENTICATE, pamh, flags, argc, argv); +} + + +PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, + const char **argv ) +{ + return pam_sss(SSS_PAM_SETCRED, pamh, flags, argc, argv); +} + +PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, + const char **argv ) +{ + return pam_sss(SSS_PAM_ACCT_MGMT, pamh, flags, argc, argv); +} + +PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, + const char **argv ) +{ + return pam_sss(SSS_PAM_CHAUTHTOK, pamh, flags, argc, argv); +} + +PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, + const char **argv ) +{ + return pam_sss(SSS_PAM_OPEN_SESSION, pamh, flags, argc, argv); +} + +PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, + const char **argv ) +{ + return pam_sss(SSS_PAM_CLOSE_SESSION, pamh, flags, argc, argv); +} + + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_sssd_modstruct ={ + "pam_sssd", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + pam_sm_open_session, + pam_sm_close_session, + pam_sm_chauthtok +}; + +#endif diff --git a/src/sss_client/pam_test_client.c b/src/sss_client/pam_test_client.c new file mode 100644 index 00000000..a088981b --- /dev/null +++ b/src/sss_client/pam_test_client.c @@ -0,0 +1,95 @@ +/* + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 Red Hat + + 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/>. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <unistd.h> + +#include <security/pam_appl.h> +#include <security/pam_misc.h> + +static struct pam_conv conv = { + misc_conv, + NULL +}; + +int main(int argc, char *argv[]) { + + pam_handle_t *pamh; + char *user; + char *action; + int ret; + + if (argc == 1) { + fprintf(stderr, "missing action and user name, using default\n"); + action = strdup("auth"); + user = strdup("dummy"); + } else if (argc == 2) { + fprintf(stdout, "using first argument as action and default user name\n"); + action = strdup(argv[1]); + user = strdup("dummy"); + } else { + action = strdup(argv[1]); + user = strdup(argv[2]); + } + + fprintf(stdout, "action: %s\nuser: %s\n", action,user); + + ret = pam_start("sss_test", user, &conv, &pamh); + if (ret != PAM_SUCCESS) { + fprintf(stderr, "pam_start failed: %s\n", pam_strerror(pamh, ret)); + return 1; + } + + if ( strncmp(action, "auth", 4)== 0 ) { + fprintf(stdout, "testing pam_authenticate\n"); + ret = pam_authenticate(pamh, 0); + fprintf(stderr, "pam_authenticate: %s\n", pam_strerror(pamh, ret)); + } else if ( strncmp(action, "chau", 4)== 0 ) { + fprintf(stdout, "testing pam_chauthtok\n"); + ret = pam_chauthtok(pamh, 0); + fprintf(stderr, "pam_chauthtok: %s\n", pam_strerror(pamh, ret)); + } else if ( strncmp(action, "acct", 4)== 0 ) { + fprintf(stdout, "testing pam_acct_mgmt\n"); + ret = pam_acct_mgmt(pamh, 0); + fprintf(stderr, "pam_acct_mgmt: %s\n", pam_strerror(pamh, ret)); + } else if ( strncmp(action, "setc", 4)== 0 ) { + fprintf(stdout, "testing pam_setcred\n"); + ret = pam_setcred(pamh, 0); + fprintf(stderr, "pam_setcred: %d[%s]\n", ret, pam_strerror(pamh, ret)); + } else if ( strncmp(action, "open", 4)== 0 ) { + fprintf(stdout, "testing pam_open_session\n"); + ret = pam_open_session(pamh, 0); + fprintf(stderr, "pam_open_session: %s\n", pam_strerror(pamh, ret)); + } else if ( strncmp(action, "clos", 4)== 0 ) { + fprintf(stdout, "testing pam_close_session\n"); + ret = pam_close_session(pamh, 0); + fprintf(stderr, "pam_close_session: %s\n", pam_strerror(pamh, ret)); + } else { + fprintf(stderr, "unknown action\n"); + } + + pam_end(pamh, ret); + + return 0; +} diff --git a/src/sss_client/passwd.c b/src/sss_client/passwd.c new file mode 100644 index 00000000..0d70b684 --- /dev/null +++ b/src/sss_client/passwd.c @@ -0,0 +1,373 @@ +/* + * System Security Services Daemon. NSS client interface + * + * Copyright (C) Simo Sorce 2007 + * + * 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. + */ + +/* PASSWD database NSS interface */ + +#include <nss.h> +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include "sss_cli.h" + +static struct sss_nss_getpwent_data { + size_t len; + size_t ptr; + uint8_t *data; +} sss_nss_getpwent_data; + +static void sss_nss_getpwent_data_clean(void) { + + if (sss_nss_getpwent_data.data != NULL) { + free(sss_nss_getpwent_data.data); + sss_nss_getpwent_data.data = NULL; + } + sss_nss_getpwent_data.len = 0; + sss_nss_getpwent_data.ptr = 0; +} + +/* GETPWNAM Request: + * + * 0-X: string with name + * + * GERTPWUID Request: + * + * 0-3: 32bit number with uid + * + * Replies: + * + * 0-3: 32bit unsigned number of results + * 4-7: 32bit unsigned (reserved/padding) + * For each result: + * 0-3: 32bit number uid + * 4-7: 32bit number gid + * 8-X: sequence of 5, 0 terminated, strings (name, passwd, gecos, dir, shell) + */ + +struct sss_nss_pw_rep { + struct passwd *result; + char *buffer; + size_t buflen; +}; + +static int sss_nss_getpw_readrep(struct sss_nss_pw_rep *pr, + uint8_t *buf, size_t *len) +{ + size_t i, slen, dlen; + char *sbuf; + + if (*len < 13) { /* not enough space for data, bad packet */ + return EBADMSG; + } + + pr->result->pw_uid = ((uint32_t *)buf)[0]; + pr->result->pw_gid = ((uint32_t *)buf)[1]; + + sbuf = (char *)&buf[8]; + slen = *len - 8; + dlen = pr->buflen; + + i = 0; + pr->result->pw_name = &(pr->buffer[i]); + while (slen > i && dlen > 0) { + pr->buffer[i] = sbuf[i]; + if (pr->buffer[i] == '\0') break; + i++; + dlen--; + } + if (slen <= i) { /* premature end of buf */ + return EBADMSG; + } + if (dlen <= 0) { /* not enough memory */ + return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ + } + i++; + dlen--; + + pr->result->pw_passwd = &(pr->buffer[i]); + while (slen > i && dlen > 0) { + pr->buffer[i] = sbuf[i]; + if (pr->buffer[i] == '\0') break; + i++; + dlen--; + } + if (slen <= i) { /* premature end of buf */ + return EBADMSG; + } + if (dlen <= 0) { /* not enough memory */ + return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ + } + i++; + dlen--; + + pr->result->pw_gecos = &(pr->buffer[i]); + while (slen > i && dlen > 0) { + pr->buffer[i] = sbuf[i]; + if (pr->buffer[i] == '\0') break; + i++; + dlen--; + } + if (slen <= i) { /* premature end of buf */ + return EBADMSG; + } + if (dlen <= 0) { /* not enough memory */ + return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ + } + i++; + dlen--; + + pr->result->pw_dir = &(pr->buffer[i]); + while (slen > i && dlen > 0) { + pr->buffer[i] = sbuf[i]; + if (pr->buffer[i] == '\0') break; + i++; + dlen--; + } + if (slen <= i) { /* premature end of buf */ + return EBADMSG; + } + if (dlen <= 0) { /* not enough memory */ + return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ + } + i++; + dlen--; + + pr->result->pw_shell = &(pr->buffer[i]); + while (slen > i && dlen > 0) { + pr->buffer[i] = sbuf[i]; + if (pr->buffer[i] == '\0') break; + i++; + dlen--; + } + if (slen <= i) { /* premature end of buf */ + return EBADMSG; + } + if (dlen <= 0) { /* not enough memory */ + return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */ + } + + *len = slen -i -1; + + return 0; +} + +enum nss_status _nss_sss_getpwnam_r(const char *name, struct passwd *result, + char *buffer, size_t buflen, int *errnop) +{ + struct sss_cli_req_data rd; + struct sss_nss_pw_rep pwrep; + uint8_t *repbuf; + size_t replen, len; + enum nss_status nret; + int ret; + + /* Caught once glibc passing in buffer == 0x0 */ + if (!buffer || !buflen) return ERANGE; + + rd.len = strlen(name) + 1; + rd.data = name; + + nret = sss_nss_make_request(SSS_NSS_GETPWNAM, &rd, + &repbuf, &replen, errnop); + if (nret != NSS_STATUS_SUCCESS) { + return nret; + } + + pwrep.result = result; + pwrep.buffer = buffer; + pwrep.buflen = buflen; + + /* no results if not found */ + if (((uint32_t *)repbuf)[0] == 0) { + free(repbuf); + return NSS_STATUS_NOTFOUND; + } + + /* only 1 result is accepted for this function */ + if (((uint32_t *)repbuf)[0] != 1) { + *errnop = EBADMSG; + return NSS_STATUS_TRYAGAIN; + } + + len = replen - 8; + ret = sss_nss_getpw_readrep(&pwrep, repbuf+8, &len); + free(repbuf); + if (ret) { + *errnop = ret; + return NSS_STATUS_TRYAGAIN; + } + + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_sss_getpwuid_r(uid_t uid, struct passwd *result, + char *buffer, size_t buflen, int *errnop) +{ + struct sss_cli_req_data rd; + struct sss_nss_pw_rep pwrep; + uint8_t *repbuf; + size_t replen, len; + enum nss_status nret; + uint32_t user_uid; + int ret; + + /* Caught once glibc passing in buffer == 0x0 */ + if (!buffer || !buflen) return ERANGE; + + user_uid = uid; + rd.len = sizeof(uint32_t); + rd.data = &user_uid; + + nret = sss_nss_make_request(SSS_NSS_GETPWUID, &rd, + &repbuf, &replen, errnop); + if (nret != NSS_STATUS_SUCCESS) { + return nret; + } + + pwrep.result = result; + pwrep.buffer = buffer; + pwrep.buflen = buflen; + + /* no results if not found */ + if (((uint32_t *)repbuf)[0] == 0) { + free(repbuf); + return NSS_STATUS_NOTFOUND; + } + + /* only 1 result is accepted for this function */ + if (((uint32_t *)repbuf)[0] != 1) { + *errnop = EBADMSG; + return NSS_STATUS_TRYAGAIN; + } + + len = replen - 8; + ret = sss_nss_getpw_readrep(&pwrep, repbuf+8, &len); + free(repbuf); + if (ret) { + *errnop = ret; + return NSS_STATUS_TRYAGAIN; + } + + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_sss_setpwent(void) +{ + enum nss_status nret; + int errnop; + + /* make sure we do not have leftovers, and release memory */ + sss_nss_getpwent_data_clean(); + + nret = sss_nss_make_request(SSS_NSS_SETPWENT, + NULL, NULL, NULL, &errnop); + if (nret != NSS_STATUS_SUCCESS) { + errno = errnop; + return nret; + } + + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_sss_getpwent_r(struct passwd *result, + char *buffer, size_t buflen, + int *errnop) +{ + struct sss_cli_req_data rd; + struct sss_nss_pw_rep pwrep; + uint8_t *repbuf; + size_t replen; + enum nss_status nret; + uint32_t num_entries; + int ret; + + /* Caught once glibc passing in buffer == 0x0 */ + if (!buffer || !buflen) return ERANGE; + + /* if there are leftovers return the next one */ + if (sss_nss_getpwent_data.data != NULL && + sss_nss_getpwent_data.ptr < sss_nss_getpwent_data.len) { + + repbuf = sss_nss_getpwent_data.data + sss_nss_getpwent_data.ptr; + replen = sss_nss_getpwent_data.len - sss_nss_getpwent_data.ptr; + + pwrep.result = result; + pwrep.buffer = buffer; + pwrep.buflen = buflen; + + ret = sss_nss_getpw_readrep(&pwrep, repbuf, &replen); + if (ret) { + *errnop = ret; + return NSS_STATUS_TRYAGAIN; + } + + /* advance buffer pointer */ + sss_nss_getpwent_data.ptr = sss_nss_getpwent_data.len - replen; + + return NSS_STATUS_SUCCESS; + } + + /* release memory if any */ + sss_nss_getpwent_data_clean(); + + /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */ + num_entries = SSS_NSS_MAX_ENTRIES; + rd.len = sizeof(uint32_t); + rd.data = &num_entries; + + nret = sss_nss_make_request(SSS_NSS_GETPWENT, &rd, + &repbuf, &replen, errnop); + if (nret != NSS_STATUS_SUCCESS) { + return nret; + } + + /* no results if not found */ + if ((((uint32_t *)repbuf)[0] == 0) || (replen - 8 == 0)) { + free(repbuf); + return NSS_STATUS_NOTFOUND; + } + + sss_nss_getpwent_data.data = repbuf; + sss_nss_getpwent_data.len = replen; + sss_nss_getpwent_data.ptr = 8; /* skip metadata fields */ + + /* call again ourselves, this will return the first result */ + return _nss_sss_getpwent_r(result, buffer, buflen, errnop); +} + +enum nss_status _nss_sss_endpwent(void) +{ + enum nss_status nret; + int errnop; + + /* make sure we do not have leftovers, and release memory */ + sss_nss_getpwent_data_clean(); + + nret = sss_nss_make_request(SSS_NSS_ENDPWENT, + NULL, NULL, NULL, &errnop); + if (nret != NSS_STATUS_SUCCESS) { + errno = errnop; + return nret; + } + + return NSS_STATUS_SUCCESS; +} diff --git a/src/sss_client/protos.h b/src/sss_client/protos.h new file mode 100644 index 00000000..adb0b7bb --- /dev/null +++ b/src/sss_client/protos.h @@ -0,0 +1,137 @@ +/* + * System Security Services Daemon. NSS Interface + * + * Copyright (C) Simo Sorce 2007 + * + * You can used this header file in any way you see fit provided copyright + * notices are preserved. + * + */ + +#if 0 +/* SHADOW database NSS interface */ +enum nss_status _nss_sss_getspnam_r(const char *name, struct spwd *result, + char *buffer, size_t buflen, int *errnop); +enum nss_status _nss_sss_setspent(void); +enum nss_status _nss_sss_getspent_r(struct spwd *result, + char *buffer, size_t buflen, int *errnop); +enum nss_status _nss_sss_endspent(void); + + +/* HOSTS database NSS interface */ +enum nss_status _nss_sss_gethostbyname_r(const char *name, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); +enum nss_status _nss_sss_gethostbyname2_r(const char *name, int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); +enum nss_status _nss_sss_gethostbyaddr_r(const void *addr, socklen_t len, + int af, struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); +enum nss_status _nss_sss_sethostent(void); +enum nss_status _nss_sss_gethostent_r(struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); +enum nss_status _nss_sss_endhostent(void); + +/* NETGROUP database NSS interface */ +enum nss_status _nss_sss_setnetgrent(const char *netgroup, + struct __netgrent *result); +enum nss_status _nss_sss_getnetgrent_r(struct __netgrent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_ldap_endnetgrent(void); +/* too bad innetgr is currently implemented as an iteration over + * {set|get|end}netgroup ... */ + +/* NETWORKS database NSS interface */ +enum nss_status _nss_sss_getnetbyname_r(const char *name, + struct netent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); +enum nss_status _nss_sss_getnetbyaddr_r(uint32_t addr, int af, + struct netent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); +enum nss_status _nss_sss_setnetent(void); +enum nss_status _nss_sss_getnetent_r(struct netent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); +enum nss_status _nss_sss_endnetent(void); + + +/* PROTOCOLS database NSS interface */ +enum nss_status _nss_sss_getprotobyname_r(const char *name, + struct protoent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_getprotobynumber_r(int number, + struct protoent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_setprotoent(void); +enum nss_status _nss_sss_getprotoent_r(struct protoent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_endprotoent(void); + +/* SERVICES database NSS interface */ +enum nss_status _nss_sss_getservbyname_r(const char *name, + const char *protocol, + struct servent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_getservbyport_r(int port, const char *protocol, + struct servent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_setservent(void); +enum nss_status _nss_sss_getservent_r(struct servent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_endservent(void); + +/* ALIASES database NSS interface */ +enum nss_status _nss_sss_getaliasbyname_r(const char *name, + struct aliasent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_setaliasent(void); +enum nss_status _nss_sss_getaliasent_r(struct aliasent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_endaliasent(void); + +/* ETHERS database NSS interface */ +enum nss_status _nss_sss_gethostton_r(const char *name, + struct etherent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_getntohost_r(const struct ether_addr *addr, + struct etherent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_setetherent(void); +enum nss_status _nss_sss_getetherent_r(struct etherent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_endetherent(void); + +/* RPC database NSS interface */ +enum nss_status _nss_sss_getrpcbyname_r(const char *name, + struct rpcent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_getrpcbynumber_r(int number, struct rpcent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_setrpcent(void); +enum nss_status _nss_sss_getrpcent_r(struct rpcent *result, + char *buffer, size_t buflen, + int *errnop); +enum nss_status _nss_sss_endrpcent(void); + +#endif diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h new file mode 100644 index 00000000..7e9a81ff --- /dev/null +++ b/src/sss_client/sss_cli.h @@ -0,0 +1,220 @@ +/* + * System Security Services Daemon. Client Interface for NSS and PAM. + * + * Copyright (C) Simo Sorce 2007 + * + * You can used this header file in any way you see fit provided copyright + * notices are preserved. + * + */ + +#ifndef _SSSCLI_H +#define _SSSCLI_H + +#include <nss.h> +#include <pwd.h> +#include <grp.h> + +#ifndef HAVE_ERRNO_T +#define HAVE_ERRNO_T +typedef int errno_t; +#endif + +#define SSS_NSS_PROTOCOL_VERSION 1 +#define SSS_PAM_PROTOCOL_VERSION 3 + +enum sss_cli_command { +/* null */ + SSS_CLI_NULL = 0x0000, + +/* version */ + SSS_GET_VERSION = 0x0001, + +/* passwd */ + + SSS_NSS_GETPWNAM = 0x0011, + SSS_NSS_GETPWUID = 0x0012, + SSS_NSS_SETPWENT = 0x0013, + SSS_NSS_GETPWENT = 0x0014, + SSS_NSS_ENDPWENT = 0x0015, + +/* group */ + + SSS_NSS_GETGRNAM = 0x0021, + SSS_NSS_GETGRGID = 0x0022, + SSS_NSS_SETGRENT = 0x0023, + SSS_NSS_GETGRENT = 0x0024, + SSS_NSS_ENDGRENT = 0x0025, + SSS_NSS_INITGR = 0x0026, + +#if 0 +/* aliases */ + + SSS_NSS_GETALIASBYNAME = 0x0031, + SSS_NSS_GETALIASBYPORT = 0x0032, + SSS_NSS_SETALIASENT = 0x0033, + SSS_NSS_GETALIASENT = 0x0034, + SSS_NSS_ENDALIASENT = 0x0035, + +/* ethers */ + + SSS_NSS_GETHOSTTON = 0x0041, + SSS_NSS_GETNTOHOST = 0x0042, + SSS_NSS_SETETHERENT = 0x0043, + SSS_NSS_GETETHERENT = 0x0044, + SSS_NSS_ENDETHERENT = 0x0045, + +/* hosts */ + + SSS_NSS_GETHOSTBYNAME = 0x0051, + SSS_NSS_GETHOSTBYNAME2 = 0x0052, + SSS_NSS_GETHOSTBYADDR = 0x0053, + SSS_NSS_SETHOSTENT = 0x0054, + SSS_NSS_GETHOSTENT = 0x0055, + SSS_NSS_ENDHOSTENT = 0x0056, + +/* netgroup */ + + SSS_NSS_SETNETGRENT = 0x0061, + SSS_NSS_GETNETGRENT = 0x0062, + SSS_NSS_ENDNETGRENT = 0x0063, + /* SSS_NSS_INNETGR = 0x0064, */ + +/* networks */ + + SSS_NSS_GETNETBYNAME = 0x0071, + SSS_NSS_GETNETBYADDR = 0x0072, + SSS_NSS_SETNETENT = 0x0073, + SSS_NSS_GETNETENT = 0x0074, + SSS_NSS_ENDNETENT = 0x0075, + +/* protocols */ + + SSS_NSS_GETPROTOBYNAME = 0x0081, + SSS_NSS_GETPROTOBYNUM = 0x0082, + SSS_NSS_SETPROTOENT = 0x0083, + SSS_NSS_GETPROTOENT = 0x0084, + SSS_NSS_ENDPROTOENT = 0x0085, + +/* rpc */ + + SSS_NSS_GETRPCBYNAME = 0x0091, + SSS_NSS_GETRPCBYNUM = 0x0092, + SSS_NSS_SETRPCENT = 0x0093, + SSS_NSS_GETRPCENT = 0x0094, + SSS_NSS_ENDRPCENT = 0x0095, + +/* services */ + + SSS_NSS_GETSERVBYNAME = 0x00A1, + SSS_NSS_GETSERVBYPORT = 0x00A2, + SSS_NSS_SETSERVENT = 0x00A3, + SSS_NSS_GETSERVENT = 0x00A4, + SSS_NSS_ENDSERVENT = 0x00A5, + +/* shadow */ + + SSS_NSS_GETSPNAM = 0x00B1, + SSS_NSS_GETSPUID = 0x00B2, + SSS_NSS_SETSPENT = 0x00B3, + SSS_NSS_GETSPENT = 0x00B4, + SSS_NSS_ENDSPENT = 0x00B5, +#endif + +/* PAM related calls */ + SSS_PAM_AUTHENTICATE = 0x00F1, + SSS_PAM_SETCRED = 0x00F2, + SSS_PAM_ACCT_MGMT = 0x00F3, + SSS_PAM_OPEN_SESSION = 0x00F4, + SSS_PAM_CLOSE_SESSION = 0x00F5, + SSS_PAM_CHAUTHTOK = 0x00F6, + SSS_PAM_CHAUTHTOK_PRELIM = 0x00F7, + +}; + +enum sss_authtok_type { + SSS_AUTHTOK_TYPE_EMPTY = 0x0000, + SSS_AUTHTOK_TYPE_PASSWORD = 0x0001, +}; + +#define SSS_START_OF_PAM_REQUEST 0x4d415049 +#define SSS_END_OF_PAM_REQUEST 0x4950414d + +enum pam_item_type { + SSS_PAM_ITEM_EMPTY = 0x0000, + SSS_PAM_ITEM_USER, + SSS_PAM_ITEM_SERVICE, + SSS_PAM_ITEM_TTY, + SSS_PAM_ITEM_RUSER, + SSS_PAM_ITEM_RHOST, + SSS_PAM_ITEM_AUTHTOK, + SSS_PAM_ITEM_NEWAUTHTOK, + SSS_PAM_ITEM_CLI_LOCALE, + SSS_PAM_ITEM_CLI_PID, +}; + +#define SSS_NSS_MAX_ENTRIES 256 +#define SSS_NSS_HEADER_SIZE (sizeof(uint32_t) * 4) +struct sss_cli_req_data { + size_t len; + const void *data; +}; + +/* this is in milliseconds, wait up to 300 seconds */ +#define SSS_CLI_SOCKET_TIMEOUT 300000 + +enum sss_status { + SSS_STATUS_UNAVAIL, + SSS_STATUS_SUCCESS +}; + +enum response_type { + SSS_PAM_SYSTEM_INFO = 0x01, + SSS_PAM_DOMAIN_NAME, + SSS_PAM_ENV_ITEM, /* only pam environment */ + SSS_ENV_ITEM, /* only user environment */ + SSS_ALL_ENV_ITEM, /* pam and user environment */ + SSS_PAM_USER_INFO +}; + +enum user_info_type { + SSS_PAM_USER_INFO_OFFLINE_AUTH = 0x01, + SSS_PAM_USER_INFO_OFFLINE_AUTH_DELAYED, + SSS_PAM_USER_INFO_OFFLINE_CHPASS, + SSS_PAM_USER_INFO_CHPASS_ERROR +}; + +enum nss_status sss_nss_make_request(enum sss_cli_command cmd, + struct sss_cli_req_data *rd, + uint8_t **repbuf, size_t *replen, + int *errnop); + +int sss_pam_make_request(enum sss_cli_command cmd, + struct sss_cli_req_data *rd, + uint8_t **repbuf, size_t *replen, + int *errnop); + +#endif /* _SSSCLI_H */ + +#if 0 + +/* GETSPNAM Request: + * + * 0-X: string with name + * + * Replies: + * + * 0-3: 32bit unsigned number of results + * 4-7: 32bit unsigned (reserved/padding) + * For each result: + * 0-7: 64bit unsigned with Date of last change + * 8-15: 64bit unsigned with Min #days between changes + * 16-23: 64bit unsigned with Max #days between changes + * 24-31: 64bit unsigned with #days before pwd expires + * 32-39: 64bit unsigned with #days after pwd expires until account is disabled + * 40-47: 64bit unsigned with expiration date in days since 1970-01-01 + * 48-55: 64bit unsigned (flags/reserved) + * 56-X: sequence of 2, 0 terminated, strings (name, pwd) 64bit padded + */ +#endif + diff --git a/src/sss_client/sss_nss.exports b/src/sss_client/sss_nss.exports new file mode 100644 index 00000000..bcc6b10e --- /dev/null +++ b/src/sss_client/sss_nss.exports @@ -0,0 +1,73 @@ +EXPORTED { + + # public functions + global: + + _nss_sss_getpwnam_r; + _nss_sss_getpwuid_r; + _nss_sss_setpwent; + _nss_sss_getpwent_r; + _nss_sss_endpwent; + + _nss_sss_getgrnam_r; + _nss_sss_getgrgid_r; + _nss_sss_setgrent; + _nss_sss_getgrent_r; + _nss_sss_endgrent; + _nss_sss_initgroups_dyn; + + #_nss_sss_getaliasbyname_r; + #_nss_sss_setaliasent; + #_nss_sss_getaliasent_r; + #_nss_sss_endaliasent; + + #_nss_sss_gethostton_r; + #_nss_sss_getntohost_r; + #_nss_sss_setetherent; + #_nss_sss_getetherent_r; + #_nss_sss_endetherent; + + #_nss_sss_gethostbyname_r; + #_nss_sss_gethostbyname2_r; + #_nss_sss_gethostbyaddr_r; + #_nss_sss_sethostent; + #_nss_sss_gethostent_r; + #_nss_sss_endhostent; + + #_nss_sss_setnetgrent; + #_nss_sss_getnetgrent_r; + #_nss_sss_endnetgrent; + + #_nss_sss_getnetbyname_r; + #_nss_sss_getnetbyaddr_r; + #_nss_sss_setnetent; + #_nss_sss_getnetent_r; + #_nss_sss_endnetent; + + #_nss_sss_getprotobyname_r; + #_nss_sss_getprotobynumber_r; + #_nss_sss_setprotoent; + #_nss_sss_getprotoent_r; + #_nss_sss_endprotoent; + + #_nss_sss_getrpcbyname_r; + #_nss_sss_getrpcbynumber_r; + #_nss_sss_setrpcent; + #_nss_sss_getrpcent_r; + #_nss_sss_endrpcent; + + #_nss_sss_getservbyname_r; + #_nss_sss_getservbyport_r; + #_nss_sss_setservent; + #_nss_sss_getservent_r; + #_nss_sss_endservent; + + #_nss_sss_getspnam_r; + #_nss_sss_setspent; + #_nss_sss_getspent_r; + #_nss_sss_endspent; + + # everything else is local + local: + *; +}; diff --git a/src/sss_client/sss_pam.exports b/src/sss_client/sss_pam.exports new file mode 100644 index 00000000..9afa106b --- /dev/null +++ b/src/sss_client/sss_pam.exports @@ -0,0 +1,4 @@ +{ + global: + *; +}; diff --git a/src/sss_client/sss_pam_macros.h b/src/sss_client/sss_pam_macros.h new file mode 100644 index 00000000..5277acd0 --- /dev/null +++ b/src/sss_client/sss_pam_macros.h @@ -0,0 +1,30 @@ +/* + * System Security Services Daemon. Client Interface for NSS and PAM. + * + * Copyright (C) Stephen Gallagher 2009 + * + * You can used this header file in any way you see fit provided copyright + * notices are preserved. + * + */ + +#ifndef _SSS_PAM_MACROS_H +#define _SSS_PAM_MACROS_H + +/* Older versions of the pam development headers do not include the + * _pam_overwrite_n(n,x) macro. This implementation is copied from + * the Fedora 11 _pam_macros.h. + */ +#include <security/_pam_macros.h> +#ifndef _pam_overwrite_n +#define _pam_overwrite_n(x,n) \ +do { \ + register char *__xx__; \ + register unsigned int __i__ = 0; \ + if ((__xx__=(x))) \ + for (;__i__<n; __i__++) \ + __xx__[__i__] = 0; \ +} while (0) +#endif /* _pam_overwrite_n */ + +#endif /* _SSS_PAM_MACROS_H */ |