Skip to content

Commit

Permalink
LDAP: Allow ignoring the ppolicy extension
Browse files Browse the repository at this point in the history
Introduce `ldap_use_ppolicy` and allow disabling it to interact with
providers that send broken ppolicy responses.
This fixes interaction with the Okta LDAP gateway.

Resolves: #6666

:config: Add a ldap_use_ppolicy option for backends with broken ppolicy
  extension handling.

Reviewed-by: Pavel Březina <pbrezina@redhat.com>
Reviewed-by: Tomáš Halman <thalman@redhat.com>
  • Loading branch information
viraptor authored and alexey-tikhonov committed Dec 11, 2023
1 parent 5bbc146 commit 1980e2c
Show file tree
Hide file tree
Showing 14 changed files with 90 additions and 37 deletions.
1 change: 1 addition & 0 deletions src/config/SSSDConfig/sssdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ def __init__(self):

'ldap_disable_paging': _('Disable the LDAP paging control'),
'ldap_disable_range_retrieval': _('Disable Active Directory range retrieval'),
'ldap_use_ppolicy': _('Use the ppolicy extension'),

# [provider/ldap/id]
'ldap_search_timeout': _('Length of time to wait for a search request'),
Expand Down
1 change: 1 addition & 0 deletions src/config/cfg_rules.ini
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ option = ldap_tls_cipher_suite
option = ldap_tls_key
option = ldap_tls_reqcert
option = ldap_uri
option = ldap_use_ppolicy
option = ldap_user_ad_account_expires
option = ldap_user_ad_user_account_control
option = ldap_user_authorized_host
Expand Down
1 change: 1 addition & 0 deletions src/config/etc/sssd.api.d/sssd-ldap.conf
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ ldap_connection_idle_timeout = int, None, false
ldap_disable_paging = bool, None, false
ldap_disable_range_retrieval = bool, None, false
wildcard_limit = int, None, false
ldap_use_ppolicy = bool, None, false

[provider/ldap/id]
ldap_search_timeout = int, None, false
Expand Down
15 changes: 15 additions & 0 deletions src/man/sssd-ldap.5.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1639,6 +1639,21 @@ ldap_access_filter = (employeeType=admin)
</listitem>
</varlistentry>

<varlistentry>
<term>ldap_use_ppolicy (boolean)</term>
<listitem>
<para>
Turns on requesting and relying on the server-side
password policy controls. Disabling this allows
interacting with services which send back invalid
ppolicy extension.
</para>
<para>
Default: true
</para>
</listitem>
</varlistentry>

