URL: https://github.com/SSSD/sssd/pull/438 Author: lslebodn Title: #438: krb5_child: Distinguish between expired & disabled AD user Action: synchronized
To pull the PR as Git branch: git remote add ghsssd https://github.com/SSSD/sssd git fetch ghsssd pull/438/head:pr438 git checkout pr438
From 99a8458f6040f2cf688081487f94b90601569d1c Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik <[email protected]> Date: Wed, 25 Oct 2017 10:12:00 +0200 Subject: [PATCH 1/2] KRB5: Pass special flag to krb5_child We will need to distinguish between standard version of krb5_get_init_creds_password or custom one which can distinguish KERB-EXT-ERROR error code for expired and disabled AD users. Flag is set only in case of auth provider ad. Resolves: https://pagure.io/SSSD/sssd/issue/3198 --- src/providers/ad/ad_init.c | 1 + src/providers/krb5/krb5_auth.h | 1 + src/providers/krb5/krb5_child.c | 3 +++ src/providers/krb5/krb5_child_handler.c | 12 +++++++++++- src/providers/krb5/krb5_common.h | 1 + 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c index 22a3ecf7e..e6f3316e2 100644 --- a/src/providers/ad/ad_init.c +++ b/src/providers/ad/ad_init.c @@ -320,6 +320,7 @@ static errno_t ad_init_auth_ctx(TALLOC_CTX *mem_ctx, } krb5_auth_ctx->config_type = K5C_GENERIC; + krb5_auth_ctx->sss_creds_password = true; krb5_auth_ctx->service = ad_options->service->krb5_service; ret = ad_get_auth_options(krb5_auth_ctx, ad_options, be_ctx, diff --git a/src/providers/krb5/krb5_auth.h b/src/providers/krb5/krb5_auth.h index 8ad3aeff2..847fbf52b 100644 --- a/src/providers/krb5/krb5_auth.h +++ b/src/providers/krb5/krb5_auth.h @@ -46,6 +46,7 @@ #define CHILD_OPT_USE_FAST "use-fast" #define CHILD_OPT_FAST_PRINCIPAL "fast-principal" #define CHILD_OPT_CANONICALIZE "canonicalize" +#define CHILD_OPT_SSS_CREDS_PASSWORD "sss-creds-password" struct krb5child_req { struct pam_data *pd; diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index b1905bb0c..4e6333c0f 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -3059,6 +3059,7 @@ int main(int argc, const char *argv[]) uid_t fast_uid; gid_t fast_gid; struct cli_opts cli_opts = { 0 }; + int sss_creds_password = 0; struct poptOption long_options[] = { POPT_AUTOHELP @@ -3091,6 +3092,8 @@ int main(int argc, const char *argv[]) _("Specifies the server principal to use for FAST"), NULL}, {CHILD_OPT_CANONICALIZE, 0, POPT_ARG_NONE, NULL, 'C', _("Requests canonicalization of the principal name"), NULL}, + {CHILD_OPT_SSS_CREDS_PASSWORD, 0, POPT_ARG_NONE, &sss_creds_password, + 0, _("Use custom version of krb5_get_init_creds_password"), NULL}, POPT_TABLEEND }; diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c index 8ef38ff2a..352ff980d 100644 --- a/src/providers/krb5/krb5_child_handler.c +++ b/src/providers/krb5/krb5_child_handler.c @@ -306,7 +306,7 @@ errno_t set_extra_args(TALLOC_CTX *mem_ctx, struct krb5_ctx *krb5_ctx, return EINVAL; } - extra_args = talloc_zero_array(mem_ctx, const char *, 9); + extra_args = talloc_zero_array(mem_ctx, const char *, 10); if (extra_args == NULL) { DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n"); return ENOMEM; @@ -400,6 +400,16 @@ errno_t set_extra_args(TALLOC_CTX *mem_ctx, struct krb5_ctx *krb5_ctx, c++; } + if (krb5_ctx->sss_creds_password) { + extra_args[c] = talloc_strdup(extra_args, + "--" CHILD_OPT_SSS_CREDS_PASSWORD); + if (extra_args[c] == NULL) { + ret = ENOMEM; + goto done; + } + c++; + } + extra_args[c] = NULL; *krb5_child_extra_args = extra_args; diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index bc35c5df6..48368a528 100644 --- a/src/providers/krb5/krb5_common.h +++ b/src/providers/krb5/krb5_common.h @@ -124,6 +124,7 @@ struct krb5_ctx { struct deferred_auth_ctx *deferred_auth_ctx; struct renew_tgt_ctx *renew_tgt_ctx; bool use_fast; + bool sss_creds_password; hash_table_t *wait_queue_hash; From 266b1a775e9ca6d53e5a46727801d725211ebae7 Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik <[email protected]> Date: Wed, 25 Oct 2017 18:18:47 +0200 Subject: [PATCH 2/2] krb5_child: Distinguish between expired & disabled AD user Active directory return krb5 error code KRB5KDC_ERR_CLIENT_REVOKED "-1765328366/Clients credentials have been revoked" for expired and disabled user. This is difference between AD and IPA https://pagure.io/SSSD/sssd/issue/2924. Therefore we need to distinguish between these two states. AD provides krb5 error data together with krb5 error code. They contains KERB-EXT-ERROR which is documeted in "[MS-KILE]: Kerberos Protocol Extensions"[1] and contains NTSTATUS in KERB-EXT-ERROR The function sss_krb5_get_init_creds_password is simpler version of krb5_get_init_creds_password and there is a small difference that the krb5 native API actually retries by locating a KDC master server if the initial attempt fails and the KDC that was contacted is not a master KDC. Special version for AD(and even IPA) is not a issue. Retry will be no-op because every server is considered a master (or no masters exist at all in DNS SRV records [1] https://msdn.microsoft.com/en-us/library/cc233855.aspx Resolves: https://pagure.io/SSSD/sssd/issue/3198 --- src/providers/krb5/krb5_child.c | 182 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 168 insertions(+), 14 deletions(-) diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 4e6333c0f..a578930a9 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -48,6 +48,14 @@ sss_authtok_get_type((tok)) == SSS_AUTHTOK_TYPE_SC_PIN \ || sss_authtok_get_type((tok)) == SSS_AUTHTOK_TYPE_SC_KEYPAD) +typedef krb5_error_code +(*k5_init_creds_password_fn_t)(krb5_context context, krb5_creds *creds, + krb5_principal client, const char *password, + krb5_prompter_fct prompter, void *data, + krb5_deltat start_time, + const char *in_tkt_service, + krb5_get_init_creds_opt *k5_gic_options); + enum k5c_fast_opt { K5C_FAST_NEVER, K5C_FAST_TRY, @@ -76,6 +84,7 @@ struct krb5_req { char *otp_token_id; char *otp_challenge; krb5_get_init_creds_opt *options; + k5_init_creds_password_fn_t krb5_get_init_creds_password; struct pam_data *pd; @@ -1477,6 +1486,146 @@ static krb5_error_code get_and_save_tgt_with_keytab(krb5_context ctx, } +/* [MS-KILE]: Kerberos Protocol Extensions + * https://msdn.microsoft.com/en-us/library/cc233855.aspx + * http://download.microsoft.com/download/9/5/E/95EF66AF-9026-4BB0-A41D-A4F81802D92C/%5BMS-KILE%5D.pdf + * 2.2.1 KERB-EXT-ERROR + */ +bool have_ms_kile_ext_error(unsigned char *data, unsigned int length, + uint32_t *_ntstatus) +{ + /* [MS-KILE] 2.2.2 KERB-ERROR-DATA + * Kerberos V5 messages are defined using Abstract Syntax Notation One + * (ASN.1) + * KERB-ERROR-DATA ::= SEQUENCE { + * data-type [1] INTEGER, + * data-value [2] OCTET STRING OPTIONAL + * } + * We are interested in data-type 3 KERB_ERR_TYPE_EXTENDED + */ + uint8_t kile_asn1_begining[] = { + 0x30, 0x15, /* 0x30 is SEQUENCE, 0x15 length */ + 0xA1, 0x03, /* 0xA1 is 1st element of sequence, 0x03 length */ + 0x02, 0x01, 0x03, /* 0x02 is INTEGER, 0x01 length, 0x03 value */ + 0xA2, 0x0E, /* 0xA2 is 2nd element of sequence, 0x0E length */ + 0x04, 0x0C, /* 0x04 is OCTET STRING, 0x0C length (12 bytes) */ + }; + const size_t offset = sizeof(kile_asn1_begining); + uint32_t value; + + if (length != 23 || data == NULL) { + return false; + } + + if (memcmp(data, kile_asn1_begining, offset) != 0) { + return false; + } + + /* [MS-KILE] 2.2.1 KERB-EXT-ERROR + * typedef struct KERB_EXT_ERROR { + * unsigned long status; + * unsigned long reserved; + * unsigned long flags; + * } KERB_EXT_ERROR; + * Status: An NTSTATUS value. See [MS-ERREF] section 2.3. + */ + value = data[offset + 3] << 24 + | data[offset + 2] << 16 + | data[offset + 1] << 8 + | data[offset + 0]; + + *_ntstatus = value; + return true; +} + +/* Following NTSTATUS values are from: + * [MS-ERREF]: Windows Error Codes -> Section 2.3.1 + * https://msdn.microsoft.com/en-us/library/cc231196.aspx + * http://download.microsoft.com/download/9/5/E/95EF66AF-9026-4BB0-A41D-A4F81802D92C/%5BMS-ERREF%5D.pdf + */ +#define NT_STATUS_ACCOUNT_EXPIRED 0xC0000193 +#define NT_STATUS_ACCOUNT_DISABLED 0xC0000072 + +void check_ms_kile_ext_krb5err(krb5_context context, + krb5_init_creds_context init_cred_ctx, + krb5_error_code *_kerr) +{ + krb5_error_code err; + krb5_error *error = NULL; + uint32_t ntstatus; + + err = krb5_init_creds_get_error(context, init_cred_ctx, &error); + if (err != 0 || error == NULL) { + KRB5_CHILD_DEBUG(SSSDBG_TRACE_FUNC, err); + return; + } + + if (have_ms_kile_ext_error((unsigned char *)error->e_data.data, error->e_data.length, + &ntstatus)) { + switch (ntstatus) { + case NT_STATUS_ACCOUNT_EXPIRED: + *_kerr = KRB5KDC_ERR_NAME_EXP; + break; + case NT_STATUS_ACCOUNT_DISABLED: + *_kerr = KRB5KDC_ERR_CLIENT_REVOKED; + break; + } + } +} + +krb5_error_code +sss_krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, + krb5_principal client, const char *password, + krb5_prompter_fct prompter, void *data, + krb5_deltat start_time, + const char *in_tkt_service, + krb5_get_init_creds_opt *k5_gic_options) +{ + krb5_error_code kerr; + krb5_init_creds_context init_cred_ctx = NULL; + + kerr = krb5_init_creds_init(context, client, prompter, data, + start_time, k5_gic_options, + &init_cred_ctx); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + goto done; + } + + if (password != NULL) { + kerr = krb5_init_creds_set_password(context, init_cred_ctx, password); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + goto done; + } + } + + if (in_tkt_service != NULL) { + kerr = krb5_init_creds_set_service(context, init_cred_ctx, + in_tkt_service); + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + goto done; + } + } + + kerr = krb5_init_creds_get(context, init_cred_ctx); + if (kerr == KRB5KDC_ERR_CLIENT_REVOKED) { + check_ms_kile_ext_krb5err(context, init_cred_ctx, &kerr); + } + + if (kerr != 0) { + KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr); + goto done; + } + + kerr = krb5_init_creds_get_creds(context, init_cred_ctx, creds); + +done: + krb5_init_creds_free(context, init_cred_ctx); + return kerr; +} + static krb5_error_code get_and_save_tgt(struct krb5_req *kr, const char *password) { @@ -1528,10 +1677,10 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr, DEBUG(SSSDBG_TRACE_FUNC, "Attempting kinit for realm [%s]\n",realm_name); - kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, - password_or_responder(password), - sss_krb5_prompter, kr, 0, - NULL, kr->options); + kerr = kr->krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, + password_or_responder(password), + sss_krb5_prompter, kr, 0, NULL, + kr->options); if (kr->pd->cmd == SSS_PAM_PREAUTH && kerr != KRB5KDC_ERR_KEY_EXP) { /* Any errors except KRB5KDC_ERR_KEY_EXP are ignored during pre-auth, * only data is collected to be send back to the client. @@ -1757,11 +1906,11 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim) DEBUG(SSSDBG_TRACE_FUNC, "Attempting kinit for realm [%s]\n",realm_name); - kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, - password_or_responder(password), - prompter, kr, 0, - SSSD_KRB5_CHANGEPW_PRINCIPAL, - kr->options); + kerr = kr->krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ, + password_or_responder(password), + prompter, kr, 0, + SSSD_KRB5_CHANGEPW_PRINCIPAL, + kr->options); DEBUG(SSSDBG_TRACE_INTERNAL, "chpass is%s using OTP\n", kr->otp ? "" : " not"); if (kerr != 0) { @@ -1995,11 +2144,11 @@ static errno_t tgt_req_child(struct krb5_req *kr) } set_changepw_options(kr->options); - kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ_orig, - password_or_responder(password), - sss_krb5_prompter, kr, 0, - SSSD_KRB5_CHANGEPW_PRINCIPAL, - kr->options); + kerr = kr->krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ_orig, + password_or_responder(password), + sss_krb5_prompter, kr, 0, + SSSD_KRB5_CHANGEPW_PRINCIPAL, + kr->options); krb5_free_cred_contents(kr->ctx, kr->creds); @@ -3151,6 +3300,11 @@ int main(int argc, const char *argv[]) kr->fast_uid = fast_uid; kr->fast_gid = fast_gid; kr->cli_opts = &cli_opts; + if (sss_creds_password != 0) { + kr->krb5_get_init_creds_password = sss_krb5_get_init_creds_password; + } else { + kr->krb5_get_init_creds_password = krb5_get_init_creds_password; + } ret = k5c_recv_data(kr, STDIN_FILENO, &offline); if (ret != EOK) {
_______________________________________________ sssd-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
