From 978bc8681e74ffa17f96fd5d4355094c4a26691c Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Mon, 9 May 2016 16:14:51 +1200 Subject: [PATCH] kerberos: Return enc data on PREAUTH_FAILED 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 Reviewed-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11539 Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Tue Jul 5 10:52:32 CEST 2016 on sn-devel-144 --- source4/heimdal/kdc/kerberos5.c | 178 +++++++++++++++++++++---------------- source4/torture/krb5/kdc-heimdal.c | 18 ++++ 2 files changed, 117 insertions(+), 79 deletions(-) diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c index 3762abe0120..b8bb5fa6609 100644 --- a/source4/heimdal/kdc/kerberos5.c +++ b/source4/heimdal/kdc/kerberos5.c @@ -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 diff --git a/source4/torture/krb5/kdc-heimdal.c b/source4/torture/krb5/kdc-heimdal.c index fffe773e21b..52014f75706 100644 --- a/source4/torture/krb5/kdc-heimdal.c +++ b/source4/torture/krb5/kdc-heimdal.c @@ -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"); -- 2.11.4.GIT