summaryrefslogtreecommitdiffstats
path: root/source4/scripting/python/samba
Commit message (Expand)AuthorAgeFilesLines
* use a base64 encoded password when changing passwordsAndrew Tridgell2009-03-121-2/+3
* fixed more embedded spaces in LDIFAndrew Tridgell2009-03-121-3/+3
* removed leading spaces from embedded ldifAndrew Tridgell2009-03-121-10/+10
* Don't print the admin password if we don't set one.Andrew Bartlett2009-03-041-1/+2
* Fix test_surrounding in samba4.rpcecho.python.Jelmer Vernooij2009-03-021-1/+1
* Merge branch 'master' of ssh://git.samba.org/data/git/sambaJelmer Vernooij2009-02-251-16/+109
|\
| * Updates to the recent cn=config support for the OpenLDAP backendOliver Liebel2009-02-251-18/+6
| * Added mmr and olc to the OpenLDAP backend provisioning-scriptsOliver Liebel2009-02-241-41/+146
* | expand tabs in python file, consistent with the rest of the file.Jelmer Vernooij2009-02-244-9/+9
* | expand tabs in python file, consistent with the rest of the file.Jelmer Vernooij2009-02-241-39/+39
* | Fix finding of setup data.Jelmer Vernooij2009-02-241-1/+1
|/
* paper over failure to reprovision with os.unlink()Andrew Bartlett2009-02-181-1/+2
* Provide a SamDB TestCase-class that can be used by OpenChange.Jelmer Vernooij2009-02-121-0/+10
* Use created loadparm context, rather than the one specified on the command-line.Jelmer Vernooij2009-02-121-2/+2
* Catch specific exceptions, rather than catching everything, which might hide ...Jelmer Vernooij2009-02-111-3/+3
* Use convenience function for finding setup dir in SaMDB tests.Jelmer Vernooij2009-02-111-2/+2
* Use convenience function for finding setup_dir based on location ofJelmer Vernooij2009-02-111-0/+17
* Move some samdb-specific code out of provision.Jelmer Vernooij2009-02-112-6/+10
* Make it possible to override the setup path.Jelmer Vernooij2009-02-111-9/+11
* Cancel transactions when exceptions are raised.Jelmer Vernooij2009-02-112-83/+93
* Remove obsolete samr Python module - use samba.dcerpc.samr instead.Jelmer Vernooij2009-01-221-759/+0
* Make sure server_role gets initialized in backend provisioning code -Jelmer Vernooij2009-01-191-1/+17
* Print more useful suggestion for the main provision command lineAndrew Bartlett2009-01-191-1/+1
* Find default smb.conf path correctly, when it was not specified on theJelmer Vernooij2009-01-161-0/+8
* Only do special DN tracking for normal DNs in OpenLDAP backend.Andrew Bartlett2009-01-091-2/+2
* Fix typoMatthias Dieter Wallnöfer2009-01-061-1/+1
* More work to have OpenLDAP accept the full AD schemaAndrew Bartlett2009-01-051-2/+2
* samba.tests.samdb: Fix test after merger of samba.security andJelmer Vernooij2008-12-231-1/+1
* Fix more tests, improve repr() functions for various Python types.Jelmer Vernooij2008-12-212-8/+13
* Simplify customization of pidl-generated Python modules.Jelmer Vernooij2008-12-211-1/+1
* Move tests for ParamFile.Jelmer Vernooij2008-12-213-3/+37
* Provide simple Python replacement for ParamFile, which currently existsJelmer Vernooij2008-12-211-2/+51
* Fix various Python-related bugs.Jelmer Vernooij2008-12-211-1/+1
* Fix more introduced regressions in new bindings.Jelmer Vernooij2008-12-211-1/+1
* py: Fix initialisation of subtypes, fix segfaults.Jelmer Vernooij2008-12-214-21/+25
* Support subtypes of ldb.Ldb.Jelmer Vernooij2008-12-201-1/+1
* Merge branch 'master' of ssh://git.samba.org/data/git/sambaJelmer Vernooij2008-12-192-44/+64
|\
| * Move aggregate schema stub to it's own fileAndrew Bartlett2008-12-191-0/+2
| * Cope with slight changes in tdb API.Jelmer Vernooij2008-12-181-31/+46
| * Use plain Python C API for registry module, rather than SWIG.Jelmer Vernooij2008-12-181-1/+1
| * Handle different failure modes when we wipe the db in provisionAndrew Bartlett2008-12-181-7/+8
| * s4:provision: use extended_dn_out_ldb or extended_dn_out_dereference dependin...Andrew Bartlett2008-12-171-4/+5
| * s4:dsdb: split extended_dn into extended_dn_in, extended_dn_out and extended_...Andrew Bartlett2008-12-171-1/+2
* | Avoid use of parentheses in Python import statements, as it's not supported b...Jelmer Vernooij2008-12-191-2/+2
|/
* Fix rpcecho test.Jelmer Vernooij2008-10-211-2/+1
* Fix blackbox tests on IPv6-only hosts.Jelmer Vernooij2008-10-201-4/+15
* Move the password_hash module up the module stack.Andrew Bartlett2008-10-161-1/+2
* Enable winreg Python tests - authentication works now.Jelmer Vernooij2008-09-301-1/+2
* s4:provision: don't do the full provision in the become_dcStefan Metzmacher2008-09-271-0/+2
* Make it clear that the MMR password can differ from the admin passswordAndrew Bartlett2008-09-081-6/+8
id='n659' href='#n659'>659 660 661 662 663 664 665 666 667 668 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);
}