/* * Copyright (c) 2015 Andreas Schneider * Copyright (c) 2015 Jakub Hrozek * * 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 . */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SECURITY_PAM_APPL_H #include #endif #ifdef HAVE_SECURITY_PAM_MODULES_H #include #endif #ifdef HAVE_SECURITY_PAM_EXT_H #include #endif #include "pwrap_compat.h" #ifdef HAVE_GCC_THREAD_LOCAL_STORAGE # define PWRAP_THREAD __thread #else # define PWRAP_THREAD #endif #ifdef HAVE_CONSTRUCTOR_ATTRIBUTE #define CONSTRUCTOR_ATTRIBUTE __attribute__ ((constructor)) #else #define CONSTRUCTOR_ATTRIBUTE #endif /* HAVE_CONSTRUCTOR_ATTRIBUTE */ #ifdef HAVE_DESTRUCTOR_ATTRIBUTE #define DESTRUCTOR_ATTRIBUTE __attribute__ ((destructor)) #else #define DESTRUCTOR_ATTRIBUTE #endif /* HAVE_DESTRUCTOR_ATTRIBUTE */ #ifdef HAVE_ADDRESS_SANITIZER_ATTRIBUTE #define DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE __attribute__((no_sanitize_address)) #else /* DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE */ #define DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE #endif /* DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE */ /* GCC have printf type attribute check. */ #ifdef HAVE_FUNCTION_ATTRIBUTE_FORMAT #define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) #else #define PRINTF_ATTRIBUTE(a,b) #endif /* HAVE_FUNCTION_ATTRIBUTE_FORMAT */ #ifndef SAFE_FREE #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0) #endif #ifndef discard_const #define discard_const(ptr) ((void *)((uintptr_t)(ptr))) #endif #ifndef discard_const_p #define discard_const_p(type, ptr) ((type *)discard_const(ptr)) #endif /***************** * LOGGING *****************/ enum pwrap_dbglvl_e { PWRAP_LOG_ERROR = 0, PWRAP_LOG_WARN, PWRAP_LOG_DEBUG, PWRAP_LOG_TRACE }; static void pwrap_log(enum pwrap_dbglvl_e dbglvl, const char *function, const char *format, ...) PRINTF_ATTRIBUTE(3, 4); # define PWRAP_LOG(dbglvl, ...) pwrap_log((dbglvl), __func__, __VA_ARGS__) static void pwrap_vlog(enum pwrap_dbglvl_e dbglvl, const char *function, const char *format, va_list args) PRINTF_ATTRIBUTE(3, 0); static void pwrap_vlog(enum pwrap_dbglvl_e dbglvl, const char *function, const char *format, va_list args) { char buffer[1024]; const char *d; unsigned int lvl = 0; const char *prefix = "PWRAP"; d = getenv("PAM_WRAPPER_DEBUGLEVEL"); if (d != NULL) { lvl = atoi(d); } if (lvl < dbglvl) { return; } 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; } fprintf(stderr, "%s(%d) - %s: %s\n", prefix, (int)getpid(), function, buffer); } static void pwrap_log(enum pwrap_dbglvl_e dbglvl, const char *function, const char *format, ...) { va_list va; va_start(va, format); pwrap_vlog(dbglvl, function, format, va); va_end(va); } /***************** * LIBC *****************/ #define LIBPAM_NAME "libpam.so.0" typedef int (*__libpam_pam_start)(const char *service_name, const char *user, const struct pam_conv *pam_conversation, 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); typedef int (*__libpam_pam_chauthtok)(pam_handle_t *pamh, int flags); typedef int (*__libpam_pam_acct_mgmt)(pam_handle_t *pamh, int flags); typedef int (*__libpam_pam_putenv)(pam_handle_t *pamh, const char *name_value); typedef const char * (*__libpam_pam_getenv)(pam_handle_t *pamh, const char *name); typedef char ** (*__libpam_pam_getenvlist)(pam_handle_t *pamh); typedef int (*__libpam_pam_open_session)(pam_handle_t *pamh, int flags); typedef int (*__libpam_pam_close_session)(pam_handle_t *pamh, int flags); typedef int (*__libpam_pam_setcred)(pam_handle_t *pamh, int flags); typedef int (*__libpam_pam_get_item)(const pam_handle_t *pamh, int item_type, const void **item); typedef int (*__libpam_pam_set_item)(pam_handle_t *pamh, int item_type, const void *item); typedef int (*__libpam_pam_get_data)(const pam_handle_t *pamh, const char *module_data_name, const void **data); typedef int (*__libpam_pam_set_data)(pam_handle_t *pamh, const char *module_data_name, void *data, void (*cleanup)(pam_handle_t *pamh, void *data, int error_status)); typedef int (*__libpam_pam_vprompt)(pam_handle_t *pamh, int style, char **response, const char *fmt, va_list args); typedef const char * (*__libpam_pam_strerror)(pam_handle_t *pamh, int errnum); #ifdef HAVE_PAM_VSYSLOG typedef void (*__libpam_pam_vsyslog)(const pam_handle_t *pamh, int priority, const char *fmt, va_list args); #endif #define PWRAP_SYMBOL_ENTRY(i) \ union { \ __libpam_##i f; \ void *obj; \ } _libpam_##i struct pwrap_libpam_symbols { PWRAP_SYMBOL_ENTRY(pam_start); PWRAP_SYMBOL_ENTRY(pam_end); PWRAP_SYMBOL_ENTRY(pam_authenticate); PWRAP_SYMBOL_ENTRY(pam_chauthtok); PWRAP_SYMBOL_ENTRY(pam_acct_mgmt); PWRAP_SYMBOL_ENTRY(pam_putenv); PWRAP_SYMBOL_ENTRY(pam_getenv); PWRAP_SYMBOL_ENTRY(pam_getenvlist); PWRAP_SYMBOL_ENTRY(pam_open_session); PWRAP_SYMBOL_ENTRY(pam_close_session); PWRAP_SYMBOL_ENTRY(pam_setcred); PWRAP_SYMBOL_ENTRY(pam_get_item); PWRAP_SYMBOL_ENTRY(pam_set_item); PWRAP_SYMBOL_ENTRY(pam_get_data); PWRAP_SYMBOL_ENTRY(pam_set_data); PWRAP_SYMBOL_ENTRY(pam_vprompt); PWRAP_SYMBOL_ENTRY(pam_strerror); #ifdef HAVE_PAM_VSYSLOG PWRAP_SYMBOL_ENTRY(pam_vsyslog); #endif }; struct pwrap { struct { void *handle; struct pwrap_libpam_symbols symbols; } libpam; bool enabled; bool initialised; char *config_dir; char *libpam_so; }; static struct pwrap pwrap; /********************************************************* * PWRAP PROTOTYPES *********************************************************/ bool pam_wrapper_enabled(void); void pwrap_constructor(void) CONSTRUCTOR_ATTRIBUTE; void pwrap_destructor(void) DESTRUCTOR_ATTRIBUTE; /********************************************************* * PWRAP LIBC LOADER FUNCTIONS *********************************************************/ enum pwrap_lib { PWRAP_LIBPAM, }; static void *pwrap_load_lib_handle(enum pwrap_lib lib) { int flags = RTLD_LAZY; void *handle = NULL; #ifdef RTLD_DEEPBIND flags |= RTLD_DEEPBIND; #endif switch (lib) { case PWRAP_LIBPAM: handle = pwrap.libpam.handle; if (handle == NULL) { handle = dlopen(pwrap.libpam_so, flags); if (handle != NULL) { PWRAP_LOG(PWRAP_LOG_DEBUG, "Opened %s\n", pwrap.libpam_so); pwrap.libpam.handle = handle; break; } } break; } if (handle == NULL) { PWRAP_LOG(PWRAP_LOG_ERROR, "Failed to dlopen library: %s\n", dlerror()); exit(-1); } return handle; } static void *_pwrap_bind_symbol(enum pwrap_lib lib, const char *fn_name) { void *handle; void *func; handle = pwrap_load_lib_handle(lib); func = dlsym(handle, fn_name); if (func == NULL) { PWRAP_LOG(PWRAP_LOG_ERROR, "Failed to find %s: %s\n", fn_name, dlerror()); exit(-1); } return func; } #define pwrap_bind_symbol_libpam(sym_name) \ if (pwrap.libpam.symbols._libpam_##sym_name.obj == NULL) { \ pwrap.libpam.symbols._libpam_##sym_name.obj = \ _pwrap_bind_symbol(PWRAP_LIBPAM, #sym_name); \ } \ /* * IMPORTANT * * Functions especially from libpam need to be loaded individually, you can't * load all at once or gdb will segfault at startup. The same applies to * 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. */ static int libpam_pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh) { pwrap_bind_symbol_libpam(pam_start); return pwrap.libpam.symbols._libpam_pam_start.f(service_name, user, pam_conversation, pamh); } static int libpam_pam_end(pam_handle_t *pamh, int pam_status) { pwrap_bind_symbol_libpam(pam_end); return pwrap.libpam.symbols._libpam_pam_end.f(pamh, pam_status); } static int libpam_pam_authenticate(pam_handle_t *pamh, int flags) { pwrap_bind_symbol_libpam(pam_authenticate); return pwrap.libpam.symbols._libpam_pam_authenticate.f(pamh, flags); } static int libpam_pam_chauthtok(pam_handle_t *pamh, int flags) { pwrap_bind_symbol_libpam(pam_chauthtok); return pwrap.libpam.symbols._libpam_pam_chauthtok.f(pamh, flags); } static int libpam_pam_acct_mgmt(pam_handle_t *pamh, int flags) { pwrap_bind_symbol_libpam(pam_acct_mgmt); return pwrap.libpam.symbols._libpam_pam_acct_mgmt.f(pamh, flags); } static int libpam_pam_putenv(pam_handle_t *pamh, const char *name_value) { pwrap_bind_symbol_libpam(pam_putenv); return pwrap.libpam.symbols._libpam_pam_putenv.f(pamh, name_value); } static const char *libpam_pam_getenv(pam_handle_t *pamh, const char *name) { pwrap_bind_symbol_libpam(pam_getenv); return pwrap.libpam.symbols._libpam_pam_getenv.f(pamh, name); } static char **libpam_pam_getenvlist(pam_handle_t *pamh) { pwrap_bind_symbol_libpam(pam_getenvlist); return pwrap.libpam.symbols._libpam_pam_getenvlist.f(pamh); } static int libpam_pam_open_session(pam_handle_t *pamh, int flags) { pwrap_bind_symbol_libpam(pam_open_session); return pwrap.libpam.symbols._libpam_pam_open_session.f(pamh, flags); } static int libpam_pam_close_session(pam_handle_t *pamh, int flags) { pwrap_bind_symbol_libpam(pam_close_session); return pwrap.libpam.symbols._libpam_pam_close_session.f(pamh, flags); } static int libpam_pam_setcred(pam_handle_t *pamh, int flags) { pwrap_bind_symbol_libpam(pam_setcred); return pwrap.libpam.symbols._libpam_pam_setcred.f(pamh, flags); } static int libpam_pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) { pwrap_bind_symbol_libpam(pam_get_item); return pwrap.libpam.symbols._libpam_pam_get_item.f(pamh, item_type, item); } static int libpam_pam_set_item(pam_handle_t *pamh, int item_type, const void *item) { pwrap_bind_symbol_libpam(pam_set_item); return pwrap.libpam.symbols._libpam_pam_set_item.f(pamh, item_type, item); } static int libpam_pam_get_data(const pam_handle_t *pamh, const char *module_data_name, const void **data) { pwrap_bind_symbol_libpam(pam_get_data); return pwrap.libpam.symbols._libpam_pam_get_data.f(pamh, module_data_name, data); } static int libpam_pam_set_data(pam_handle_t *pamh, const char *module_data_name, void *data, void (*cleanup)(pam_handle_t *pamh, void *data, int error_status)) { pwrap_bind_symbol_libpam(pam_set_data); return pwrap.libpam.symbols._libpam_pam_set_data.f(pamh, module_data_name, data, cleanup); } static int libpam_pam_vprompt(pam_handle_t *pamh, int style, char **response, const char *fmt, va_list args) { pwrap_bind_symbol_libpam(pam_vprompt); return pwrap.libpam.symbols._libpam_pam_vprompt.f(pamh, style, response, fmt, args); } #ifdef HAVE_PAM_STRERROR_CONST static const char *libpam_pam_strerror(const pam_handle_t *pamh, int errnum) #else static const char *libpam_pam_strerror(pam_handle_t *pamh, int errnum) #endif { pwrap_bind_symbol_libpam(pam_strerror); return pwrap.libpam.symbols._libpam_pam_strerror.f(discard_const_p(pam_handle_t, pamh), errnum); } static void libpam_pam_vsyslog(const pam_handle_t *pamh, int priority, const char *fmt, va_list args) { #ifdef HAVE_PAM_VSYSLOG pwrap_bind_symbol_libpam(pam_vsyslog); pwrap.libpam.symbols._libpam_pam_vsyslog.f(pamh, priority, fmt, args); #endif } /********************************************************* * PWRAP INIT *********************************************************/ #define BUFFER_SIZE 32768 /* copy file from src to dst, overwrites dst */ static int p_copy(const char *src, const char *dst, const char *pdir, mode_t mode) { int srcfd = -1; int dstfd = -1; int rc = -1; ssize_t bread, bwritten; struct stat sb; char buf[BUFFER_SIZE]; int cmp; 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; } for (;;) { char *p; bread = read(srcfd, buf, BUFFER_SIZE); if (bread == 0) { /* done */ break; } else if (bread < 0) { errno = EIO; rc = -1; goto out; } /* EXTRA UGLY HACK */ if (pdir != NULL) { p = buf; while (p < buf + BUFFER_SIZE) { if (*p == '/') { cmp = memcmp(p, "/etc/pam.d", 10); if (cmp == 0) { memcpy(p, pdir, 10); } } p++; } } 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; } /* Do not pass any flag if not defined */ #ifndef FTW_ACTIONRETVAL #define FTW_ACTIONRETVAL 0 #endif /* Action return values */ #ifndef FTW_STOP #define FTW_STOP -1 #endif #ifndef FTW_CONTINUE #define FTW_CONTINUE 0 #endif #ifndef FTW_SKIP_SUBTREE #define FTW_SKIP_SUBTREE 0 #endif static int copy_ftw(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { int rc; char buf[BUFFER_SIZE]; switch (typeflag) { case FTW_D: case FTW_DNR: /* We want to copy the directories from this directory */ if (ftwbuf->level == 0) { return FTW_CONTINUE; } return FTW_SKIP_SUBTREE; case FTW_F: break; default: return FTW_CONTINUE; } rc = snprintf(buf, BUFFER_SIZE, "%s/%s", pwrap.config_dir, fpath + ftwbuf->base); if (rc >= BUFFER_SIZE) { return FTW_STOP; } PWRAP_LOG(PWRAP_LOG_TRACE, "Copying %s", fpath); rc = p_copy(fpath, buf, NULL, sb->st_mode); if (rc != 0) { return FTW_STOP; } return FTW_CONTINUE; } static int copy_confdir(const char *src) { int rc; PWRAP_LOG(PWRAP_LOG_DEBUG, "Copy config files from %s to %s", src, pwrap.config_dir); rc = nftw(src, copy_ftw, 1, FTW_ACTIONRETVAL); if (rc != 0) { return -1; } return 0; } static int p_rmdirs(const char *path); static void pwrap_clean_stale_dirs(const char *dir) { size_t len = strlen(dir); char pidfile[len + 5]; ssize_t rc; char buf[8] = {0}; long int tmp; pid_t pid; int fd; snprintf(pidfile, sizeof(pidfile), "%s/pid", dir); /* read the pidfile */ fd = open(pidfile, O_RDONLY); if (fd < 0) { if (errno == ENOENT) { PWRAP_LOG(PWRAP_LOG_TRACE, "pidfile %s missing, nothing to do\n", pidfile); } else { PWRAP_LOG(PWRAP_LOG_ERROR, "Failed to open pidfile %s - error: %s", pidfile, strerror(errno)); } return; } rc = read(fd, buf, sizeof(buf)); close(fd); if (rc < 0) { PWRAP_LOG(PWRAP_LOG_ERROR, "Failed to read pidfile %s - error: %s", pidfile, strerror(errno)); return; } buf[sizeof(buf) - 1] = '\0'; tmp = strtol(buf, NULL, 10); if (tmp == 0 || tmp > 0xFFFF || errno == ERANGE) { PWRAP_LOG(PWRAP_LOG_ERROR, "Failed to parse pid, buf=%s", buf); return; } pid = (pid_t)(tmp & 0xFFFF); rc = kill(pid, 0); if (rc == -1) { PWRAP_LOG(PWRAP_LOG_TRACE, "Remove stale pam_wrapper dir: %s", dir); p_rmdirs(dir); } return; } static void pwrap_init(void) { char tmp_config_dir[] = "/tmp/pam.X"; size_t len = strlen(tmp_config_dir); const char *env; uint32_t i; int rc; char pam_library[128] = { 0 }; char libpam_path[1024] = { 0 }; ssize_t ret; FILE *pidfile; char pidfile_path[1024] = { 0 }; if (!pam_wrapper_enabled()) { return; } if (pwrap.initialised) { return; } PWRAP_LOG(PWRAP_LOG_DEBUG, "Initialize pam_wrapper"); for (i = 0; i < 36; i++) { struct stat sb; char c; if (i < 10) { c = (char)(i + 48); } else { c = (char)(i + 87); } tmp_config_dir[len - 1] = c; 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); continue; } else if (errno == ENOENT) { break; } } if (i == 36) { PWRAP_LOG(PWRAP_LOG_ERROR, "Failed to find a possible path to create " "pam_wrapper config dir: %s", tmp_config_dir); exit(1); } 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); } /* create lib subdirectory */ snprintf(libpam_path, sizeof(libpam_path), "%s/lib", pwrap.config_dir); rc = mkdir(libpam_path, 0755); if (rc != 0) { PWRAP_LOG(PWRAP_LOG_ERROR, "Failed to create pam_wrapper config dir: %s - %s", tmp_config_dir, strerror(errno)); p_rmdirs(pwrap.config_dir); exit(1); } snprintf(libpam_path, sizeof(libpam_path), "%s/lib/%s", pwrap.config_dir, LIBPAM_NAME); pwrap.libpam_so = strdup(libpam_path); if (pwrap.libpam_so == NULL) { PWRAP_LOG(PWRAP_LOG_ERROR, "No memory"); p_rmdirs(pwrap.config_dir); exit(1); } /* copy libpam.so.0 */ snprintf(libpam_path, sizeof(libpam_path), "%s", PAM_LIBRARY); PWRAP_LOG(PWRAP_LOG_TRACE, "PAM path: %s", libpam_path); ret = readlink(libpam_path, pam_library, sizeof(pam_library) - 1); PWRAP_LOG(PWRAP_LOG_TRACE, "PAM library: %s", pam_library); if (ret <= 0) { PWRAP_LOG(PWRAP_LOG_ERROR, "Failed to read %s link", LIBPAM_NAME); p_rmdirs(pwrap.config_dir); exit(1); } if (pam_library[0] == '/') { snprintf(libpam_path, sizeof(libpam_path), "%s", pam_library); } else { char libpam_path_cp[sizeof(libpam_path)]; char *dname; strncpy(libpam_path_cp, libpam_path, sizeof(libpam_path_cp)); libpam_path_cp[sizeof(libpam_path_cp) - 1] = '\0'; dname = dirname(libpam_path_cp); if (dname == NULL) { PWRAP_LOG(PWRAP_LOG_ERROR, "No directory component in %s", libpam_path); p_rmdirs(pwrap.config_dir); exit(1); } snprintf(libpam_path, sizeof(libpam_path), "%s/%s", dname, pam_library); } PWRAP_LOG(PWRAP_LOG_TRACE, "Reconstructed PAM path: %s", libpam_path); PWRAP_LOG(PWRAP_LOG_DEBUG, "Copy %s to %s", libpam_path, pwrap.libpam_so); rc = p_copy(libpam_path, pwrap.libpam_so, pwrap.config_dir, 0644); if (rc != 0) { PWRAP_LOG(PWRAP_LOG_ERROR, "Failed to copy %s - error: %s", LIBPAM_NAME, strerror(errno)); p_rmdirs(pwrap.config_dir); exit(1); } 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"); } bool pam_wrapper_enabled(void) { const char *env; pwrap.enabled = false; env = getenv("PAM_WRAPPER"); if (env != NULL && env[0] == '1') { pwrap.enabled = true; } if (pwrap.enabled) { pwrap.enabled = false; env = getenv("PAM_WRAPPER_SERVICE_DIR"); if (env != NULL && env[0] != '\0') { pwrap.enabled = true; } } 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, const struct pam_conv *pam_conversation, pam_handle_t **pamh) { int rv; char fullpath[1024]; rv = openpam_set_feature(OPENPAM_RESTRICT_SERVICE_NAME, 0); if (rv != PAM_SUCCESS) { PWRAP_LOG(PWRAP_LOG_ERROR, "Cannot disable OPENPAM_RESTRICT_SERVICE_NAME"); return rv; } rv = openpam_set_feature(OPENPAM_RESTRICT_MODULE_NAME, 0); if (rv != PAM_SUCCESS) { PWRAP_LOG(PWRAP_LOG_ERROR, "Cannot disable OPENPAM_RESTRICT_MODULE_NAME"); return rv; } rv = openpam_set_feature(OPENPAM_VERIFY_MODULE_FILE, 0); if (rv != PAM_SUCCESS) { PWRAP_LOG(PWRAP_LOG_ERROR, "Cannot disable OPENPAM_VERIFY_MODULE_FILE"); return rv; } rv = openpam_set_feature(OPENPAM_VERIFY_POLICY_FILE, 0); if (rv != PAM_SUCCESS) { PWRAP_LOG(PWRAP_LOG_ERROR, "Cannot disable OPENPAM_VERIFY_POLICY_FILE"); return rv; } snprintf(fullpath, sizeof(fullpath), "%s/%s", pwrap.config_dir, service_name); return libpam_pam_start(fullpath, user, pam_conversation, pamh); } #endif static int pwrap_pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh) { 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); #else return libpam_pam_start(service_name, user, pam_conversation, pamh); #endif } int pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh) { return pwrap_pam_start(service_name, user, pam_conversation, pamh); } static int pwrap_pam_end(pam_handle_t *pamh, int pam_status) { PWRAP_LOG(PWRAP_LOG_TRACE, "pam_end status=%d", pam_status); return libpam_pam_end(pamh, pam_status); } int pam_end(pam_handle_t *pamh, int pam_status) { return pwrap_pam_end(pamh, pam_status); } static int pwrap_pam_authenticate(pam_handle_t *pamh, int flags) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_pam_authenticate flags=%d", flags); return libpam_pam_authenticate(pamh, flags); } int pam_authenticate(pam_handle_t *pamh, int flags) { return pwrap_pam_authenticate(pamh, flags); } static int pwrap_pam_chauthtok(pam_handle_t *pamh, int flags) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_pam_chauthtok flags=%d", flags); return libpam_pam_chauthtok(pamh, flags); } int pam_chauthtok(pam_handle_t *pamh, int flags) { return pwrap_pam_chauthtok(pamh, flags); } static int pwrap_pam_acct_mgmt(pam_handle_t *pamh, int flags) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_pam_acct_mgmt flags=%d", flags); return libpam_pam_acct_mgmt(pamh, flags); } int pam_acct_mgmt(pam_handle_t *pamh, int flags) { return pwrap_pam_acct_mgmt(pamh, flags); } static int pwrap_pam_putenv(pam_handle_t *pamh, const char *name_value) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_putenv name_value=%s", name_value); return libpam_pam_putenv(pamh, name_value); } int pam_putenv(pam_handle_t *pamh, const char *name_value) { return pwrap_pam_putenv(pamh, name_value); } static const char *pwrap_pam_getenv(pam_handle_t *pamh, const char *name) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_getenv name=%s", name); return libpam_pam_getenv(pamh, name); } const char *pam_getenv(pam_handle_t *pamh, const char *name) { return pwrap_pam_getenv(pamh, name); } static char **pwrap_pam_getenvlist(pam_handle_t *pamh) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_getenvlist called"); return libpam_pam_getenvlist(pamh); } char **pam_getenvlist(pam_handle_t *pamh) { return pwrap_pam_getenvlist(pamh); } static int pwrap_pam_open_session(pam_handle_t *pamh, int flags) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_pam_open_session flags=%d", flags); return libpam_pam_open_session(pamh, flags); } int pam_open_session(pam_handle_t *pamh, int flags) { return pwrap_pam_open_session(pamh, flags); } static int pwrap_pam_close_session(pam_handle_t *pamh, int flags) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_pam_close_session flags=%d", flags); return libpam_pam_close_session(pamh, flags); } int pam_close_session(pam_handle_t *pamh, int flags) { return pwrap_pam_close_session(pamh, flags); } static int pwrap_pam_setcred(pam_handle_t *pamh, int flags) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_pam_setcred flags=%d", flags); return libpam_pam_setcred(pamh, flags); } int pam_setcred(pam_handle_t *pamh, int flags) { return pwrap_pam_setcred(pamh, flags); } static const char *pwrap_get_service(const char *libpam_service) { #ifdef HAVE_OPENPAM const char *service_name; PWRAP_LOG(PWRAP_LOG_TRACE, "internal PAM_SERVICE=%s", libpam_service); service_name = strrchr(libpam_service, '/'); if (service_name != NULL && service_name[0] == '/') { service_name++; } PWRAP_LOG(PWRAP_LOG_TRACE, "PAM_SERVICE=%s", service_name); return service_name; #else return libpam_service; #endif } static int pwrap_pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) { int rc; const char *svc; PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item called"); rc = libpam_pam_get_item(pamh, item_type, item); if (rc == PAM_SUCCESS) { switch(item_type) { case PAM_USER: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item PAM_USER=%s", (const char *)*item); break; case PAM_SERVICE: svc = pwrap_get_service((const char *) *item); PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item PAM_SERVICE=%s", svc); *item = svc; break; case PAM_USER_PROMPT: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item PAM_USER_PROMPT=%s", (const char *)*item); break; case PAM_TTY: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item PAM_TTY=%s", (const char *)*item); break; case PAM_RUSER: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item PAM_RUSER=%s", (const char *)*item); break; case PAM_RHOST: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item PAM_RHOST=%s", (const char *)*item); break; case PAM_AUTHTOK: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item PAM_AUTHTOK=%s", (const char *)*item); break; case PAM_OLDAUTHTOK: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item PAM_OLDAUTHTOK=%s", (const char *)*item); break; case PAM_CONV: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item PAM_CONV=%p", (const void *)*item); break; default: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item item_type=%d item=%p", item_type, (const void *)*item); break; } } else { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_item failed rc=%d", rc); } return rc; } int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) { return pwrap_pam_get_item(pamh, item_type, item); } static int pwrap_pam_set_item(pam_handle_t *pamh, int item_type, const void *item) { int rc; PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item called"); rc = libpam_pam_set_item(pamh, item_type, item); if (rc == PAM_SUCCESS) { switch(item_type) { case PAM_USER: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item PAM_USER=%s", (const char *)item); break; case PAM_SERVICE: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item PAM_SERVICE=%s", (const char *)item); break; case PAM_USER_PROMPT: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item PAM_USER_PROMPT=%s", (const char *)item); break; case PAM_TTY: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item PAM_TTY=%s", (const char *)item); break; case PAM_RUSER: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item PAM_RUSER=%s", (const char *)item); break; case PAM_RHOST: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item PAM_RHOST=%s", (const char *)item); break; case PAM_AUTHTOK: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item PAM_AUTHTOK=%s", (const char *)item); break; case PAM_OLDAUTHTOK: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item PAM_OLDAUTHTOK=%s", (const char *)item); break; case PAM_CONV: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item PAM_CONV=%p", item); break; default: PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item item_type=%d item=%p", item_type, item); break; } } else { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_item failed rc=%d", rc); } return rc; } int pam_set_item(pam_handle_t *pamh, int item_type, const void *item) { return pwrap_pam_set_item(pamh, item_type, item); } static int pwrap_pam_get_data(const pam_handle_t *pamh, const char *module_data_name, const void **data) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_get_data module_data_name=%s", module_data_name); return libpam_pam_get_data(pamh, module_data_name, data); } int pam_get_data(const pam_handle_t *pamh, const char *module_data_name, const void **data) { return pwrap_pam_get_data(pamh, module_data_name, data); } static int pwrap_pam_set_data(pam_handle_t *pamh, const char *module_data_name, void *data, void (*cleanup)(pam_handle_t *pamh, void *data, int error_status)) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_set_data module_data_name=%s data=%p", module_data_name, data); return libpam_pam_set_data(pamh, module_data_name, data, cleanup); } int pam_set_data(pam_handle_t *pamh, const char *module_data_name, void *data, void (*cleanup)(pam_handle_t *pamh, void *data, int error_status)) { return pwrap_pam_set_data(pamh, module_data_name, data, cleanup); } #ifdef HAVE_PAM_VPROMPT_CONST static int pwrap_pam_vprompt(const pam_handle_t *pamh, #else static int pwrap_pam_vprompt(pam_handle_t *pamh, #endif int style, char **response, const char *fmt, va_list args) { PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_pam_vprompt style=%d", style); return libpam_pam_vprompt(discard_const_p(pam_handle_t, pamh), style, response, fmt, args); } #ifdef HAVE_PAM_VPROMPT_CONST int pam_vprompt(const pam_handle_t *pamh, int style, char **response, const char *fmt, va_list args) #else int pam_vprompt(pam_handle_t *pamh, int style, char **response, const char *fmt, va_list args) #endif { return pwrap_pam_vprompt(discard_const_p(pam_handle_t, pamh), style, response, fmt, args); } #ifdef HAVE_PAM_PROMPT_CONST int pam_prompt(const pam_handle_t *pamh, int style, char **response, const char *fmt, ...) #else int pam_prompt(pam_handle_t *pamh, int style, char **response, const char *fmt, ...) #endif { va_list args; int rv; va_start(args, fmt); rv = pwrap_pam_vprompt(discard_const_p(pam_handle_t, pamh), style, response, fmt, args); va_end(args); return rv; } #ifdef HAVE_PAM_STRERROR_CONST static const char *pwrap_pam_strerror(const pam_handle_t *pamh, int errnum) #else static const char *pwrap_pam_strerror(pam_handle_t *pamh, int errnum) #endif { const char *str; pwrap_init(); PWRAP_LOG(PWRAP_LOG_TRACE, "pam_strerror errnum=%d", errnum); str = libpam_pam_strerror(discard_const_p(pam_handle_t, pamh), errnum); PWRAP_LOG(PWRAP_LOG_TRACE, "pam_strerror error=%s", str); return str; } #ifdef HAVE_PAM_STRERROR_CONST const char *pam_strerror(const pam_handle_t *pamh, int errnum) #else const char *pam_strerror(pam_handle_t *pamh, int errnum) #endif { return pwrap_pam_strerror(discard_const_p(pam_handle_t, pamh), errnum); } static void pwrap_pam_vsyslog(const pam_handle_t *pamh, int priority, const char *fmt, va_list args) PRINTF_ATTRIBUTE(3, 0); static void pwrap_pam_vsyslog(const pam_handle_t *pamh, int priority, const char *fmt, va_list args) { const char *d; char syslog_str[32] = {0}; enum pwrap_dbglvl_e dbglvl = PWRAP_LOG_TRACE; PWRAP_LOG(PWRAP_LOG_TRACE, "pwrap_pam_vsyslog called"); d = getenv("PAM_WRAPPER_USE_SYSLOG"); if (d != NULL && d[0] == '1') { libpam_pam_vsyslog(pamh, priority, fmt, args); return; } switch(priority) { case 0: /* LOG_EMERG */ case 1: /* LOG_ALERT */ case 2: /* LOG_CRIT */ case 3: /* LOG_ERR */ dbglvl = PWRAP_LOG_ERROR; break; case 4: /* LOG_WARN */ dbglvl = PWRAP_LOG_WARN; break; case 5: /* LOG_NOTICE */ case 6: /* LOG_INFO */ case 7: /* LOG_DEBUG */ dbglvl = PWRAP_LOG_DEBUG; break; default: dbglvl = PWRAP_LOG_TRACE; break; } snprintf(syslog_str, sizeof(syslog_str), "SYSLOG(%d)", priority); pwrap_vlog(dbglvl, syslog_str, fmt, args); } #ifdef HAVE_PAM_VSYSLOG void pam_vsyslog(const pam_handle_t *pamh, int priority, const char *fmt, va_list args) { pwrap_pam_vsyslog(pamh, priority, fmt, args); } #endif #ifdef HAVE_PAM_SYSLOG void pam_syslog(const pam_handle_t *pamh, int priority, const char *fmt, ...) { va_list args; va_start(args, fmt); pwrap_pam_vsyslog(pamh, priority, fmt, args); va_end(args); } #endif /* This might be called by pam_end() running with sshd */ int audit_open(void); int audit_open(void) { /* * Tell the application that the kernel doesn't * have audit compiled in. */ errno = EPROTONOSUPPORT; return -1; } /* Disable BSD auditing */ int cannot_audit(int x); int cannot_audit(int x) { (void) x; return 1; } /**************************** * DESTRUCTOR ***************************/ static int p_rmdirs_at(const char *path, int parent_fd) { DIR *d; struct dirent *dp; struct stat sb; int path_fd; int rc; /* If path is absolute, parent_fd is ignored. */ PWRAP_LOG(PWRAP_LOG_TRACE, "p_rmdirs_at removing %s at %d\n", path, parent_fd); path_fd = openat(parent_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); if (path_fd == -1) { return -1; } d = fdopendir(path_fd); if (d == NULL) { close(path_fd); return -1; } while ((dp = readdir(d)) != NULL) { /* skip '.' and '..' */ if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) { continue; } rc = fstatat(path_fd, dp->d_name, &sb, AT_SYMLINK_NOFOLLOW); if (rc != 0) { continue; } if (S_ISDIR(sb.st_mode)) { rc = p_rmdirs_at(dp->d_name, path_fd); } else { rc = unlinkat(path_fd, dp->d_name, 0); } if (rc != 0) { continue; } } closedir(d); rc = unlinkat(parent_fd, path, AT_REMOVEDIR); if (rc != 0) { rc = errno; PWRAP_LOG(PWRAP_LOG_TRACE, "cannot unlink %s error %d\n", path, rc); return -1; } return 0; } static int p_rmdirs(const char *path) { /* * If path is absolute, p_rmdirs_at ignores parent_fd. * If it's relative, start from cwd. */ return p_rmdirs_at(path, AT_FDCWD); } /* * This function is called when the library is unloaded and makes sure that * resources are freed. */ void pwrap_destructor(void) { const char *env; PWRAP_LOG(PWRAP_LOG_TRACE, "entering pwrap_destructor"); if (pwrap.libpam.handle != NULL) { dlclose(pwrap.libpam.handle); } if (pwrap.libpam_so != NULL) { free(pwrap.libpam_so); pwrap.libpam_so = NULL; } if (!pwrap.initialised) { return; } PWRAP_LOG(PWRAP_LOG_TRACE, "destructor called for pam_wrapper dir %s", pwrap.config_dir); env = getenv("PAM_WRAPPER_KEEP_DIR"); if (env == NULL || env[0] != '1') { p_rmdirs(pwrap.config_dir); } if (pwrap.config_dir != NULL) { free(pwrap.config_dir); pwrap.config_dir = NULL; } }