URL: https://github.com/SSSD/sssd/pull/438 Author: lslebodn Title: #438: krb5_child: Distinguish between expired & disabled AD user Action: opened
PR body: """ This is an updated version of patchset which was prepared a long time ago. It had to be changed due to commit 78027feeb56d6fe216f699be86a4716aaef3f628 which introduced different handling of password due to conditional build in `password_or_responder` I did not test with older version of krb5 which does not have defined`HAVE_KRB5_GET_INIT_CREDS_OPT_SET_RESPONDER` It was initially discussed on sssd-devel https://lists.fedorahosted.org/archives/list/[email protected]/message/ZKV7PR5MASG2R43A534BDZ6FNQJNH2TH/ as part of mail thread https://lists.fedorahosted.org/archives/list/[email protected]/thread/OM2BME5DKH3HBD23BB5SC73I5VTATIGD/#M6E4V5PWMXNNTJIILOAJGX2H32RACPWJ and @simo5 did most of review at that time. """ 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 3572c244dbdab49dfd5509cdba0f53e075e4ef86 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 e62025d4a..4defecf4a 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 b44f3a20f..07f006428 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -3026,6 +3026,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 @@ -3058,6 +3059,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 6501b89953168fc793cc43249105a75f1a5e121c 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 07f006428..4dc38c93e 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) { /* Any errors are ignored during pre-auth, only data is collected to * be send back to the client.*/ @@ -1753,11 +1902,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) { @@ -1978,11 +2127,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); if (kerr == 0) { @@ -3117,6 +3266,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]
