diff options
author | Andreas Schneider <asn@samba.org> | 2020-03-20 15:25:47 +0100 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2020-03-25 06:58:38 +0000 |
commit | 61a6bf82d4cd7b1deecaf41d5d4f7d9e73f168be (patch) | |
tree | 5d3e27c4fb8f8f35f9a006cd703483f186875481 /third_party | |
parent | 2a5d994b1a136af1ed1542fbb008c3744ac661bb (diff) | |
download | samba-61a6bf82d4cd7b1deecaf41d5d4f7d9e73f168be.tar.gz |
third_party: Update pam_wrapper to version 1.1.2
Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Diffstat (limited to 'third_party')
-rw-r--r-- | third_party/pam_wrapper/pam_wrapper.c | 472 | ||||
-rw-r--r-- | third_party/pam_wrapper/python/pypamtest.c | 67 | ||||
-rw-r--r-- | third_party/pam_wrapper/wscript | 14 |
3 files changed, 479 insertions, 74 deletions
diff --git a/third_party/pam_wrapper/pam_wrapper.c b/third_party/pam_wrapper/pam_wrapper.c index 48d2c2ae566..dd69c43f021 100644 --- a/third_party/pam_wrapper/pam_wrapper.c +++ b/third_party/pam_wrapper/pam_wrapper.c @@ -36,6 +36,8 @@ #include <limits.h> #include <ctype.h> +#include <pthread.h> + #include <ftw.h> #ifdef HAVE_SECURITY_PAM_APPL_H @@ -97,6 +99,19 @@ * LOGGING *****************/ +#ifndef HAVE_GETPROGNAME +static const char *getprogname(void) +{ +#if defined(HAVE_PROGRAM_INVOCATION_SHORT_NAME) + return program_invocation_short_name; +#elif defined(HAVE_GETEXECNAME) + return getexecname(); +#else + return NULL; +#endif /* HAVE_PROGRAM_INVOCATION_SHORT_NAME */ +} +#endif /* HAVE_GETPROGNAME */ + enum pwrap_dbglvl_e { PWRAP_LOG_ERROR = 0, PWRAP_LOG_WARN, @@ -123,6 +138,7 @@ static void pwrap_vlog(enum pwrap_dbglvl_e dbglvl, const char *d; unsigned int lvl = 0; const char *prefix = "PWRAP"; + const char *progname = getprogname(); d = getenv("PAM_WRAPPER_DEBUGLEVEL"); if (d != NULL) { @@ -136,24 +152,29 @@ static void pwrap_vlog(enum pwrap_dbglvl_e dbglvl, vsnprintf(buffer, sizeof(buffer), format, args); switch (dbglvl) { - case PWRAP_LOG_ERROR: - prefix = "PWRAP_ERROR"; - break; - case PWRAP_LOG_WARN: - prefix = "PWRAP_WARN"; - break; - case PWRAP_LOG_DEBUG: - prefix = "PWRAP_DEBUG"; - break; - case PWRAP_LOG_TRACE: - prefix = "PWRAP_TRACE"; - break; + case PWRAP_LOG_ERROR: + prefix = "PWRAP_ERROR"; + break; + case PWRAP_LOG_WARN: + prefix = "PWRAP_WARN"; + break; + case PWRAP_LOG_DEBUG: + prefix = "PWRAP_DEBUG"; + break; + case PWRAP_LOG_TRACE: + prefix = "PWRAP_TRACE"; + break; + } + + if (progname == NULL) { + progname = "<unknown>"; } fprintf(stderr, - "%s(%d) - %s: %s\n", + "%s[%s (%u)] - %s: %s\n", prefix, - (int)getpid(), + progname, + (unsigned int)getpid(), function, buffer); } @@ -180,6 +201,12 @@ typedef int (*__libpam_pam_start)(const char *service_name, const struct pam_conv *pam_conversation, pam_handle_t **pamh); +typedef int (*__libpam_pam_start_confdir)(const char *service_name, + const char *user, + const struct pam_conv *pam_conversation, + const char *confdir, + pam_handle_t **pamh); + typedef int (*__libpam_pam_end)(pam_handle_t *pamh, int pam_status); typedef int (*__libpam_pam_authenticate)(pam_handle_t *pamh, int flags); @@ -243,6 +270,7 @@ typedef void (*__libpam_pam_vsyslog)(const pam_handle_t *pamh, struct pwrap_libpam_symbols { PWRAP_SYMBOL_ENTRY(pam_start); + PWRAP_SYMBOL_ENTRY(pam_start_confdir); PWRAP_SYMBOL_ENTRY(pam_end); PWRAP_SYMBOL_ENTRY(pam_authenticate); PWRAP_SYMBOL_ENTRY(pam_chauthtok); @@ -300,15 +328,25 @@ static void *pwrap_load_lib_handle(enum pwrap_lib lib) void *handle = NULL; #ifdef RTLD_DEEPBIND - const char *env = getenv("LD_PRELOAD"); + const char *env_preload = getenv("LD_PRELOAD"); + const char *env_deepbind = getenv("UID_WRAPPER_DISABLE_DEEPBIND"); + bool enable_deepbind = true; /* Don't do a deepbind if we run with libasan */ - if (env != NULL && strlen(env) < PATH_MAX) { - const char *p = strstr(env, "libasan.so"); - if (p == NULL) { - flags |= RTLD_DEEPBIND; + if (env_preload != NULL && strlen(env_preload) < 1024) { + const char *p = strstr(env_preload, "libasan.so"); + if (p != NULL) { + enable_deepbind = false; } } + + if (env_deepbind != NULL && strlen(env_deepbind) >= 1) { + enable_deepbind = false; + } + + if (enable_deepbind) { + flags |= RTLD_DEEPBIND; + } #endif switch (lib) { @@ -368,6 +406,22 @@ static void *_pwrap_bind_symbol(enum pwrap_lib lib, const char *fn_name) * valgrind and has probably something todo with with the linker. * So we need load each function at the point it is called the first time. */ +#ifdef HAVE_PAM_START_CONFDIR +static int libpam_pam_start_confdir(const char *service_name, + const char *user, + const struct pam_conv *pam_conversation, + const char *confdir, + pam_handle_t **pamh) +{ + pwrap_bind_symbol_libpam(pam_start_confdir); + + return pwrap.libpam.symbols._libpam_pam_start_confdir.f(service_name, + user, + pam_conversation, + confdir, + pamh); +} +#else static int libpam_pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, @@ -381,6 +435,8 @@ static int libpam_pam_start(const char *service_name, pamh); } +#endif + static int libpam_pam_end(pam_handle_t *pamh, int pam_status) { pwrap_bind_symbol_libpam(pam_end); @@ -748,14 +804,270 @@ static void pwrap_clean_stale_dirs(const char *dir) return; } +#ifdef HAVE_PAM_START_CONFDIR +static void pwrap_init(void) +{ + char tmp_config_dir[] = "/tmp/pam.X"; + size_t len = strlen(tmp_config_dir); + const char *env; + struct stat sb; + int rc; + unsigned i; + ssize_t ret; + FILE *pidfile; + char pidfile_path[1024] = { 0 }; + char letter; + + if (!pam_wrapper_enabled()) { + return; + } + + if (pwrap.initialised) { + return; + } + + /* + * The name is selected to match/replace /etc/pam.d + * We start from a random alphanum trying letters until + * an available directory is found. + */ + letter = 48 + (getpid() % 70); + for (i = 0; i < 127; i++) { + if (isalpha(letter) || isdigit(letter)) { + tmp_config_dir[len - 1] = letter; + + rc = lstat(tmp_config_dir, &sb); + if (rc == 0) { + PWRAP_LOG(PWRAP_LOG_TRACE, + "Check if pam_wrapper dir %s is a " + "stale directory", + tmp_config_dir); + pwrap_clean_stale_dirs(tmp_config_dir); + } else if (rc < 0) { + if (errno != ENOENT) { + continue; + } + break; /* found */ + } + } + + letter++; + letter %= 127; + } + + if (i == 127) { + PWRAP_LOG(PWRAP_LOG_ERROR, + "Failed to find a possible path to create " + "pam_wrapper config dir: %s", + tmp_config_dir); + exit(1); + } + + PWRAP_LOG(PWRAP_LOG_DEBUG, "Initialize pam_wrapper"); + + pwrap.config_dir = strdup(tmp_config_dir); + if (pwrap.config_dir == NULL) { + PWRAP_LOG(PWRAP_LOG_ERROR, + "No memory"); + exit(1); + } + PWRAP_LOG(PWRAP_LOG_TRACE, + "pam_wrapper config dir: %s", + tmp_config_dir); + + rc = mkdir(pwrap.config_dir, 0755); + if (rc != 0) { + PWRAP_LOG(PWRAP_LOG_ERROR, + "Failed to create pam_wrapper config dir: %s - %s", + tmp_config_dir, strerror(errno)); + } + + /* Create file with the PID of the the process */ + ret = snprintf(pidfile_path, sizeof(pidfile_path), + "%s/pid", pwrap.config_dir); + if (ret < 0) { + p_rmdirs(pwrap.config_dir); + exit(1); + } + + pidfile = fopen(pidfile_path, "w"); + if (pidfile == NULL) { + p_rmdirs(pwrap.config_dir); + exit(1); + } + + rc = fprintf(pidfile, "%d", getpid()); + fclose(pidfile); + if (rc <= 0) { + p_rmdirs(pwrap.config_dir); + exit(1); + } + + pwrap.libpam_so = strdup(PAM_LIBRARY); + if (pwrap.libpam_so == NULL) { + PWRAP_LOG(PWRAP_LOG_ERROR, "No memory"); + p_rmdirs(pwrap.config_dir); + exit(1); + } + + PWRAP_LOG(PWRAP_LOG_TRACE, "Using libpam path: %s", pwrap.libpam_so); + + pwrap.initialised = true; + + env = getenv("PAM_WRAPPER_SERVICE_DIR"); + if (env == NULL) { + PWRAP_LOG(PWRAP_LOG_ERROR, "No config file"); + p_rmdirs(pwrap.config_dir); + exit(1); + } + + rc = copy_confdir(env); + if (rc != 0) { + PWRAP_LOG(PWRAP_LOG_ERROR, "Failed to copy config files"); + p_rmdirs(pwrap.config_dir); + exit(1); + } + + setenv("PAM_WRAPPER_RUNTIME_DIR", pwrap.config_dir, 1); + + PWRAP_LOG(PWRAP_LOG_DEBUG, "Successfully initialized pam_wrapper"); +} + +#else /* HAVE_PAM_START_CONFDIR */ + +#ifdef HAVE_PAM_MODUTIL_SEARCH_KEY +/* + * This is needed to workaround Tumbleweed which packages a libpam git version. + */ +static int pso_copy(const char *src, const char *dst, const char *pdir, mode_t mode) +{ +#define PSO_COPY_READ_SIZE 16 + int srcfd = -1; + int dstfd = -1; + int rc = -1; + ssize_t bread, bwritten; + struct stat sb; + char buf[PSO_COPY_READ_SIZE + 1]; + size_t pso_copy_read_size = PSO_COPY_READ_SIZE; + int cmp; + size_t to_read; + bool found_slash; + + cmp = strcmp(src, dst); + if (cmp == 0) { + return -1; + } + + srcfd = open(src, O_RDONLY, 0); + if (srcfd < 0) { + return -1; + } + + if (mode == 0) { + rc = fstat(srcfd, &sb); + if (rc != 0) { + rc = -1; + goto out; + } + mode = sb.st_mode; + } + + dstfd = open(dst, O_CREAT|O_WRONLY|O_TRUNC, mode); + if (dstfd < 0) { + rc = -1; + goto out; + } + + found_slash = false; + to_read = 1; + + for (;;) { + bread = read(srcfd, buf, to_read); + if (bread == 0) { + /* done */ + break; + } else if (bread < 0) { + errno = EIO; + rc = -1; + goto out; + } + + to_read = 1; + if (!found_slash && buf[0] == '/') { + found_slash = true; + to_read = pso_copy_read_size; + } + + if (found_slash && bread == PSO_COPY_READ_SIZE) { + cmp = memcmp(buf, "usr/etc/pam.d/%s", 16); + if (cmp == 0) { + char tmp[16] = {0}; + + snprintf(tmp, sizeof(tmp), "%s/%%s", pdir + 1); + + memcpy(buf, tmp, 12); + memset(&buf[12], '\0', 4); + + /* + * If we found this string, we need to reduce + * the read size to not miss, the next one. + */ + pso_copy_read_size = 13; + } else { + cmp = memcmp(buf, "usr/etc/pam.d", 13); + if (cmp == 0) { + memcpy(buf, pdir + 1, 9); + memset(&buf[9], '\0', 4); + } else { + cmp = memcmp(buf, "etc/pam.d", 9); + if (cmp == 0) { + memcpy(buf, pdir + 1, 9); + } + } + } + found_slash = false; + } + + bwritten = write(dstfd, buf, bread); + if (bwritten < 0) { + errno = EIO; + rc = -1; + goto out; + } + + if (bread != bwritten) { + errno = EFAULT; + rc = -1; + goto out; + } + } + + rc = 0; +out: + if (srcfd != -1) { + close(srcfd); + } + if (dstfd != -1) { + close(dstfd); + } + if (rc < 0) { + unlink(dst); + } + + return rc; +#undef PSO_COPY_READ_SIZE +} +#else /* HAVE_PAM_MODUTIL_SEARCH_KEY */ + static int pso_copy(const char *src, const char *dst, const char *pdir, mode_t mode) { +#define PSO_COPY_READ_SIZE 9 int srcfd = -1; int dstfd = -1; int rc = -1; ssize_t bread, bwritten; struct stat sb; - char buf[10]; + char buf[PSO_COPY_READ_SIZE + 1]; int cmp; size_t to_read; bool found_slash; @@ -802,13 +1114,13 @@ static int pso_copy(const char *src, const char *dst, const char *pdir, mode_t m to_read = 1; if (!found_slash && buf[0] == '/') { found_slash = true; - to_read = 9; + to_read = PSO_COPY_READ_SIZE; } - if (found_slash && bread == 9) { - cmp = memcmp(buf, "etc/pam.d", 9); + if (found_slash && bread == PSO_COPY_READ_SIZE) { + cmp = memcmp(buf, "etc/pam.d", PSO_COPY_READ_SIZE); if (cmp == 0) { - memcpy(buf, pdir + 1, 9); + memcpy(buf, pdir + 1, PSO_COPY_READ_SIZE); } found_slash = false; } @@ -840,7 +1152,9 @@ out: } return rc; +#undef PSO_COPY_READ_SIZE } +#endif /* HAVE_PAM_MODUTIL_SEARCH_KEY */ static void pwrap_init(void) { @@ -1027,6 +1341,8 @@ static void pwrap_init(void) exit(1); } + PWRAP_LOG(PWRAP_LOG_TRACE, "Using libpam path: %s", pwrap.libpam_so); + pwrap.initialised = true; env = getenv("PAM_WRAPPER_SERVICE_DIR"); @@ -1047,6 +1363,7 @@ static void pwrap_init(void) PWRAP_LOG(PWRAP_LOG_DEBUG, "Successfully initialized pam_wrapper"); } +#endif /* HAVE_PAM_START_CONFDIR */ bool pam_wrapper_enabled(void) { @@ -1071,19 +1388,6 @@ bool pam_wrapper_enabled(void) return pwrap.enabled; } -/**************************** - * CONSTRUCTOR - ***************************/ -void pwrap_constructor(void) -{ - /* - * Here is safe place to call pwrap_init() and initialize data - * for main process. - */ - pwrap_init(); -} - - #ifdef HAVE_OPENPAM static int pwrap_openpam_start(const char *service_name, const char *user, @@ -1139,22 +1443,35 @@ static int pwrap_pam_start(const char *service_name, const struct pam_conv *pam_conversation, pam_handle_t **pamh) { + int rc; + + pwrap_init(); + PWRAP_LOG(PWRAP_LOG_TRACE, "pam_start service=%s, user=%s", service_name, user); -#ifdef HAVE_OPENPAM - return pwrap_openpam_start(service_name, - user, - pam_conversation, - pamh); +#if defined(HAVE_OPENPAM) + rc = pwrap_openpam_start(service_name, + user, + pam_conversation, + pamh); +#elif defined (HAVE_PAM_START_CONFDIR) + rc = libpam_pam_start_confdir(service_name, + user, + pam_conversation, + pwrap.config_dir, + pamh); #else - return libpam_pam_start(service_name, - user, - pam_conversation, - pamh); + rc = libpam_pam_start(service_name, + user, + pam_conversation, + pamh); #endif + PWRAP_LOG(PWRAP_LOG_TRACE, "pam_start rc=%d", rc); + + return rc; } @@ -1679,20 +1996,76 @@ int cannot_audit(int x) } /**************************** + * CONSTRUCTOR + ***************************/ + +/* + * Handler executed before fork(2) processing starts. + */ +static void pwrap_thread_prepare(void) +{ +} + +/* + * Handler that is executed in the parent process after fork(2) processing + * completes. + */ +static void pwrap_thread_parent(void) +{ +} + +/* + * Handler that is executed in the child process after fork(2) processing + * completes. + */ +static void pwrap_thread_child(void) +{ + pwrap.initialised = false; +} + +void pwrap_constructor(void) +{ + /* + * If we hold a lock and the application forks, then the child + * is not able to unlock the mutex and we are in a deadlock. + * This should prevent such deadlocks. + */ + pthread_atfork(&pwrap_thread_prepare, + &pwrap_thread_parent, + &pwrap_thread_child); + + /* + * Here is safe place to call pwrap_init() and initialize data + * for main process. + */ + pwrap_init(); +} + +/**************************** * DESTRUCTOR ***************************/ static int p_rmdirs_at(const char *path, int parent_fd) { - DIR *d; - struct dirent *dp; + DIR *d = NULL; + struct dirent *dp = NULL; struct stat sb; + char fd_str[64] = { 0 }; int path_fd; int rc; + switch(parent_fd) { + case AT_FDCWD: + snprintf(fd_str, sizeof(fd_str), "CWD"); + break; + default: + snprintf(fd_str, sizeof(fd_str), "fd=%d", parent_fd); + break; + } + /* If path is absolute, parent_fd is ignored. */ PWRAP_LOG(PWRAP_LOG_TRACE, - "p_rmdirs_at removing %s at %d\n", path, parent_fd); + "p_rmdirs_at removing %s at %s\n", path, fd_str); path_fd = openat(parent_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); @@ -1773,6 +2146,7 @@ void pwrap_destructor(void) if (!pwrap.initialised) { return; } + pwrap.initialised = false; PWRAP_LOG(PWRAP_LOG_TRACE, "destructor called for pam_wrapper dir %s", diff --git a/third_party/pam_wrapper/python/pypamtest.c b/third_party/pam_wrapper/python/pypamtest.c index 6bb1e204539..8de05e9c39b 100644 --- a/third_party/pam_wrapper/python/pypamtest.c +++ b/third_party/pam_wrapper/python/pypamtest.c @@ -166,11 +166,13 @@ static char **new_conv_list(const size_t list_size) return list; } -static const char **sequence_as_string_list(PyObject *seq, - const char *paramname) +static int sequence_as_string_list(PyObject *seq, + const char *paramname, + const char **str_list[], + size_t *num_str_list) { const char *p = paramname ? paramname : "attribute values"; - const char **ret; + const char **result; PyObject *utf_item; int i; Py_ssize_t len; @@ -179,18 +181,18 @@ static const char **sequence_as_string_list(PyObject *seq, if (!PySequence_Check(seq)) { PyErr_Format(PyExc_TypeError, "The object must be a sequence\n"); - return NULL; + return -1; } len = PySequence_Size(seq); if (len == -1) { - return NULL; + return -1; } - ret = PyMem_New(const char *, (len + 1)); - if (!ret) { + result = PyMem_New(const char *, (len + 1)); + if (result == NULL) { PyErr_NoMemory(); - return NULL; + return -1; } for (i = 0; i < len; i++) { @@ -202,20 +204,24 @@ static const char **sequence_as_string_list(PyObject *seq, utf_item = get_utf8_string(item, p); if (utf_item == NULL) { Py_DECREF(item); - return NULL; + return -1; } - ret[i] = py_strdup(PyBytes_AsString(utf_item)); + result[i] = py_strdup(PyBytes_AsString(utf_item)); Py_DECREF(utf_item); - if (!ret[i]) { + if (result[i] == NULL) { Py_DECREF(item); - return NULL; + return -1; } Py_DECREF(item); } - ret[i] = NULL; - return ret; + result[i] = NULL; + + *str_list = result; + *num_str_list = (size_t)len; + + return 0; } static PyObject *string_list_as_tuple(char **str_list) @@ -225,7 +231,7 @@ static PyObject *string_list_as_tuple(char **str_list) PyObject *tup; PyObject *py_str; - for (len=0; len < PAM_CONV_MSG_MAX; len++) { + for (len=0; str_list[len] != NULL; len++) { if (str_list[len][0] == '\0') { /* unused string, stop counting */ break; @@ -297,7 +303,7 @@ set_pypamtest_exception(PyObject *exc, if (test_repr[0] != '\0' && failed != NULL) { PyErr_Format(exc, - "Error [%d]: Test case %s retured [%d]", + "Error [%d]: Test case %s returned [%d]", perr, test_repr, failed->op_rv); } else { obj = Py_BuildValue(discard_const_p(char, "(i,s)"), @@ -808,27 +814,42 @@ static int fill_conv_data(PyObject *py_echo_off, PyObject *py_echo_on, struct pamtest_conv_data *conv_data) { + size_t conv_count = 0; + size_t count = 0; + int rc; + conv_data->in_echo_on = NULL; conv_data->in_echo_off = NULL; conv_data->out_err = NULL; conv_data->out_info = NULL; if (py_echo_off != NULL) { - conv_data->in_echo_off = sequence_as_string_list(py_echo_off, - "echo_off"); - if (conv_data->in_echo_off == NULL) { + rc = sequence_as_string_list(py_echo_off, + "echo_off", + &conv_data->in_echo_off, + &count); + if (rc != 0) { free_conv_data(conv_data); return ENOMEM; } + conv_count += count; } if (py_echo_on != NULL) { - conv_data->in_echo_on = sequence_as_string_list(py_echo_on, - "echo_on"); - if (conv_data->in_echo_on == NULL) { + rc = sequence_as_string_list(py_echo_on, + "echo_on", + &conv_data->in_echo_on, + &count); + if (rc != 0) { free_conv_data(conv_data); return ENOMEM; } + conv_count += count; + } + + if (conv_count > PAM_CONV_MSG_MAX) { + free_conv_data(conv_data); + return ENOMEM; } conv_data->out_info = new_conv_list(PAM_CONV_MSG_MAX); @@ -885,7 +906,7 @@ static int py_tc_list_to_cstruct_list(PyObject *py_test_list, PyDoc_STRVAR(RunPamTest__doc__, "Run PAM tests\n\n" "This function runs PAM test cases and reports result\n" -"Paramaters:\n" +"Parameters:\n" "service: The PAM service to use in the conversation (string)\n" "username: The user to run PAM conversation as\n" "test_list: Sequence of pypamtest.TestCase objects\n" diff --git a/third_party/pam_wrapper/wscript b/third_party/pam_wrapper/wscript index aface6192c5..fb57800587a 100644 --- a/third_party/pam_wrapper/wscript +++ b/third_party/pam_wrapper/wscript @@ -2,7 +2,7 @@ import os -VERSION="1.0.7" +VERSION="1.1.2" def find_library(library_names, lookup_paths): for directory in lookup_paths: @@ -51,6 +51,16 @@ def configure(conf): checklibc=False, headers='security/pam_ext.h') + conf.CHECK_FUNCS_IN('pam_start_confdir', + 'pam', + checklibc=False, + headers='security/pam_appl.h') + + conf.CHECK_FUNCS_IN('pam_modutil_search_key', + 'pam', + checklibc=False, + headers='security/pam_modutil.h') + conf.CHECK_C_PROTOTYPE('pam_vprompt', 'int pam_vprompt(const pam_handle_t *_pamh, int _style, char **_resp, const char *_fmt, va_list _ap)', define='HAVE_PAM_VPROMPT_CONST', headers='stdio.h sys/types.h security/pam_appl.h security/pam_modules.h') @@ -86,7 +96,7 @@ def build(bld): # breaks preloading! bld.SAMBA_LIBRARY('pam_wrapper', source='pam_wrapper.c', - deps='dl', + deps='dl pthread', install=False, realname='libpam-wrapper.so') |