From 542b845f4b8b477251d26af6f857de5bfeb2c90e Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Thu, 8 Aug 2013 15:16:26 -0400 Subject: Shoehorn in some nsswitch wrappers --- tests/Makefile.am | 8 ++ tests/wrap-nsswitch.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/wrap-pam.c | 210 +++++++++++++++++++++++++++++ 3 files changed, 584 insertions(+) create mode 100644 tests/wrap-nsswitch.c create mode 100644 tests/wrap-pam.c diff --git a/tests/Makefile.am b/tests/Makefile.am index f2d7638..9264902 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,12 @@ SUBDIRS = clients + +noinst_PROGRAMS = wrappers.so +wrappers_so_SOURCES = wrap-nsswitch.c wrap-pam.c +wrappers_so_CFLAGS = $(AM_CFLAGS) -fPIC +wrappers_so_LDADD = -ldl +wrappers_so_LDFLAGS = -shared +wrappers_so_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(wrappers_so_LDFLAGS) $(LDFLAGS) -o $@ + EXTRA_DIST = run-tests.sh test*/*.ldif test*/*.sh test*/*.txt config/slapd-collations.conf config/userRoot.ldif config/schema *.supp if CAN_TEST check: diff --git a/tests/wrap-nsswitch.c b/tests/wrap-nsswitch.c new file mode 100644 index 0000000..23ce89a --- /dev/null +++ b/tests/wrap-nsswitch.c @@ -0,0 +1,366 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * 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; version 2 of the License. + * + * 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, write to the + * + * Free Software Foundation, Inc. + * 59 Temple Place, Suite 330 + * Boston, MA 02111-1307 USA + * + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int +getpwnam_r(const char *name, + struct passwd *resultbuf, + char *buffer, size_t buflen, + struct passwd **result) +{ + FILE *fp; + static int (*next)(const char *, struct passwd *, + char *, size_t, + struct passwd **); + int error; + + if (next == NULL) { + next = dlsym(RTLD_NEXT, "getpwnam_r"); + } + if ((getenv("WRAPPERS_PASSWD") != NULL) && + ((fp = fopen(getenv("WRAPPERS_PASSWD"), "r")) != NULL)) { + while ((error = fgetpwent_r(fp, resultbuf, + buffer, buflen, result)) == 0) { + if (strcmp(name, resultbuf->pw_name) == 0) { + fclose(fp); + return 0; + } + } + fclose(fp); + if ((error != 0) && (error != ENOENT)) { + return error; + } + } + return (*next)(name, resultbuf, buffer, buflen, result); +} + +struct passwd * +getpwnam(const char *name) +{ + static struct passwd * (*next)(const char *); + static struct passwd pwd, *pwdp; + static char *buffer; + static size_t buflen = 16; + + if (next == NULL) { + next = dlsym(RTLD_NEXT, "getpwnam"); + } + if (buffer == NULL) { + buffer = malloc(buflen); + } + if (buffer != NULL) { + retry: + switch (getpwnam_r(name, &pwd, buffer, buflen, &pwdp)) { + case 0: + return &pwd; + break; + case ERANGE: + free(buffer); + buffer = malloc((buflen + 1) * 2); + if (buffer != NULL) { + buflen = ((buflen + 1) * 2); + goto retry; + } + errno = ERANGE; + return NULL; + break; + } + } + if (next == NULL) { + errno = ENOSYS; + return NULL; + } + return (*next)(name); +} + +int +getpwuid_r(uid_t uid, + struct passwd *resultbuf, + char *buffer, size_t buflen, + struct passwd **result) +{ + FILE *fp; + static int (*next)(uid_t, struct passwd *, + char *, size_t, + struct passwd **); + int error; + + if (next == NULL) { + next = dlsym(RTLD_NEXT, "getpwuid_r"); + } + if ((getenv("WRAPPERS_PASSWD") != NULL) && + ((fp = fopen(getenv("WRAPPERS_PASSWD"), "r")) != NULL)) { + while ((error = fgetpwent_r(fp, resultbuf, + buffer, buflen, result)) == 0) { + if (resultbuf->pw_uid == uid) { + fclose(fp); + return 0; + } + } + fclose(fp); + if ((error != 0) && (error != ENOENT)) { + return error; + } + } + return (*next)(uid, resultbuf, buffer, buflen, result); +} + +struct passwd * +getpwuid(uid_t uid) +{ + static struct passwd * (*next)(uid_t); + static struct passwd pwd, *pwdp; + static char *buffer; + static size_t buflen = 16; + + if (next == NULL) { + next = dlsym(RTLD_NEXT, "getpwuid"); + } + if (buffer == NULL) { + buffer = malloc(buflen); + } + if (buffer != NULL) { + retry: + switch (getpwuid_r(uid, &pwd, buffer, buflen, &pwdp)) { + case 0: + return &pwd; + break; + case ERANGE: + free(buffer); + buffer = malloc((buflen + 1) * 2); + if (buffer != NULL) { + buflen = ((buflen + 1) * 2); + goto retry; + } + errno = ERANGE; + return NULL; + break; + } + } + if (next == NULL) { + errno = ENOSYS; + return NULL; + } + return (*next)(uid); +} + +int +getgrnam_r(const char *name, + struct group *resultbuf, + char *buffer, size_t buflen, + struct group **result) +{ + FILE *fp; + static int (*next)(const char *, struct group *, + char *, size_t, + struct group **); + int error; + + if (next == NULL) { + next = dlsym(RTLD_NEXT, "getgrnam_r"); + } + if ((getenv("WRAPPERS_GROUP") != NULL) && + ((fp = fopen(getenv("WRAPPERS_GROUP"), "r")) != NULL)) { + while ((error = fgetgrent_r(fp, resultbuf, + buffer, buflen, result)) == 0) { + if (strcmp(name, resultbuf->gr_name) == 0) { + fclose(fp); + return 0; + } + } + fclose(fp); + if ((error != 0) && (error != ENOENT)) { + return error; + } + } + return (*next)(name, resultbuf, buffer, buflen, result); +} + +struct group * +getgrnam(const char *name) +{ + static struct group * (*next)(const char *); + static struct group grp, *grpp; + static char *buffer; + static size_t buflen = 16; + + if (next == NULL) { + next = dlsym(RTLD_NEXT, "getgrnam"); + } + if (buffer == NULL) { + buffer = malloc(buflen); + } + if (buffer != NULL) { + retry: + switch (getgrnam_r(name, &grp, buffer, buflen, &grpp)) { + case 0: + return &grp; + break; + case ERANGE: + free(buffer); + buffer = malloc((buflen + 1) * 2); + if (buffer != NULL) { + buflen = ((buflen + 1) * 2); + goto retry; + } + errno = ERANGE; + return NULL; + break; + } + } + if (next == NULL) { + errno = ENOSYS; + return NULL; + } + return (*next)(name); +} + +int +getgrgid_r(gid_t gid, + struct group *resultbuf, + char *buffer, size_t buflen, + struct group **result) +{ + FILE *fp; + static int (*next)(gid_t, struct group *, + char *, size_t, + struct group **); + int error; + + if (next == NULL) { + next = dlsym(RTLD_NEXT, "getgrgid_r"); + } + if ((getenv("WRAPPERS_GROUP") != NULL) && + ((fp = fopen(getenv("WRAPPERS_GROUP"), "r")) != NULL)) { + while ((error = fgetgrent_r(fp, resultbuf, + buffer, buflen, result)) == 0) { + if (resultbuf->gr_gid == gid) { + fclose(fp); + return 0; + } + } + fclose(fp); + if ((error != 0) && (error != ENOENT)) { + return error; + } + } + return (*next)(gid, resultbuf, buffer, buflen, result); +} + +struct group * +getgrgid(gid_t gid) +{ + static struct group * (*next)(gid_t); + static struct group grp, *grpp; + static char *buffer; + static size_t buflen = 16; + + if (next == NULL) { + next = dlsym(RTLD_NEXT, "getgrgid"); + } + if (buffer == NULL) { + buffer = malloc(buflen); + } + if (buffer != NULL) { + retry: + switch (getgrgid_r(gid, &grp, buffer, buflen, &grpp)) { + case 0: + return &grp; + break; + case ERANGE: + free(buffer); + buffer = malloc((buflen + 1) * 2); + if (buffer != NULL) { + buflen = ((buflen + 1) * 2); + goto retry; + } + errno = ERANGE; + return NULL; + break; + } + } + if (next == NULL) { + errno = ENOSYS; + return NULL; + } + return (*next)(gid); +} + + + +int +getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups) +{ + FILE *fp; + static int (*next)(const char *, gid_t, gid_t *, int *); + static char *buffer; + static size_t buflen = 16; + struct group grp, *grpp; + int error, i, count = 0; + + if (next == NULL) { + next = dlsym(RTLD_NEXT, "getgrouplist"); + } + + if ((getenv("WRAPPERS_GROUP") != NULL) && + ((fp = fopen(getenv("WRAPPERS_GROUP"), "r")) != NULL)) { + while ((error = fgetgrent_r(fp, &grp, + buffer, buflen, &grpp)) == 0) { + for (i = 0; + (grp.gr_mem != NULL) && (grp.gr_mem[i] != NULL); + i++) { + if (strcmp(grp.gr_mem[i], user) == 0) { + if (count >= *ngroups) { + *ngroups = count + 1; + fclose(fp); + errno = ERANGE; + return -1; + break; + } + groups[count++] = grp.gr_gid; + } + } + } + fclose(fp); + if ((error != 0) && (error != ENOENT)) { + return error; + } + if (count > 0) { + *ngroups = count; + return count; + } + } + if (next == NULL) { + errno = ENOSYS; + return -1; + } + return (*next)(user, group, groups, ngroups); +} diff --git a/tests/wrap-pam.c b/tests/wrap-pam.c new file mode 100644 index 0000000..bd728ac --- /dev/null +++ b/tests/wrap-pam.c @@ -0,0 +1,210 @@ +/* + * Copyright 2013 Red Hat, Inc. + * + * 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; version 2 of the License. + * + * 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, write to the + * + * Free Software Foundation, Inc. + * 59 Temple Place, Suite 330 + * Boston, MA 02111-1307 USA + * + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include + +static const struct { + int value; + const char *name; +} pam_errors[] = { + {PAM_SUCCESS, "SUCCESS"}, + {PAM_SUCCESS, "0"}, + {PAM_OPEN_ERR, "OPEN_ERR"}, + {PAM_SYMBOL_ERR, "SYMBOL_ERR"}, + {PAM_SERVICE_ERR, "SERVICE_ERR"}, + {PAM_SYSTEM_ERR, "SYSTEM_ERR"}, + {PAM_BUF_ERR, "BUF_ERR"}, + {PAM_PERM_DENIED, "PERM_DENIED"}, + {PAM_AUTH_ERR, "AUTH_ERR"}, + {PAM_CRED_INSUFFICIENT, "CRED_INSUFFICIENT"}, + {PAM_AUTHINFO_UNAVAIL, "AUTHINFO_UNAVAIL"}, + {PAM_USER_UNKNOWN, "USER_UNKNOWN"}, + {PAM_MAXTRIES, "MAXTRIES"}, + {PAM_NEW_AUTHTOK_REQD, "NEW_AUTHTOK_REQD"}, + {PAM_ACCT_EXPIRED, "ACCT_EXPIRED"}, + {PAM_SESSION_ERR, "SESSION_ERR"}, + {PAM_CRED_UNAVAIL, "CRED_UNAVAIL"}, + {PAM_CRED_EXPIRED, "CRED_EXPIRED"}, + {PAM_CRED_ERR, "CRED_ERR"}, + {PAM_NO_MODULE_DATA, "NO_MODULE_DATA"}, + {PAM_CONV_ERR, "CONV_ERR"}, + {PAM_AUTHTOK_ERR, "AUTHTOK_ERR"}, + {PAM_AUTHTOK_RECOVERY_ERR, "AUTHTOK_RECOVERY_ERR"}, + {PAM_AUTHTOK_LOCK_BUSY, "AUTHTOK_LOCK_BUSY"}, + {PAM_AUTHTOK_DISABLE_AGING, "AUTHTOK_DISABLE_AGING"}, + {PAM_TRY_AGAIN, "TRY_AGAIN"}, + {PAM_IGNORE, "IGNORE"}, + {PAM_ABORT, "ABORT"}, + {PAM_AUTHTOK_EXPIRED, "AUTHTOK_EXPIRED"}, + {PAM_MODULE_UNKNOWN, "UNKNOWN"}, + {PAM_BAD_ITEM, "BAD_ITEM"}, + {PAM_CONV_AGAIN, "CONV_AGAIN"}, + {PAM_INCOMPLETE, "INCOMPLETE"}, +}; + +typedef struct pam_handle { + char *authtok, errbuf[LINE_MAX]; + struct pam_conv conv; + int auth, acct; +} pam_handle_t; + +static int +pam_numerror(const char *name) +{ + unsigned int i, l; + + for (i = 0; i < sizeof(pam_errors) / sizeof(pam_errors[0]); i++) { + l = strlen(pam_errors[i].name); + if (strncasecmp(pam_errors[i].name, name, l) == 0) { + return pam_errors[i].value; + } + } + return -1; +} + +const char * +pam_strerror(pam_handle_t *pamh, int errnum) +{ + unsigned int i; + + for (i = 0; i < sizeof(pam_errors) / sizeof(pam_errors[0]); i++) { + if (pam_errors[i].value == errnum) { + return pam_errors[i].name; + } + } + snprintf(pamh->errbuf, sizeof(pamh->errbuf), "PAM error %d", errnum); + return pamh->errbuf; +} + +int +pam_start(const char *service_name, const char *user, + const struct pam_conv *pam_conversation, pam_handle_t **pamh) +{ + FILE *fp; + char buf[LINE_MAX], *p, *q; + pam_handle_t *ret; + + ret = calloc(1, sizeof(*ret)); + if (ret == NULL) { + return PAM_BUF_ERR; + } + ret->conv = *pam_conversation; + if (getenv("WRAPPERS_PAM_CREDS") == NULL) { + return PAM_ABORT; + } + fp = fopen(getenv("WRAPPERS_PAM_CREDS"), "r"); + if (fp == NULL) { + return PAM_ABORT; + } + while (fgets(buf, sizeof(buf), fp) != NULL) { + buf[strcspn(buf, "\r\n")] = '\0'; + if ((strlen(buf) > strlen(user)) && + (strncmp(user, buf, strlen(user)) == 0) && + (buf[strlen(user)] == ':')) { + p = buf + strcspn(buf, ":"); + if (*p != '\0') { + p++; + q = p + strcspn(p, ":"); + ret->authtok = strndup(p, q - p); + p = q; + } + if (*p != '\0') { + p++; + q = p + strcspn(p, ":"); + ret->auth = pam_numerror(p); + p = q; + } + if (*p != '\0') { + p++; + q = p + strcspn(p, ":"); + ret->acct = pam_numerror(p); + p = q; + } + break; + } + } + fclose(fp); + *pamh = ret; + return PAM_SUCCESS; +} + +int +pam_end(pam_handle_t *pamh, int pam_status) +{ + if (pamh == NULL) { + return PAM_SYSTEM_ERR; + } + free(pamh->authtok); + free(pamh); + return PAM_SUCCESS; +} + +int +pam_authenticate(pam_handle_t *pamh, int flags) +{ + struct pam_response *resp; + struct pam_message messages[] = { + {.msg_style = PAM_PROMPT_ECHO_OFF, .msg = "Password: "}, + }; + const struct pam_message *msgs = &messages[0]; + int ret; + + resp = NULL; + if (pamh == NULL) { + return PAM_SYSTEM_ERR; + } + if (pamh->authtok == NULL) { + return pamh->auth ? pamh->auth : PAM_USER_UNKNOWN; + } + if (pamh->conv.conv == NULL) { + return PAM_CONV_ERR; + } + ret = pamh->conv.conv(1, &msgs, &resp, pamh->conv.appdata_ptr); + if (ret != PAM_SUCCESS) { + return ret; + } + if (strcmp(pamh->authtok, resp->resp) == 0) { + ret = pamh->auth; + } else { + ret = PAM_AUTH_ERR; + } + free(resp->resp); + free(resp); + return ret; +} + +int +pam_acct_mgmt(pam_handle_t *pamh, int flags) +{ + if (pamh == NULL) { + return PAM_SYSTEM_ERR; + } + return pamh->acct; +} -- cgit