From fc57f57b805a5b91348a8355e74ceb4444881729 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Wed, 27 Mar 2019 09:04:53 +0100
Subject: [PATCH 17/21] authtok: add dedicated type for 2fa with single string
Currently the password type is used to send two-factor authentication
credentials entered in a single string to the backend, This is
unreliable and only works properly if password authentication is not
available for the user as well.
To support 2FA credentials in a single string better a new authtok type
is added.
Related to https://pagure.io/SSSD/sssd/issue/3264
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
(cherry picked from commit ac4b33f765ac322949ac7c2f24985d3b9c178168)
---
src/providers/krb5/krb5_auth.c | 1 +
src/providers/krb5/krb5_child.c | 13 +++++++
src/providers/krb5/krb5_child_handler.c | 4 +++
src/responder/pam/pamsrv_cmd.c | 1 +
src/sss_client/sss_cli.h | 3 ++
src/tests/cmocka/test_authtok.c | 45 +++++++++++++++++++++++++
src/util/authtok.c | 42 +++++++++++++++++++++++
src/util/authtok.h | 35 +++++++++++++++++++
8 files changed, 144 insertions(+)
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index d40d2afed..9a9250434 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -495,6 +495,7 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
case SSS_PAM_CHAUTHTOK:
if (authtok_type != SSS_AUTHTOK_TYPE_PASSWORD
&& authtok_type != SSS_AUTHTOK_TYPE_2FA
+ && authtok_type != SSS_AUTHTOK_TYPE_2FA_SINGLE
&& authtok_type != SSS_AUTHTOK_TYPE_SC_PIN
&& authtok_type != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
/* handle empty password gracefully */
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index a578930a9..a86d9a7ae 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -503,6 +503,15 @@ static krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx,
return ret;
}
+ return tokeninfo_matches_pwd(mem_ctx, ti, pwd, len, out_token, out_pin);
+ break;
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ ret = sss_authtok_get_2fa_single(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:
@@ -2091,6 +2100,7 @@ static errno_t tgt_req_child(struct krb5_req *kr)
/* No password is needed for pre-auth or if we have 2FA or SC */
if (kr->pd->cmd != SSS_PAM_PREAUTH
&& sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA
+ && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA_SINGLE
&& sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_SC_PIN
&& sss_authtok_get_type(kr->pd->authtok)
!= SSS_AUTHTOK_TYPE_SC_KEYPAD) {
@@ -2349,6 +2359,9 @@ 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_SINGLE:
+ ret = sss_authtok_set_2fa_single(tok, (char *)(buf + *p), 0);
+ break;
case SSS_AUTHTOK_TYPE_2FA:
case SSS_AUTHTOK_TYPE_SC_PIN:
case SSS_AUTHTOK_TYPE_SC_KEYPAD:
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 352ff980d..b7fb54499 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -79,6 +79,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_SINGLE:
+ ret = sss_authtok_get_2fa_single(tok, &data, &len);
+ auth_token_length = len + 1;
+ break;
case SSS_AUTHTOK_TYPE_2FA:
case SSS_AUTHTOK_TYPE_SC_PIN:
case SSS_AUTHTOK_TYPE_SC_KEYPAD:
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 94867a0fe..6f3a7e56b 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -160,6 +160,7 @@ static int extract_authtok_v2(struct sss_auth_token *tok,
}
break;
case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
case SSS_AUTHTOK_TYPE_SC_PIN:
case SSS_AUTHTOK_TYPE_SC_KEYPAD:
ret = sss_authtok_set(tok, auth_token_type,
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 7e748c281..23ef21608 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -340,6 +340,9 @@ enum sss_authtok_type {
* Smart Card authentication is used
* and that the PIN will be entered
* at the card reader. */
+ SSS_AUTHTOK_TYPE_2FA_SINGLE = 0x0006, /**< Authentication token has two
+ * factors in a single string, it 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 9422f96bc..84e209783 100644
--- a/src/tests/cmocka/test_authtok.c
+++ b/src/tests/cmocka/test_authtok.c
@@ -652,6 +652,49 @@ void test_sss_authtok_sc_pin(void **state)
assert_int_equal(ret, EFAULT);
}
+/* Test when type has value SSS_AUTHTOK_TYPE_2FA_SINGLE */
+static void test_sss_authtok_2fa_single(void **state)
+{
+ size_t len;
+ errno_t ret;
+ char *data;
+ size_t ret_len;
+ const char *pwd;
+ struct test_state *ts;
+ enum sss_authtok_type type;
+
+ ts = talloc_get_type_abort(*state, struct test_state);
+ data = talloc_strdup(ts, "1stfacto2ndfactor");
+ assert_non_null(data);
+
+ len = strlen(data) + 1;
+ type = SSS_AUTHTOK_TYPE_2FA_SINGLE;
+ ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *)data, len);
+
+ assert_int_equal(ret, EOK);
+ assert_int_equal(type, sss_authtok_get_type(ts->authtoken));
+ assert_int_equal(len, sss_authtok_get_size(ts->authtoken));
+ assert_string_equal(data, sss_authtok_get_data(ts->authtoken));
+
+ ret = sss_authtok_get_2fa_single(ts->authtoken, &pwd, &ret_len);
+
+ assert_int_equal(ret, EOK);
+ assert_string_equal(data, pwd);
+ assert_int_equal(len - 1, ret_len);
+
+ ret = sss_authtok_set_2fa_single(ts->authtoken, data, len);
+ assert_int_equal(ret, EOK);
+
+ ret = sss_authtok_get_2fa_single(ts->authtoken, &pwd, &ret_len);
+ 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);
+}
+
+
int main(int argc, const char *argv[])
{
poptContext pc;
@@ -687,6 +730,8 @@ int main(int argc, const char *argv[])
setup, teardown),
cmocka_unit_test_setup_teardown(test_sss_authtok_sc_blobs,
setup, teardown),
+ cmocka_unit_test_setup_teardown(test_sss_authtok_2fa_single,
+ setup, teardown),
};
/* Set debug level to invalid value so we can decide if -d 0 was used. */
diff --git a/src/util/authtok.c b/src/util/authtok.c
index c2f78be32..0cac24598 100644
--- a/src/util/authtok.c
+++ b/src/util/authtok.c
@@ -41,6 +41,7 @@ size_t sss_authtok_get_size(struct sss_auth_token *tok)
case SSS_AUTHTOK_TYPE_2FA:
case SSS_AUTHTOK_TYPE_SC_PIN:
case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
return tok->length;
case SSS_AUTHTOK_TYPE_EMPTY:
return 0;
@@ -76,6 +77,7 @@ errno_t sss_authtok_get_password(struct sss_auth_token *tok,
case SSS_AUTHTOK_TYPE_2FA:
case SSS_AUTHTOK_TYPE_SC_PIN:
case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
return EACCES;
}
@@ -101,6 +103,33 @@ errno_t sss_authtok_get_ccfile(struct sss_auth_token *tok,
case SSS_AUTHTOK_TYPE_2FA:
case SSS_AUTHTOK_TYPE_SC_PIN:
case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ return EACCES;
+ }
+
+ return EINVAL;
+}
+
+errno_t sss_authtok_get_2fa_single(struct sss_auth_token *tok,
+ const char **str, size_t *len)
+{
+ if (!tok) {
+ return EINVAL;
+ }
+ switch (tok->type) {
+ case SSS_AUTHTOK_TYPE_EMPTY:
+ return ENOENT;
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ *str = (const char *)tok->data;
+ if (len) {
+ *len = tok->length - 1;
+ }
+ return EOK;
+ case SSS_AUTHTOK_TYPE_PASSWORD:
+ case SSS_AUTHTOK_TYPE_2FA:
+ case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_CCFILE:
return EACCES;
}
@@ -151,6 +180,7 @@ void sss_authtok_set_empty(struct sss_auth_token *tok)
case SSS_AUTHTOK_TYPE_PASSWORD:
case SSS_AUTHTOK_TYPE_2FA:
case SSS_AUTHTOK_TYPE_SC_PIN:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
safezero(tok->data, tok->length);
break;
case SSS_AUTHTOK_TYPE_CCFILE:
@@ -181,6 +211,15 @@ errno_t sss_authtok_set_ccfile(struct sss_auth_token *tok,
"ccfile", ccfile, len);
}
+errno_t sss_authtok_set_2fa_single(struct sss_auth_token *tok,
+ const char *str, size_t len)
+{
+ sss_authtok_set_empty(tok);
+
+ return sss_authtok_set_string(tok, SSS_AUTHTOK_TYPE_2FA_SINGLE,
+ "2fa_single", str, len);
+}
+
static errno_t sss_authtok_set_2fa_from_blob(struct sss_auth_token *tok,
const uint8_t *data, size_t len);
@@ -199,6 +238,8 @@ errno_t sss_authtok_set(struct sss_auth_token *tok,
return sss_authtok_set_sc_from_blob(tok, data, len);
case SSS_AUTHTOK_TYPE_SC_KEYPAD:
return sss_authtok_set_sc_from_blob(tok, data, len);
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
+ return sss_authtok_set_2fa_single(tok, (const char *) data, len);
case SSS_AUTHTOK_TYPE_EMPTY:
sss_authtok_set_empty(tok);
return EOK;
@@ -566,6 +607,7 @@ errno_t sss_authtok_get_sc_pin(struct sss_auth_token *tok, const char **_pin,
case SSS_AUTHTOK_TYPE_CCFILE:
case SSS_AUTHTOK_TYPE_2FA:
case SSS_AUTHTOK_TYPE_SC_KEYPAD:
+ case SSS_AUTHTOK_TYPE_2FA_SINGLE:
return EACCES;
}
diff --git a/src/util/authtok.h b/src/util/authtok.h
index a55e89fd2..dae3ff6b1 100644
--- a/src/util/authtok.h
+++ b/src/util/authtok.h
@@ -348,4 +348,39 @@ errno_t sss_authtok_get_sc(struct sss_auth_token *tok,
const char **_token_name, size_t *_token_name_len,
const char **_module_name, size_t *_module_name_len,
const char **_key_id, size_t *_key_id_len);
+
+
+/**
+ * @brief Returns a const string if the auth token is of type
+ SSS_AUTHTOK_TYPE_2FA_SINGLE, otherwise it returns an error
+ *
+ * @param tok A pointer to an sss_auth_token
+ * @param pwd A pointer to a const char *, that will point to a null
+ * terminated string
+ * @param len The length of the credential string
+ *
+ * @return EOK on success
+ * ENOENT if the token is empty
+ * EACCESS if the token is not a password token
+ */
+errno_t sss_authtok_get_2fa_single(struct sss_auth_token *tok,
+ const char **str, size_t *len);
+
+/**
+ * @brief Set a 2FA credentials in a single strings into an auth token,
+ * replacing any previous data
+ *
+ * @param tok A pointer to an sss_auth_token structure to change, also
+ * used as a memory context to allocate the internal data.
+ * @param password A string where the two authentication factors are
+ * concatenated together
+ * @param len The length of the string or, if 0 is passed,
+ * then strlen(password) will be used internally.
+ *
+ * @return EOK on success
+ * ENOMEM on error
+ */
+errno_t sss_authtok_set_2fa_single(struct sss_auth_token *tok,
+ const char *str, size_t len);
+
#endif /* __AUTHTOK_H__ */
--
2.19.1