From 168a6c7d4778a2a3c729e3ac24e4ad9dfacb46c0 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: May 26 2016 16:47:05 +0000 Subject: Ensure that ipa-otpd bind auths validate an OTP Before this patch, if the user was configured for either OTP or password it was possible to do a 1FA authentication through ipa-otpd. Because this correctly respected the configuration, it is not a security error. However, once we begin to insert authentication indicators into the Kerberos tickets, we cannot allow 1FA authentications through this code path. Otherwise the ticket would contain a 2FA indicator when only 1FA was actually performed. To solve this problem, we have ipa-otpd send a critical control during the bind operation which informs the LDAP server that it *MUST* validate an OTP token for authentication to be successful. Next, we implement support for this control in the ipa-pwd-extop plugin. The end result is that the bind operation will always fail if the control is present and no OTP is validated. https://fedorahosted.org/freeipa/ticket/433 Reviewed-By: Sumit Bose --- diff --git a/daemons/ipa-otpd/bind.c b/daemons/ipa-otpd/bind.c index c985ccd..022525b 100644 --- a/daemons/ipa-otpd/bind.c +++ b/daemons/ipa-otpd/bind.c @@ -26,9 +26,12 @@ */ #include "internal.h" +#include "../ipa-slapi-plugins/ipa-pwd-extop/otpctrl.h" static void on_bind_writable(verto_ctx *vctx, verto_ev *ev) { + LDAPControl control = { OTP_REQUIRED_OID, {}, true }; + LDAPControl *ctrls[] = { &control, NULL }; struct otpd_queue *push = &ctx.stdio.responses; const krb5_data *data; struct berval cred; @@ -55,7 +58,7 @@ static void on_bind_writable(verto_ctx *vctx, verto_ev *ev) cred.bv_val = data->data; cred.bv_len = data->length; i = ldap_sasl_bind(verto_get_private(ev), item->user.dn, LDAP_SASL_SIMPLE, - &cred, NULL, NULL, &item->msgid); + &cred, ctrls, NULL, &item->msgid); if (i != LDAP_SUCCESS) { otpd_log_err(errno, "Unable to initiate bind: %s", ldap_err2string(i)); verto_break(ctx.vctx); diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/otpctrl.h b/daemons/ipa-slapi-plugins/ipa-pwd-extop/otpctrl.h index c38d491..4cbc17b 100644 --- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/otpctrl.h +++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/otpctrl.h @@ -55,6 +55,9 @@ */ #define OTP_SYNC_REQUEST_OID "2.16.840.1.113730.3.8.10.6" +/* This control has no data. */ +#define OTP_REQUIRED_OID "2.16.840.1.113730.3.8.10.7" + bool otpctrl_present(Slapi_PBlock *pb, const char *oid); bool otpctrl_sync_handle(const struct otp_config *cfg, Slapi_PBlock *pb, diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c index f41b1ac..5c70021 100644 --- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c @@ -1172,7 +1172,7 @@ done: * validation. */ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry, - struct berval *creds) + struct berval *creds, bool otpreq) { uint32_t auth_types; @@ -1204,10 +1204,10 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry, return false; } - /* If the user has no active tokens, succeed. */ + /* With no tokens, succeed if tokens aren't required. */ if (tokens[0] == NULL) { otp_token_free_array(tokens); - return true; + return !otpreq; } if (otp_token_validate_berval(tokens, creds, NULL)) { @@ -1218,7 +1218,7 @@ static bool ipapwd_pre_bind_otp(const char *bind_dn, Slapi_Entry *entry, otp_token_free_array(tokens); } - return auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD; + return (auth_types & OTP_CONFIG_AUTH_TYPE_PASSWORD) && !otpreq; } static int ipapwd_authenticate(const char *dn, Slapi_Entry *entry, @@ -1394,6 +1394,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) char *dn = NULL; int method = 0; bool syncreq; + bool otpreq; int ret = 0; time_t current_time; time_t expire_time; @@ -1451,7 +1452,8 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) /* Try to do OTP first. */ syncreq = otpctrl_present(pb, OTP_SYNC_REQUEST_OID); - if (!syncreq && !ipapwd_pre_bind_otp(dn, entry, credentials)) + otpreq = otpctrl_present(pb, OTP_REQUIRED_OID); + if (!syncreq && !ipapwd_pre_bind_otp(dn, entry, credentials, otpreq)) goto invalid_creds; /* Ensure that there is a password. */ @@ -1488,6 +1490,7 @@ int ipapwd_pre_init(Slapi_PBlock *pb) int ret; slapi_register_supported_control(OTP_SYNC_REQUEST_OID, SLAPI_OPERATION_BIND); + slapi_register_supported_control(OTP_REQUIRED_OID, SLAPI_OPERATION_BIND); ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&ipapwd_plugin_desc);