</variablelist>
</para>
</refsect1>
Expand Down
1 change: 1 addition & 0 deletions src/providers/ad/ad_opts.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ struct dp_option ad_def_ldap_opts[] = {
{ "ldap_pwdlockout_dn", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "wildcard_limit", DP_OPT_NUMBER, { .number = 1000 }, NULL_NUMBER},
{ "ldap_library_debug_level", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
{ "ldap_use_ppolicy", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
DP_OPTION_TERMINATOR
};

Expand Down
5 changes: 4 additions & 1 deletion src/providers/ipa/ipa_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ static void ipa_pam_auth_handler_connect_done(struct tevent_req *subreq)
struct ldb_message *msg;
const char *dn;
int timeout;
bool use_ppolicy;
errno_t ret;

req = tevent_req_callback_data(subreq, struct tevent_req);
Expand Down Expand Up @@ -379,9 +380,11 @@ static void ipa_pam_auth_handler_connect_done(struct tevent_req *subreq)

timeout = dp_opt_get_int(state->auth_ctx->sdap_auth_ctx->opts->basic,
SDAP_OPT_TIMEOUT);
use_ppolicy = dp_opt_get_bool(state->auth_ctx->sdap_auth_ctx->opts->basic,
SDAP_USE_PPOLICY);

subreq = sdap_auth_send(state, state->ev, sh, NULL, NULL, dn,
state->pd->authtok, timeout);
state->pd->authtok, timeout, use_ppolicy);
if (subreq == NULL) {
goto done;
}
Expand Down
1 change: 1 addition & 0 deletions src/providers/ipa/ipa_opts.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ struct dp_option ipa_def_ldap_opts[] = {
{ "ldap_pwdlockout_dn", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "wildcard_limit", DP_OPT_NUMBER, { .number = 1000 }, NULL_NUMBER},
{ "ldap_library_debug_level", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
{ "ldap_use_ppolicy", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
DP_OPTION_TERMINATOR
};

Expand Down
10 changes: 7 additions & 3 deletions src/providers/ldap/ldap_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -891,12 +891,14 @@ static void auth_do_bind(struct tevent_req *req)
{
struct auth_state *state = tevent_req_data(req, struct auth_state);
struct tevent_req *subreq;
bool use_ppolicy = dp_opt_get_bool(state->ctx->opts->basic,
SDAP_USE_PPOLICY);
int timeout = dp_opt_get_int(state->ctx->opts->basic, SDAP_OPT_TIMEOUT);

subreq = sdap_auth_send(state, state->ev, state->sh,
NULL, NULL, state->dn,
state->authtok,
dp_opt_get_int(state->ctx->opts->basic,
SDAP_OPT_TIMEOUT));
timeout, use_ppolicy);
if (!subreq) {
tevent_req_error(req, ENOMEM);
return;
Expand Down Expand Up @@ -1160,6 +1162,7 @@ sdap_pam_change_password_send(TALLOC_CTX *mem_ctx,
char *pwd_attr;
int timeout;
errno_t ret;
bool use_ppolicy;

pwd_attr = opts->user_map[SDAP_AT_USER_PWD].name;

Expand All @@ -1186,9 +1189,10 @@ sdap_pam_change_password_send(TALLOC_CTX *mem_ctx,

switch (opts->pwmodify_mode) {
case SDAP_PWMODIFY_EXOP:
use_ppolicy = dp_opt_get_int(opts->basic, SDAP_USE_PPOLICY);
subreq = sdap_exop_modify_passwd_send(state, ev, sh, user_dn,
password, new_password,
timeout);
timeout, use_ppolicy);
break;
case SDAP_PWMODIFY_LDAP:
subreq = sdap_modify_passwd_send(state, ev, sh, timeout, pwd_attr,
Expand Down
1 change: 1 addition & 0 deletions src/providers/ldap/ldap_opts.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ struct dp_option default_basic_opts[] = {
{ "ldap_pwdlockout_dn", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "wildcard_limit", DP_OPT_NUMBER, { .number = 1000 }, NULL_NUMBER},
{ "ldap_library_debug_level", DP_OPT_NUMBER, NULL_NUMBER, NULL_NUMBER},
{ "ldap_use_ppolicy", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
DP_OPTION_TERMINATOR
};

Expand Down
1 change: 1 addition & 0 deletions src/providers/ldap/sdap.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ enum sdap_basic_opt {
SDAP_PWDLOCKOUT_DN,
SDAP_WILDCARD_LIMIT,
SDAP_LIBRARY_DEBUG_LEVEL,
SDAP_USE_PPOLICY,

SDAP_OPTS_BASIC /* opts counter */
};
Expand Down
21 changes: 12 additions & 9 deletions src/providers/ldap/sdap_async.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,8 @@ struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx,
char *user_dn,
const char *password,
const char *new_password,
int timeout)
int timeout,
bool use_ppolicy)
{
struct tevent_req *req = NULL;
struct sdap_exop_modify_passwd_state *state;
Expand Down Expand Up @@ -652,15 +653,17 @@ struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx,
return NULL;
}

ret = sdap_control_create(state->sh, LDAP_CONTROL_PASSWORDPOLICYREQUEST,
0, NULL, 0, &ctrls[0]);
if (ret != LDAP_SUCCESS && ret != LDAP_NOT_SUPPORTED) {
DEBUG(SSSDBG_CRIT_FAILURE, "sdap_control_create failed to create "
"Password Policy control.\n");
ret = ERR_INTERNAL;
goto fail;
if (use_ppolicy) {
ret = sdap_control_create(state->sh, LDAP_CONTROL_PASSWORDPOLICYREQUEST,
0, NULL, 0, &ctrls[0]);
if (ret != LDAP_SUCCESS && ret != LDAP_NOT_SUPPORTED) {
DEBUG(SSSDBG_CRIT_FAILURE, "sdap_control_create failed to create "
"Password Policy control.\n");
ret = ERR_INTERNAL;
goto fail;
}
request_controls = ctrls;
}
request_controls = ctrls;

DEBUG(SSSDBG_CONF_SETTINGS, "Executing extended operation\n");

Expand Down
6 changes: 4 additions & 2 deletions src/providers/ldap/sdap_async.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx,
const char *sasl_user,
const char *user_dn,
struct sss_auth_token *authtok,
int simple_bind_timeout);
int simple_bind_timeout,
bool use_ppolicy);

errno_t sdap_auth_recv(struct tevent_req *req,
TALLOC_CTX *memctx,
Expand All @@ -170,7 +171,8 @@ struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx,
char *user_dn,
const char *password,
const char *new_password,
int timeout);
int timeout,
bool use_ppolicy);
errno_t sdap_exop_modify_passwd_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
char **user_error_msg);
Expand Down
47 changes: 29 additions & 18 deletions src/providers/ldap/sdap_async_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct sdap_rebind_proc_params {
struct sdap_options *opts;
struct sdap_handle *sh;
bool use_start_tls;
bool use_ppolicy;
};

static int sdap_rebind_proc(LDAP *ldap, LDAP_CONST char *url, ber_tag_t request,
Expand Down Expand Up @@ -240,6 +241,8 @@ static void sdap_sys_connect_done(struct tevent_req *subreq)
rebind_proc_params->opts = state->opts;
rebind_proc_params->sh = state->sh;
rebind_proc_params->use_start_tls = state->use_start_tls;
rebind_proc_params->use_ppolicy = dp_opt_get_bool(state->opts->basic,
SDAP_USE_PPOLICY);

lret = ldap_set_rebind_proc(state->sh->ldap, sdap_rebind_proc,
rebind_proc_params);
Expand Down Expand Up @@ -659,7 +662,8 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx,
struct sdap_handle *sh,
int timeout,
const char *user_dn,
struct berval *pw)
struct berval *pw,
bool use_ppolicy)
{
struct tevent_req *req;
struct simple_bind_state *state;
Expand All @@ -683,14 +687,16 @@ static struct tevent_req *simple_bind_send(TALLOC_CTX *memctx,
state->sh = sh;
state->user_dn = user_dn;

ret = sss_ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
0, NULL, 0, &ctrls[0]);
if (ret != LDAP_SUCCESS && ret != LDAP_NOT_SUPPORTED) {
DEBUG(SSSDBG_CRIT_FAILURE, "sss_ldap_control_create failed to create "
"Password Policy control.\n");
goto fail;
if (use_ppolicy) {
ret = sss_ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
0, NULL, 0, &ctrls[0]);
if (ret != LDAP_SUCCESS && ret != LDAP_NOT_SUPPORTED) {
DEBUG(SSSDBG_CRIT_FAILURE, "sss_ldap_control_create failed to create "
"Password Policy control.\n");
goto fail;
}
request_controls = ctrls;
}
request_controls = ctrls;

DEBUG(SSSDBG_CONF_SETTINGS,
"Executing simple bind as: %s\n", state->user_dn);
Expand Down Expand Up @@ -1358,7 +1364,8 @@ struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx,
const char *sasl_user,
const char *user_dn,
struct sss_auth_token *authtok,
int simple_bind_timeout)
int simple_bind_timeout,
bool use_ppolicy)
{
struct tevent_req *req, *subreq;
struct sdap_auth_state *state;
Expand Down Expand Up @@ -1397,7 +1404,7 @@ struct tevent_req *sdap_auth_send(TALLOC_CTX *memctx,
pw.bv_len = pwlen;

state->is_sasl = false;
subreq = simple_bind_send(state, ev, sh, simple_bind_timeout, user_dn, &pw);
subreq = simple_bind_send(state, ev, sh, simple_bind_timeout, user_dn, &pw, use_ppolicy);
if (!subreq) {
tevent_req_error(req, ENOMEM);
return tevent_req_post(req, ev);
Expand Down Expand Up @@ -1972,7 +1979,9 @@ static void sdap_cli_auth_step(struct tevent_req *req)
SDAP_SASL_AUTHID),
user_dn, authtok,
dp_opt_get_int(state->opts->basic,
SDAP_OPT_TIMEOUT));
SDAP_OPT_TIMEOUT),
dp_opt_get_bool(state->opts->basic,
SDAP_USE_PPOLICY));
talloc_free(authtok);
if (!subreq) {
tevent_req_error(req, ENOMEM);
Expand Down Expand Up @@ -2320,15 +2329,17 @@ static int sdap_rebind_proc(LDAP *ldap, LDAP_CONST char *url, ber_tag_t request,
sasl_mech = dp_opt_get_string(p->opts->basic, SDAP_SASL_MECH);

if (sasl_mech == NULL) {
ret = sss_ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
0, NULL, 0, &ctrls[0]);
if (ret != LDAP_SUCCESS && ret != LDAP_NOT_SUPPORTED) {
DEBUG(SSSDBG_CRIT_FAILURE,
"sss_ldap_control_create failed to create "
if (p->use_ppolicy) {
ret = sss_ldap_control_create(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
0, NULL, 0, &ctrls[0]);
if (ret != LDAP_SUCCESS && ret != LDAP_NOT_SUPPORTED) {
DEBUG(SSSDBG_CRIT_FAILURE,
"sss_ldap_control_create failed to create "
"Password Policy control.\n");
goto done;
goto done;
}
request_controls = ctrls;
}
request_controls = ctrls;

user_dn = dp_opt_get_string(p->opts->basic, SDAP_DEFAULT_BIND_DN);
if (user_dn != NULL) {
Expand Down
16 changes: 12 additions & 4 deletions src/tests/system/tests/test_ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
@pytest.mark.importance("critical")
@pytest.mark.authentication
@pytest.mark.parametrize("modify_mode", ["exop", "ldap_modify"])
@pytest.mark.parametrize("use_ppolicy", ["true", "false"])
@pytest.mark.topology(KnownTopology.LDAP)
def test_ldap__change_password(client: Client, ldap: LDAP, modify_mode: str):
def test_ldap__change_password(client: Client, ldap: LDAP, modify_mode: str, use_ppolicy: str):
"""
:title: Change password with "ldap_pwmodify_mode" set to @modify_mode
:setup:
Expand Down Expand Up @@ -45,6 +46,7 @@ def test_ldap__change_password(client: Client, ldap: LDAP, modify_mode: str):
ldap.aci.add('(targetattr="userpassword")(version 3.0; acl "pwp test"; allow (all) userdn="ldap:///self";)')

client.sssd.domain["ldap_pwmodify_mode"] = modify_mode
client.sssd.domain["ldap_use_ppolicy"] = use_ppolicy
client.sssd.start()

assert client.auth.ssh.password(user, old_pass), "Authentication with old correct password failed"
Expand All @@ -57,8 +59,9 @@ def test_ldap__change_password(client: Client, ldap: LDAP, modify_mode: str):

@pytest.mark.ticket(bz=[795044, 1695574])
@pytest.mark.parametrize("modify_mode", ["exop", "ldap_modify"])
@pytest.mark.parametrize("use_ppolicy", ["true", "false"])
@pytest.mark.topology(KnownTopology.LDAP)
def test_ldap__change_password_new_pass_not_match(client: Client, ldap: LDAP, modify_mode: str):
def test_ldap__change_password_new_pass_not_match(client: Client, ldap: LDAP, modify_mode: str, use_ppolicy: str):
"""
:title: Change password with "ldap_pwmodify_mode" set to @modify_mode, but retyped password do not match
:setup:
Expand All @@ -76,6 +79,7 @@ def test_ldap__change_password_new_pass_not_match(client: Client, ldap: LDAP, mo
ldap.aci.add('(targetattr="userpassword")(version 3.0; acl "pwp test"; allow (all) userdn="ldap:///self";)')

client.sssd.domain["ldap_pwmodify_mode"] = modify_mode
client.sssd.domain["ldap_use_ppolicy"] = use_ppolicy
client.sssd.start()

assert not client.auth.passwd.password(
Expand All @@ -85,8 +89,9 @@ def test_ldap__change_password_new_pass_not_match(client: Client, ldap: LDAP, mo

@pytest.mark.ticket(bz=[795044, 1695574, 1795220])
@pytest.mark.parametrize("modify_mode", ["exop", "ldap_modify"])
@pytest.mark.parametrize("use_ppolicy", ["true", "false"])
@pytest.mark.topology(KnownTopology.LDAP)
def test_ldap__change_password_lowercase(client: Client, ldap: LDAP, modify_mode: str):
def test_ldap__change_password_lowercase(client: Client, ldap: LDAP, modify_mode: str, use_ppolicy: str):
"""
:title: Change password to lower-case letters, password check fail
:setup:
Expand All @@ -108,6 +113,7 @@ def test_ldap__change_password_lowercase(client: Client, ldap: LDAP, modify_mode
ldap.ldap.modify("cn=config", replace={"passwordCheckSyntax": "on"})

client.sssd.domain["ldap_pwmodify_mode"] = modify_mode
client.sssd.domain["ldap_use_ppolicy"] = use_ppolicy
client.sssd.start()

assert not client.auth.passwd.password(
Expand All @@ -122,8 +128,9 @@ def test_ldap__change_password_lowercase(client: Client, ldap: LDAP, modify_mode

@pytest.mark.ticket(bz=[1695574, 1795220])
@pytest.mark.parametrize("modify_mode", ["exop", "ldap_modify"])
@pytest.mark.parametrize("use_ppolicy", ["true", "false"])
@pytest.mark.topology(KnownTopology.LDAP)
def test_ldap__change_password_wrong_current(client: Client, ldap: LDAP, modify_mode: str):
def test_ldap__change_password_wrong_current(client: Client, ldap: LDAP, modify_mode: str, use_ppolicy: str):
"""
:title: Password change failed because an incorrect password was used
:setup:
Expand All @@ -141,6 +148,7 @@ def test_ldap__change_password_wrong_current(client: Client, ldap: LDAP, modify_
ldap.aci.add('(targetattr="userpassword")(version 3.0; acl "pwp test"; allow (all) userdn="ldap:///self";)')

client.sssd.domain["ldap_pwmodify_mode"] = modify_mode
client.sssd.domain["ldap_use_ppolicy"] = use_ppolicy
client.sssd.start()

assert not client.auth.passwd.password("user1", "wrong123", "Newpass123"), "Password change did not fail"
Expand Down

0 comments on commit 1980e2c

Please sign in to comment.