common.c revision 5bb0c0596765dd5dd1973b7fc2d1e830bca3e345
/*
* 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
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <nss.h>
#include <security/pam_modules.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>
#include <libintl.h>
#include "sss_cli.h"
#if HAVE_PTHREAD
#include <pthread.h>
#endif
/*
* Note we set MSG_NOSIGNAL to avoid
* having to fiddle with signal masks
* but also do not want to die in case
* SIGPIPE gets raised and the application
* does not handle it.
*/
#ifdef MSG_NOSIGNAL
#define SSS_DEFAULT_WRITE_FLAGS MSG_NOSIGNAL
#else
#define SSS_DEFAULT_WRITE_FLAGS 0
#endif
/* common functions */
#endif
static void sss_cli_close_socket(void)
{
if (sss_cli_sd != -1) {
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
*/
struct sss_cli_req_data *rd,
int *errnop)
{
header[2] = 0;
header[3] = 0;
datasent = 0;
int rdsent;
*errnop = 0;
do {
errno = 0;
/* If error is EINTR here, we'll try again
* If it's any other error, we'll catch it
* below.
*/
switch (res) {
case -1:
break;
case 0:
break;
case 1:
}
}
break;
default: /* more than one avail ?? */
break;
}
if (*errnop) {
return SSS_STATUS_UNAVAIL;
}
errno = 0;
if (datasent < SSS_NSS_HEADER_SIZE) {
} else {
}
/* If the write was interrupted, go back through
* the loop and try again
*/
continue;
}
/* Write failed */
return SSS_STATUS_UNAVAIL;
}
}
return SSS_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
*/
int *errnop)
{
bool pollhup = false;
int len;
int ret;
header[1] = 0;
header[2] = 0;
header[3] = 0;
datarecv = 0;
len = 0;
*errnop = 0;
int bufrecv;
do {
errno = 0;
/* If error is EINTR here, we'll try again
* If it's any other error, we'll catch it
* below.
*/
switch (res) {
case -1:
break;
case 0:
break;
case 1:
pollhup = true;
}
}
}
break;
default: /* more than one avail ?? */
break;
}
if (*errnop) {
goto failed;
}
errno = 0;
if (datarecv < SSS_NSS_HEADER_SIZE) {
} else {
}
/* If the read was interrupted, go back through
* the loop and try again
*/
continue;
}
/* 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. */
goto failed;
}
/* 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 */
goto failed;
} else {
goto failed;
}
}
/* wrong command id */
goto failed;
}
if (header[0] > SSS_NSS_HEADER_SIZE) {
if (!buf) {
goto failed;
}
}
}
}
if (pollhup) {
}
return SSS_STATUS_SUCCESS;
return ret;
}
/* 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 sss_status sss_cli_make_request_nochecks(
enum sss_cli_command cmd,
struct sss_cli_req_data *rd,
int *errnop)
{
enum sss_status ret;
int len = 0;
/* send data */
if (ret != SSS_STATUS_SUCCESS) {
return ret;
}
/* data sent, now get reply */
if (ret != SSS_STATUS_SUCCESS) {
return ret;
}
/* we got through, now we have the custom data in buf if any,
* return it if requested */
if (replen) {
}
} else {
if (replen) {
*replen = 0;
}
}
return SSS_STATUS_SUCCESS;
}
/* GET_VERSION Reply:
* 0-3: 32bit unsigned version number
*/
static bool sss_cli_check_version(const char *socket_name)
{
enum sss_status nret;
int errnop;
struct sss_cli_req_data req;
} else {
return false;
}
if (nret != SSS_STATUS_SUCCESS) {
return false;
}
if (!repbuf) {
return false;
}
return (obtained_version == expected_version);
}
/* 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
{
int new_fd;
#ifdef F_DUPFD
return -1;
}
/* Paranoia */
if (new_fd < 3) {
return -1;
}
return new_fd;
#else
if (limit <= 0)
return -1;
if (new_fd == -1)
return -1;
/* use the program stack to hold our list of FDs to close */
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)
{
if (new_fd == -1) {
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
return -1;
}
flags |= FLAG_TO_SET;
return -1;
}
/* Socket should be closed on exec() */
#ifdef FD_CLOEXEC
if (flags >= 0) {
flags |= FD_CLOEXEC;
}
if (result < 0) {
return -1;
}
#endif
return new_fd;
}
{
struct sockaddr_un nssaddr;
bool inprogress = true;
bool connected = false;
unsigned int wait_time;
unsigned int sleep_time;
int ret;
int sd;
if (sd == -1) {
return -1;
}
/* set as non-blocking, close on exec, and make sure standard
* descriptors are not used */
if (sd == -1) {
return -1;
}
/* this piece is adapted from winbind client code */
wait_time = 0;
sleep_time = 0;
while (inprogress) {
int connect_errno = 0;
wait_time += sleep_time;
sizeof(nssaddr));
if (ret == 0) {
connected = true;
break;
}
switch(errno) {
case EINPROGRESS:
if (ret > 0) {
errnosize = sizeof(connect_errno);
&connect_errno, &errnosize);
if (ret >= 0 && connect_errno == 0) {
connected = true;
break;
}
}
break;
case EAGAIN:
if (wait_time < SSS_CLI_SOCKET_TIMEOUT) {
}
break;
default:
inprogress = false;
break;
}
if (wait_time >= SSS_CLI_SOCKET_TIMEOUT) {
inprogress = false;
}
if (connected) {
inprogress = false;
}
}
if (!connected) {
return -1;
}
if (ret != 0) {
return -1;
}
return sd;
}
{
int mysd;
int ret;
if (ret == 0) {
}
}
sss_cli_sd = -1;
}
/* check if the socket has been closed on the other side */
if (sss_cli_sd != -1) {
*errnop = 0;
do {
errno = 0;
/* If error is EINTR here, we'll try again
* If it's any other error, we'll catch it
* below.
*/
switch (res) {
case -1:
break;
case 0:
break;
case 1:
}
}
break;
default: /* more than one avail ?? */
break;
}
if (*errnop == 0) {
return SSS_STATUS_SUCCESS;
}
}
if (mysd == -1) {
return SSS_STATUS_UNAVAIL;
}
sss_cli_sd = mysd;
if (sss_cli_check_version(socket_name)) {
return SSS_STATUS_SUCCESS;
}
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 */
struct sss_cli_req_data *rd,
int *errnop)
{
enum sss_status ret;
char *envval;
/* avoid looping in the nss daemon */
return NSS_STATUS_NOTFOUND;
}
if (ret != SSS_STATUS_SUCCESS) {
errno = 0;
return NSS_STATUS_NOTFOUND;
#else
return NSS_STATUS_UNAVAIL;
#endif
}
switch (ret) {
case SSS_STATUS_TRYAGAIN:
return NSS_STATUS_TRYAGAIN;
case SSS_STATUS_SUCCESS:
return NSS_STATUS_SUCCESS;
case SSS_STATUS_UNAVAIL:
default:
errno = 0;
return NSS_STATUS_NOTFOUND;
#else
return NSS_STATUS_UNAVAIL;
#endif
}
}
struct sss_cli_req_data *rd,
int *errnop)
{
enum sss_status ret;
char *envval;
/* avoid looping in the nss daemon */
return NSS_STATUS_NOTFOUND;
}
if (ret != SSS_STATUS_SUCCESS) {
return NSS_STATUS_UNAVAIL;
}
switch (ret) {
case SSS_STATUS_TRYAGAIN:
return NSS_STATUS_TRYAGAIN;
case SSS_STATUS_SUCCESS:
return NSS_STATUS_SUCCESS;
case SSS_STATUS_UNAVAIL:
default:
return NSS_STATUS_UNAVAIL;
}
}
{
#ifdef HAVE_UCRED
int ret;
struct ucred server_cred;
if (ret != 0) {
return errno;
}
if (server_cred_len != sizeof(struct ucred)) {
return ESSS_BAD_CRED_MSG;
}
return ESSS_SERVER_NOT_TRUSTED;
}
#endif
return 0;
}
struct sss_cli_req_data *rd,
int *errnop)
{
enum sss_status status;
char *envval;
sss_pam_lock();
/* avoid looping in the pam daemon */
goto out;
}
/* only root shall use the privileged pipe */
if (statret != 0) {
goto out;
}
goto out;
}
} else {
if (statret != 0) {
goto out;
}
goto out;
}
}
if (status != SSS_STATUS_SUCCESS) {
goto out;
}
if (error != 0) {
goto out;
}
if (status == SSS_STATUS_SUCCESS) {
ret = PAM_SUCCESS;
} else {
}
out:
return ret;
}
void sss_pam_close_fd(void)
{
sss_pam_lock();
if (sss_cli_sd != -1) {
sss_cli_sd = -1;
}
}
struct sss_cli_req_data *rd,
int *errnop)
{
if (ret != SSS_STATUS_SUCCESS) {
return SSS_STATUS_UNAVAIL;
}
return ret;
}
struct sss_cli_req_data *rd,
int *errnop)
{
if (ret != SSS_STATUS_SUCCESS) {
return SSS_STATUS_UNAVAIL;
}
return ret;
}
struct sss_cli_req_data *rd,
int *errnop)
{
if (ret != SSS_STATUS_SUCCESS) {
return SSS_STATUS_UNAVAIL;
}
return ret;
}
const char *ssscli_err2string(int err)
{
const char *m;
switch(err) {
case ESSS_BAD_PRIV_SOCKET:
return _("Privileged socket has wrong ownership or permissions.");
break;
case ESSS_BAD_PUB_SOCKET:
return _("Public socket has wrong ownership or permissions.");
break;
case ESSS_BAD_CRED_MSG:
return _("Unexpected format of the server credential message.");
break;
case ESSS_SERVER_NOT_TRUSTED:
return _("SSSD is not run by root.");
break;
default:
if (m == NULL) {
return _("An error occurred, but no description can be found.");
}
return m;
break;
}
return _("Unexpected error while looking for an error description");
}
/* Return strlen(str) or maxlen, whichever is shorter
* Returns EINVAL if str is NULL, EFBIG if str is longer than maxlen
* _len will return the result
*
* This function is useful for preventing buffer overflow attacks.
*/
{
if (!str) {
return EINVAL;
}
#if defined __USE_GNU
#else
*len = 0;
(*len)++;
}
#endif
return EFBIG;
}
return 0;
}
#if HAVE_PTHREAD
typedef void (*sss_mutex_init)(void);
struct sss_mutex {
};
static void sss_nss_mt_init(void);
static void sss_pam_mt_init(void);
.init = sss_nss_mt_init };
.init = sss_pam_mt_init };
/* Wrappers for robust mutex support */
{
#elif defined(HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP)
#else
return 0;
#endif
}
{
return pthread_mutex_consistent(mtx);
#elif defined(HAVE_PTHREAD_MUTEX_CONSISTENT_NP)
return pthread_mutex_consistent_np(mtx);
#else
return 0;
#endif
}
/* Generic mutex init, lock, unlock functions */
static void sss_mt_init(struct sss_mutex *m)
{
if (pthread_mutexattr_init(&attr) != 0) {
return;
}
if (sss_mutexattr_setrobust(&attr) != 0) {
return;
}
}
static void sss_mt_lock(struct sss_mutex *m)
{
sss_mutex_consistent(&m->mtx);
}
}
static void sss_mt_unlock(struct sss_mutex *m)
{
pthread_mutex_unlock(&m->mtx);
}
/* NSS mutex wrappers */
static void sss_nss_mt_init(void)
{
}
void sss_nss_lock(void)
{
}
void sss_nss_unlock(void)
{
}
/* NSS mutex wrappers */
static void sss_pam_mt_init(void)
{
}
void sss_pam_lock(void)
{
}
void sss_pam_unlock(void)
{
}
#else
/* sorry no mutexes available */
void sss_nss_lock(void) { return; }
void sss_nss_unlock(void) { return; }
void sss_pam_lock(void) { return; }
void sss_pam_unlock(void) { return; }
#endif
char **out,
{
size_t i = 0;
if ((*out)[i] == '\0') break;
i++;
(*offset)++;
(*dlen)--;
}
return EBADMSG;
}
if (*dlen == 0) { /* not enough memory */
return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
}
(*offset)++;
(*dlen)--;
if (size) {
*size = i;
}
return EOK;
}