diff --git a/0100-ad_opts-Use-different-default-attribute-for-group-na.patch b/0100-ad_opts-Use-different-default-attribute-for-group-na.patch new file mode 100644 index 0000000..8c92078 --- /dev/null +++ b/0100-ad_opts-Use-different-default-attribute-for-group-na.patch @@ -0,0 +1,40 @@ +From d471a746673208821a7967eed9ee47e4d009a6f4 Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Tue, 7 Apr 2015 09:47:17 +0200 +Subject: [PATCH 100/114] ad_opts: Use different default attribute for group + name + +The MSFT docs [1,2] for LDAP attributes says: +samAccountName is mandotory for 'user' and 'group' objectclasses +via the 'Security-Principal' aux-class + +name is part of the 'top' class and *not* mandatory for 'user' or 'group'. + +[1] https://msdn.microsoft.com/en-us/library/ms679635%28v=vs.85%29.aspx +[2] https://msdn.microsoft.com/en-us/library/ms678697%28v=vs.85%29.aspx + +Resolves: +https://fedorahosted.org/sssd/ticket/2593 + +Reviewed-by: Sumit Bose +(cherry picked from commit b83620d6a2aaf988b353969ae12a47a616250f47) +--- + src/providers/ad/ad_opts.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/providers/ad/ad_opts.h b/src/providers/ad/ad_opts.h +index f4c1c523bdc57a824105dfd781eb90a88e068908..0b7255a828e95785d31437968a37bc20fbf62aef 100644 +--- a/src/providers/ad/ad_opts.h ++++ b/src/providers/ad/ad_opts.h +@@ -220,7 +220,7 @@ struct sdap_attr_map ad_2008r2_user_map[] = { + struct sdap_attr_map ad_2008r2_group_map[] = { + { "ldap_group_object_class", "group", SYSDB_GROUP_CLASS, NULL }, + { "ldap_group_object_class_alt", NULL, SYSDB_GROUP_CLASS, NULL }, +- { "ldap_group_name", "name", SYSDB_NAME, NULL }, ++ { "ldap_group_name", "sAMAccountName", SYSDB_NAME, NULL }, + { "ldap_group_pwd", NULL, SYSDB_PWD, NULL }, + { "ldap_group_gid_number", "gidNumber", SYSDB_GIDNUM, NULL }, + { "ldap_group_member", "member", SYSDB_MEMBER, NULL }, +-- +2.4.0 + diff --git a/0101-Add-leak-check-and-command-line-option-to-test_autht.patch b/0101-Add-leak-check-and-command-line-option-to-test_autht.patch new file mode 100644 index 0000000..74ad2f7 --- /dev/null +++ b/0101-Add-leak-check-and-command-line-option-to-test_autht.patch @@ -0,0 +1,208 @@ +From c1be24c9c4050f3c9efa8478867f3e6957919a9d Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 12 Feb 2015 21:53:15 +0100 +Subject: [PATCH 101/114] Add leak check and command line option to + test_authtok +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 80b5dbe123ec94c5a8fcb99f9a4953c1513deb58) +--- + Makefile.am | 3 ++ + src/tests/cmocka/test_authtok.c | 67 +++++++++++++++++++++++++++++++++++------ + 2 files changed, 60 insertions(+), 10 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 99729ff6041a29dc79de7f90511d60420af8fd19..fc369f6bdae5d414fad1cc6abd3a514ce931ecec 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1856,11 +1856,14 @@ test_authtok_SOURCES = \ + test_authtok_CFLAGS = \ + $(AM_CFLAGS) \ + $(TALLOC_CFLAGS) \ ++ $(POPT_CFLAGS) \ + $(DHASH_CFLAGS) + test_authtok_LDADD = \ + $(TALLOC_LIBS) \ + $(CMOCKA_LIBS) \ + $(DHASH_LIBS) \ ++ $(POPT_LIBS) \ ++ libsss_test_common.la \ + libsss_debug.la + + sss_nss_idmap_tests_SOURCES = \ +diff --git a/src/tests/cmocka/test_authtok.c b/src/tests/cmocka/test_authtok.c +index e37e92f68373d564f53b1267f078ea89c31ae051..0c7b7197fb2c03d69dc4df2310229ea100ad28d4 100644 +--- a/src/tests/cmocka/test_authtok.c ++++ b/src/tests/cmocka/test_authtok.c +@@ -22,11 +22,10 @@ + along with this program. If not, see . + */ + +-#include +-#include +-#include +-#include + #include ++#include ++ ++#include "tests/cmocka/common_mock.h" + + #include "util/authtok.h" + +@@ -39,12 +38,15 @@ static int setup(void **state) + { + struct test_state *ts = NULL; + +- ts = talloc(NULL, struct test_state); ++ assert_true(leak_check_setup()); ++ ++ ts = talloc(global_talloc_context, struct test_state); + assert_non_null(ts); + + ts->authtoken = sss_authtok_new(ts); + assert_non_null(ts->authtoken); + ++ check_leaks_push(ts); + *state = (void *)ts; + return 0; + } +@@ -52,7 +54,12 @@ static int setup(void **state) + static int teardown(void **state) + { + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); ++ ++ assert_non_null(ts); ++ ++ assert_true(check_leaks_pop(ts) == true); + talloc_free(ts); ++ assert_true(leak_check_teardown()); + return 0; + } + +@@ -85,7 +92,7 @@ static void test_sss_authtok_password(void **state) + { + size_t len; + errno_t ret; +- const char *data; ++ char *data; + size_t ret_len; + const char *pwd; + struct test_state *ts; +@@ -117,6 +124,9 @@ static void test_sss_authtok_password(void **state) + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); ++ ++ talloc_free(data); ++ sss_authtok_set_empty(ts->authtoken); + } + + /* Test when type has value SSS_AUTHTOK_TYPE_CCFILE */ +@@ -124,7 +134,7 @@ static void test_sss_authtok_ccfile(void **state) + { + size_t len; + errno_t ret; +- const char *data; ++ char *data; + size_t ret_len; + const char *pwd; + struct test_state *ts; +@@ -172,6 +182,9 @@ static void test_sss_authtok_ccfile(void **state) + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); + assert_int_equal(len - 1, ret_len); ++ ++ talloc_free(data); ++ sss_authtok_set_empty(ts->authtoken); + } + + /* Test when type has value SSS_AUTHTOK_TYPE_EMPTY */ +@@ -226,7 +239,7 @@ static void test_sss_authtok_wipe_password(void **state) + { + size_t len; + errno_t ret; +- const char *data; ++ char *data; + size_t ret_len; + const char *pwd; + struct test_state *ts; +@@ -249,13 +262,16 @@ static void test_sss_authtok_wipe_password(void **state) + assert_int_equal(ret, EOK); + assert_string_equal(pwd, ""); + assert_int_equal(len - 1, ret_len); ++ ++ sss_authtok_set_empty(ts->authtoken); ++ talloc_free(data); + } + + static void test_sss_authtok_copy(void **state) + { + size_t len; + errno_t ret; +- const char *data; ++ char *data; + struct test_state *ts; + enum sss_authtok_type type; + struct sss_auth_token *dest_authtoken; +@@ -276,6 +292,7 @@ static void test_sss_authtok_copy(void **state) + assert_int_equal(EOK, sss_authtok_copy(ts->authtoken, dest_authtoken)); + assert_int_equal(type, sss_authtok_get_type(dest_authtoken)); + ++ sss_authtok_set_empty(dest_authtoken); + type = SSS_AUTHTOK_TYPE_PASSWORD; + ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *)data, len); + +@@ -287,10 +304,23 @@ static void test_sss_authtok_copy(void **state) + assert_int_equal(type, sss_authtok_get_type(dest_authtoken)); + assert_string_equal(data, sss_authtok_get_data(dest_authtoken)); + assert_int_equal(len, sss_authtok_get_size(dest_authtoken)); ++ ++ sss_authtok_set_empty(dest_authtoken); ++ talloc_free(dest_authtoken); ++ sss_authtok_set_empty(ts->authtoken); ++ talloc_free(data); + } + +-int main(void) ++int main(int argc, const char *argv[]) + { ++ poptContext pc; ++ int opt; ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ SSSD_DEBUG_OPTS ++ POPT_TABLEEND ++ }; ++ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_sss_authtok_new, + setup, teardown), +@@ -306,5 +336,22 @@ int main(void) + setup, teardown) + }; + ++ /* Set debug level to invalid value so we can deside if -d 0 was used. */ ++ debug_level = SSSDBG_INVALID; ++ ++ pc = poptGetContext(argv[0], argc, argv, long_options, 0); ++ while((opt = poptGetNextOpt(pc)) != -1) { ++ switch(opt) { ++ default: ++ fprintf(stderr, "\nInvalid option %s: %s\n\n", ++ poptBadOption(pc, 0), poptStrerror(opt)); ++ poptPrintUsage(pc, stderr, 0); ++ return 1; ++ } ++ } ++ poptFreeContext(pc); ++ ++ DEBUG_CLI_INIT(debug_level); ++ + return cmocka_run_group_tests(tests, NULL, NULL); + } +-- +2.4.0 + diff --git a/0102-utils-add-sss_authtok_-gs-et_2fa.patch b/0102-utils-add-sss_authtok_-gs-et_2fa.patch new file mode 100644 index 0000000..a4cdc9c --- /dev/null +++ b/0102-utils-add-sss_authtok_-gs-et_2fa.patch @@ -0,0 +1,765 @@ +From 3e3445a31a97d0e680ec436e4d627b46fcebc04e Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 7 Jan 2015 18:11:16 +0100 +Subject: [PATCH 102/114] utils: add sss_authtok_[gs]et_2fa +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit bc052ea17d858c19f9cb9c9e2bc602e754f68831) +--- + Makefile.am | 5 ++ + src/sss_client/pam_sss.c | 1 + + src/sss_client/sss_cli.h | 3 + + src/tests/cmocka/test_authtok.c | 189 +++++++++++++++++++++++++++++++++++++++- + src/util/authtok-utils.c | 74 ++++++++++++++++ + src/util/authtok-utils.h | 70 +++++++++++++++ + src/util/authtok.c | 181 ++++++++++++++++++++++++++++++++++++++ + src/util/authtok.h | 44 ++++++++++ + 8 files changed, 564 insertions(+), 3 deletions(-) + create mode 100644 src/util/authtok-utils.c + create mode 100644 src/util/authtok-utils.h + +diff --git a/Makefile.am b/Makefile.am +index fc369f6bdae5d414fad1cc6abd3a514ce931ecec..3bc37a3984a5fa0471a1f3247bda9b869fc823e5 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -519,6 +519,7 @@ dist_noinst_HEADERS = \ + src/util/atomic_io.h \ + src/util/auth_utils.h \ + src/util/authtok.h \ ++ src/util/authtok-utils.h \ + src/util/util_safealign.h \ + src/util/util_sss_idmap.h \ + src/monitor/monitor.h \ +@@ -752,6 +753,7 @@ libsss_util_la_SOURCES = \ + src/util/murmurhash3.c \ + src/util/atomic_io.c \ + src/util/authtok.c \ ++ src/util/authtok-utils.c \ + src/util/sss_selinux.c \ + src/util/domain_info_utils.c \ + src/util/util_lock.c \ +@@ -1852,6 +1854,7 @@ test_negcache_LDADD = \ + test_authtok_SOURCES = \ + src/tests/cmocka/test_authtok.c \ + src/util/authtok.c \ ++ src/util/authtok-utils.c \ + src/util/util.c + test_authtok_CFLAGS = \ + $(AM_CFLAGS) \ +@@ -2703,6 +2706,7 @@ krb5_child_SOURCES = \ + src/util/find_uid.c \ + src/util/atomic_io.c \ + src/util/authtok.c \ ++ src/util/authtok-utils.c \ + src/util/util.c \ + src/util/signal.c \ + src/util/strtonum.c \ +@@ -2734,6 +2738,7 @@ ldap_child_SOURCES = \ + src/util/sss_krb5.c \ + src/util/atomic_io.c \ + src/util/authtok.c \ ++ src/util/authtok-utils.c \ + src/util/util.c \ + src/util/signal.c \ + src/util/become_user.c \ +diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c +index 28a36d5af95297b394a74f39d6614f48831bb901..4007d125e34932dfb5ac6bc840f4d25306e3008f 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -41,6 +41,7 @@ + + #include "sss_cli.h" + #include "util/atomic_io.h" ++#include "util/authtok-utils.h" + + #include + #define _(STRING) dgettext (PACKAGE, STRING) +diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h +index d508a0671cd1b3ee087e0967f7015628ceabe20f..9a19d7d47d0a9d7dabeac36dc2c866c3420ef501 100644 +--- a/src/sss_client/sss_cli.h ++++ b/src/sss_client/sss_cli.h +@@ -301,6 +301,9 @@ enum sss_authtok_type { + * a Kerberos credential cache file, + * it may or may no contain + * a trailing \\0 */ ++ SSS_AUTHTOK_TYPE_2FA = 0x0003, /**< Authentication token has two ++ * factors, they may or may no contain ++ * a trailing \\0 */ + }; + + /** +diff --git a/src/tests/cmocka/test_authtok.c b/src/tests/cmocka/test_authtok.c +index 0c7b7197fb2c03d69dc4df2310229ea100ad28d4..5aa47c7b6b8c955666a9c73d5f9627d6378d13e0 100644 +--- a/src/tests/cmocka/test_authtok.c ++++ b/src/tests/cmocka/test_authtok.c +@@ -57,7 +57,7 @@ static int teardown(void **state) + + assert_non_null(ts); + +- assert_true(check_leaks_pop(ts) == true); ++ assert_true(check_leaks_pop(ts)); + talloc_free(ts); + assert_true(leak_check_teardown()); + return 0; +@@ -118,8 +118,8 @@ static void test_sss_authtok_password(void **state) + assert_int_equal(len - 1, ret_len); + + ret = sss_authtok_set_password(ts->authtoken, data, len); +- + assert_int_equal(ret, EOK); ++ + ret = sss_authtok_get_password(ts->authtoken, &pwd, &ret_len); + assert_int_equal(ret, EOK); + assert_string_equal(data, pwd); +@@ -311,6 +311,183 @@ static void test_sss_authtok_copy(void **state) + talloc_free(data); + } + ++void test_sss_authtok_2fa(void **state) ++{ ++ int ret; ++ const char *fa1; ++ size_t fa1_size; ++ const char *fa2; ++ size_t fa2_size; ++ struct test_state *ts; ++ ++ ts = talloc_get_type_abort(*state, struct test_state); ++ ++ ret = sss_authtok_set_2fa(NULL, "a", 0, "b", 0); ++ assert_int_equal(ret, EINVAL); ++ ++ /* Test missing first factor */ ++ ret = sss_authtok_set_2fa(ts->authtoken, NULL, 1, "b", 1); ++ assert_int_equal(ret, EINVAL); ++ /* Test missing second factor */ ++ ret = sss_authtok_set_2fa(ts->authtoken, "a", 1, NULL, 1); ++ assert_int_equal(ret, EINVAL); ++ /* Test wrong first factor length */ ++ ret = sss_authtok_set_2fa(ts->authtoken, "ab", 1, "b", 1); ++ assert_int_equal(ret, EINVAL); ++ /* Test wrong second factor length */ ++ ret = sss_authtok_set_2fa(ts->authtoken, "a", 1, "bc", 1); ++ assert_int_equal(ret, EINVAL); ++ ++ ret = sss_authtok_set_2fa(ts->authtoken, "a", 1, "bc", 2); ++ assert_int_equal(ret, EOK); ++ assert_int_equal(sss_authtok_get_size(ts->authtoken), ++ 2 * sizeof(uint32_t) + 5); ++ assert_int_equal(sss_authtok_get_type(ts->authtoken), SSS_AUTHTOK_TYPE_2FA); ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++ assert_memory_equal(sss_authtok_get_data(ts->authtoken), ++ "\2\0\0\0\3\0\0\0a\0bc\0", ++ 2 * sizeof(uint32_t) + 5); ++#else ++ assert_memory_equal(sss_authtok_get_data(ts->authtoken), ++ "\0\0\0\2\0\0\0\3a\0bc\0", ++ 2 * sizeof(uint32_t) + 5); ++#endif ++ ++ ret = sss_authtok_get_2fa(ts->authtoken, &fa1, &fa1_size, &fa2, &fa2_size); ++ assert_int_equal(ret, EOK); ++ assert_int_equal(fa1_size, 1); ++ assert_string_equal(fa1, "a"); ++ assert_int_equal(fa2_size, 2); ++ assert_string_equal(fa2, "bc"); ++ ++ sss_authtok_set_empty(ts->authtoken); ++ ++ /* check return code of empty token */ ++ ret = sss_authtok_get_2fa(ts->authtoken, &fa1, &fa1_size, &fa2, &fa2_size); ++ assert_int_equal(ret, ENOENT); ++ ++ /* check return code for other token type */ ++ ret = sss_authtok_set_password(ts->authtoken, "abc", 0); ++ assert_int_equal(ret, EOK); ++ ++ ret = sss_authtok_get_2fa(ts->authtoken, &fa1, &fa1_size, &fa2, &fa2_size); ++ assert_int_equal(ret, EACCES); ++ ++ sss_authtok_set_empty(ts->authtoken); ++ ++ /* check return code for garbage */ ++ ret = sss_authtok_set(ts->authtoken, SSS_AUTHTOK_TYPE_2FA, ++ (const uint8_t *) "1111222233334444", 16); ++ assert_int_equal(ret, EINVAL); ++ ++ sss_authtok_set_empty(ts->authtoken); ++} ++ ++void test_sss_authtok_2fa_blobs(void **state) ++{ ++ int ret; ++ struct test_state *ts; ++ size_t needed_size; ++ uint8_t *buf; ++ char *fa1; ++ size_t fa1_len; ++ char *fa2; ++ size_t fa2_len; ++ ++ ts = talloc_get_type_abort(*state, struct test_state); ++ ++ ret = sss_auth_pack_2fa_blob(NULL, 0, "defg", 0, NULL, 0, &needed_size); ++ assert_int_equal(ret, EINVAL); ++ ++ ret = sss_auth_pack_2fa_blob("abc", 0, NULL, 0, NULL, 0, &needed_size); ++ assert_int_equal(ret, EINVAL); ++ ++ ret = sss_auth_pack_2fa_blob("", 0, "defg", 0, NULL, 0, &needed_size); ++ assert_int_equal(ret, EINVAL); ++ ++ ret = sss_auth_pack_2fa_blob("abc", 0, "", 0, NULL, 0, &needed_size); ++ assert_int_equal(ret, EINVAL); ++ ++ ret = sss_auth_pack_2fa_blob("abc", 0, "defg", 0, NULL, 0, &needed_size); ++ assert_int_equal(ret, EAGAIN); ++ ++ buf = talloc_size(ts, needed_size); ++ assert_non_null(buf); ++ ++ ret = sss_auth_pack_2fa_blob("abc", 0, "defg", 0, buf, needed_size, ++ &needed_size); ++ assert_int_equal(ret, EOK); ++ ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++ assert_memory_equal(buf, "\4\0\0\0\5\0\0\0abc\0defg\0", needed_size); ++#else ++ assert_memory_equal(buf, "\0\0\0\4\0\0\0\5abc\0defg\0", needed_size); ++#endif ++ ++ ret = sss_auth_unpack_2fa_blob(ts, buf, needed_size, &fa1, &fa1_len, &fa2, ++ &fa2_len); ++ assert_int_equal(ret, EOK); ++ assert_int_equal(fa1_len, 3); ++ assert_string_equal(fa1, "abc"); ++ assert_int_equal(fa2_len, 4); ++ assert_string_equal(fa2, "defg"); ++ ++ talloc_free(buf); ++ talloc_free(fa1); ++ talloc_free(fa2); ++} ++ ++#define MISSING_NULL_CHECK do { \ ++ assert_int_equal(ret, EOK); \ ++ assert_int_equal(fa1_len, 3); \ ++ assert_string_equal(fa1, "abc"); \ ++ assert_int_equal(fa2_len, 4); \ ++ assert_string_equal(fa2, "defg"); \ ++ \ ++ talloc_free(fa1); \ ++ talloc_free(fa2); \ ++} while (0) ++ ++void test_sss_authtok_2fa_blobs_missing_null(void **state) ++{ ++ int ret; ++ struct test_state *ts; ++ char *fa1; ++ size_t fa1_len; ++ char *fa2; ++ size_t fa2_len; ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++ uint8_t b0[] = {0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 'a', 'b', 'c', 0x00, 'd', 'e', 'f', 'g', 0x00}; ++ uint8_t b1[] = {0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 0x00}; ++ uint8_t b2[] = {0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 'a', 'b', 'c', 0x00, 'd', 'e', 'f', 'g'}; ++ uint8_t b3[] = {0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 'a', 'b', 'c', 'd', 'e', 'f', 'g'}; ++#else ++ uint8_t b0[] = {0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 'a', 'b', 'c', 0x00, 'd', 'e', 'f', 'g', 0x00}; ++ uint8_t b1[] = {0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 0x00}; ++ uint8_t b2[] = {0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 'a', 'b', 'c', 0x00, 'd', 'e', 'f', 'g'}; ++ uint8_t b3[] = {0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 'a', 'b', 'c', 'd', 'e', 'f', 'g'}; ++#endif ++ ++ ++ ts = talloc_get_type_abort(*state, struct test_state); ++ ++ ret = sss_auth_unpack_2fa_blob(ts, b0, sizeof(b0), &fa1, &fa1_len, &fa2, ++ &fa2_len); ++ MISSING_NULL_CHECK; ++ ++ ret = sss_auth_unpack_2fa_blob(ts, b1, sizeof(b1), &fa1, &fa1_len, &fa2, ++ &fa2_len); ++ MISSING_NULL_CHECK; ++ ++ ret = sss_auth_unpack_2fa_blob(ts, b2, sizeof(b2), &fa1, &fa1_len, &fa2, ++ &fa2_len); ++ MISSING_NULL_CHECK; ++ ++ ret = sss_auth_unpack_2fa_blob(ts, b3, sizeof(b3), &fa1, &fa1_len, &fa2, ++ &fa2_len); ++ MISSING_NULL_CHECK; ++} ++ + int main(int argc, const char *argv[]) + { + poptContext pc; +@@ -333,7 +510,13 @@ int main(int argc, const char *argv[]) + cmocka_unit_test_setup_teardown(test_sss_authtok_wipe_password, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_authtok_copy, +- setup, teardown) ++ setup, teardown), ++ cmocka_unit_test_setup_teardown(test_sss_authtok_2fa, ++ setup, teardown), ++ cmocka_unit_test_setup_teardown(test_sss_authtok_2fa_blobs, ++ setup, teardown), ++ cmocka_unit_test_setup_teardown(test_sss_authtok_2fa_blobs_missing_null, ++ setup, teardown), + }; + + /* Set debug level to invalid value so we can deside if -d 0 was used. */ +diff --git a/src/util/authtok-utils.c b/src/util/authtok-utils.c +new file mode 100644 +index 0000000000000000000000000000000000000000..65fba9022db11786c0c7e4dcab6fec89c9e0cb19 +--- /dev/null ++++ b/src/util/authtok-utils.c +@@ -0,0 +1,74 @@ ++/* ++ SSSD - auth utils helpers ++ ++ Copyright (C) Sumit Bose 2015 ++ ++ 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 . ++*/ ++ ++/* This file is use by SSSD clients and the main daemons. Please do not add ++ * code which is specific to only one of them. */ ++ ++#include ++ ++#include "sss_client/sss_cli.h" ++ ++errno_t sss_auth_pack_2fa_blob(const char *fa1, size_t fa1_len, ++ const char *fa2, size_t fa2_len, ++ uint8_t *buf, size_t buf_len, ++ size_t *_2fa_blob_len) ++{ ++ size_t c; ++ uint32_t tmp_uint32_t; ++ ++ if (fa1 == NULL || *fa1 == '\0' || fa1_len > UINT32_MAX ++ || fa2 == NULL || *fa2 == '\0' || fa2_len > UINT32_MAX ++ || (buf == NULL && buf_len != 0)) { ++ return EINVAL; ++ } ++ ++ if (fa1_len == 0) { ++ fa1_len = strlen(fa1); ++ } else { ++ if (fa1[fa1_len] != '\0') { ++ return EINVAL; ++ } ++ } ++ ++ if (fa2_len == 0) { ++ fa2_len = strlen(fa2); ++ } else { ++ if (fa2[fa2_len] != '\0') { ++ return EINVAL; ++ } ++ } ++ ++ *_2fa_blob_len = fa1_len + fa2_len + 2 + 2 * sizeof(uint32_t); ++ if (buf == NULL || buf_len < *_2fa_blob_len) { ++ return EAGAIN; ++ } ++ ++ c = 0; ++ tmp_uint32_t = (uint32_t) fa1_len + 1; ++ SAFEALIGN_COPY_UINT32(buf, &tmp_uint32_t, &c); ++ tmp_uint32_t = (uint32_t) fa2_len + 1; ++ SAFEALIGN_COPY_UINT32(buf + c, &tmp_uint32_t, &c); ++ ++ memcpy(buf + c, fa1, fa1_len + 1); ++ c += fa1_len + 1; ++ ++ memcpy(buf + c, fa2, fa2_len + 1); ++ ++ return 0; ++} +diff --git a/src/util/authtok-utils.h b/src/util/authtok-utils.h +new file mode 100644 +index 0000000000000000000000000000000000000000..07aef3c18395d6e967289f6e345f27e9ee868da2 +--- /dev/null ++++ b/src/util/authtok-utils.h +@@ -0,0 +1,70 @@ ++/* ++ SSSD - auth utils helpers ++ ++ Copyright (C) Sumit Bose 2015 ++ ++ 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 . ++*/ ++ ++#ifndef __AUTHTOK_UTILS_H__ ++#define __AUTHTOK_UTILS_H__ ++ ++#include ++ ++#include "sss_client/sss_cli.h" ++ ++/** ++ * @brief Fill memory buffer with 2FA blob ++ * ++ * @param[in] fa1 First authentication factor, null terminated ++ * @param[in] fa1_len Length of the first authentication factor, if 0 ++ * strlen() will be called internally ++ * @param[in] fa2 Second authentication factor, null terminated ++ * @param[in] fa2_len Length of the second authentication factor, if 0 ++ * strlen() will be called internally ++ * @param[in] buf memory buffer of size buf_len ++ * @param[in] buf_len size of memory buffer buf ++ * ++ * @param[out] _2fa_blob_len size of the 2FA blob ++ * ++ * @return EOK on success ++ * EINVAL if input data is not consistent ++ * EAGAIN if provided buffer is too small, _2fa_blob_len ++ * contains the size needed to store the 2FA blob ++ */ ++errno_t sss_auth_pack_2fa_blob(const char *fa1, size_t fa1_len, ++ const char *fa2, size_t fa2_len, ++ uint8_t *buf, size_t buf_len, ++ size_t *_2fa_blob_len); ++ ++/** ++ * @brief Extract 2FA data from memory buffer ++ * ++ * @param[in] mem_ctx Talloc memory context to allocate the 2FA data on ++ * @param[in] blob Memory buffer containing the 2FA data ++ * @param[in] blob_len Size of the memory buffer ++ * @param[out] _fa1 First authentication factor, null terminated ++ * @param[out] _fa1_len Length of the first authentication factor ++ * @param[out] _fa2 Second authentication factor, null terminated ++ * @param[out] _fa2_len Length of the second authentication factor ++ * ++ * @return EOK on success ++ * EINVAL if input data is not consistent ++ * EINVAL if no memory can be allocated ++ */ ++errno_t sss_auth_unpack_2fa_blob(TALLOC_CTX *mem_ctx, ++ const uint8_t *blob, size_t blob_len, ++ char **fa1, size_t *_fa1_len, ++ char **fa2, size_t *_fa2_len); ++#endif /* __AUTHTOK_UTILS_H__ */ +diff --git a/src/util/authtok.c b/src/util/authtok.c +index b7bc17ed0cdc5cfee7f455b0d7047803e628274a..45761df80175fded8a6c6e5dac8a90180b11d225 100644 +--- a/src/util/authtok.c ++++ b/src/util/authtok.c +@@ -38,6 +38,7 @@ size_t sss_authtok_get_size(struct sss_auth_token *tok) + switch (tok->type) { + case SSS_AUTHTOK_TYPE_PASSWORD: + case SSS_AUTHTOK_TYPE_CCFILE: ++ case SSS_AUTHTOK_TYPE_2FA: + return tok->length; + case SSS_AUTHTOK_TYPE_EMPTY: + return 0; +@@ -70,6 +71,7 @@ errno_t sss_authtok_get_password(struct sss_auth_token *tok, + } + return EOK; + case SSS_AUTHTOK_TYPE_CCFILE: ++ case SSS_AUTHTOK_TYPE_2FA: + return EACCES; + } + +@@ -92,6 +94,7 @@ errno_t sss_authtok_get_ccfile(struct sss_auth_token *tok, + } + return EOK; + case SSS_AUTHTOK_TYPE_PASSWORD: ++ case SSS_AUTHTOK_TYPE_2FA: + return EACCES; + } + +@@ -140,6 +143,7 @@ void sss_authtok_set_empty(struct sss_auth_token *tok) + case SSS_AUTHTOK_TYPE_EMPTY: + return; + case SSS_AUTHTOK_TYPE_PASSWORD: ++ case SSS_AUTHTOK_TYPE_2FA: + safezero(tok->data, tok->length); + break; + case SSS_AUTHTOK_TYPE_CCFILE: +@@ -169,6 +173,9 @@ errno_t sss_authtok_set_ccfile(struct sss_auth_token *tok, + "ccfile", ccfile, len); + } + ++static errno_t sss_authtok_set_2fa_from_blob(struct sss_auth_token *tok, ++ const uint8_t *data, size_t len); ++ + errno_t sss_authtok_set(struct sss_auth_token *tok, + enum sss_authtok_type type, + const uint8_t *data, size_t len) +@@ -178,6 +185,8 @@ errno_t sss_authtok_set(struct sss_auth_token *tok, + return sss_authtok_set_password(tok, (const char *)data, len); + case SSS_AUTHTOK_TYPE_CCFILE: + return sss_authtok_set_ccfile(tok, (const char *)data, len); ++ case SSS_AUTHTOK_TYPE_2FA: ++ return sss_authtok_set_2fa_from_blob(tok, data, len); + case SSS_AUTHTOK_TYPE_EMPTY: + sss_authtok_set_empty(tok); + return EOK; +@@ -230,3 +239,175 @@ void sss_authtok_wipe_password(struct sss_auth_token *tok) + safezero(tok->data, tok->length); + } + ++errno_t sss_auth_unpack_2fa_blob(TALLOC_CTX *mem_ctx, ++ const uint8_t *blob, size_t blob_len, ++ char **fa1, size_t *_fa1_len, ++ char **fa2, size_t *_fa2_len) ++{ ++ size_t c; ++ uint32_t fa1_len; ++ uint32_t fa2_len; ++ ++ if (blob_len < 2 * sizeof(uint32_t)) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n"); ++ return EINVAL; ++ } ++ ++ c = 0; ++ SAFEALIGN_COPY_UINT32(&fa1_len, blob, &c); ++ SAFEALIGN_COPY_UINT32(&fa2_len, blob + c, &c); ++ ++ if (blob_len != 2 * sizeof(uint32_t) + fa1_len + fa2_len) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n"); ++ return EINVAL; ++ } ++ ++ if (fa1_len != 0) { ++ *fa1 = talloc_strndup(mem_ctx, (const char *) blob + c, fa1_len); ++ if (*fa1 == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ return ENOMEM; ++ } ++ } else { ++ *fa1 = NULL; ++ } ++ ++ if (fa2_len != 0) { ++ *fa2 = talloc_strndup(mem_ctx, (const char *) blob + c + fa1_len, ++ fa2_len); ++ if (*fa2 == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ talloc_free(*fa1); ++ return ENOMEM; ++ } ++ } else { ++ *fa2 = NULL; ++ } ++ ++ /* Re-calculate length for the case where \0 was missing in the blob */ ++ *_fa1_len = (*fa1 == NULL) ? 0 : strlen(*fa1); ++ *_fa2_len = (*fa2 == NULL) ? 0 : strlen(*fa2); ++ ++ return EOK; ++} ++ ++static errno_t sss_authtok_set_2fa_from_blob(struct sss_auth_token *tok, ++ const uint8_t *data, size_t len) ++{ ++ TALLOC_CTX *tmp_ctx; ++ int ret; ++ char *fa1; ++ size_t fa1_len; ++ char *fa2; ++ size_t fa2_len; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sss_auth_unpack_2fa_blob(tmp_ctx, data, len, &fa1, &fa1_len, ++ &fa2, &fa2_len); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_unpack_2fa_blob failed.\n"); ++ goto done; ++ } ++ ++ ret = sss_authtok_set_2fa(tok, fa1, fa1_len, fa2, fa2_len); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_set_2fa failed.\n"); ++ goto done; ++ } ++ ++ ret = EOK; ++done: ++ talloc_free(tmp_ctx); ++ ++ if (ret != EOK) { ++ sss_authtok_set_empty(tok); ++ } ++ ++ return ret; ++} ++ ++errno_t sss_authtok_get_2fa(struct sss_auth_token *tok, ++ const char **fa1, size_t *fa1_len, ++ const char **fa2, size_t *fa2_len) ++{ ++ size_t c; ++ uint32_t tmp_uint32_t; ++ ++ if (tok->type != SSS_AUTHTOK_TYPE_2FA) { ++ return (tok->type == SSS_AUTHTOK_TYPE_EMPTY) ? ENOENT : EACCES; ++ } ++ ++ if (tok->length < 2 * sizeof(uint32_t)) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Blob too small.\n"); ++ return EINVAL; ++ } ++ ++ c = 0; ++ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data, &c); ++ *fa1_len = tmp_uint32_t - 1; ++ SAFEALIGN_COPY_UINT32(&tmp_uint32_t, tok->data + c, &c); ++ *fa2_len = tmp_uint32_t - 1; ++ ++ if (*fa1_len == 0 || *fa2_len == 0 ++ || tok->length != 2 * sizeof(uint32_t) + *fa1_len + *fa2_len + 2) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Blob size mismatch.\n"); ++ return EINVAL; ++ } ++ ++ if (tok->data[c + *fa1_len] != '\0' ++ || tok->data[c + *fa1_len + 1 + *fa2_len] != '\0') { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Missing terminating null character.\n"); ++ return EINVAL; ++ } ++ ++ *fa1 = (const char *) tok->data + c; ++ *fa2 = (const char *) tok->data + c + *fa1_len + 1; ++ ++ return EOK; ++} ++ ++errno_t sss_authtok_set_2fa(struct sss_auth_token *tok, ++ const char *fa1, size_t fa1_len, ++ const char *fa2, size_t fa2_len) ++{ ++ int ret; ++ size_t needed_size; ++ ++ if (tok == NULL) { ++ return EINVAL; ++ } ++ ++ sss_authtok_set_empty(tok); ++ ++ ret = sss_auth_pack_2fa_blob(fa1, fa1_len, fa2, fa2_len, NULL, 0, ++ &needed_size); ++ if (ret != EAGAIN) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "sss_auth_pack_2fa_blob unexpectedly returned [%d].\n", ret); ++ return EINVAL; ++ } ++ ++ tok->data = talloc_size(tok, needed_size); ++ if (tok->data == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n"); ++ return ENOMEM; ++ } ++ ++ ret = sss_auth_pack_2fa_blob(fa1, fa1_len, fa2, fa2_len, tok->data, ++ needed_size, &needed_size); ++ if (ret != EOK) { ++ talloc_free(tok->data); ++ DEBUG(SSSDBG_OP_FAILURE, "sss_auth_pack_2fa_blob failed.\n"); ++ return ret; ++ } ++ tok->length = needed_size; ++ tok->type = SSS_AUTHTOK_TYPE_2FA; ++ ++ return EOK; ++} +diff --git a/src/util/authtok.h b/src/util/authtok.h +index 1f6def4c3b6a1cbf6c4f34bb76c496ddae6f9d5f..cb366270832852281a222018f8e27feb1500ff01 100644 +--- a/src/util/authtok.h ++++ b/src/util/authtok.h +@@ -21,6 +21,7 @@ + #define __AUTHTOK_H__ + + #include "util/util.h" ++#include "util/authtok-utils.h" + #include "sss_client/sss_cli.h" + + /* Use sss_authtok_* accesor functions instead of struct sss_auth_token +@@ -179,4 +180,47 @@ void sss_authtok_wipe_password(struct sss_auth_token *tok); + */ + struct sss_auth_token *sss_authtok_new(TALLOC_CTX *mem_ctx); + ++/** ++ * @brief Set authtoken with 2FA data ++ * ++ * @param tok A pointer to a sss_auth_token structure to change, also ++ * used as a memory context to allocate the internal data. ++ * @param[in] fa1 First authentication factor, null terminated ++ * @param[in] fa1_len Length of the first authentication factor, if 0 ++ * strlen() will be called internally ++ * @param[in] fa2 Second authentication factor, null terminated ++ * @param[in] fa2_len Length of the second authentication factor, if 0 ++ * strlen() will be called internally ++ * ++ * @return EOK on success ++ * ENOMEM if memory allocation failed ++ * EINVAL if input data is not consistent ++ */ ++errno_t sss_authtok_set_2fa(struct sss_auth_token *tok, ++ const char *fa1, size_t fa1_len, ++ const char *fa2, size_t fa2_len); ++ ++/** ++ * @brief Get 2FA factors from authtoken ++ * ++ * @param tok A pointer to a sss_auth_token structure to change, also ++ * used as a memory context to allocate the internal data. ++ * @param[out] fa1 A pointer to a const char *, that will point to a ++ * null terminated string holding the first ++ * authentication factor, may not be modified or freed ++ * @param[out] fa1_len Length of the first authentication factor ++ * @param[out] fa2 A pointer to a const char *, that will point to a ++ * null terminated string holding the second ++ * authentication factor, may not be modified or freed ++ * @param[out] fa2_len Length of the second authentication factor ++ * ++ * @return EOK on success ++ * ENOMEM if memory allocation failed ++ * EINVAL if input data is not consistent ++ * ENOENT if the token is empty ++ * EACCESS if the token is not a 2FA token ++ */ ++errno_t sss_authtok_get_2fa(struct sss_auth_token *tok, ++ const char **fa1, size_t *fa1_len, ++ const char **fa2, size_t *fa2_len); + #endif /* __AUTHTOK_H__ */ +-- +2.4.0 + diff --git a/0103-pam-handle-2FA-authentication-token-in-the-responder.patch b/0103-pam-handle-2FA-authentication-token-in-the-responder.patch new file mode 100644 index 0000000..d6a478e --- /dev/null +++ b/0103-pam-handle-2FA-authentication-token-in-the-responder.patch @@ -0,0 +1,32 @@ +From 004632f9a319d829e83fcd1617be4b6a2f15e7a4 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 8 Jan 2015 17:10:42 +0100 +Subject: [PATCH 103/114] pam: handle 2FA authentication token in the responder +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit ea98a7af0584d7667b6c07c19a4b22942c94ca5d) +--- + src/responder/pam/pamsrv_cmd.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index 0b54402729e77f22391c6bd17fd8c937ddea3592..2ca5aa789ab98aea9005b891be1a36ea91ab40f4 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -143,6 +143,10 @@ static int extract_authtok_v2(struct sss_auth_token *tok, + auth_token_length); + } + break; ++ case SSS_AUTHTOK_TYPE_2FA: ++ ret = sss_authtok_set(tok, SSS_AUTHTOK_TYPE_2FA, ++ auth_token_data, auth_token_length); ++ break; + default: + return EINVAL; + } +-- +2.4.0 + diff --git a/0104-Add-pre-auth-request.patch b/0104-Add-pre-auth-request.patch new file mode 100644 index 0000000..1ab1ef2 --- /dev/null +++ b/0104-Add-pre-auth-request.patch @@ -0,0 +1,112 @@ +From 4f913c8472fe7c10fcaedddbb620774ff8838c2b Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 24 Mar 2015 17:24:50 +0100 +Subject: [PATCH 104/114] Add pre-auth request +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit fb045f6e5a9a7f8936ad6f89c28862dcd035a4fe) +--- + src/providers/data_provider_be.c | 1 + + src/providers/dp_pam_data_util.c | 2 ++ + src/providers/ipa/ipa_auth.c | 1 + + src/providers/krb5/krb5_auth.c | 2 ++ + src/responder/pam/pamsrv_cmd.c | 7 +++++++ + src/sss_client/sss_cli.h | 4 ++++ + 6 files changed, 17 insertions(+) + +diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c +index 9a752e31ed2d644fd590d9a556d5c4f9cc17c4f6..b44784724e2c4afb6cea3c5769dceab415027c6b 100644 +--- a/src/providers/data_provider_be.c ++++ b/src/providers/data_provider_be.c +@@ -1373,6 +1373,7 @@ static int be_pam_handler(struct sbus_request *dbus_req, void *user_data) + + switch (pd->cmd) { + case SSS_PAM_AUTHENTICATE: ++ case SSS_PAM_PREAUTH: + target = BET_AUTH; + break; + case SSS_PAM_ACCT_MGMT: +diff --git a/src/providers/dp_pam_data_util.c b/src/providers/dp_pam_data_util.c +index 313948b369cf605c91eb608b9a394d32a1e128d1..8724bf936f3f46fb8393c8a3da57215a73b4191a 100644 +--- a/src/providers/dp_pam_data_util.c ++++ b/src/providers/dp_pam_data_util.c +@@ -43,6 +43,8 @@ static const char *pamcmd2str(int cmd) { + return "PAM_CHAUTHTOK"; + case SSS_PAM_CHAUTHTOK_PRELIM: + return "PAM_CHAUTHTOK_PRELIM"; ++ case SSS_PAM_PREAUTH: ++ return "SSS_PAM_PREAUTH"; + default: + return "UNKNOWN"; + } +diff --git a/src/providers/ipa/ipa_auth.c b/src/providers/ipa/ipa_auth.c +index f9a0706be7c7fee2b8431cabad82e3c559795db4..f8badbdd16bfc4761ea177fdf5179ff2d4158080 100644 +--- a/src/providers/ipa/ipa_auth.c ++++ b/src/providers/ipa/ipa_auth.c +@@ -208,6 +208,7 @@ void ipa_auth(struct be_req *be_req) + + switch (state->pd->cmd) { + case SSS_PAM_AUTHENTICATE: ++ case SSS_PAM_PREAUTH: + state->ipa_auth_ctx = talloc_get_type( + be_ctx->bet_info[BET_AUTH].pvt_bet_data, + struct ipa_auth_ctx); +diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c +index 25caf7b788a3f373f47e9d8aad38a2ea6fc12621..5ce45b1579f93d618da455b7ab2687c078332067 100644 +--- a/src/providers/krb5/krb5_auth.c ++++ b/src/providers/krb5/krb5_auth.c +@@ -441,6 +441,8 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, + goto done; + } + break; ++ case SSS_PAM_PREAUTH: ++ break; + default: + DEBUG(SSSDBG_CONF_SETTINGS, "Unexpected pam task %d.\n", pd->cmd); + state->pam_status = PAM_SYSTEM_ERR; +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index 2ca5aa789ab98aea9005b891be1a36ea91ab40f4..c7eb697f29b6de9f7edaaf7715a58d2b7afdc733 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -1454,6 +1454,12 @@ static int pam_cmd_chauthtok_prelim(struct cli_ctx *cctx) { + return pam_forwarder(cctx, SSS_PAM_CHAUTHTOK_PRELIM); + } + ++static int pam_cmd_preauth(struct cli_ctx *cctx) ++{ ++ DEBUG(SSSDBG_CONF_SETTINGS, "entering pam_cmd_preauth\n"); ++ return pam_forwarder(cctx, SSS_PAM_PREAUTH); ++} ++ + struct cli_protocol_version *register_cli_protocol_version(void) + { + static struct cli_protocol_version pam_cli_protocol_version[] = { +@@ -1477,6 +1483,7 @@ struct sss_cmd_table *get_pam_cmds(void) + {SSS_PAM_CLOSE_SESSION, pam_cmd_close_session}, + {SSS_PAM_CHAUTHTOK, pam_cmd_chauthtok}, + {SSS_PAM_CHAUTHTOK_PRELIM, pam_cmd_chauthtok_prelim}, ++ {SSS_PAM_PREAUTH, pam_cmd_preauth}, + {SSS_CLI_NULL, NULL} + }; + +diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h +index 9a19d7d47d0a9d7dabeac36dc2c866c3420ef501..2895659b9c3ed4ab520ca90846379c22fd9567f7 100644 +--- a/src/sss_client/sss_cli.h ++++ b/src/sss_client/sss_cli.h +@@ -220,6 +220,10 @@ enum sss_cli_command { + SSS_CMD_RENEW = 0x00F8, /**< Renew a credential with a limited + * lifetime, e.g. a Kerberos Ticket + * Granting Ticket (TGT) */ ++ SSS_PAM_PREAUTH = 0x00F9, /**< Request which can be run before ++ * an authentication request to find ++ * out which authentication methods ++ * are available for the given user. */ + + /* PAC responder calls */ + SSS_PAC_ADD_PAC_USER = 0x0101, +-- +2.4.0 + diff --git a/0105-krb5-child-add-preauth-and-split-2fa-token-support.patch b/0105-krb5-child-add-preauth-and-split-2fa-token-support.patch new file mode 100644 index 0000000..0571c58 --- /dev/null +++ b/0105-krb5-child-add-preauth-and-split-2fa-token-support.patch @@ -0,0 +1,427 @@ +From 416dd81689f625105d7d022a9a46041947fa07c3 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 24 Mar 2015 17:26:53 +0100 +Subject: [PATCH 105/114] krb5-child: add preauth and split 2fa token support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 4b1b2e60d0764fed289eada9a7afbfd1993cadcd) +--- + src/providers/krb5/krb5_auth.c | 3 +- + src/providers/krb5/krb5_child.c | 265 +++++++++++++++++++++++++++++--- + src/providers/krb5/krb5_child_handler.c | 4 + + src/sss_client/sss_cli.h | 6 + + 4 files changed, 257 insertions(+), 21 deletions(-) + +diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c +index 5ce45b1579f93d618da455b7ab2687c078332067..651a9201756be8bc80199636b494b2773cff9c3e 100644 +--- a/src/providers/krb5/krb5_auth.c ++++ b/src/providers/krb5/krb5_auth.c +@@ -394,7 +394,8 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx, + switch (pd->cmd) { + case SSS_PAM_AUTHENTICATE: + case SSS_PAM_CHAUTHTOK: +- if (authtok_type != SSS_AUTHTOK_TYPE_PASSWORD) { ++ if (authtok_type != SSS_AUTHTOK_TYPE_PASSWORD ++ && authtok_type != SSS_AUTHTOK_TYPE_2FA) { + /* handle empty password gracefully */ + if (authtok_type == SSS_AUTHTOK_TYPE_EMPTY) { + DEBUG(SSSDBG_CRIT_FAILURE, +diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c +index 0fcec981633989593d7155a57811d02a997db251..4b976ddb86b7a1cf6fdc14f99d0b5f4b321814c0 100644 +--- a/src/providers/krb5/krb5_child.c ++++ b/src/providers/krb5/krb5_child.c +@@ -54,6 +54,9 @@ struct krb5_req { + char* name; + krb5_creds *creds; + bool otp; ++ char *otp_vendor; ++ char *otp_token_id; ++ char *otp_challenge; + krb5_get_init_creds_opt *options; + + struct pam_data *pd; +@@ -268,7 +271,87 @@ static int token_pin_destructor(char *mem) + return 0; + } + +-static krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx, ++static krb5_error_code tokeninfo_matches_2fa(TALLOC_CTX *mem_ctx, ++ const krb5_responder_otp_tokeninfo *ti, ++ const char *fa1, size_t fa1_len, ++ const char *fa2, size_t fa2_len, ++ char **out_token, char **out_pin) ++{ ++ char *token = NULL, *pin = NULL; ++ checker check = NULL; ++ int i; ++ ++ if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_NEXTOTP) { ++ return ENOTSUP; ++ } ++ ++ if (ti->challenge != NULL) { ++ return ENOTSUP; ++ } ++ ++ /* This is a non-sensical value. */ ++ if (ti->length == 0) { ++ return EPROTO; ++ } ++ ++ if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_TOKEN) { ++ if (ti->length > 0 && ti->length != fa2_len) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Expected [%d] and given [%zu] token size " ++ "do not match.\n", ti->length, fa2_len); ++ return EMSGSIZE; ++ } ++ ++ if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_COLLECT_PIN) { ++ if (ti->flags & KRB5_RESPONDER_OTP_FLAGS_SEPARATE_PIN) { ++ ++ pin = talloc_strndup(mem_ctx, fa1, fa1_len); ++ if (pin == NULL) { ++ talloc_free(token); ++ return ENOMEM; ++ } ++ talloc_set_destructor(pin, token_pin_destructor); ++ ++ token = talloc_strndup(mem_ctx, fa2, fa2_len); ++ if (token == NULL) { ++ return ENOMEM; ++ } ++ talloc_set_destructor(token, token_pin_destructor); ++ ++ check = pick_checker(ti->format); ++ } ++ } else { ++ token = talloc_asprintf(mem_ctx, "%s%s", fa1, fa2); ++ if (token == NULL) { ++ return ENOMEM; ++ } ++ talloc_set_destructor(token, token_pin_destructor); ++ ++ check = pick_checker(ti->format); ++ } ++ } else { ++ /* Assuming PIN only required */ ++ pin = talloc_strndup(mem_ctx, fa1, fa1_len); ++ if (pin == NULL) { ++ return ENOMEM; ++ } ++ talloc_set_destructor(pin, token_pin_destructor); ++ } ++ ++ /* If check is set, we need to verify the contents of the token. */ ++ for (i = 0; check != NULL && token[i] != '\0'; i++) { ++ if (!check(token[i])) { ++ talloc_free(token); ++ talloc_free(pin); ++ return EBADMSG; ++ } ++ } ++ ++ *out_token = token; ++ *out_pin = pin; ++ return 0; ++} ++static krb5_error_code tokeninfo_matches_pwd(TALLOC_CTX *mem_ctx, + const krb5_responder_otp_tokeninfo *ti, + const char *pwd, size_t len, + char **out_token, char **out_pin) +@@ -364,15 +447,52 @@ static krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx, + return 0; + } + ++static krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx, ++ const krb5_responder_otp_tokeninfo *ti, ++ struct sss_auth_token *auth_tok, ++ char **out_token, char **out_pin) ++{ ++ int ret; ++ const char *pwd; ++ size_t len; ++ const char *fa2; ++ size_t fa2_len; ++ ++ switch (sss_authtok_get_type(auth_tok)) { ++ case SSS_AUTHTOK_TYPE_PASSWORD: ++ ret = sss_authtok_get_password(auth_tok, &pwd, &len); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_password failed.\n"); ++ return ret; ++ } ++ ++ return tokeninfo_matches_pwd(mem_ctx, ti, pwd, len, out_token, out_pin); ++ break; ++ case SSS_AUTHTOK_TYPE_2FA: ++ ret = sss_authtok_get_2fa(auth_tok, &pwd, &len, &fa2, &fa2_len); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_2fa failed.\n"); ++ return ret; ++ } ++ ++ return tokeninfo_matches_2fa(mem_ctx, ti, pwd, len, fa2, fa2_len, ++ out_token, out_pin); ++ break; ++ default: ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported authtok type.\n"); ++ } ++ ++ return EINVAL; ++} ++ + static krb5_error_code answer_otp(krb5_context ctx, + struct krb5_req *kr, + krb5_responder_context rctx) + { + krb5_responder_otp_challenge *chl; + char *token = NULL, *pin = NULL; +- const char *pwd = NULL; + krb5_error_code ret; +- size_t i, len; ++ size_t i; + + ret = krb5_responder_otp_get_challenge(ctx, rctx, &chl); + if (ret != EOK || chl == NULL) { +@@ -388,14 +508,37 @@ static krb5_error_code answer_otp(krb5_context ctx, + + kr->otp = true; + +- /* Validate our assumptions about the contents of authtok. */ +- ret = sss_authtok_get_password(kr->pd->authtok, &pwd, &len); +- if (ret != EOK) +- goto done; ++ if (kr->pd->cmd == SSS_PAM_PREAUTH) { ++ for (i = 0; chl->tokeninfo[i] != NULL; i++) { ++ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Vendor [%s].\n", ++ i, chl->tokeninfo[i]->vendor); ++ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Token-ID [%s].\n", ++ i, chl->tokeninfo[i]->token_id); ++ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Challenge [%s].\n", ++ i, chl->tokeninfo[i]->challenge); ++ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Flags [%d].\n", ++ i, chl->tokeninfo[i]->flags); ++ } ++ ++ if (chl->tokeninfo[0]->vendor != NULL) { ++ kr->otp_vendor = talloc_strdup(kr, chl->tokeninfo[0]->vendor); ++ } ++ if (chl->tokeninfo[0]->token_id != NULL) { ++ kr->otp_token_id = talloc_strdup(kr, chl->tokeninfo[0]->token_id); ++ } ++ if (chl->tokeninfo[0]->challenge != NULL) { ++ kr->otp_challenge = talloc_strdup(kr, chl->tokeninfo[0]->challenge); ++ } ++ /* Allocation errors are ignored on purpose */ ++ ++ DEBUG(SSSDBG_TRACE_INTERNAL, "Exit answer_otp during pre-auth.\n"); ++ return EAGAIN; ++ } + + /* Find the first supported tokeninfo which matches our authtoken. */ + for (i = 0; chl->tokeninfo[i] != NULL; i++) { +- ret = tokeninfo_matches(kr, chl->tokeninfo[i], pwd, len, &token, &pin); ++ ret = tokeninfo_matches(kr, chl->tokeninfo[i], kr->pd->authtok, ++ &token, &pin); + if (ret == EOK) { + break; + } +@@ -683,6 +826,58 @@ static errno_t pack_response_packet(TALLOC_CTX *mem_ctx, errno_t error, + return EOK; + } + ++static errno_t k5c_attach_otp_info_msg(struct krb5_req *kr) ++{ ++ uint8_t *msg = NULL; ++ size_t msg_len; ++ int ret; ++ size_t vendor_len = 0; ++ size_t token_id_len = 0; ++ size_t challenge_len = 0; ++ size_t idx = 0; ++ ++ msg_len = 3; ++ if (kr->otp_vendor != NULL) { ++ vendor_len = strlen(kr->otp_vendor); ++ msg_len += vendor_len; ++ } ++ ++ if (kr->otp_token_id != NULL) { ++ token_id_len = strlen(kr->otp_token_id); ++ msg_len += token_id_len; ++ } ++ ++ if (kr->otp_challenge != NULL) { ++ challenge_len = strlen(kr->otp_challenge); ++ msg_len += challenge_len; ++ } ++ ++ msg = talloc_zero_size(kr, msg_len); ++ if (msg == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n"); ++ return ENOMEM; ++ } ++ ++ if (kr->otp_vendor != NULL) { ++ memcpy(msg, kr->otp_vendor, vendor_len); ++ } ++ idx += vendor_len +1; ++ ++ if (kr->otp_token_id != NULL) { ++ memcpy(msg + idx, kr->otp_token_id, token_id_len); ++ } ++ idx += token_id_len +1; ++ ++ if (kr->otp_challenge != NULL) { ++ memcpy(msg + idx, kr->otp_challenge, challenge_len); ++ } ++ ++ ret = pam_add_response(kr->pd, SSS_PAM_OTP_INFO, msg_len, msg); ++ talloc_zfree(msg); ++ ++ return ret; ++} ++ + static errno_t k5c_attach_ccname_msg(struct krb5_req *kr) + { + char *msg = NULL; +@@ -996,9 +1191,18 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, + discard_const(password), + sss_krb5_prompter, kr, 0, + NULL, kr->options); +- if (kerr != 0) { +- KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); +- return kerr; ++ if (kr->pd->cmd == SSS_PAM_PREAUTH) { ++ /* Any errors are ignored during pre-auth, only data is collected to ++ * be send back to the client.*/ ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "krb5_get_init_creds_password returned [%d} during pre-auth.\n", ++ kerr); ++ return 0; ++ } else { ++ if (kerr != 0) { ++ KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); ++ return kerr; ++ } + } + + if (kr->validate) { +@@ -1300,8 +1504,11 @@ static errno_t tgt_req_child(struct krb5_req *kr) + + DEBUG(SSSDBG_TRACE_LIBS, "Attempting to get a TGT\n"); + +- ret = sss_authtok_get_password(kr->pd->authtok, &password, NULL); +- switch (ret) { ++ /* No password is needed for pre-auth, or if we have 2FA */ ++ if (kr->pd->cmd != SSS_PAM_PREAUTH ++ && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA) { ++ ret = sss_authtok_get_password(kr->pd->authtok, &password, NULL); ++ switch (ret) { + case EOK: + break; + +@@ -1314,13 +1521,21 @@ static errno_t tgt_req_child(struct krb5_req *kr) + DEBUG(SSSDBG_OP_FAILURE, "No credentials available\n"); + return ERR_NO_CREDS; + break; ++ } + } + + kerr = get_and_save_tgt(kr, password); + + if (kerr != KRB5KDC_ERR_KEY_EXP) { +- if (kerr == 0) { +- kerr = k5c_attach_ccname_msg(kr); ++ if (kr->pd->cmd == SSS_PAM_PREAUTH) { ++ /* add OTP tokeninfo messge if available */ ++ if (kr->otp) { ++ kerr = k5c_attach_otp_info_msg(kr); ++ } ++ } else { ++ if (kerr == 0) { ++ kerr = k5c_attach_ccname_msg(kr); ++ } + } + ret = map_krb5_error(kerr); + goto done; +@@ -1523,6 +1738,10 @@ static errno_t unpack_authtok(struct sss_auth_token *tok, + case SSS_AUTHTOK_TYPE_CCFILE: + ret = sss_authtok_set_ccfile(tok, (char *)(buf + *p), 0); + break; ++ case SSS_AUTHTOK_TYPE_2FA: ++ ret = sss_authtok_set(tok, SSS_AUTHTOK_TYPE_2FA, (buf + *p), ++ auth_token_length); ++ break; + default: + return EINVAL; + } +@@ -2285,11 +2504,13 @@ static krb5_error_code privileged_krb5_setup(struct krb5_req *kr, + } + + /* For ccache types FILE: and DIR: we might need to create some directory +- * components as root */ +- ret = k5c_ccache_setup(kr, offline); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, "k5c_ccache_setup failed.\n"); +- return ret; ++ * components as root. Cache files are not needed during preauth. */ ++ if (kr->pd->cmd != SSS_PAM_PREAUTH) { ++ ret = k5c_ccache_setup(kr, offline); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "k5c_ccache_setup failed.\n"); ++ return ret; ++ } + } + + if (!(offline || +@@ -2464,6 +2685,10 @@ int main(int argc, const char *argv[]) + DEBUG(SSSDBG_TRACE_FUNC, "Will perform ticket renewal\n"); + ret = renew_tgt_child(kr); + break; ++ case SSS_PAM_PREAUTH: ++ DEBUG(SSSDBG_TRACE_FUNC, "Will perform pre-auth\n"); ++ ret = tgt_req_child(kr); ++ break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, + "PAM command [%d] not supported.\n", kr->pd->cmd); +diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c +index 633cd917737d3f39526b049cc3d930b67f8b5c66..1f839ab5ebf93271556371b2f172f6c524da6270 100644 +--- a/src/providers/krb5/krb5_child_handler.c ++++ b/src/providers/krb5/krb5_child_handler.c +@@ -77,6 +77,10 @@ static errno_t pack_authtok(struct io_buffer *buf, size_t *rp, + ret = sss_authtok_get_ccfile(tok, &data, &len); + auth_token_length = len + 1; + break; ++ case SSS_AUTHTOK_TYPE_2FA: ++ data = (char *) sss_authtok_get_data(tok); ++ auth_token_length = sss_authtok_get_size(tok); ++ break; + default: + ret = EINVAL; + } +diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h +index 2895659b9c3ed4ab520ca90846379c22fd9567f7..1d7e8549cd548b00eeedba95080f346439afc3dd 100644 +--- a/src/sss_client/sss_cli.h ++++ b/src/sss_client/sss_cli.h +@@ -402,6 +402,12 @@ enum response_type { + * the user.This should only be used in the case where + * it is not possile to use SSS_PAM_USER_INFO. + * @param A zero terminated string. */ ++ SSS_PAM_OTP_INFO, /**< A message which optionally may contain the name ++ * of the vendor, the ID of an OTP token and a ++ * challenge. ++ * @param Three zero terminated strings, if one of the ++ * strings is missing the message will contain only ++ * an empty string (\0) for that component. */ + SSS_OTP, /**< Indicates that the autotok was a OTP, so don't + * cache it. There is no message. + * @param None. */ +-- +2.4.0 + diff --git a/0106-IPA-create-preauth-indicator-file-at-startup.patch b/0106-IPA-create-preauth-indicator-file-at-startup.patch new file mode 100644 index 0000000..a4a5466 --- /dev/null +++ b/0106-IPA-create-preauth-indicator-file-at-startup.patch @@ -0,0 +1,115 @@ +From f64b8751987ccf52039614f0e7bbe3b5035afd47 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 24 Mar 2015 11:19:46 +0100 +Subject: [PATCH 106/114] IPA: create preauth indicator file at startup +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit deb28a893c76f7c94b6cc8e596742665e23d97d5) +--- + src/providers/ipa/ipa_init.c | 66 ++++++++++++++++++++++++++++++++++++++++++++ + src/sss_client/sss_cli.h | 2 ++ + 2 files changed, 68 insertions(+) + +diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c +index 4b26e8baad4d0592729aec9a0b188ae89973fa98..15ec2339d95754db2e54f383bf8e423e780e9838 100644 +--- a/src/providers/ipa/ipa_init.c ++++ b/src/providers/ipa/ipa_init.c +@@ -371,6 +371,62 @@ done: + return ret; + } + ++void cleanup_ipa_preauth_indicator(void) ++{ ++ int ret; ++ ++ ret = unlink(PAM_PREAUTH_INDICATOR); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to remove preauth indicator file [%s].\n", ++ PAM_PREAUTH_INDICATOR); ++ } ++} ++ ++static errno_t create_ipa_preauth_indicator(void) ++{ ++ int ret; ++ TALLOC_CTX *tmp_ctx = NULL; ++ int fd; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); ++ return ENOMEM; ++ } ++ ++ fd = open(PAM_PREAUTH_INDICATOR, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW, ++ 0644); ++ if (fd < 0) { ++ if (errno != EEXIST) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to create preauth indicator file [%s].\n", ++ PAM_PREAUTH_INDICATOR); ++ ret = EOK; ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Preauth indicator file [%s] already exists. " ++ "Maybe it is left after an unplanned exit. Continuing.\n", ++ PAM_PREAUTH_INDICATOR); ++ } else { ++ close(fd); ++ } ++ ++ ret = atexit(cleanup_ipa_preauth_indicator); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "atexit failed. Continuing.\n"); ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} ++ + int sssm_ipa_auth_init(struct be_ctx *bectx, + struct bet_ops **ops, + void **pvt_data) +@@ -469,6 +525,16 @@ int sssm_ipa_auth_init(struct be_ctx *bectx, + goto done; + } + ++ ret = create_ipa_preauth_indicator(); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to create preauth indicator file, special password " ++ "prompting might not be available.\n"); ++ sss_log(SSSDBG_CRIT_FAILURE, ++ "Failed to create preauth indicator file, special password " ++ "prompting might not be available.\n"); ++ } ++ + *ops = &ipa_auth_ops; + *pvt_data = ipa_auth_ctx; + ret = EOK; +diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h +index 1d7e8549cd548b00eeedba95080f346439afc3dd..317700ef8cfcbb1b58e2a7d1ffcc7f00658fe815 100644 +--- a/src/sss_client/sss_cli.h ++++ b/src/sss_client/sss_cli.h +@@ -317,6 +317,8 @@ enum sss_authtok_type { + #define SSS_START_OF_PAM_REQUEST 0x4d415049 + #define SSS_END_OF_PAM_REQUEST 0x4950414d + ++#define PAM_PREAUTH_INDICATOR PUBCONF_PATH"/pam_preauth_available" ++ + enum pam_item_type { + SSS_PAM_ITEM_EMPTY = 0x0000, + SSS_PAM_ITEM_USER, +-- +2.4.0 + diff --git a/0107-pam_sss-add-pre-auth-and-2fa-support.patch b/0107-pam_sss-add-pre-auth-and-2fa-support.patch new file mode 100644 index 0000000..8dad6fd --- /dev/null +++ b/0107-pam_sss-add-pre-auth-and-2fa-support.patch @@ -0,0 +1,373 @@ +From e79111aa1780ee9a3871bebb3e0b69dc053755ce Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 12 Feb 2015 23:08:12 +0100 +Subject: [PATCH 107/114] pam_sss: add pre-auth and 2fa support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit e5698314b87e147c0223d0d8bcac206733dfae8c) +--- + Makefile.am | 1 + + src/sss_client/pam_sss.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 234 insertions(+), 2 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 3bc37a3984a5fa0471a1f3247bda9b869fc823e5..312901da3315e2d0471055541a114a8be36dc976 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -2353,6 +2353,7 @@ pam_sss_la_SOURCES = \ + src/sss_client/common.c \ + src/sss_client/sss_cli.h \ + src/util/atomic_io.c \ ++ src/util/authtok-utils.c \ + src/sss_client/sss_pam_macros.h \ + src/sss_client/sss_pam_compat.h + +diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c +index 4007d125e34932dfb5ac6bc840f4d25306e3008f..f11871a47d1b29f44c179e57a33d8f41be79078d 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -51,6 +51,7 @@ + #define FLAGS_USE_AUTHTOK (1 << 2) + #define FLAGS_IGNORE_UNKNOWN_USER (1 << 3) + #define FLAGS_IGNORE_AUTHINFO_UNAVAIL (1 << 4) ++#define FLAGS_USE_2FA (1 << 5) + + #define PWEXP_FLAG "pam_sss:password_expired_flag" + #define FD_DESTRUCTOR "pam_sss:fd_destructor" +@@ -88,6 +89,10 @@ struct pam_items { + char *domain_name; + const char *requested_domains; + size_t requested_domains_size; ++ char *otp_vendor; ++ char *otp_token_id; ++ char *otp_challenge; ++ char *first_factor; + }; + + #define DEBUG_MGS_LEN 1024 +@@ -224,6 +229,12 @@ static void overwrite_and_free_authtoks(struct pam_items *pi) + pi->pam_newauthtok = NULL; + } + ++ if (pi->first_factor != NULL) { ++ _pam_overwrite_n((void *)pi->first_factor, strlen(pi->first_factor)); ++ free((void *)pi->first_factor); ++ pi->first_factor = NULL; ++ } ++ + pi->pamstack_authtok = NULL; + pi->pamstack_oldauthtok = NULL; + } +@@ -234,6 +245,15 @@ static void overwrite_and_free_pam_items(struct pam_items *pi) + + free(pi->domain_name); + pi->domain_name = NULL; ++ ++ free(pi->otp_vendor); ++ pi->otp_vendor = NULL; ++ ++ free(pi->otp_token_id); ++ pi->otp_token_id = NULL; ++ ++ free(pi->otp_challenge); ++ pi->otp_challenge = NULL; + } + + static int pack_message_v3(struct pam_items *pi, size_t *size, +@@ -969,6 +989,7 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + int32_t type; + int32_t len; + int32_t pam_status; ++ size_t offset; + + if (buflen < (2*sizeof(int32_t))) { + D(("response buffer is too small")); +@@ -1075,6 +1096,45 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf, + pam_strerror(pamh,ret))); + } + break; ++ case SSS_PAM_OTP_INFO: ++ if (buf[p + (len - 1)] != '\0') { ++ D(("system info does not end with \\0.")); ++ break; ++ } ++ ++ pi->otp_vendor = strdup((char *) &buf[p]); ++ if (pi->otp_vendor == NULL) { ++ D(("strdup failed")); ++ break; ++ } ++ ++ offset = strlen(pi->otp_vendor) + 1; ++ if (offset >= len) { ++ D(("OTP message size mismatch")); ++ free(pi->otp_vendor); ++ pi->otp_vendor = NULL; ++ break; ++ } ++ pi->otp_token_id = strdup((char *) &buf[p + offset]); ++ if (pi->otp_token_id == NULL) { ++ D(("strdup failed")); ++ break; ++ } ++ ++ offset += strlen(pi->otp_token_id) + 1; ++ if (offset >= len) { ++ D(("OTP message size mismatch")); ++ free(pi->otp_token_id); ++ pi->otp_token_id = NULL; ++ break; ++ } ++ pi->otp_challenge = strdup((char *) &buf[p + offset]); ++ if (pi->otp_challenge == NULL) { ++ D(("strdup failed")); ++ break; ++ } ++ ++ break; + default: + D(("Unknown response type [%d]", type)); + } +@@ -1096,6 +1156,7 @@ static int get_pam_items(pam_handle_t *pamh, struct pam_items *pi) + pi->pam_newauthtok_type = SSS_AUTHTOK_TYPE_EMPTY; + pi->pam_newauthtok = NULL; + pi->pam_newauthtok_size = 0; ++ pi->first_factor = NULL; + + ret = pam_get_item(pamh, PAM_SERVICE, (const void **) &(pi->pam_service)); + if (ret != PAM_SUCCESS) return ret; +@@ -1150,6 +1211,10 @@ static int get_pam_items(pam_handle_t *pamh, struct pam_items *pi) + if (pi->requested_domains == NULL) pi->requested_domains = ""; + pi->requested_domains_size = strlen(pi->requested_domains) + 1; + ++ pi->otp_vendor = NULL; ++ pi->otp_token_id = NULL; ++ pi->otp_challenge = NULL; ++ + return PAM_SUCCESS; + } + +@@ -1281,6 +1346,7 @@ static int send_and_receive(pam_handle_t *pamh, struct pam_items *pi, + case SSS_PAM_OPEN_SESSION: + case SSS_PAM_SETCRED: + case SSS_PAM_CLOSE_SESSION: ++ case SSS_PAM_PREAUTH: + break; + default: + D(("Illegal task [%d]", task)); +@@ -1328,6 +1394,133 @@ static int prompt_password(pam_handle_t *pamh, struct pam_items *pi, + return PAM_SUCCESS; + } + ++static int prompt_2fa(pam_handle_t *pamh, struct pam_items *pi, ++ const char *prompt_fa1, const char *prompt_fa2) ++{ ++ int ret; ++ const struct pam_conv *conv; ++ const struct pam_message *mesg[2] = { NULL, NULL }; ++ struct pam_message *m1; ++ struct pam_message *m2; ++ struct pam_response *resp = NULL; ++ size_t needed_size; ++ ++ ret = pam_get_item(pamh, PAM_CONV, (const void **) &conv); ++ if (ret != PAM_SUCCESS) { ++ return ret; ++ } ++ ++ m1 = malloc(sizeof(struct pam_message)); ++ if (m1 == NULL) { ++ D(("Malloc failed.")); ++ return PAM_SYSTEM_ERR; ++ } ++ ++ m2 = malloc(sizeof(struct pam_message)); ++ if (m2 == NULL) { ++ D(("Malloc failed.")); ++ free(m1); ++ return PAM_SYSTEM_ERR; ++ } ++ m1->msg_style = PAM_PROMPT_ECHO_OFF; ++ m1->msg = prompt_fa1; ++ m2->msg_style = PAM_PROMPT_ECHO_OFF; ++ m2->msg = prompt_fa2; ++ ++ mesg[0] = (const struct pam_message *) m1; ++ mesg[1] = (const struct pam_message *) m2; ++ ++ ret = conv->conv(2, mesg, &resp, conv->appdata_ptr); ++ free(m1); ++ free(m2); ++ if (ret != PAM_SUCCESS) { ++ D(("Conversation failure: %s.", pam_strerror(pamh, ret))); ++ return ret; ++ } ++ ++ if (resp == NULL) { ++ D(("response expected, but resp==NULL")); ++ return PAM_SYSTEM_ERR; ++ } ++ ++ if (resp[0].resp == NULL || *(resp[0].resp) == '\0') { ++ D(("Missing factor.")); ++ ret = PAM_CRED_INSUFFICIENT; ++ goto done; ++ } ++ ++ if (resp[1].resp == NULL || *(resp[1].resp) == '\0' ++ || (pi->pam_service != NULL && strcmp(pi->pam_service, "sshd") == 0 ++ && strcmp(resp[0].resp, resp[1].resp) == 0)) { ++ /* Missing second factor, assume first factor contains combined 2FA ++ * credentials. ++ * Special handling for SSH with password authentication. Combined ++ * 2FA credentials are used but SSH puts them in both responses. */ ++ ++ pi->pam_authtok = strndup(resp[0].resp, MAX_AUTHTOK_SIZE); ++ if (pi->pam_authtok == NULL) { ++ D(("strndup failed.")); ++ ret = PAM_BUF_ERR; ++ goto done; ++ } ++ pi->pam_authtok_size = strlen(pi->pam_authtok) + 1; ++ pi->pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; ++ } else { ++ ++ ret = sss_auth_pack_2fa_blob(resp[0].resp, 0, resp[1].resp, 0, NULL, 0, ++ &needed_size); ++ if (ret != EAGAIN) { ++ D(("sss_auth_pack_2fa_blob failed.")); ++ ret = PAM_BUF_ERR; ++ goto done; ++ } ++ ++ pi->pam_authtok = malloc(needed_size); ++ if (pi->pam_authtok == NULL) { ++ D(("malloc failed.")); ++ ret = PAM_BUF_ERR; ++ goto done; ++ } ++ ++ ret = sss_auth_pack_2fa_blob(resp[0].resp, 0, resp[1].resp, 0, ++ (uint8_t *) pi->pam_authtok, needed_size, ++ &needed_size); ++ if (ret != EOK) { ++ D(("sss_auth_pack_2fa_blob failed.")); ++ ret = PAM_BUF_ERR; ++ goto done; ++ } ++ ++ pi->pam_authtok_size = needed_size; ++ pi->pam_authtok_type = SSS_AUTHTOK_TYPE_2FA; ++ pi->first_factor = strndup(resp[0].resp, MAX_AUTHTOK_SIZE); ++ if (pi->first_factor == NULL) { ++ D(("strndup failed.")); ++ ret = PAM_BUF_ERR; ++ goto done; ++ } ++ } ++ ++ ret = PAM_SUCCESS; ++ ++done: ++ if (resp != NULL) { ++ if (resp[0].resp != NULL) { ++ _pam_overwrite((void *)resp[0].resp); ++ free(resp[0].resp); ++ } ++ if (resp[1].resp != NULL) { ++ _pam_overwrite((void *)resp[1].resp); ++ free(resp[1].resp); ++ } ++ ++ free(resp); ++ resp = NULL; ++ } ++ ++ return ret; ++} ++ + static int prompt_new_password(pam_handle_t *pamh, struct pam_items *pi) + { + int ret; +@@ -1411,6 +1604,8 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv, + *flags |= FLAGS_IGNORE_UNKNOWN_USER; + } else if (strcmp(*argv, "ignore_authinfo_unavail") == 0) { + *flags |= FLAGS_IGNORE_AUTHINFO_UNAVAIL; ++ } else if (strcmp(*argv, "use_2fa") == 0) { ++ *flags |= FLAGS_USE_2FA; + } else { + logger(pamh, LOG_WARNING, "unknown option: %s", *argv); + } +@@ -1434,14 +1629,28 @@ static int get_authtok_for_authentication(pam_handle_t *pamh, + } + pi->pam_authtok_size = strlen(pi->pam_authtok); + } else { +- ret = prompt_password(pamh, pi, _("Password: ")); ++ if (flags & FLAGS_USE_2FA ++ || (pi->otp_vendor != NULL && pi->otp_token_id != NULL ++ && pi->otp_challenge != NULL)) { ++ ret = prompt_2fa(pamh, pi, _("First Factor: "), ++ _("Second Factor: ")); ++ } else { ++ ret = prompt_password(pamh, pi, _("Password: ")); ++ } + if (ret != PAM_SUCCESS) { + D(("failed to get password from user")); + return ret; + } + + if (flags & FLAGS_FORWARD_PASS) { +- ret = pam_set_item(pamh, PAM_AUTHTOK, pi->pam_authtok); ++ if (pi->pam_authtok_type == SSS_AUTHTOK_TYPE_PASSWORD) { ++ ret = pam_set_item(pamh, PAM_AUTHTOK, pi->pam_authtok); ++ } else if (pi->pam_authtok_type == SSS_AUTHTOK_TYPE_2FA ++ && pi->first_factor != NULL) { ++ ret = pam_set_item(pamh, PAM_AUTHTOK, pi->first_factor); ++ } else { ++ ret = EINVAL; ++ } + if (ret != PAM_SUCCESS) { + D(("Failed to set PAM_AUTHTOK [%s], " + "authtok may not be available for other modules", +@@ -1576,6 +1785,27 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, + + switch(task) { + case SSS_PAM_AUTHENTICATE: ++ /* ++ * Only do preauth if ++ * - FLAGS_USE_FIRST_PASS is not set ++ * - no password is on the stack ++ * - preauth indicator file exists. ++ */ ++ if ( !(flags & FLAGS_USE_FIRST_PASS) && pi.pam_authtok == NULL ++ && access(PAM_PREAUTH_INDICATOR, F_OK) == 0) { ++ pam_status = send_and_receive(pamh, &pi, SSS_PAM_PREAUTH, ++ quiet_mode); ++ if (pam_status != PAM_SUCCESS) { ++ D(("send_and_receive returned [%d] during pre-auth", ++ pam_status)); ++ /* ++ * Since we are only interested in the result message ++ * and will always use password authentication ++ * as a fallback, errors can be ignored here. ++ */ ++ } ++ } ++ + ret = get_authtok_for_authentication(pamh, &pi, flags); + if (ret != PAM_SUCCESS) { + D(("failed to get authentication token: %s", +@@ -1588,6 +1818,7 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh, + if (ret != PAM_SUCCESS) { + D(("failed to get tokens for password change: %s", + pam_strerror(pamh, ret))); ++ overwrite_and_free_pam_items(&pi); + return ret; + } + if (pam_flags & PAM_PRELIM_CHECK) { +-- +2.4.0 + diff --git a/0108-Add-cache_credentials_minimal_first_factor_length-co.patch b/0108-Add-cache_credentials_minimal_first_factor_length-co.patch new file mode 100644 index 0000000..9d330de --- /dev/null +++ b/0108-Add-cache_credentials_minimal_first_factor_length-co.patch @@ -0,0 +1,145 @@ +From ca95d0e56d59516fc294ae62489de5ea1cd1a864 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 24 Mar 2015 13:00:14 +0100 +Subject: [PATCH 108/114] Add cache_credentials_minimal_first_factor_length + config option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 932c3e22e3c59a9c33f30dcc09e6bef257e14320) +--- + src/confdb/confdb.c | 11 +++++++++++ + src/confdb/confdb.h | 4 ++++ + src/config/SSSDConfigTest.py | 2 ++ + src/config/etc/sssd.api.conf | 1 + + src/man/sssd.conf.5.xml | 22 ++++++++++++++++++++++ + src/util/domain_info_utils.c | 2 ++ + 6 files changed, 42 insertions(+) + +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index dd93410cfcac3040d5a72329929f99f43fc592f1..90d413f9ebf2ae72305e7281f03150b672c721bb 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -956,6 +956,17 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, + goto done; + } + ++ ret = get_entry_as_uint32(res->msgs[0], ++ &domain->cache_credentials_min_ff_length, ++ CONFDB_DOMAIN_CACHE_CREDS_MIN_FF_LENGTH, ++ CONFDB_DEFAULT_CACHE_CREDS_MIN_FF_LENGTH); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "Invalid value for %s\n", ++ CONFDB_DOMAIN_CACHE_CREDS_MIN_FF_LENGTH); ++ goto done; ++ } ++ + ret = get_entry_as_bool(res->msgs[0], &domain->legacy_passwords, + CONFDB_DOMAIN_LEGACY_PASS, 0); + if(ret != EOK) { +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index 19c56402069f9a7001188e91f77db8ad8525d690..c8c91288cd63df7629a98802b7b5373df92d6ca4 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -162,6 +162,9 @@ + #define CONFDB_DOMAIN_MINID "min_id" + #define CONFDB_DOMAIN_MAXID "max_id" + #define CONFDB_DOMAIN_CACHE_CREDS "cache_credentials" ++#define CONFDB_DOMAIN_CACHE_CREDS_MIN_FF_LENGTH \ ++ "cache_credentials_minimal_first_factor_length" ++#define CONFDB_DEFAULT_CACHE_CREDS_MIN_FF_LENGTH 8 + #define CONFDB_DOMAIN_LEGACY_PASS "store_legacy_passwords" + #define CONFDB_DOMAIN_MPG "magic_private_groups" + #define CONFDB_DOMAIN_FQ "use_fully_qualified_names" +@@ -221,6 +224,7 @@ struct sss_domain_info { + uint32_t id_max; + + bool cache_credentials; ++ uint32_t cache_credentials_min_ff_length; + bool legacy_passwords; + bool case_sensitive; + bool case_preserve; +diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py +index 3a5312ea945b5247c69e97b73565b7061e037b69..db16bc433cf4c47c6a15760d85b322a6655aa0c1 100755 +--- a/src/config/SSSDConfigTest.py ++++ b/src/config/SSSDConfigTest.py +@@ -494,6 +494,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): + 'command', + 'enumerate', + 'cache_credentials', ++ 'cache_credentials_minimal_first_factor_length', + 'store_legacy_passwords', + 'use_fully_qualified_names', + 'ignore_group_members', +@@ -853,6 +854,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): + 'command', + 'enumerate', + 'cache_credentials', ++ 'cache_credentials_minimal_first_factor_length', + 'store_legacy_passwords', + 'use_fully_qualified_names', + 'ignore_group_members', +diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf +index 4fa542704fbd3af065843e777b84b6305ec3e78b..f1ac6366c73c03fe5c60c79bfe7b15fde9382215 100644 +--- a/src/config/etc/sssd.api.conf ++++ b/src/config/etc/sssd.api.conf +@@ -110,6 +110,7 @@ subdomain_enumerate = str, None, false + force_timeout = int, None, false + offline_timeout = int, None, false + cache_credentials = bool, None, false ++cache_credentials_minimal_first_factor_length = int, None, false + store_legacy_passwords = bool, None, false + use_fully_qualified_names = bool, None, false + ignore_group_members = bool, None, false +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index bb4c1d3c65818d8d949482569868e14cf60c5db5..ce21956c0ea8af3ccf2c764aad7906b0d7a7f655 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -1389,6 +1389,28 @@ pam_account_expired_message = Account expired, please call help desk. + + + ++ ++ ++ cache_credentials_minimal_first_factor_length (int) ++ ++ ++ If 2-Factor-Authentication (2FA) is used and ++ credentials should be saved this value determines ++ the minimal lenght the first authentication factor ++ (long term password) must have to be saved as SHA512 ++ hash into the cache. ++ ++ ++ This should avoid that the short PINs of a PIN based ++ 2FA scheme are saved in the cache which would make ++ them easy targets for brute-force attacks. ++ ++ ++ Default: 8 ++ ++ ++ ++ + + account_cache_expiration (integer) + +diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c +index 9fb2110eb34c7e7f5d9933f1aabed43970be1149..c25ef53e280785e81e36f111c2bf09fd88148292 100644 +--- a/src/util/domain_info_utils.c ++++ b/src/util/domain_info_utils.c +@@ -288,6 +288,8 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx, + dom->id_max = parent->id_max ? parent->id_max : 0xffffffff; + dom->pwd_expiration_warning = parent->pwd_expiration_warning; + dom->cache_credentials = parent->cache_credentials; ++ dom->cache_credentials_min_ff_length = ++ parent->cache_credentials_min_ff_length; + dom->case_sensitive = false; + dom->user_timeout = parent->user_timeout; + dom->group_timeout = parent->group_timeout; +-- +2.4.0 + diff --git a/0109-sysdb-add-sysdb_cache_password_ex.patch b/0109-sysdb-add-sysdb_cache_password_ex.patch new file mode 100644 index 0000000..ca1efa2 --- /dev/null +++ b/0109-sysdb-add-sysdb_cache_password_ex.patch @@ -0,0 +1,174 @@ +From 54807b916daa84e4779f7d1ca209196fbc726376 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 24 Mar 2015 15:35:01 +0100 +Subject: [PATCH 109/114] sysdb: add sysdb_cache_password_ex() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 55b7fdd837a780ab0f71cbfaa2403f4626993922) +--- + src/db/sysdb.h | 9 +++++++++ + src/db/sysdb_ops.c | 25 ++++++++++++++++++++--- + src/tests/sysdb-tests.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 84 insertions(+), 3 deletions(-) + +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index a1b6f207399555c85c14c8decf89edc498deb871..63d6d3cdc0baf49dff86a1aa62f61a4eacee7465 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -24,6 +24,7 @@ + + #include "util/util.h" + #include "confdb/confdb.h" ++#include "sss_client/sss_cli.h" + #include + + #define CACHE_SYSDB_FILE "cache_%s.ldb" +@@ -105,6 +106,8 @@ + #define SYSDB_SERVERHOSTNAME "serverHostname" + + #define SYSDB_CACHEDPWD "cachedPassword" ++#define SYSDB_CACHEDPWD_TYPE "cachedPasswordType" ++#define SYSDB_CACHEDPWD_FA2_LEN "cachedPasswordSecondFactorLen" + + #define SYSDB_UUID "uniqueID" + #define SYSDB_SID "objectSID" +@@ -888,6 +891,12 @@ int sysdb_cache_password(struct sss_domain_info *domain, + const char *username, + const char *password); + ++int sysdb_cache_password_ex(struct sss_domain_info *domain, ++ const char *username, ++ const char *password, ++ enum sss_authtok_type authtok_type, ++ size_t second_factor_size); ++ + errno_t check_failed_login_attempts(struct confdb_ctx *cdb, + struct ldb_message *ldb_msg, + uint32_t *failed_login_attempts, +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index ea786d59158eb8a82952c7e457ea83286abbf2c4..083d2778c97fe4d6149e4fc030885c482c511105 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -2226,9 +2226,11 @@ int sysdb_remove_group_member(struct sss_domain_info *domain, + + /* =Password-Caching====================================================== */ + +-int sysdb_cache_password(struct sss_domain_info *domain, +- const char *username, +- const char *password) ++int sysdb_cache_password_ex(struct sss_domain_info *domain, ++ const char *username, ++ const char *password, ++ enum sss_authtok_type authtok_type, ++ size_t second_factor_len) + { + TALLOC_CTX *tmp_ctx; + struct sysdb_attrs *attrs; +@@ -2261,6 +2263,15 @@ int sysdb_cache_password(struct sss_domain_info *domain, + ret = sysdb_attrs_add_string(attrs, SYSDB_CACHEDPWD, hash); + if (ret) goto fail; + ++ ret = sysdb_attrs_add_long(attrs, SYSDB_CACHEDPWD_TYPE, authtok_type); ++ if (ret) goto fail; ++ ++ if (authtok_type == SSS_AUTHTOK_TYPE_2FA && second_factor_len > 0) { ++ ret = sysdb_attrs_add_long(attrs, SYSDB_CACHEDPWD_FA2_LEN, ++ second_factor_len); ++ if (ret) goto fail; ++ } ++ + /* FIXME: should we use a different attribute for chache passwords ?? */ + ret = sysdb_attrs_add_long(attrs, "lastCachedPasswordChange", + (long)time(NULL)); +@@ -2285,6 +2296,14 @@ fail: + return ret; + } + ++int sysdb_cache_password(struct sss_domain_info *domain, ++ const char *username, ++ const char *password) ++{ ++ return sysdb_cache_password_ex(domain, username, password, ++ SSS_AUTHTOK_TYPE_PASSWORD, 0); ++} ++ + /* =Custom Search================== */ + + int sysdb_search_custom(TALLOC_CTX *mem_ctx, +diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c +index 450a9d1d693135c296f3433d905d1aba115548b8..3d5e97afbfaa5441281ef193d072122204db0514 100644 +--- a/src/tests/sysdb-tests.c ++++ b/src/tests/sysdb-tests.c +@@ -1808,6 +1808,57 @@ START_TEST (test_sysdb_cache_password) + } + END_TEST + ++START_TEST (test_sysdb_cache_password_ex) ++{ ++ struct sysdb_test_ctx *test_ctx; ++ struct test_data *data; ++ int ret; ++ struct ldb_result *res; ++ const char *attrs[] = { SYSDB_CACHEDPWD_TYPE, SYSDB_CACHEDPWD_FA2_LEN, ++ NULL }; ++ int val; ++ ++ /* Setup */ ++ ret = setup_sysdb_tests(&test_ctx); ++ fail_unless(ret == EOK, "Could not set up the test"); ++ ++ data = talloc_zero(test_ctx, struct test_data); ++ data->ctx = test_ctx; ++ data->ev = test_ctx->ev; ++ data->username = talloc_asprintf(data, "testuser%d", _i); ++ ++ ret = sysdb_get_user_attr(test_ctx, test_ctx->domain, data->username, ++ attrs, &res); ++ fail_unless(ret == EOK, "sysdb_get_user_attr request failed [%d].", ret); ++ ++ val = ldb_msg_find_attr_as_int(res->msgs[0], SYSDB_CACHEDPWD_TYPE, 0); ++ fail_unless(val == SSS_AUTHTOK_TYPE_PASSWORD, ++ "Unexptected authtok type, found [%d], expected [%d].", ++ val, SSS_AUTHTOK_TYPE_PASSWORD); ++ ++ ret = sysdb_cache_password_ex(test_ctx->domain, data->username, ++ data->username, SSS_AUTHTOK_TYPE_2FA, 12); ++ ++ fail_unless(ret == EOK, "sysdb_cache_password request failed [%d].", ret); ++ ++ ret = sysdb_get_user_attr(test_ctx, test_ctx->domain, data->username, ++ attrs, &res); ++ fail_unless(ret == EOK, "sysdb_get_user_attr request failed [%d].", ret); ++ ++ val = ldb_msg_find_attr_as_int(res->msgs[0], SYSDB_CACHEDPWD_TYPE, 0); ++ fail_unless(val == SSS_AUTHTOK_TYPE_2FA, ++ "Unexptected authtok type, found [%d], expected [%d].", ++ val, SSS_AUTHTOK_TYPE_2FA); ++ ++ val = ldb_msg_find_attr_as_int(res->msgs[0], SYSDB_CACHEDPWD_FA2_LEN, 0); ++ fail_unless(val == 12, ++ "Unexptected second factor lenght, found [%d], expected [%d].", ++ val, 12); ++ ++ talloc_free(test_ctx); ++} ++END_TEST ++ + static void cached_authentication_without_expiration(const char *username, + const char *password, + int expected_result) +@@ -6256,6 +6307,8 @@ Suite *create_sysdb_suite(void) + 27010, 27011); + tcase_add_loop_test(tc_sysdb, test_sysdb_cached_authentication, 27010, 27011); + ++ tcase_add_loop_test(tc_sysdb, test_sysdb_cache_password_ex, 27010, 27011); ++ + /* ASQ search test */ + tcase_add_loop_test(tc_sysdb, test_sysdb_prepare_asq_test_user, 28011, 28020); + tcase_add_test(tc_sysdb, test_sysdb_asq_search); +-- +2.4.0 + diff --git a/0110-krb5-save-hash-of-the-first-authentication-factor-to.patch b/0110-krb5-save-hash-of-the-first-authentication-factor-to.patch new file mode 100644 index 0000000..a3f5d2e --- /dev/null +++ b/0110-krb5-save-hash-of-the-first-authentication-factor-to.patch @@ -0,0 +1,76 @@ +From a6eb7ef2ee5e07dc4b8b1cc15423d4774bfd7bd1 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 24 Mar 2015 15:53:17 +0100 +Subject: [PATCH 110/114] krb5: save hash of the first authentication factor to + the cache +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit c5ae04b2da970a3991f21173acae3e892198ce0c) +--- + src/providers/krb5/krb5_auth.c | 26 +++++++++++++++++++++++--- + 1 file changed, 23 insertions(+), 3 deletions(-) + +diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c +index 651a9201756be8bc80199636b494b2773cff9c3e..b003a8a000117722078d299127cf60337a016ca5 100644 +--- a/src/providers/krb5/krb5_auth.c ++++ b/src/providers/krb5/krb5_auth.c +@@ -265,6 +265,9 @@ static void krb5_auth_store_creds(struct sss_domain_info *domain, + struct pam_data *pd) + { + const char *password = NULL; ++ const char *fa2; ++ size_t password_len; ++ size_t fa2_len = 0; + int ret = EOK; + + switch(pd->cmd) { +@@ -276,7 +279,20 @@ static void krb5_auth_store_creds(struct sss_domain_info *domain, + break; + case SSS_PAM_AUTHENTICATE: + case SSS_PAM_CHAUTHTOK_PRELIM: +- ret = sss_authtok_get_password(pd->authtok, &password, NULL); ++ if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_2FA) { ++ ret = sss_authtok_get_2fa(pd->authtok, &password, &password_len, ++ &fa2, &fa2_len); ++ if (ret == EOK && password_len < ++ domain->cache_credentials_min_ff_length) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "First factor is too short to be cache, " ++ "minimum length is [%u].\n", ++ domain->cache_credentials_min_ff_length); ++ ret = EINVAL; ++ } ++ } else { ++ ret = sss_authtok_get_password(pd->authtok, &password, NULL); ++ } + break; + case SSS_PAM_CHAUTHTOK: + ret = sss_authtok_get_password(pd->newauthtok, &password, NULL); +@@ -302,7 +318,8 @@ static void krb5_auth_store_creds(struct sss_domain_info *domain, + return; + } + +- ret = sysdb_cache_password(domain, pd->user, password); ++ ret = sysdb_cache_password_ex(domain, pd->user, password, ++ sss_authtok_get_type(pd->authtok), fa2_len); + if (ret) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to cache password, offline auth may not work." +@@ -1018,7 +1035,10 @@ static void krb5_auth_done(struct tevent_req *subreq) + goto done; + } + +- if (state->be_ctx->domain->cache_credentials == TRUE && !res->otp) { ++ if (state->be_ctx->domain->cache_credentials == TRUE ++ && (!res->otp ++ || (res->otp && sss_authtok_get_type(pd->authtok) == ++ SSS_AUTHTOK_TYPE_2FA))) { + krb5_auth_store_creds(state->domain, pd); + } + +-- +2.4.0 + diff --git a/0111-krb5-try-delayed-online-authentication-only-for-sing.patch b/0111-krb5-try-delayed-online-authentication-only-for-sing.patch new file mode 100644 index 0000000..f3a8607 --- /dev/null +++ b/0111-krb5-try-delayed-online-authentication-only-for-sing.patch @@ -0,0 +1,36 @@ +From e1ad152a8a5b305f5a0267eba7fc9d300d19a4f1 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 25 Mar 2015 12:04:57 +0100 +Subject: [PATCH 111/114] krb5: try delayed online authentication only for + single factor auth +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 2d0e7658198d1aa6e3926bf967ff683660249114) +--- + src/providers/krb5/krb5_auth.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c +index b003a8a000117722078d299127cf60337a016ca5..91989df428b2a574a0e45ec01569cf94f7841725 100644 +--- a/src/providers/krb5/krb5_auth.c ++++ b/src/providers/krb5/krb5_auth.c +@@ -207,6 +207,13 @@ static void krb5_auth_cache_creds(struct krb5_ctx *krb5_ctx, + const char *password = NULL; + errno_t ret; + ++ if (sss_authtok_get_type(pd->authtok) != SSS_AUTHTOK_TYPE_PASSWORD) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Delayed authentication is only available for password " ++ "authentication (single factor).\n"); ++ return; ++ } ++ + ret = sss_authtok_get_password(pd->authtok, &password, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, +-- +2.4.0 + diff --git a/0112-2FA-offline-auth.patch b/0112-2FA-offline-auth.patch new file mode 100644 index 0000000..85d99d1 --- /dev/null +++ b/0112-2FA-offline-auth.patch @@ -0,0 +1,173 @@ +From d87a4bdeaa480ce5a2effe33e8e93cea5de715c9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 27 Mar 2015 15:20:13 +0100 +Subject: [PATCH 112/114] 2FA offline auth +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit 219f5b698fa72c0d5a8da2b0dd99daec3f924c94) +--- + src/db/sysdb_ops.c | 77 ++++++++++++++++++++++++++++++++++++++++-- + src/responder/pam/pamsrv_cmd.c | 35 +++++++++++++++++-- + 2 files changed, 107 insertions(+), 5 deletions(-) + +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index 083d2778c97fe4d6149e4fc030885c482c511105..ed936e0fbe4451e9813402466d4850f0f586c1f5 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -3155,6 +3155,76 @@ done: + return ret; + } + ++static errno_t check_for_combined_2fa_password(struct sss_domain_info *domain, ++ struct ldb_message *ldb_msg, ++ const char *password, ++ const char *userhash) ++{ ++ ++ unsigned int cached_authtok_type; ++ unsigned int cached_fa2_len; ++ char *short_pw; ++ char *comphash; ++ size_t pw_len; ++ TALLOC_CTX *tmp_ctx; ++ int ret; ++ ++ cached_authtok_type = ldb_msg_find_attr_as_uint(ldb_msg, ++ SYSDB_CACHEDPWD_TYPE, ++ SSS_AUTHTOK_TYPE_EMPTY); ++ if (cached_authtok_type != SSS_AUTHTOK_TYPE_2FA) { ++ DEBUG(SSSDBG_TRACE_LIBS, "Wrong authtok type.\n"); ++ return EINVAL; ++ } ++ ++ cached_fa2_len = ldb_msg_find_attr_as_uint(ldb_msg, SYSDB_CACHEDPWD_FA2_LEN, ++ 0); ++ if (cached_fa2_len == 0) { ++ DEBUG(SSSDBG_TRACE_LIBS, "Second factor size not available.\n"); ++ return EINVAL; ++ } ++ ++ pw_len = strlen(password); ++ if (pw_len < cached_fa2_len + domain->cache_credentials_min_ff_length) { ++ DEBUG(SSSDBG_TRACE_LIBS, "Password too short.\n"); ++ return EINVAL; ++ } ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); ++ return ENOMEM; ++ } ++ ++ short_pw = talloc_strndup(tmp_ctx, password, (pw_len - cached_fa2_len)); ++ if (short_pw == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = s3crypt_sha512(tmp_ctx, short_pw, userhash, &comphash); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CONF_SETTINGS, "Failed to create password hash.\n"); ++ ret = ERR_INTERNAL; ++ goto done; ++ } ++ ++ if (strcmp(userhash, comphash) != 0) { ++ DEBUG(SSSDBG_MINOR_FAILURE, ++ "Hash of shorten password does not match.\n"); ++ ret = ERR_AUTH_FAILED; ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} ++ + int sysdb_cache_auth(struct sss_domain_info *domain, + const char *name, + const char *password, +@@ -3168,7 +3238,8 @@ int sysdb_cache_auth(struct sss_domain_info *domain, + SYSDB_LAST_LOGIN, SYSDB_LAST_ONLINE_AUTH, + "lastCachedPasswordChange", + "accountExpires", SYSDB_FAILED_LOGIN_ATTEMPTS, +- SYSDB_LAST_FAILED_LOGIN, NULL }; ++ SYSDB_LAST_FAILED_LOGIN, SYSDB_CACHEDPWD_TYPE, ++ SYSDB_CACHEDPWD_FA2_LEN, NULL }; + struct ldb_message *ldb_msg; + const char *userhash; + char *comphash; +@@ -3279,7 +3350,9 @@ int sysdb_cache_auth(struct sss_domain_info *domain, + goto done; + } + +- if (strcmp(userhash, comphash) == 0) { ++ if (strcmp(userhash, comphash) == 0 ++ || check_for_combined_2fa_password(domain, ldb_msg, ++ password, userhash) == EOK) { + /* TODO: probable good point for audit logging */ + DEBUG(SSSDBG_CONF_SETTINGS, "Hashes do match!\n"); + authentication_successful = true; +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index c7eb697f29b6de9f7edaaf7715a58d2b7afdc733..e8d2b65fe429bcb390f33ef994934f9b82b1a4b7 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -528,6 +528,34 @@ static void pam_reply_delay(struct tevent_context *ev, struct tevent_timer *te, + pam_reply(preq); + } + ++static errno_t get_password_for_cache_auth(struct sss_auth_token *authtok, ++ const char **password) ++{ ++ int ret; ++ size_t pw_len; ++ const char *fa2; ++ size_t fa2_len; ++ ++ switch (sss_authtok_get_type(authtok)) { ++ case SSS_AUTHTOK_TYPE_PASSWORD: ++ ret = sss_authtok_get_password(authtok, password, NULL); ++ break; ++ case SSS_AUTHTOK_TYPE_2FA: ++ ret = sss_authtok_get_2fa(authtok, password, &pw_len, &fa2, &fa2_len); ++ break; ++ default: ++ DEBUG(SSSDBG_FATAL_FAILURE, "Unsupported auth token type [%d].\n", ++ sss_authtok_get_type(authtok)); ++ ret = EINVAL; ++ } ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get password.\n"); ++ return ret; ++ } ++ ++ return EOK; ++} ++ + static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd); + static void pam_handle_cached_login(struct pam_auth_req *preq, int ret, + time_t expire_date, time_t delayed_until); +@@ -586,9 +614,10 @@ static void pam_reply(struct pam_auth_req *preq) + goto done; + } + +- ret = sss_authtok_get_password(pd->authtok, &password, NULL); +- if (ret) { +- DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get password.\n"); ++ ret = get_password_for_cache_auth(pd->authtok, &password); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_FATAL_FAILURE, ++ "get_password_and_type_for_cache_auth failed.\n"); + goto done; + } + +-- +2.4.0 + diff --git a/0113-pam_sss-move-message-encoding-into-separate-file.patch b/0113-pam_sss-move-message-encoding-into-separate-file.patch new file mode 100644 index 0000000..6deebd2 --- /dev/null +++ b/0113-pam_sss-move-message-encoding-into-separate-file.patch @@ -0,0 +1,502 @@ +From 5f7544073f03badac2c2c9f20bcff67aff003fe8 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 18 Mar 2015 16:02:47 +0100 +Subject: [PATCH 113/114] pam_sss: move message encoding into separate file +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit bf6c3f07d653d474da9e43b2b7cced57fc4ea069) +--- + Makefile.am | 2 + + src/sss_client/pam_message.c | 178 +++++++++++++++++++++++++++++++++++++++++++ + src/sss_client/pam_message.h | 61 +++++++++++++++ + src/sss_client/pam_sss.c | 177 +----------------------------------------- + 4 files changed, 242 insertions(+), 176 deletions(-) + create mode 100644 src/sss_client/pam_message.c + create mode 100644 src/sss_client/pam_message.h + +diff --git a/Makefile.am b/Makefile.am +index 312901da3315e2d0471055541a114a8be36dc976..84819fc6a20d9e713786a55c2b6aa909405aa459 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -614,6 +614,7 @@ dist_noinst_HEADERS = \ + src/tests/cmocka/common_mock_resp.h \ + src/tests/cmocka/common_mock_sdap.h \ + src/tests/cmocka/common_mock_sysdb_objects.h \ ++ src/sss_client/pam_message.h \ + src/sss_client/ssh/sss_ssh_client.h \ + src/sss_client/sudo/sss_sudo.h \ + src/sss_client/libwbclient/libwbclient.h \ +@@ -2350,6 +2351,7 @@ endif + pamlib_LTLIBRARIES = pam_sss.la + pam_sss_la_SOURCES = \ + src/sss_client/pam_sss.c \ ++ src/sss_client/pam_message.c \ + src/sss_client/common.c \ + src/sss_client/sss_cli.h \ + src/util/atomic_io.c \ +diff --git a/src/sss_client/pam_message.c b/src/sss_client/pam_message.c +new file mode 100644 +index 0000000000000000000000000000000000000000..b8104c680d0e733b713c665e6206dc4b0d379237 +--- /dev/null ++++ b/src/sss_client/pam_message.c +@@ -0,0 +1,178 @@ ++/* ++ Authors: ++ Sumit Bose ++ ++ PAM client - create message blob ++ ++ Copyright (C) 2015 Red Hat ++ ++ 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 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 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, see . ++*/ ++ ++#include ++#include ++ ++#include "sss_pam_compat.h" ++#include "sss_pam_macros.h" ++ ++#include "pam_message.h" ++ ++#include "sss_cli.h" ++ ++static size_t add_authtok_item(enum pam_item_type type, ++ enum sss_authtok_type authtok_type, ++ const char *tok, const size_t size, ++ uint8_t *buf) ++{ ++ size_t rp = 0; ++ uint32_t c; ++ ++ if (tok == NULL) return 0; ++ ++ c = type; ++ memcpy(&buf[rp], &c, sizeof(uint32_t)); ++ rp += sizeof(uint32_t); ++ ++ c = size + sizeof(uint32_t); ++ memcpy(&buf[rp], &c, sizeof(uint32_t)); ++ rp += sizeof(uint32_t); ++ ++ c = authtok_type; ++ memcpy(&buf[rp], &c, sizeof(uint32_t)); ++ rp += sizeof(uint32_t); ++ ++ memcpy(&buf[rp], tok, size); ++ rp += size; ++ ++ return rp; ++} ++ ++static size_t add_uint32_t_item(enum pam_item_type type, const uint32_t val, ++ uint8_t *buf) ++{ ++ size_t rp = 0; ++ uint32_t c; ++ ++ c = type; ++ memcpy(&buf[rp], &c, sizeof(uint32_t)); ++ rp += sizeof(uint32_t); ++ ++ c = sizeof(uint32_t); ++ memcpy(&buf[rp], &c, sizeof(uint32_t)); ++ rp += sizeof(uint32_t); ++ ++ c = val; ++ memcpy(&buf[rp], &c, sizeof(uint32_t)); ++ rp += sizeof(uint32_t); ++ ++ return rp; ++} ++ ++static size_t add_string_item(enum pam_item_type type, const char *str, ++ const size_t size, uint8_t *buf) ++{ ++ size_t rp = 0; ++ uint32_t c; ++ ++ if (str == NULL || *str == '\0') return 0; ++ ++ c = type; ++ memcpy(&buf[rp], &c, sizeof(uint32_t)); ++ rp += sizeof(uint32_t); ++ ++ c = size; ++ memcpy(&buf[rp], &c, sizeof(uint32_t)); ++ rp += sizeof(uint32_t); ++ ++ memcpy(&buf[rp], str, size); ++ rp += size; ++ ++ return rp; ++} ++ ++int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer) ++{ ++ int len; ++ uint8_t *buf; ++ size_t rp; ++ ++ len = sizeof(uint32_t) + ++ 2*sizeof(uint32_t) + pi->pam_user_size + ++ sizeof(uint32_t); ++ len += *pi->pam_service != '\0' ? ++ 2*sizeof(uint32_t) + pi->pam_service_size : 0; ++ len += *pi->pam_tty != '\0' ? ++ 2*sizeof(uint32_t) + pi->pam_tty_size : 0; ++ len += *pi->pam_ruser != '\0' ? ++ 2*sizeof(uint32_t) + pi->pam_ruser_size : 0; ++ len += *pi->pam_rhost != '\0' ? ++ 2*sizeof(uint32_t) + pi->pam_rhost_size : 0; ++ len += pi->pam_authtok != NULL ? ++ 3*sizeof(uint32_t) + pi->pam_authtok_size : 0; ++ len += pi->pam_newauthtok != NULL ? ++ 3*sizeof(uint32_t) + pi->pam_newauthtok_size : 0; ++ len += 3*sizeof(uint32_t); /* cli_pid */ ++ len += *pi->requested_domains != '\0' ? ++ 2*sizeof(uint32_t) + pi->requested_domains_size : 0; ++ ++ buf = malloc(len); ++ if (buf == NULL) { ++ D(("malloc failed.")); ++ return PAM_BUF_ERR; ++ } ++ ++ rp = 0; ++ SAFEALIGN_SETMEM_UINT32(buf, SSS_START_OF_PAM_REQUEST, &rp); ++ ++ rp += add_string_item(SSS_PAM_ITEM_USER, pi->pam_user, pi->pam_user_size, ++ &buf[rp]); ++ ++ rp += add_string_item(SSS_PAM_ITEM_SERVICE, pi->pam_service, ++ pi->pam_service_size, &buf[rp]); ++ ++ rp += add_string_item(SSS_PAM_ITEM_TTY, pi->pam_tty, pi->pam_tty_size, ++ &buf[rp]); ++ ++ rp += add_string_item(SSS_PAM_ITEM_RUSER, pi->pam_ruser, pi->pam_ruser_size, ++ &buf[rp]); ++ ++ rp += add_string_item(SSS_PAM_ITEM_RHOST, pi->pam_rhost, pi->pam_rhost_size, ++ &buf[rp]); ++ ++ rp += add_string_item(SSS_PAM_ITEM_REQUESTED_DOMAINS, pi->requested_domains, pi->requested_domains_size, ++ &buf[rp]); ++ ++ rp += add_uint32_t_item(SSS_PAM_ITEM_CLI_PID, (uint32_t) pi->cli_pid, ++ &buf[rp]); ++ ++ rp += add_authtok_item(SSS_PAM_ITEM_AUTHTOK, pi->pam_authtok_type, ++ pi->pam_authtok, pi->pam_authtok_size, &buf[rp]); ++ ++ rp += add_authtok_item(SSS_PAM_ITEM_NEWAUTHTOK, pi->pam_newauthtok_type, ++ pi->pam_newauthtok, pi->pam_newauthtok_size, ++ &buf[rp]); ++ ++ SAFEALIGN_SETMEM_UINT32(buf + rp, SSS_END_OF_PAM_REQUEST, &rp); ++ ++ if (rp != len) { ++ D(("error during packet creation.")); ++ free(buf); ++ return PAM_BUF_ERR; ++ } ++ ++ *size = len; ++ *buffer = buf; ++ ++ return 0; ++} +diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h +new file mode 100644 +index 0000000000000000000000000000000000000000..8ade6d871b840d4d0153bbf56e0d458861ab3816 +--- /dev/null ++++ b/src/sss_client/pam_message.h +@@ -0,0 +1,61 @@ ++/* ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2015 Red Hat ++ ++ PAM client - create message blob ++ ++ 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 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 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, see . ++*/ ++ ++#ifndef _PAM_MESSAGE_H_ ++#define _PAM_MESSAGE_H_ ++ ++#include ++#include ++ ++struct pam_items { ++ const char *pam_service; ++ const char *pam_user; ++ const char *pam_tty; ++ const char *pam_ruser; ++ const char *pam_rhost; ++ char *pam_authtok; ++ char *pam_newauthtok; ++ const char *pamstack_authtok; ++ const char *pamstack_oldauthtok; ++ size_t pam_service_size; ++ size_t pam_user_size; ++ size_t pam_tty_size; ++ size_t pam_ruser_size; ++ size_t pam_rhost_size; ++ int pam_authtok_type; ++ size_t pam_authtok_size; ++ int pam_newauthtok_type; ++ size_t pam_newauthtok_size; ++ pid_t cli_pid; ++ const char *login_name; ++ char *domain_name; ++ const char *requested_domains; ++ size_t requested_domains_size; ++ char *otp_vendor; ++ char *otp_token_id; ++ char *otp_challenge; ++ char *first_factor; ++}; ++ ++int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer); ++ ++#endif /* _PAM_MESSAGE_H_ */ +diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c +index f11871a47d1b29f44c179e57a33d8f41be79078d..e01c5031650d3837a23f8a7404d334a9d2f55441 100644 +--- a/src/sss_client/pam_sss.c ++++ b/src/sss_client/pam_sss.c +@@ -40,6 +40,7 @@ + #include "sss_pam_macros.h" + + #include "sss_cli.h" ++#include "pam_message.h" + #include "util/atomic_io.h" + #include "util/authtok-utils.h" + +@@ -65,36 +66,6 @@ + #define EXP_ACC_MSG _("Permission denied. ") + #define SRV_MSG _("Server message: ") + +-struct pam_items { +- const char* pam_service; +- const char* pam_user; +- const char* pam_tty; +- const char* pam_ruser; +- const char* pam_rhost; +- char* pam_authtok; +- char* pam_newauthtok; +- const char* pamstack_authtok; +- const char* pamstack_oldauthtok; +- size_t pam_service_size; +- size_t pam_user_size; +- size_t pam_tty_size; +- size_t pam_ruser_size; +- size_t pam_rhost_size; +- int pam_authtok_type; +- size_t pam_authtok_size; +- int pam_newauthtok_type; +- size_t pam_newauthtok_size; +- pid_t cli_pid; +- const char *login_name; +- char *domain_name; +- const char *requested_domains; +- size_t requested_domains_size; +- char *otp_vendor; +- char *otp_token_id; +- char *otp_challenge; +- char *first_factor; +-}; +- + #define DEBUG_MGS_LEN 1024 + #define MAX_AUTHTOK_SIZE (1024*1024) + #define CHECK_AND_RETURN_PI_STRING(s) ((s != NULL && *s != '\0')? s : "(not available)") +@@ -146,75 +117,6 @@ static void close_fd(pam_handle_t *pamh, void *ptr, int err) + sss_pam_close_fd(); + } + +-static size_t add_authtok_item(enum pam_item_type type, +- enum sss_authtok_type authtok_type, +- const char *tok, const size_t size, +- uint8_t *buf) { +- size_t rp=0; +- uint32_t c; +- +- if (tok == NULL) return 0; +- +- c = type; +- memcpy(&buf[rp], &c, sizeof(uint32_t)); +- rp += sizeof(uint32_t); +- +- c = size + sizeof(uint32_t); +- memcpy(&buf[rp], &c, sizeof(uint32_t)); +- rp += sizeof(uint32_t); +- +- c = authtok_type; +- memcpy(&buf[rp], &c, sizeof(uint32_t)); +- rp += sizeof(uint32_t); +- +- memcpy(&buf[rp], tok, size); +- rp += size; +- +- return rp; +-} +- +- +-static size_t add_uint32_t_item(enum pam_item_type type, const uint32_t val, +- uint8_t *buf) { +- size_t rp=0; +- uint32_t c; +- +- c = type; +- memcpy(&buf[rp], &c, sizeof(uint32_t)); +- rp += sizeof(uint32_t); +- +- c = sizeof(uint32_t); +- memcpy(&buf[rp], &c, sizeof(uint32_t)); +- rp += sizeof(uint32_t); +- +- c = val; +- memcpy(&buf[rp], &c, sizeof(uint32_t)); +- rp += sizeof(uint32_t); +- +- return rp; +-} +- +-static size_t add_string_item(enum pam_item_type type, const char *str, +- const size_t size, uint8_t *buf) { +- size_t rp=0; +- uint32_t c; +- +- if (str == NULL || *str == '\0') return 0; +- +- c = type; +- memcpy(&buf[rp], &c, sizeof(uint32_t)); +- rp += sizeof(uint32_t); +- +- c = size; +- memcpy(&buf[rp], &c, sizeof(uint32_t)); +- rp += sizeof(uint32_t); +- +- memcpy(&buf[rp], str, size); +- rp += size; +- +- return rp; +-} +- + static void overwrite_and_free_authtoks(struct pam_items *pi) + { + if (pi->pam_authtok != NULL) { +@@ -256,83 +158,6 @@ static void overwrite_and_free_pam_items(struct pam_items *pi) + pi->otp_challenge = NULL; + } + +-static int pack_message_v3(struct pam_items *pi, size_t *size, +- uint8_t **buffer) { +- int len; +- uint8_t *buf; +- size_t rp; +- +- len = sizeof(uint32_t) + +- 2*sizeof(uint32_t) + pi->pam_user_size + +- sizeof(uint32_t); +- len += *pi->pam_service != '\0' ? +- 2*sizeof(uint32_t) + pi->pam_service_size : 0; +- len += *pi->pam_tty != '\0' ? +- 2*sizeof(uint32_t) + pi->pam_tty_size : 0; +- len += *pi->pam_ruser != '\0' ? +- 2*sizeof(uint32_t) + pi->pam_ruser_size : 0; +- len += *pi->pam_rhost != '\0' ? +- 2*sizeof(uint32_t) + pi->pam_rhost_size : 0; +- len += pi->pam_authtok != NULL ? +- 3*sizeof(uint32_t) + pi->pam_authtok_size : 0; +- len += pi->pam_newauthtok != NULL ? +- 3*sizeof(uint32_t) + pi->pam_newauthtok_size : 0; +- len += 3*sizeof(uint32_t); /* cli_pid */ +- len += *pi->requested_domains != '\0' ? +- 2*sizeof(uint32_t) + pi->requested_domains_size : 0; +- +- +- buf = malloc(len); +- if (buf == NULL) { +- D(("malloc failed.")); +- return PAM_BUF_ERR; +- } +- +- rp = 0; +- SAFEALIGN_SETMEM_UINT32(buf, SSS_START_OF_PAM_REQUEST, &rp); +- +- rp += add_string_item(SSS_PAM_ITEM_USER, pi->pam_user, pi->pam_user_size, +- &buf[rp]); +- +- rp += add_string_item(SSS_PAM_ITEM_SERVICE, pi->pam_service, +- pi->pam_service_size, &buf[rp]); +- +- rp += add_string_item(SSS_PAM_ITEM_TTY, pi->pam_tty, pi->pam_tty_size, +- &buf[rp]); +- +- rp += add_string_item(SSS_PAM_ITEM_RUSER, pi->pam_ruser, pi->pam_ruser_size, +- &buf[rp]); +- +- rp += add_string_item(SSS_PAM_ITEM_RHOST, pi->pam_rhost, pi->pam_rhost_size, +- &buf[rp]); +- +- rp += add_string_item(SSS_PAM_ITEM_REQUESTED_DOMAINS, pi->requested_domains, pi->requested_domains_size, +- &buf[rp]); +- +- rp += add_uint32_t_item(SSS_PAM_ITEM_CLI_PID, (uint32_t) pi->cli_pid, +- &buf[rp]); +- +- rp += add_authtok_item(SSS_PAM_ITEM_AUTHTOK, pi->pam_authtok_type, +- pi->pam_authtok, pi->pam_authtok_size, &buf[rp]); +- +- rp += add_authtok_item(SSS_PAM_ITEM_NEWAUTHTOK, pi->pam_newauthtok_type, +- pi->pam_newauthtok, pi->pam_newauthtok_size, +- &buf[rp]); +- +- SAFEALIGN_SETMEM_UINT32(buf + rp, SSS_END_OF_PAM_REQUEST, &rp); +- +- if (rp != len) { +- D(("error during packet creation.")); +- free(buf); +- return PAM_BUF_ERR; +- } +- +- *size = len; +- *buffer = buf; +- +- return 0; +-} +- + static int null_strcmp(const char *s1, const char *s2) { + if (s1 == NULL && s2 == NULL) return 0; + if (s1 == NULL && s2 != NULL) return -1; +-- +2.4.0 + diff --git a/0114-PAM-add-PAM-responder-unit-test.patch b/0114-PAM-add-PAM-responder-unit-test.patch new file mode 100644 index 0000000..ccb9b9e --- /dev/null +++ b/0114-PAM-add-PAM-responder-unit-test.patch @@ -0,0 +1,1060 @@ +From b7232800065190677d3035e902c1d044f1de7fca Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 19 Mar 2015 13:12:11 +0100 +Subject: [PATCH 114/114] PAM: add PAM responder unit test +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Lukáš Slebodník +(cherry picked from commit ea422c7061072c125eb53b40d7f3ca444d886913) +--- + Makefile.am | 32 ++ + src/sss_client/sss_cli.h | 4 +- + src/tests/cmocka/test_pam_srv.c | 965 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 999 insertions(+), 2 deletions(-) + create mode 100644 src/tests/cmocka/test_pam_srv.c + +diff --git a/Makefile.am b/Makefile.am +index 84819fc6a20d9e713786a55c2b6aa909405aa459..5ab80197f21eaec566e4eaf3fdfc3c542374b7b7 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -220,6 +220,7 @@ if HAVE_CMOCKA + test_copy_keytab \ + test_child_common \ + test_fo_srv \ ++ pam-srv-tests \ + $(NULL) + + if HAVE_LIBRESOLV +@@ -1773,6 +1774,37 @@ nss_srv_tests_LDADD = \ + libsss_test_common.la \ + libsss_idmap.la + ++EXTRA_pam_srv_tests_DEPENDENCIES = \ ++ $(ldblib_LTLIBRARIES) \ ++ $(NULL) ++pam_srv_tests_SOURCES = \ ++ $(TEST_MOCK_RESP_OBJ) \ ++ src/tests/cmocka/test_pam_srv.c \ ++ src/sss_client/pam_message.c \ ++ src/responder/pam/pamsrv_cmd.c \ ++ src/responder/pam/pam_helpers.c \ ++ src/responder/pam/pamsrv_dp.c \ ++ src/responder/pam/pam_LOCAL_domain.c \ ++ $(NULL) ++pam_srv_tests_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(NULL) ++pam_srv_tests_LDFLAGS = \ ++ -Wl,-wrap,sss_packet_get_body \ ++ -Wl,-wrap,sss_packet_get_cmd \ ++ -Wl,-wrap,sss_cmd_send_empty \ ++ -Wl,-wrap,sss_cmd_done \ ++ -Wl,-wrap,pam_dp_send_req \ ++ $(NULL) ++pam_srv_tests_LDADD = \ ++ $(CMOCKA_LIBS) \ ++ $(PAM_LIBS) \ ++ $(SSSD_LIBS) \ ++ $(SSSD_INTERNAL_LTLIBS) \ ++ libsss_test_common.la \ ++ libsss_idmap.la \ ++ $(NULL) ++ + EXTRA_responder_get_domains_tests_DEPENDENCIES = \ + $(ldblib_LTLIBRARIES) + responder_get_domains_tests_SOURCES = \ +diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h +index 317700ef8cfcbb1b58e2a7d1ffcc7f00658fe815..6902d711e30ef327f1c7bcf8348ff419d3f63092 100644 +--- a/src/sss_client/sss_cli.h ++++ b/src/sss_client/sss_cli.h +@@ -430,12 +430,12 @@ enum response_type { + * - #SSS_PAM_USER_INFO_OFFLINE_CHPASS + * uint32_t | uint32_t | uint32_t + * ----------|----------|---------- +- * 0x06 | 0x01 | 0x03 ++ * 0x06 | 0x04 | 0x03 + * + * - #SSS_PAM_USER_INFO_CHPASS_ERROR + * uint32_t | uint32_t | uint32_t | uint32_t | uint8_t[3] + * ----------|----------|----------|----------|------------ +- * 0x06 | 0x05 | 0x04 | 0x03 | abc ++ * 0x06 | 0x0B | 0x04 | 0x03 | abc + * @{ + */ + +diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c +new file mode 100644 +index 0000000000000000000000000000000000000000..03faea249a31359d721e82cfd5bc2f9f375d0592 +--- /dev/null ++++ b/src/tests/cmocka/test_pam_srv.c +@@ -0,0 +1,965 @@ ++/* ++ Authors: ++ Sumit Bose ++ ++ Copyright (C) 2015 Red Hat ++ ++ SSSD tests: PAM responder tests ++ ++ 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 ++#include ++ ++#include "tests/cmocka/common_mock.h" ++#include "tests/cmocka/common_mock_resp.h" ++#include "responder/common/responder_packet.h" ++#include "responder/common/negcache.h" ++#include "responder/pam/pamsrv.h" ++#include "responder/pam/pam_helpers.h" ++#include "sss_client/pam_message.h" ++#include "sss_client/sss_cli.h" ++ ++#include "util/crypto/nss/nss_util.h" ++ ++#define TESTS_PATH "tests_pam" ++#define TEST_CONF_DB "test_pam_conf.ldb" ++#define TEST_DOM_NAME "pam_test" ++#define TEST_SUBDOM_NAME "test.subdomain" ++#define TEST_ID_PROVIDER "ldap" ++ ++struct pam_test_ctx { ++ struct sss_test_ctx *tctx; ++ struct sss_domain_info *subdom; ++ ++ struct resp_ctx *rctx; ++ struct cli_ctx *cctx; ++ struct sss_cmd_table *pam_cmds; ++ struct pam_ctx *pctx; ++ ++ int ncache_hits; ++ int exp_pam_status; ++}; ++ ++/* Must be global because it is needed in some wrappers */ ++struct pam_test_ctx *pam_test_ctx; ++ ++struct pam_ctx *mock_pctx(TALLOC_CTX *mem_ctx) ++{ ++ struct pam_ctx *pctx; ++ errno_t ret; ++ ++ pctx = talloc_zero(mem_ctx, struct pam_ctx); ++ assert_non_null(pctx); ++ ++ ret = sss_ncache_init(pctx, &pctx->ncache); ++ assert_int_equal(ret, EOK); ++ ++ pctx->neg_timeout = 10; ++ ++ ret = sss_hash_create(pctx, 10, &pctx->id_table); ++ assert_int_equal(ret, EOK); ++ ++ return pctx; ++} ++ ++void test_pam_setup(struct sss_test_conf_param params[], ++ void **state) ++{ ++ errno_t ret; ++ ++ pam_test_ctx = talloc_zero(NULL, struct pam_test_ctx); ++ assert_non_null(pam_test_ctx); ++ ++ pam_test_ctx->tctx = create_dom_test_ctx(pam_test_ctx, TESTS_PATH, ++ TEST_CONF_DB, TEST_DOM_NAME, ++ TEST_ID_PROVIDER, params); ++ assert_non_null(pam_test_ctx->tctx); ++ ++ pam_test_ctx->pam_cmds = get_pam_cmds(); ++ assert_non_null(pam_test_ctx->pam_cmds); ++ ++ /* FIXME - perhaps this should be folded into sssd_domain_init or stricty ++ * used together ++ */ ++ ret = sss_names_init(pam_test_ctx, pam_test_ctx->tctx->confdb, ++ TEST_DOM_NAME, &pam_test_ctx->tctx->dom->names); ++ assert_int_equal(ret, EOK); ++ ++ /* Initialize the PAM responder */ ++ pam_test_ctx->pctx = mock_pctx(pam_test_ctx); ++ assert_non_null(pam_test_ctx->pctx); ++ ++ pam_test_ctx->rctx = mock_rctx(pam_test_ctx, pam_test_ctx->tctx->ev, ++ pam_test_ctx->tctx->dom, pam_test_ctx->pctx); ++ assert_non_null(pam_test_ctx->rctx); ++ pam_test_ctx->rctx->cdb = pam_test_ctx->tctx->confdb; ++ pam_test_ctx->pctx->rctx = pam_test_ctx->rctx; ++ ++ /* Create client context */ ++ pam_test_ctx->cctx = mock_cctx(pam_test_ctx, pam_test_ctx->rctx); ++ assert_non_null(pam_test_ctx->cctx); ++ ++ pam_test_ctx->cctx->cli_protocol_version = register_cli_protocol_version(); ++} ++ ++static int pam_test_setup(void **state) ++{ ++ int ret; ++ ++ struct sss_test_conf_param params[] = { ++ { "enumerate", "false" }, ++ { "cache_credentials", "true" }, ++ { NULL, NULL }, /* Sentinel */ ++ }; ++ ++ test_pam_setup(params, state); ++ ++ /* Prime the cache with a valid user */ ++ ret = sysdb_add_user(pam_test_ctx->tctx->dom, ++ "pamuser", 123, 456, "pam user", ++ "/home/pamuser", "/bin/sh", NULL, ++ NULL, 300, 0); ++ assert_int_equal(ret, EOK); ++ ++ /* Add entry to the initgr cache to make sure no initgr request is sent to ++ * the backend */ ++ ret = pam_initgr_cache_set(pam_test_ctx->pctx->rctx->ev, ++ pam_test_ctx->pctx->id_table, ++ discard_const("pamuser"), ++ pam_test_ctx->pctx->id_timeout); ++ assert_int_equal(ret, EOK); ++ return 0; ++} ++ ++static int pam_test_teardown(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_delete_user(pam_test_ctx->tctx->dom, "pamuser", 0); ++ assert_int_equal(ret, EOK); ++ ++ talloc_free(pam_test_ctx); ++ return 0; ++} ++ ++typedef int (*cmd_cb_fn_t)(uint32_t, uint8_t *, size_t); ++ ++void __real_sss_packet_get_body(struct sss_packet *packet, ++ uint8_t **body, size_t *blen); ++ ++void __wrap_sss_packet_get_body(struct sss_packet *packet, ++ uint8_t **body, size_t *blen) ++{ ++ enum sss_test_wrapper_call wtype = sss_mock_type(enum sss_test_wrapper_call); ++ size_t len; ++ ++ if (wtype == WRAP_CALL_REAL) { ++ return __real_sss_packet_get_body(packet, body, blen); ++ } ++ ++ *body = sss_mock_ptr_type(uint8_t *); ++ len = sss_mock_type(size_t); ++ if (len == 0) { ++ len = strlen((const char *) *body) + 1; ++ } ++ *blen = len; ++ return; ++} ++ ++void __real_sss_packet_get_body(struct sss_packet *packet, ++ uint8_t **body, size_t *blen); ++ ++void __wrap_sss_cmd_done(struct cli_ctx *cctx, void *freectx) ++{ ++ struct sss_packet *packet = cctx->creq->out; ++ uint8_t *body; ++ size_t blen; ++ cmd_cb_fn_t check_cb; ++ ++ assert_non_null(packet); ++ ++ check_cb = sss_mock_ptr_type(cmd_cb_fn_t); ++ ++ __real_sss_packet_get_body(packet, &body, &blen); ++ ++ pam_test_ctx->tctx->error = check_cb(sss_packet_get_status(packet), ++ body, blen); ++ pam_test_ctx->tctx->done = true; ++} ++ ++enum sss_cli_command __wrap_sss_packet_get_cmd(struct sss_packet *packet) ++{ ++ return sss_mock_type(enum sss_cli_command); ++} ++ ++int __wrap_sss_cmd_send_empty(struct cli_ctx *cctx, TALLOC_CTX *freectx) ++{ ++ pam_test_ctx->tctx->done = true; ++ pam_test_ctx->tctx->error = ENOENT; ++ return EOK; ++} ++ ++static void set_cmd_cb(cmd_cb_fn_t fn) ++{ ++ will_return(__wrap_sss_cmd_done, fn); ++} ++ ++int __wrap_pam_dp_send_req(struct pam_auth_req *preq, int timeout) ++{ ++ ++ /* Set expected status */ ++ preq->pd->pam_status = pam_test_ctx->exp_pam_status; ++ ++ preq->callback(preq); ++ ++ return EOK; ++} ++ ++static void mock_input_pam(TALLOC_CTX *mem_ctx, const char *name, ++ const char *pwd, const char *fa2) ++{ ++ size_t buf_size; ++ uint8_t *m_buf; ++ uint8_t *buf; ++ struct pam_items pi = { 0 }; ++ int ret; ++ size_t needed_size; ++ uint8_t *authtok; ++ ++ pi.pam_user = name; ++ pi.pam_user_size = strlen(pi.pam_user) + 1; ++ ++ if (pwd != NULL) { ++ if (fa2 != NULL) { ++ ret = sss_auth_pack_2fa_blob(pwd, 0, fa2, 0, NULL, 0, &needed_size); ++ assert_int_equal(ret, EAGAIN); ++ ++ authtok = talloc_size(mem_ctx, needed_size); ++ assert_non_null(authtok); ++ ++ ret = sss_auth_pack_2fa_blob(pwd, 0, fa2, 0, authtok, ++ needed_size, &needed_size); ++ assert_int_equal(ret, EOK); ++ ++ pi.pam_authtok = (char *) authtok; ++ pi.pam_authtok_size = needed_size; ++ pi.pam_authtok_type = SSS_AUTHTOK_TYPE_2FA; ++ } else { ++ pi.pam_authtok = discard_const(pwd); ++ pi.pam_authtok_size = strlen(pi.pam_authtok) + 1; ++ pi.pam_authtok_type = SSS_AUTHTOK_TYPE_PASSWORD; ++ } ++ } ++ ++ pi.pam_service = "ssh"; ++ pi.pam_service_size = strlen(pi.pam_service) + 1; ++ pi.pam_tty = "/dev/tty"; ++ pi.pam_tty_size = strlen(pi.pam_tty) + 1; ++ pi.pam_ruser = "remuser"; ++ pi.pam_ruser_size = strlen(pi.pam_ruser) + 1; ++ pi.pam_rhost = "remhost"; ++ pi.pam_rhost_size = strlen(pi.pam_rhost) + 1; ++ pi.requested_domains = ""; ++ pi.cli_pid = 12345; ++ ++ ret = pack_message_v3(&pi, &buf_size, &m_buf); ++ assert_int_equal(ret, 0); ++ ++ buf = talloc_memdup(mem_ctx, m_buf, buf_size); ++ free(m_buf); ++ assert_non_null(buf); ++ ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_WRAPPER); ++ will_return(__wrap_sss_packet_get_body, buf); ++ will_return(__wrap_sss_packet_get_body, buf_size); ++} ++ ++static int test_pam_simple_check(uint32_t status, uint8_t *body, size_t blen) ++{ ++ size_t rp = 0; ++ uint32_t val; ++ ++ assert_int_equal(status, 0); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, pam_test_ctx->exp_pam_status); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, 1); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, SSS_PAM_DOMAIN_NAME); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, 9); ++ ++ assert_int_equal(*(body + rp + val - 1), 0); ++ assert_string_equal(body + rp, TEST_DOM_NAME); ++ ++ return EOK; ++} ++ ++static int test_pam_offline_chauthtok_check(uint32_t status, ++ uint8_t *body, size_t blen) ++{ ++ size_t rp = 0; ++ uint32_t val; ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHTOK_ERR; ++ ++ assert_int_equal(status, 0); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, pam_test_ctx->exp_pam_status); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, 2); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, SSS_PAM_DOMAIN_NAME); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, 9); ++ ++ assert_int_equal(*(body + rp + val - 1), 0); ++ assert_string_equal(body + rp, TEST_DOM_NAME); ++ rp += val; ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, SSS_PAM_USER_INFO); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, 4); ++ ++ SAFEALIGN_COPY_UINT32(&val, body + rp, &rp); ++ assert_int_equal(val, SSS_PAM_USER_INFO_OFFLINE_CHPASS); ++ ++ return EOK; ++} ++ ++ ++static int test_pam_failed_offline_auth_check(uint32_t status, uint8_t *body, ++ size_t blen) ++{ ++ pam_test_ctx->exp_pam_status = PAM_PERM_DENIED; ++ return test_pam_simple_check(status, body, blen); ++} ++ ++static int test_pam_successful_offline_auth_check(uint32_t status, ++ uint8_t *body, size_t blen) ++{ ++ pam_test_ctx->exp_pam_status = PAM_SUCCESS; ++ return test_pam_simple_check(status, body, blen); ++} ++ ++static int test_pam_wrong_pw_offline_auth_check(uint32_t status, ++ uint8_t *body, size_t blen) ++{ ++ pam_test_ctx->exp_pam_status = PAM_AUTH_ERR; ++ return test_pam_simple_check(status, body, blen); ++} ++ ++void test_pam_authenticate(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_setcreds(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_SETCRED); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_SETCRED, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_acct_mgmt(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_ACCT_MGMT); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_ACCT_MGMT, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_open_session(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_OPEN_SESSION); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_NO_MODULE_DATA; ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_OPEN_SESSION, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_close_session(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_CLOSE_SESSION); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_CLOSE_SESSION, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_chauthtok(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_CHAUTHTOK); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_CHAUTHTOK, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_chauthtok_prelim(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_CHAUTHTOK_PRELIM); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_CHAUTHTOK_PRELIM, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_preauth(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_PREAUTH); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ set_cmd_cb(test_pam_simple_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_PREAUTH, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_no_hash(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "12345", NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_failed_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_success(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password(pam_test_ctx->tctx->dom, "pamuser", "12345"); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "12345", NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_successful_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_wrong_pw(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password(pam_test_ctx->tctx->dom, "pamuser", "12345"); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "11111", NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_wrong_pw_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_success_2fa(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password(pam_test_ctx->tctx->dom, "pamuser", "12345"); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "12345", "abcde"); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_successful_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_failed_2fa(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password(pam_test_ctx->tctx->dom, "pamuser", "12345"); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "11111", "abcde"); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_wrong_pw_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_success_2fa_with_cached_2fa(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, "pamuser", "12345", ++ SSS_AUTHTOK_TYPE_2FA, 5); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "12345", "abcde"); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_successful_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_failed_2fa_with_cached_2fa(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, "pamuser", "12345", ++ SSS_AUTHTOK_TYPE_2FA, 5); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "11111", "abcde"); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_wrong_pw_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_success_pw_with_cached_2fa(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, "pamuser", "12345", ++ SSS_AUTHTOK_TYPE_2FA, 5); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "12345", NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_successful_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_failed_pw_with_cached_2fa(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, "pamuser", "12345", ++ SSS_AUTHTOK_TYPE_2FA, 5); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "11111", NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_wrong_pw_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_success_combined_pw_with_cached_2fa(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, "pamuser", ++ "12345678", SSS_AUTHTOK_TYPE_2FA, 5); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "12345678abcde", NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_successful_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_failed_combined_pw_with_cached_2fa(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, "pamuser", ++ "12345678", SSS_AUTHTOK_TYPE_2FA, 5); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "11111111abcde", NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_wrong_pw_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_auth_failed_wrong_2fa_size_with_cached_2fa(void **state) ++{ ++ int ret; ++ ++ ret = sysdb_cache_password_ex(pam_test_ctx->tctx->dom, "pamuser", ++ "12345678", SSS_AUTHTOK_TYPE_2FA, 5); ++ assert_int_equal(ret, EOK); ++ ++ mock_input_pam(pam_test_ctx, "pamuser", "12345678abcd", NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_AUTHENTICATE); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_wrong_pw_offline_auth_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_AUTHENTICATE, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_chauthtok_prelim(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_CHAUTHTOK_PRELIM); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_offline_chauthtok_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_CHAUTHTOK_PRELIM, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++void test_pam_offline_chauthtok(void **state) ++{ ++ int ret; ++ ++ mock_input_pam(pam_test_ctx, "pamuser", NULL, NULL); ++ ++ will_return(__wrap_sss_packet_get_cmd, SSS_PAM_CHAUTHTOK); ++ will_return(__wrap_sss_packet_get_body, WRAP_CALL_REAL); ++ ++ pam_test_ctx->exp_pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ set_cmd_cb(test_pam_offline_chauthtok_check); ++ ret = sss_cmd_execute(pam_test_ctx->cctx, SSS_PAM_CHAUTHTOK, ++ pam_test_ctx->pam_cmds); ++ assert_int_equal(ret, EOK); ++ ++ /* Wait until the test finishes with EOK */ ++ ret = test_ev_loop(pam_test_ctx->tctx); ++ assert_int_equal(ret, EOK); ++} ++ ++ ++int main(int argc, const char *argv[]) ++{ ++ int rv; ++ int no_cleanup = 0; ++ poptContext pc; ++ int opt; ++ struct poptOption long_options[] = { ++ POPT_AUTOHELP ++ SSSD_DEBUG_OPTS ++ { "no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0, ++ _("Do not delete the test database after a test run"), NULL }, ++ POPT_TABLEEND ++ }; ++ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test_setup_teardown(test_pam_authenticate, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_setcreds, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_acct_mgmt, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_open_session, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_close_session, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_chauthtok, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_chauthtok_prelim, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_preauth, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_offline_auth_no_hash, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_offline_auth_success, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_offline_auth_wrong_pw, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_offline_auth_success_2fa, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_offline_auth_failed_2fa, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown( ++ test_pam_offline_auth_success_2fa_with_cached_2fa, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown( ++ test_pam_offline_auth_failed_2fa_with_cached_2fa, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown( ++ test_pam_offline_auth_success_pw_with_cached_2fa, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown( ++ test_pam_offline_auth_failed_pw_with_cached_2fa, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown( ++ test_pam_offline_auth_success_combined_pw_with_cached_2fa, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown( ++ test_pam_offline_auth_failed_combined_pw_with_cached_2fa, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown( ++ test_pam_offline_auth_failed_wrong_2fa_size_with_cached_2fa, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_offline_chauthtok_prelim, ++ pam_test_setup, pam_test_teardown), ++ cmocka_unit_test_setup_teardown(test_pam_offline_chauthtok, ++ pam_test_setup, pam_test_teardown), ++ }; ++ ++ /* Set debug level to invalid value so we can deside if -d 0 was used. */ ++ debug_level = SSSDBG_INVALID; ++ ++ pc = poptGetContext(argv[0], argc, argv, long_options, 0); ++ while ((opt = poptGetNextOpt(pc)) != -1) { ++ switch (opt) { ++ default: ++ fprintf(stderr, "\nInvalid option %s: %s\n\n", ++ poptBadOption(pc, 0), poptStrerror(opt)); ++ poptPrintUsage(pc, stderr, 0); ++ return 1; ++ } ++ } ++ poptFreeContext(pc); ++ ++ DEBUG_CLI_INIT(debug_level); ++ ++ /* Even though normally the tests should clean up after themselves ++ * they might not after a failed run. Remove the old db to be sure */ ++ tests_set_cwd(); ++ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); ++ test_dom_suite_setup(TESTS_PATH); ++ ++ rv = cmocka_run_group_tests(tests, NULL, NULL); ++ if (rv == 0 && !no_cleanup) { ++ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); ++ } ++ ++#ifdef HAVE_NSS ++ /* Cleanup NSS and NSPR to make valgrund happy. */ ++ nspr_nss_cleanup(); ++#endif ++ ++ return rv; ++} +-- +2.4.0 + diff --git a/sssd.spec b/sssd.spec index 54cc342..cd80910 100644 --- a/sssd.spec +++ b/sssd.spec @@ -27,7 +27,7 @@ Name: sssd Version: 1.12.4 -Release: 7%{?dist} +Release: 8%{?dist} Group: Applications/System Summary: System Security Services Daemon License: GPLv3+ @@ -135,6 +135,21 @@ Patch0096: 0096-BUILD-Add-possibility-to-build-python-2-3-bindings.patch Patch0097: 0097-TESTS-Run-python-tests-with-all-supported-python-ver.patch Patch0098: 0098-SPEC-Replace-python_-macros-with-python2_.patch Patch0099: 0099-SPEC-Build-python3-bindings-on-available-platforms.patch +Patch0100: 0100-ad_opts-Use-different-default-attribute-for-group-na.patch +Patch0101: 0101-Add-leak-check-and-command-line-option-to-test_autht.patch +Patch0102: 0102-utils-add-sss_authtok_-gs-et_2fa.patch +Patch0103: 0103-pam-handle-2FA-authentication-token-in-the-responder.patch +Patch0104: 0104-Add-pre-auth-request.patch +Patch0105: 0105-krb5-child-add-preauth-and-split-2fa-token-support.patch +Patch0106: 0106-IPA-create-preauth-indicator-file-at-startup.patch +Patch0107: 0107-pam_sss-add-pre-auth-and-2fa-support.patch +Patch0108: 0108-Add-cache_credentials_minimal_first_factor_length-co.patch +Patch0109: 0109-sysdb-add-sysdb_cache_password_ex.patch +Patch0110: 0110-krb5-save-hash-of-the-first-authentication-factor-to.patch +Patch0111: 0111-krb5-try-delayed-online-authentication-only-for-sing.patch +Patch0112: 0112-2FA-offline-auth.patch +Patch0113: 0113-pam_sss-move-message-encoding-into-separate-file.patch +Patch0114: 0114-PAM-add-PAM-responder-unit-test.patch ### Dependencies ### Requires: sssd-common = %{version}-%{release} @@ -1092,6 +1107,14 @@ if [ $1 -eq 0 ]; then fi %changelog +* Fri May 08 2015 Lukas Slebodnik - 1.12.4-8 +- Backport important patches from upstream 1.13 prerelease +- Resolves: rhbz#1060325 - Does sssd-ad use the most suitable + attribute for group name +- Resolves: upstream #2335 - Investigate using the krb5 responder + for driving the PAM conversation with OTPs +- Enable cmocka tests for secondary architectures + * Fri May 08 2015 Lukas Slebodnik - 1.12.4-7 - Backport patches from upstream 1.12.5 prerelease - contains many fixes