diff options
author | Jakub Hrozek <jakub.hrozek@posteo.se> | 2015-09-23 16:23:58 +0200 |
---|---|---|
committer | Jakub Hrozek <jhrozek@redhat.com> | 2015-09-29 14:31:33 +0200 |
commit | cb2026e900b8892d3e2a45a91755461647f5b23d (patch) | |
tree | c7c36413643eb8671bff5d888b02d66317dc41a8 | |
parent | 709fbfdef5eb126b6cc8db28a4ef5ccd3392d9e0 (diff) | |
download | pam_wrapper-cb2026e900b8892d3e2a45a91755461647f5b23d.tar.gz pam_wrapper-cb2026e900b8892d3e2a45a91755461647f5b23d.tar.xz pam_wrapper-cb2026e900b8892d3e2a45a91755461647f5b23d.zip |
pwrap: Wrap pam_chauthtok
-rw-r--r-- | src/pam_wrapper.c | 21 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 6 | ||||
-rw-r--r-- | tests/pam_example.c | 282 | ||||
-rw-r--r-- | tests/passdb | 3 | ||||
-rw-r--r-- | tests/test_pam_wrapper.c | 199 |
5 files changed, 452 insertions, 59 deletions
diff --git a/src/pam_wrapper.c b/src/pam_wrapper.c index 5eddcdc..996ec56 100644 --- a/src/pam_wrapper.c +++ b/src/pam_wrapper.c @@ -179,6 +179,8 @@ 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); @@ -201,6 +203,7 @@ 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); @@ -338,6 +341,13 @@ static int libpam_pam_authenticate(pam_handle_t *pamh, int flags) 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); @@ -728,6 +738,17 @@ 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 called"); + 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 called"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6a690ac..c70f5c1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,14 +10,12 @@ configure_file(services/pwrap_pam.in ${CMAKE_CURRENT_BINARY_DIR}/services/pwrap_ set(PASSDB_PATH ${CMAKE_CURRENT_BINARY_DIR}/passdb) -configure_file(passdb ${PASSDB_PATH} @ONLY) - if (OSX) - set(TEST_ENVIRONMENT DYLD_FORCE_FLAT_NAMESPACE=1;DYLD_INSERT_LIBRARIES=${PAM_WRAPPER_LOCATION};PAM_WRAPPER=1;PAM_WRAPPER_CONFDIR=${CMAKE_CURRENT_BINARY_DIR}) + set(TEST_ENVIRONMENT DYLD_FORCE_FLAT_NAMESPACE=1;DYLD_INSERT_LIBRARIES=${PAM_WRAPPER_LOCATION};PAM_WRAPPER=1;PAM_WRAPPER_CONFDIR=${CMAKE_CURRENT_BINARY_DIR}/services;PWRAP_PASSDB=${PASSDB_PATH}) add_definitions(-DOSX) else () - set(TEST_ENVIRONMENT LD_PRELOAD=${PAM_WRAPPER_LOCATION};PAM_WRAPPER=1;PAM_WRAPPER_CONFDIR=${CMAKE_CURRENT_BINARY_DIR};PWRAP_PASSDB=${PASSDB_PATH}) + set(TEST_ENVIRONMENT LD_PRELOAD=${PAM_WRAPPER_LOCATION};PAM_WRAPPER=1;PAM_WRAPPER_CONFDIR=${CMAKE_CURRENT_BINARY_DIR}/services;PWRAP_PASSDB=${PASSDB_PATH}) endif () add_library(pam_example MODULE pam_example.c) diff --git a/tests/pam_example.c b/tests/pam_example.c index 5a6614a..1510453 100644 --- a/tests/pam_example.c +++ b/tests/pam_example.c @@ -30,6 +30,17 @@ } \ } while(0); + +#define wipe_authtok(tok) do { \ + if (tok != NULL) { \ + char *__tk = tok; \ + while(*__tk != '\0') { \ + *__tk = '\0'; \ + } \ + tok = NULL; \ + } \ +} while(0); + struct pam_lib_items { const char *username; const char *service; @@ -46,7 +57,6 @@ struct pam_example_ctx { struct pam_example_mod_items pmi; }; - static int pam_example_mod_items_get(const char *username, struct pam_example_mod_items *pmi) { @@ -60,7 +70,7 @@ static int pam_example_mod_items_get(const char *username, db = getenv("PWRAP_PASSDB"); if (db == NULL) { - rv = EIO; + rv = PAM_AUTHINFO_UNAVAIL; goto fail; } @@ -112,6 +122,7 @@ static int pam_example_mod_items_get(const char *username, } } + fclose(fp); return 0; fail: @@ -123,6 +134,107 @@ fail: return rv; } +static int pam_example_lib_items_put(struct pam_lib_items *pli) +{ + int rv; + const char *db; + FILE *fp = NULL; + FILE *fp_tmp = NULL; + char buf[BUFSIZ]; + char template[PATH_MAX] = { '\0' }; + char *file_user = NULL; + char *file_password = NULL; + char *file_svc = NULL; + + db = getenv("PWRAP_PASSDB"); + if (db == NULL) { + rv = PAM_AUTHINFO_UNAVAIL; + goto done; + } + + rv = snprintf(template, sizeof(template), + "%s.XXXXXX", db); + if (rv <= 0) { + rv = PAM_BUF_ERR; + goto done; + } + + /* We don't support concurrent runs.. */ + rv = mkstemp(template); + if (rv <= 0) { + rv = PAM_BUF_ERR; + goto done; + } + + fp = fopen(db, "r"); + fp_tmp = fopen(template, "w"); + if (fp == NULL || fp_tmp == NULL) { + rv = errno; + goto done; + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + char *q; + + file_user = buf; + file_password = NULL; + + /* Skip comments */ + if (file_user[0] == '#') { + continue; + } + + /* Find the user, his password and allowed service */ + NEXT_KEY(file_user, file_password); + NEXT_KEY(file_password, file_svc); + + q = file_svc; + while(q[0] != '\n' && q[0] != '\0') { + q++; + } + q[0] = '\0'; + + if (file_password == NULL) { + continue; + } + + if (strcmp(file_user, pli->username) == 0) { + if (pli->password) { + file_password = pli->password; + } + } + + rv = fprintf(fp_tmp, "%s\t%s\t%s\n", + file_user, file_password, file_svc); + if (rv < 0) { + rv = PAM_CRED_ERR; + goto done; + } + } + + rv = PAM_SUCCESS; +done: + if (fp != NULL) { + fclose(fp); + } + if (fp_tmp != NULL) { + fflush(fp_tmp); + fclose(fp_tmp); + } + + if (rv == PAM_SUCCESS) { + rv = rename(template, db); + if (rv == -1) { + rv = PAM_SYSTEM_ERR; + } + } + + if (template[0] != '\0') { + unlink(template); + }; + return rv; +} + static void pam_example_mod_items_free(struct pam_example_mod_items *pmi) { if (pmi == NULL) { @@ -133,6 +245,70 @@ static void pam_example_mod_items_free(struct pam_example_mod_items *pmi) free(pmi->service); } +static int pam_example_read_password(pam_handle_t *pamh, + int authtok_item, + const char *prompt1, + const char *prompt2, + const void **_out_tok) +{ + int rv = PAM_AUTHTOK_RECOVERY_ERR; + char *authtok1 = NULL; + char *authtok2 = NULL; + const void *item; + + rv = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, + &authtok1, "%s", prompt1); + if (authtok1 == NULL) { + goto done; + } + + if (rv == PAM_SUCCESS && prompt2 != NULL) { + rv = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, + &authtok2, "%s", prompt2); + if (rv != PAM_SUCCESS) { + goto done; + } + + if (authtok2 == NULL) { + rv = PAM_AUTHTOK_RECOVERY_ERR; + goto done; + } + + if (strcmp(authtok1, authtok2) != 0) { + pam_prompt(pamh, PAM_ERROR_MSG, NULL, + "%s", "Passwords do not match"); + rv = PAM_AUTHTOK_RECOVERY_ERR; + goto done; + } + wipe_authtok(authtok2); + } + + if (rv != PAM_SUCCESS) { + goto done; + } + + rv = pam_set_item(pamh, authtok_item, authtok1); + wipe_authtok(authtok1); + if (rv != PAM_SUCCESS) { + goto done; + } + + rv = pam_get_item(pamh, authtok_item, &item); + if (_out_tok) { + *_out_tok = item; + } + item = NULL; + if (rv != PAM_SUCCESS) { + goto done; + } + + rv = PAM_SUCCESS; +done: + wipe_authtok(authtok1); + wipe_authtok(authtok2); + return rv; +} + static int pam_lib_items_get(pam_handle_t *pamh, struct pam_lib_items *pli) { @@ -152,16 +328,6 @@ static int pam_lib_items_get(pam_handle_t *pamh, return rv; } - rv = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, - &pli->password, "%s", "Password"); - if (rv != PAM_SUCCESS) { - return rv; - } - - if (pli->password == NULL) { - return PAM_AUTHINFO_UNAVAIL; - } - return PAM_SUCCESS; } @@ -171,12 +337,12 @@ static int pam_example_get(pam_handle_t *pamh, struct pam_example_ctx *pe_ctx) rv = pam_lib_items_get(pamh, &pe_ctx->pli); if (rv != PAM_SUCCESS) { - return rv; + return rv; } rv = pam_example_mod_items_get(pe_ctx->pli.username, &pe_ctx->pmi); if (rv != PAM_SUCCESS) { - return rv; + return rv; } return PAM_SUCCESS; @@ -187,6 +353,31 @@ static void pam_example_free(struct pam_example_ctx *pe_ctx) pam_example_mod_items_free(&pe_ctx->pmi); } +static int _pam_example_auth(struct pam_example_ctx *pctx) +{ + int rv = PAM_AUTH_ERR; + + if (pctx->pli.password != NULL && + pctx->pmi.password != NULL && + strcmp(pctx->pli.password, pctx->pmi.password) == 0) { + rv = PAM_SUCCESS; + } + + return rv; +} + +static int pam_example_auth(struct pam_example_ctx *pctx) +{ + int rv = PAM_AUTH_ERR; + + rv = _pam_example_auth(pctx); + + wipe_authtok(pctx->pli.password); + wipe_authtok(pctx->pmi.password); + + return rv; +} + PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char *argv[]) @@ -205,12 +396,13 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, goto done; } - if (strcmp(pctx.pli.password, pctx.pmi.password) == 0) { - rv = PAM_SUCCESS; - goto done; + rv = pam_example_read_password(pamh, PAM_AUTHTOK, "Password: ", NULL, + (const void **) &pctx.pli.password); + if (rv != PAM_SUCCESS) { + return PAM_AUTHINFO_UNAVAIL; } - rv = PAM_AUTH_ERR; + rv = pam_example_auth(&pctx); done: pam_example_free(&pctx); return rv; @@ -246,7 +438,9 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, goto done; } - if (strcmp(pctx.pli.service, pctx.pmi.service) == 0) { + if (pctx.pli.service != NULL && + pctx.pmi.service != NULL && + strcmp(pctx.pli.service, pctx.pmi.service) == 0) { rv = PAM_SUCCESS; goto done; } @@ -328,10 +522,56 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { - (void) pamh; /* unused */ + struct pam_example_ctx pctx; + const char *old_pass; + int rv; + (void) flags; /* unused */ (void) argc; /* unused */ (void) argv; /* unused */ - return PAM_SERVICE_ERR; + memset(&pctx, 0, sizeof(struct pam_example_ctx)); + + rv = pam_example_get(pamh, &pctx); + if (rv != PAM_SUCCESS) { + goto done; + } + + if (flags & PAM_PRELIM_CHECK) { + rv = pam_example_read_password( + pamh, PAM_OLDAUTHTOK, + "Old password: ", NULL, + (const void **) &pctx.pli.password); + if (rv != PAM_SUCCESS) { + rv = PAM_AUTHINFO_UNAVAIL; + goto done; + } + + rv = pam_example_auth(&pctx); + } else if (flags & PAM_UPDATE_AUTHTOK) { + rv = pam_get_item(pamh, + PAM_OLDAUTHTOK, + (const void **) &old_pass); + if (rv != PAM_SUCCESS || old_pass == NULL) { + rv = PAM_AUTHINFO_UNAVAIL; + goto done; + } + + rv = pam_example_read_password(pamh, PAM_AUTHTOK, + "New Password :", + "Verify New Password :", + (const void **) &pctx.pli.password); + if (rv != PAM_SUCCESS) { + rv = PAM_AUTHINFO_UNAVAIL; + goto done; + } + + rv = pam_example_lib_items_put(&pctx.pli); + } else { + rv = PAM_SYSTEM_ERR; + } + +done: + pam_example_free(&pctx); + return rv; } diff --git a/tests/passdb b/tests/passdb deleted file mode 100644 index db7d3c9..0000000 --- a/tests/passdb +++ /dev/null @@ -1,3 +0,0 @@ -# The format is username password -testuser secret pwrap_pam -testuser2 secret pwrap_wrong_svc diff --git a/tests/test_pam_wrapper.c b/tests/test_pam_wrapper.c index 08c64e5..a37c312 100644 --- a/tests/test_pam_wrapper.c +++ b/tests/test_pam_wrapper.c @@ -7,6 +7,9 @@ #include <stdlib.h> #include <string.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> #include <security/pam_appl.h> struct pwrap_test_ctx { @@ -14,6 +17,11 @@ struct pwrap_test_ctx { pam_handle_t *ph; }; +struct pwrap_conv_data { + const char **authtoks; + size_t authtok_index; +}; + static int pwrap_conv(int num_msg, const struct pam_message **msgm, struct pam_response **response, void *appdata_ptr) @@ -22,19 +30,29 @@ static int pwrap_conv(int num_msg, const struct pam_message **msgm, struct pam_response *reply; const char *password; size_t pwlen; + struct pwrap_conv_data *cdata = (struct pwrap_conv_data *) appdata_ptr; + + if (cdata == NULL) { + return PAM_CONV_ERR; + } reply = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response)); if (reply == NULL) { return PAM_CONV_ERR; - } for (i=0; i < num_msg; i++) { switch (msgm[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: - password = (const char *) appdata_ptr; + password = (const char *) cdata->authtoks[cdata->authtok_index]; + if (password == NULL) { + return PAM_CONV_ERR; + } + pwlen = strlen(password) + 1; + cdata->authtok_index++; + reply[i].resp = calloc(pwlen, sizeof(char)); if (reply[i].resp == NULL) { free(reply); @@ -51,11 +69,40 @@ static int pwrap_conv(int num_msg, const struct pam_message **msgm, return PAM_SUCCESS; } -static int setup_simple(void **state) +static void setup_passdb(void) +{ + const char *db; + FILE *fp = NULL; + + db = getenv("PWRAP_PASSDB"); + assert_non_null(db); + + fp = fopen(db, "w"); + assert_non_null(fp); + + fprintf(fp, "testuser\tsecret\tpwrap_pam\n"); + fprintf(fp, "testuser2\tsecret\tpwrap_wrong_svc"); + + fflush(fp); + fclose(fp); +} + +static void teardown_passdb(void) +{ + const char *db; + + db = getenv("PWRAP_PASSDB"); + assert_non_null(db); + unlink(db); +} + +static int setup_ctx_only(void **state) { struct pwrap_test_ctx *test_ctx; - test_ctx = malloc(sizeof(struct pwrap_test_ctx)); + setup_passdb(); + + test_ctx = malloc(sizeof(struct pwrap_test_ctx)); assert_non_null(test_ctx); test_ctx->conv.conv = pwrap_conv; @@ -64,6 +111,25 @@ static int setup_simple(void **state) return 0; } +static int setup_noconv(void **state) +{ + struct pwrap_test_ctx *test_ctx; + int rv; + + setup_ctx_only(state); + test_ctx = *state; + + /* We'll get an error if the test module talks to us */ + test_ctx->conv.appdata_ptr = NULL; + + rv = pam_start("pwrap_pam", "testuser", + &test_ctx->conv, &test_ctx->ph); + assert_int_equal(rv, PAM_SUCCESS); + + *state = test_ctx; + return 0; +} + static int teardown_simple(void **state) { struct pwrap_test_ctx *test_ctx; @@ -89,30 +155,13 @@ static void test_pam_start(void **state) assert_int_equal(rv, PAM_SUCCESS); } -static int setup(void **state) -{ - struct pwrap_test_ctx *test_ctx; - int rv; - - rv = setup_simple((void **) &test_ctx); - if (rv != 0) { - return rv; - } - - test_ctx->conv.appdata_ptr = (void *) "secret"; - rv = pam_start("pwrap_pam", "testuser", - &test_ctx->conv, &test_ctx->ph); - assert_int_equal(rv, PAM_SUCCESS); - - *state = test_ctx; - return 0; -} - static int teardown(void **state) { struct pwrap_test_ctx *test_ctx; int rv; + teardown_passdb(); + test_ctx = (struct pwrap_test_ctx *) *state; rv = pam_end(test_ctx->ph, PAM_SUCCESS); @@ -126,8 +175,22 @@ static void test_pam_authenticate(void **state) int rv; struct pwrap_test_ctx *test_ctx; + const char *testuser_authtoks[] = { + "secret", + NULL, + }; + struct pwrap_conv_data testuser_auth_conv_data = { + .authtoks = testuser_authtoks, + .authtok_index = 0, + }; + test_ctx = (struct pwrap_test_ctx *) *state; + test_ctx->conv.appdata_ptr = (void *) &testuser_auth_conv_data; + rv = pam_start("pwrap_pam", "testuser", + &test_ctx->conv, &test_ctx->ph); + assert_int_equal(rv, PAM_SUCCESS); + rv = pam_authenticate(test_ctx->ph, 0); assert_int_equal(rv, PAM_SUCCESS); } @@ -137,9 +200,19 @@ static void test_pam_authenticate_err(void **state) int rv; struct pwrap_test_ctx *test_ctx; + const char *testuser_authtoks[] = { + "wrong_password", + NULL, + }; + struct pwrap_conv_data testuser_auth_err_conv_data = { + .authtoks = testuser_authtoks, + .authtok_index = 0, + }; + + test_ctx = (struct pwrap_test_ctx *) *state; - test_ctx->conv.appdata_ptr = (void *) "wrong_password"; + test_ctx->conv.appdata_ptr = (void *) &testuser_auth_err_conv_data; rv = pam_start("pwrap_pam", "testuser", &test_ctx->conv, &test_ctx->ph); assert_int_equal(rv, PAM_SUCCESS); @@ -255,30 +328,94 @@ static void test_pam_session(void **state) assert_string_equal(v, "/home/testuser"); } +static void test_pam_chauthtok(void **state) +{ + int rv; + struct pwrap_test_ctx *test_ctx; + + const char *testuser_new_authtoks[] = { + "secret", + "new_secret", + "new_secret", + NULL, + }; + struct pwrap_conv_data testuser_chpass_conv_data = { + .authtoks = testuser_new_authtoks, + .authtok_index = 0, + }; + + test_ctx = (struct pwrap_test_ctx *) *state; + + test_ctx->conv.appdata_ptr = (void *) &testuser_chpass_conv_data; + rv = pam_start("pwrap_pam", "testuser", + &test_ctx->conv, &test_ctx->ph); + assert_int_equal(rv, PAM_SUCCESS); + + rv = pam_chauthtok(test_ctx->ph, 0); + assert_int_equal(rv, PAM_SUCCESS); + + testuser_chpass_conv_data.authtok_index = 1; + rv = pam_authenticate(test_ctx->ph, 0); + assert_int_equal(rv, PAM_SUCCESS); +} + +static void test_pam_chauthtok_prelim_failed(void **state) +{ + int rv; + struct pwrap_test_ctx *test_ctx; + + const char *testuser_new_authtoks[] = { + "wrong_secret", + "new_secret", + "new_secret", + NULL, + }; + struct pwrap_conv_data testuser_chpass_conv_data = { + .authtoks = testuser_new_authtoks, + .authtok_index = 0, + }; + + test_ctx = (struct pwrap_test_ctx *) *state; + + test_ctx->conv.appdata_ptr = (void *) &testuser_chpass_conv_data; + rv = pam_start("pwrap_pam", "testuser", + &test_ctx->conv, &test_ctx->ph); + assert_int_equal(rv, PAM_SUCCESS); + + rv = pam_chauthtok(test_ctx->ph, 0); + assert_int_equal(rv, PAM_AUTH_ERR); +} + int main(void) { int rc; const struct CMUnitTest init_tests[] = { cmocka_unit_test_setup_teardown(test_pam_start, - setup_simple, + setup_noconv, teardown_simple), cmocka_unit_test_setup_teardown(test_pam_authenticate, - setup, + setup_ctx_only, teardown), cmocka_unit_test_setup_teardown(test_pam_authenticate_err, - setup_simple, + setup_noconv, teardown), cmocka_unit_test_setup_teardown(test_pam_acct, - setup, + setup_noconv, teardown), cmocka_unit_test_setup_teardown(test_pam_acct_err, - setup_simple, + setup_noconv, teardown), cmocka_unit_test_setup_teardown(test_pam_env_functions, - setup, + setup_noconv, teardown), cmocka_unit_test_setup_teardown(test_pam_session, - setup, + setup_noconv, + teardown), + cmocka_unit_test_setup_teardown(test_pam_chauthtok, + setup_ctx_only, + teardown), + cmocka_unit_test_setup_teardown(test_pam_chauthtok_prelim_failed, + setup_ctx_only, teardown), }; |