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]

Reply via email to