kerberos: Return enc data on PREAUTH_FAILED
authorGarming Sam <garming@catalyst.net.nz>
Mon, 9 May 2016 04:14:51 +0000 (16:14 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 5 Jul 2016 08:52:32 +0000 (10:52 +0200)
Without the enc data, Windows clients will perform two AS-REQ causing the password
lockout count to increase by two instead of one.

Signed-off-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
BUG: https://bugzilla.samba.org/show_bug.cgi?id=11539

Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
Autobuild-Date(master): Tue Jul  5 10:52:32 CEST 2016 on sn-devel-144

source4/heimdal/kdc/kerberos5.c
source4/torture/krb5/kdc-heimdal.c

index 3762abe..b8bb5fa 100644 (file)
@@ -976,7 +976,7 @@ _kdc_as_rep(krb5_context context,
     krb5_error_code ret = 0;
     const char *e_text = NULL;
     krb5_crypto crypto;
-    Key *ckey, *skey;
+    Key *skey;
     EncryptionKey *reply_key = NULL, session_key;
     int flags = HDB_F_FOR_AS_REQ;
 #ifdef PKINIT
@@ -1373,6 +1373,9 @@ _kdc_as_rep(krb5_context context,
            was some problem with it, other than too large skew */
        if(found_pa && et.flags.pre_authent == 0){
            kdc_log(context, config, 0, "%s -- %s", e_text, client_name);
+           if (!prepare_enc_data(context, config, &e_data, b, client)) {
+               goto out;
+           }
            e_text = NULL;
            goto out;
        }
@@ -1380,88 +1383,11 @@ _kdc_as_rep(krb5_context context,
              || b->kdc_options.request_anonymous /* hack to force anon */
              || client->entry.flags.require_preauth
              || server->entry.flags.require_preauth) {
-       METHOD_DATA method_data;
-       PA_DATA *pa;
-       unsigned char *buf;
-       size_t len;
-
     use_pa:
-       method_data.len = 0;
-       method_data.val = NULL;
-
-       ret = realloc_method_data(&method_data);
-       if (ret) {
-           free_METHOD_DATA(&method_data);
-           goto out;
-       }
-       pa = &method_data.val[method_data.len-1];
-       pa->padata_type         = KRB5_PADATA_ENC_TIMESTAMP;
-       pa->padata_value.length = 0;
-       pa->padata_value.data   = NULL;
-
-#ifdef PKINIT
-       ret = realloc_method_data(&method_data);
-       if (ret) {
-           free_METHOD_DATA(&method_data);
-           goto out;
-       }
-       pa = &method_data.val[method_data.len-1];
-       pa->padata_type         = KRB5_PADATA_PK_AS_REQ;
-       pa->padata_value.length = 0;
-       pa->padata_value.data   = NULL;
-
-       ret = realloc_method_data(&method_data);
-       if (ret) {
-           free_METHOD_DATA(&method_data);
-           goto out;
-       }
-       pa = &method_data.val[method_data.len-1];
-       pa->padata_type         = KRB5_PADATA_PK_AS_REQ_WIN;
-       pa->padata_value.length = 0;
-       pa->padata_value.data   = NULL;
-#endif
-
-       /*
-        * If there is a client key, send ETYPE_INFO{,2}
-        */
-       ret = _kdc_find_etype(context,
-                             config->preauth_use_strongest_session_key, TRUE,
-                             client, b->etype.val, b->etype.len, NULL, &ckey);
-       if (ret == 0) {
-
-           /*
-            * RFC4120 requires:
-            * - If the client only knows about old enctypes, then send
-            *   both info replies (we send 'info' first in the list).
-            * - If the client is 'modern', because it knows about 'new'
-            *   enctype types, then only send the 'info2' reply.
-            *
-            * Before we send the full list of etype-info data, we pick
-            * the client key we would have used anyway below, just pick
-            * that instead.
-            */
-
-           if (older_enctype(ckey->key.keytype)) {
-               ret = get_pa_etype_info(context, config,
-                                       &method_data, ckey);
-               if (ret) {
-                   free_METHOD_DATA(&method_data);
-                   goto out;
-               }
-           }
-           ret = get_pa_etype_info2(context, config,
-                                    &method_data, ckey);
-           if (ret) {
-               free_METHOD_DATA(&method_data);
+       if (!prepare_enc_data(context, config, &e_data, b, client)) {
                goto out;
-           }
        }
 
-       ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret);
-       free_METHOD_DATA(&method_data);
-
-       e_data.data   = buf;
-       e_data.length = len;
        e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ",
 
        ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
@@ -1834,6 +1760,100 @@ out:
     return ret;
 }
 
+krb5_boolean
+prepare_enc_data(krb5_context context,
+                krb5_kdc_configuration *config,
+                krb5_data *e_data,
+                KDC_REQ_BODY *b,
+                hdb_entry_ex *client)
+{
+       METHOD_DATA method_data;
+       PA_DATA *pa;
+       unsigned char *buf;
+       size_t len;
+       Key *ckey;
+       krb5_error_code ret;
+
+       method_data.len = 0;
+       method_data.val = NULL;
+
+       ret = realloc_method_data(&method_data);
+       if (ret) {
+           free_METHOD_DATA(&method_data);
+           return FALSE;
+       }
+       pa = &method_data.val[method_data.len-1];
+       pa->padata_type         = KRB5_PADATA_ENC_TIMESTAMP;
+       pa->padata_value.length = 0;
+       pa->padata_value.data   = NULL;
+
+#ifdef PKINIT
+       ret = realloc_method_data(&method_data);
+       if (ret) {
+           free_METHOD_DATA(&method_data);
+           return FALSE;
+       }
+       pa = &method_data.val[method_data.len-1];
+       pa->padata_type         = KRB5_PADATA_PK_AS_REQ;
+       pa->padata_value.length = 0;
+       pa->padata_value.data   = NULL;
+
+       ret = realloc_method_data(&method_data);
+       if (ret) {
+           free_METHOD_DATA(&method_data);
+           return FALSE;
+       }
+       pa = &method_data.val[method_data.len-1];
+       pa->padata_type         = KRB5_PADATA_PK_AS_REQ_WIN;
+       pa->padata_value.length = 0;
+       pa->padata_value.data   = NULL;
+#endif
+
+       /*
+        * If there is a client key, send ETYPE_INFO{,2}
+        */
+       ret = _kdc_find_etype(context,
+                             config->preauth_use_strongest_session_key, TRUE,
+                             client, b->etype.val, b->etype.len, NULL, &ckey);
+       if (ret == 0) {
+
+           /*
+            * RFC4120 requires:
+            * - If the client only knows about old enctypes, then send
+            *   both info replies (we send 'info' first in the list).
+            * - If the client is 'modern', because it knows about 'new'
+            *   enctype types, then only send the 'info2' reply.
+            *
+            * Before we send the full list of etype-info data, we pick
+            * the client key we would have used anyway below, just pick
+            * that instead.
+            */
+
+           if (older_enctype(ckey->key.keytype)) {
+               ret = get_pa_etype_info(context, config,
+                                       &method_data, ckey);
+               if (ret) {
+                   free_METHOD_DATA(&method_data);
+                   return FALSE;
+               }
+           }
+           ret = get_pa_etype_info2(context, config,
+                                    &method_data, ckey);
+           if (ret) {
+               free_METHOD_DATA(&method_data);
+               return FALSE;
+           }
+       }
+
+       ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret);
+       free_METHOD_DATA(&method_data);
+
+       e_data->data   = buf;
+       e_data->length = len;
+
+       return TRUE;
+}
+
 /*
  * Add the AuthorizationData `data´ of `type´ to the last element in
  * the sequence of authorization_data in `tkt´ wrapped in an IF_RELEVANT
index fffe773..52014f7 100644 (file)
@@ -189,6 +189,10 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex
                                                 "Got wrong error.error_code");
                        free_KRB_ERROR(&error);
                } else if (test_context->packet_count == 1) {
+                       METHOD_DATA m;
+                       size_t len;
+                       int i, ret = 0;
+                       bool found = false;
                        torture_assert_int_equal(test_context->tctx,
                                                 decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0,
                                                 "decode_AS_REP failed");
@@ -196,6 +200,20 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex
                        torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
                        torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_PREAUTH_FAILED - KRB5KDC_ERR_NONE,
                                                 "Got wrong error.error_code");
+                       torture_assert(test_context->tctx, error.e_data != NULL, "No e-data returned");
+                       ret = decode_METHOD_DATA(error.e_data->data, error.e_data->length, &m, &len);
+                       torture_assert_int_equal(test_context->tctx, ret, 0,
+                                                "Got invalid method data");
+
+                       torture_assert(test_context->tctx, m.len > 0, "No PA_DATA given");
+                       for (i = 0; i < m.len; i++) {
+                               if (m.val[i].padata_type == KRB5_PADATA_ENC_TIMESTAMP) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       torture_assert(test_context->tctx, found, "Encrypted timestamp not found");
+
                        free_KRB_ERROR(&error);
                }
                torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");