From 82f6e867e0b701f60de912629fa4b6da966b1de4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 21 Nov 2013 10:48:33 -0500 Subject: [PATCH 01/60] Add missing parameters to drs_Replicate in rodc.py * rodc.py: destination_dsa_guid parameter was neglected in drs_Replicate call * rodc.py: cancel the local_samdb transaction on error Change-Id: I962315a26ec48dc8774bb41db760387a3469c919 Signed-off-by: Garming Sam Reviewed-by: Stefan Metzmacher BUG: https://bugzilla.samba.org/show_bug.cgi?id=11145 Autobuild-User(master): Garming Sam Autobuild-Date(master): Thu Oct 23 03:05:00 CEST 2014 on sn-devel-104 (cherry picked from commit 88f9f50024b624319267ffa3684044d4e20e85c7) --- python/samba/netcmd/rodc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/samba/netcmd/rodc.py b/python/samba/netcmd/rodc.py index 2dc6112..4404b7b 100644 --- a/python/samba/netcmd/rodc.py +++ b/python/samba/netcmd/rodc.py @@ -91,11 +91,13 @@ class cmd_rodc_preload(Command): destination_dsa_guid = misc.GUID(local_samdb.get_ntds_GUID()) local_samdb.transaction_start() - repl = drs_Replicate("ncacn_ip_tcp:%s[seal,print]" % server, lp, creds, local_samdb) + repl = drs_Replicate("ncacn_ip_tcp:%s[seal,print]" % server, lp, creds, + local_samdb, destination_dsa_guid) try: repl.replicate(dn, source_dsa_invocation_id, destination_dsa_guid, exop=drsuapi.DRSUAPI_EXOP_REPL_SECRET, rodc=True) except Exception, e: + local_samdb.transaction_cancel() raise CommandError("Error replicating DN %s" % dn, e) local_samdb.transaction_commit() -- 2.1.4 From 8f76c45292ba43a50c1d3f69140ad70d6af546e0 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Thu, 2 Oct 2014 13:02:48 +0200 Subject: [PATCH 02/60] torture: add torture_assert_int_not_equal Signed-off-by: Michael Adam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit fae589be2af8efe36713536203ae715d29d78b5b) --- lib/torture/torture.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/torture/torture.h b/lib/torture/torture.h index 3a08042..e6f323a 100644 --- a/lib/torture/torture.h +++ b/lib/torture/torture.h @@ -414,6 +414,16 @@ void torture_result(struct torture_context *test, } \ } while(0) +#define torture_assert_int_not_equal(torture_ctx,got,not_expected,cmt)\ + do { int __got = (got), __not_expected = (not_expected); \ + if (__got == __not_expected) { \ + torture_result(torture_ctx, TORTURE_FAIL, \ + __location__": "#got" was %d (0x%X), expected a different number: %s", \ + __got, __got, cmt); \ + return false; \ + } \ + } while(0) + #define torture_assert_u64_equal(torture_ctx,got,expected,cmt)\ do { uint64_t __got = (got), __expected = (expected); \ if (__got != __expected) { \ -- 2.1.4 From 01f63ecbfefe5dd328b127260e32a63ad020b5ee Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 27 Feb 2014 09:29:36 +0100 Subject: [PATCH 03/60] s4:kdc: comment out unused code in db-glue.c Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit e5e5c223531e1935ba3c30b6439bdc0afd53a88d) --- source4/kdc/db-glue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index b0c3e7a..00b58fd 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -1362,10 +1362,10 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context, } else { int lret; char *short_princ; - const char *realm; + /* const char *realm; */ /* server as client principal case, but we must not lookup userPrincipalNames */ *realm_dn = ldb_get_default_basedn(kdc_db_ctx->samdb); - realm = krb5_principal_get_realm(context, principal); + /* realm = krb5_principal_get_realm(context, principal); */ /* TODO: Check if it is our realm, otherwise give referral */ -- 2.1.4 From c295eda467bf67dbb843eeb344ad023dc7c18757 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 15 Dec 2014 16:48:27 +0100 Subject: [PATCH 04/60] s4:kdc: add aes key support for trusted domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have a look at "msDS-SupportedEncryptionTypes" and >= DS_DOMAIN_FUNCTION_2008 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Günther Deschner BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Fri Dec 19 15:39:40 CET 2014 on sn-devel-104 (cherry picked from commit 8dd37327b02eaea33915a9cd206667981b8df872) --- source4/kdc/db-glue.c | 185 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 148 insertions(+), 37 deletions(-) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index 00b58fd..caeb1b2 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -858,17 +858,27 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx; const char *dnsdomain; const char *realm = lpcfg_realm(lp_ctx); - DATA_BLOB password_utf16; - struct samr_Password password_hash; + DATA_BLOB password_utf16 = data_blob_null; + DATA_BLOB password_utf8 = data_blob_null; + struct samr_Password _password_hash; + const struct samr_Password *password_hash = NULL; const struct ldb_val *password_val; struct trustAuthInOutBlob password_blob; struct samba_kdc_entry *p; bool use_previous; uint32_t current_kvno; + uint32_t num_keys = 0; enum ndr_err_code ndr_err; int ret, trust_direction_flags; unsigned int i; struct AuthenticationInformationArray *auth_array; + uint32_t supported_enctypes = ENCTYPE_ARCFOUR_HMAC; + + if (dsdb_functional_level(kdc_db_ctx->samdb) >= DS_DOMAIN_FUNCTION_2008) { + supported_enctypes = ldb_msg_find_attr_as_uint(msg, + "msDS-SupportedEncryptionTypes", + supported_enctypes); + } p = talloc(mem_ctx, struct samba_kdc_entry); if (!p) { @@ -895,6 +905,29 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, &entry_ex->entry.created_by.principal, realm, "kadmin", NULL); + entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); + if (entry_ex->entry.principal == NULL) { + krb5_clear_error_message(context); + ret = ENOMEM; + goto out; + } + + ret = copy_Principal(principal, entry_ex->entry.principal); + if (ret) { + krb5_clear_error_message(context); + goto out; + } + + /* + * While we have copied the client principal, tests + * show that Win2k3 returns the 'corrected' realm, not + * the client-specified realm. This code attempts to + * replace the client principal's realm with the one + * we determine from our records + */ + + krb5_principal_set_realm(context, entry_ex->entry.principal, realm); + entry_ex->entry.valid_start = NULL; trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0); @@ -910,13 +943,15 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, } if (!password_val || !(trust_direction_flags & direction)) { - ret = ENOENT; + krb5_clear_error_message(context); + ret = HDB_ERR_NOENTRY; goto out; } ndr_err = ndr_pull_struct_blob(password_val, mem_ctx, &password_blob, (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + krb5_clear_error_message(context); ret = EINVAL; goto out; } @@ -952,7 +987,8 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, } else { DEBUG(1,(__location__ ": Request for unknown kvno %u - current kvno is %u\n", kvno, current_kvno)); - ret = ENOENT; + krb5_clear_error_message(context); + ret = HDB_ERR_NOENTRY; goto out; } @@ -971,38 +1007,124 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, for (i=0; i < auth_array->count; i++) { if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) { + bool ok; + password_utf16 = data_blob_const(auth_array->array[i].AuthInfo.clear.password, auth_array->array[i].AuthInfo.clear.size); - /* In the future, generate all sorts of - * hashes, but for now we can't safely convert - * the random strings windows uses into - * utf8 */ + if (password_utf16.length == 0) { + break; + } + + if (supported_enctypes & ENCTYPE_ARCFOUR_HMAC) { + mdfour(_password_hash.hash, password_utf16.data, password_utf16.length); + if (password_hash == NULL) { + num_keys += 1; + } + password_hash = &_password_hash; + } - /* but as it is utf16 already, we can get the NT password/arcfour-hmac-md5 key */ - mdfour(password_hash.hash, password_utf16.data, password_utf16.length); + if (!(supported_enctypes & (ENC_HMAC_SHA1_96_AES128|ENC_HMAC_SHA1_96_AES256))) { + break; + } + + ok = convert_string_talloc(mem_ctx, + CH_UTF16MUNGED, CH_UTF8, + password_utf16.data, + password_utf16.length, + (void *)&password_utf8.data, + &password_utf8.length); + if (!ok) { + krb5_clear_error_message(context); + ret = ENOMEM; + goto out; + } + + if (supported_enctypes & ENC_HMAC_SHA1_96_AES128) { + num_keys += 1; + } + if (supported_enctypes & ENC_HMAC_SHA1_96_AES256) { + num_keys += 1; + } break; } else if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) { - password_hash = auth_array->array[i].AuthInfo.nt4owf.password; - break; + if (supported_enctypes & ENCTYPE_ARCFOUR_HMAC) { + password_hash = &auth_array->array[i].AuthInfo.nt4owf.password; + num_keys += 1; + } } } - if (i < auth_array->count) { - Key key; - /* Must have found a cleartext or MD4 password */ - entry_ex->entry.keys.val = calloc(1, sizeof(Key)); + /* Must have found a cleartext or MD4 password */ + if (num_keys == 0) { + DEBUG(1,(__location__ ": no usable key found\n")); + krb5_clear_error_message(context); + ret = HDB_ERR_NOENTRY; + goto out; + } - key.mkvno = 0; - key.salt = NULL; /* No salt for this enc type */ + entry_ex->entry.keys.val = calloc(num_keys, sizeof(Key)); + if (entry_ex->entry.keys.val == NULL) { + krb5_clear_error_message(context); + ret = ENOMEM; + goto out; + } - if (entry_ex->entry.keys.val == NULL) { - ret = ENOMEM; + if (password_utf8.length != 0) { + Key key = {}; + krb5_const_principal salt_principal = principal; + krb5_salt salt; + krb5_data cleartext_data; + + cleartext_data.data = password_utf8.data; + cleartext_data.length = password_utf8.length; + + ret = krb5_get_pw_salt(context, + salt_principal, + &salt); + if (ret != 0) { goto out; } + if (supported_enctypes & ENCTYPE_AES256_CTS_HMAC_SHA1_96) { + ret = krb5_string_to_key_data_salt(context, + ENCTYPE_AES256_CTS_HMAC_SHA1_96, + cleartext_data, + salt, + &key.key); + if (ret != 0) { + krb5_free_salt(context, salt); + goto out; + } + + entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; + entry_ex->entry.keys.len++; + } + + if (supported_enctypes & ENCTYPE_AES128_CTS_HMAC_SHA1_96) { + ret = krb5_string_to_key_data_salt(context, + ENCTYPE_AES128_CTS_HMAC_SHA1_96, + cleartext_data, + salt, + &key.key); + if (ret != 0) { + krb5_free_salt(context, salt); + goto out; + } + + entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; + entry_ex->entry.keys.len++; + } + + krb5_free_salt(context, salt); + } + + if (password_hash != NULL) { + Key key = {}; + ret = krb5_keyblock_init(context, ENCTYPE_ARCFOUR_HMAC, - password_hash.hash, sizeof(password_hash.hash), + password_hash->hash, + sizeof(password_hash->hash), &key.key); if (ret != 0) { goto out; @@ -1012,21 +1134,6 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, entry_ex->entry.keys.len++; } - entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); - - ret = copy_Principal(principal, entry_ex->entry.principal); - if (ret) { - krb5_clear_error_message(context); - goto out; - } - - /* While we have copied the client principal, tests - * show that Win2k3 returns the 'corrected' realm, not - * the client-specified realm. This code attempts to - * replace the client principal's realm with the one - * we determine from our records */ - - krb5_principal_set_realm(context, entry_ex->entry.principal, realm); entry_ex->entry.flags = int2HDBFlags(0); entry_ex->entry.flags.immutable = 1; entry_ex->entry.flags.invalid = 0; @@ -1307,7 +1414,11 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context, principal, direction, realm_dn, flags, kvno, msg, entry_ex); if (ret != 0) { - krb5_warnx(context, "samba_kdc_fetch: trust_message2entry failed"); + krb5_warnx(context, "samba_kdc_fetch: trust_message2entry failed for %s", + ldb_dn_get_linearized(msg->dn)); + krb5_set_error_message(context, ret, "samba_kdc_fetch: " + "trust_message2entry failed for %s", + ldb_dn_get_linearized(msg->dn)); } return ret; } -- 2.1.4 From a8242f46adf39e3eedb86416eb145be93f1ba54a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 20 Jan 2015 10:52:22 +0000 Subject: [PATCH 05/60] s4:kdc/db-glue: fix supported_enctypes samba_kdc_trust_message2entry() This avoids writing invalid memory, because num_keys was calculated in a wrong way... Signed-off-by: Stefan Metzmacher Reviewed-by: Guenther Deschner BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 01c02340c1700aeb16d167be45f6de8d96a91802) --- source4/kdc/db-glue.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index caeb1b2..37e2f9e 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -872,7 +872,7 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, int ret, trust_direction_flags; unsigned int i; struct AuthenticationInformationArray *auth_array; - uint32_t supported_enctypes = ENCTYPE_ARCFOUR_HMAC; + uint32_t supported_enctypes = ENC_RC4_HMAC_MD5; if (dsdb_functional_level(kdc_db_ctx->samdb) >= DS_DOMAIN_FUNCTION_2008) { supported_enctypes = ldb_msg_find_attr_as_uint(msg, @@ -1015,7 +1015,7 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, break; } - if (supported_enctypes & ENCTYPE_ARCFOUR_HMAC) { + if (supported_enctypes & ENC_RC4_HMAC_MD5) { mdfour(_password_hash.hash, password_utf16.data, password_utf16.length); if (password_hash == NULL) { num_keys += 1; @@ -1047,7 +1047,7 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, } break; } else if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) { - if (supported_enctypes & ENCTYPE_ARCFOUR_HMAC) { + if (supported_enctypes & ENC_RC4_HMAC_MD5) { password_hash = &auth_array->array[i].AuthInfo.nt4owf.password; num_keys += 1; } @@ -1085,7 +1085,7 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, goto out; } - if (supported_enctypes & ENCTYPE_AES256_CTS_HMAC_SHA1_96) { + if (supported_enctypes & ENC_HMAC_SHA1_96_AES256) { ret = krb5_string_to_key_data_salt(context, ENCTYPE_AES256_CTS_HMAC_SHA1_96, cleartext_data, @@ -1100,7 +1100,7 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, entry_ex->entry.keys.len++; } - if (supported_enctypes & ENCTYPE_AES128_CTS_HMAC_SHA1_96) { + if (supported_enctypes & ENC_HMAC_SHA1_96_AES128) { ret = krb5_string_to_key_data_salt(context, ENCTYPE_AES128_CTS_HMAC_SHA1_96, cleartext_data, -- 2.1.4 From 8d329eefdf2d564cec00db23f979b82818c94dec Mon Sep 17 00:00:00 2001 From: Garming Sam Date: Tue, 16 Dec 2014 14:54:14 +1300 Subject: [PATCH 06/60] test: improve kinit kerberos tests For enterprise and windows style kinit, a UPN is now configured. There are now additional smbclient calls and added cache removals to make the tests more robust. Change-Id: I7c58ae4c9f303ca74a52878aa5dce2cc5f7d6742 Pair-programmed-with: Andrew Bartlett Signed-off-by: Garming Sam Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 64f5984273fac19282fa1dd77c773840030a4ccb) --- testprogs/blackbox/test_kinit.sh | 44 ++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/testprogs/blackbox/test_kinit.sh b/testprogs/blackbox/test_kinit.sh index 6ec1eaf..cc235c2 100755 --- a/testprogs/blackbox/test_kinit.sh +++ b/testprogs/blackbox/test_kinit.sh @@ -71,16 +71,25 @@ enctype="-e $ENCTYPE" PWSETCONFIG="-H ldap://$SERVER -U$USERNAME%$PASSWORD" export PWSETCONFIG -KRB5CCNAME="$PREFIX/tmpccache" +KRB5CCNAME_PATH="$PREFIX/tmpccache" +KRB5CCNAME="FILE:$KRB5CCNAME_PATH" +ADMIN_KRB5CCNAME="FILE:$KRB5CCNAME_PATH" export KRB5CCNAME +rm -rf $KRB5CCNAME_PATH testit "reset password policies beside of minimum password age of 0 days" $VALGRIND $samba_tool domain passwordsettings $PWSETCONFIG set --complexity=default --history-length=default --min-pwd-length=default --min-pwd-age=0 --max-pwd-age=default || failed=`expr $failed + 1` echo $PASSWORD > $PREFIX/tmppassfile #testit "kinit with keytab" $samba4kinit $enctype --keytab=$PREFIX/dc/private/secrets.keytab $SERVER\$@$REALM || failed=`expr $failed + 1` testit "kinit with password" $samba4kinit $enctype --password-file=$PREFIX/tmppassfile --request-pac $USERNAME@$REALM || failed=`expr $failed + 1` +test_smbclient "Test login with user kerberos ccache" 'ls' -k yes || failed=`expr $failed + 1` + testit "kinit with password (enterprise style)" $samba4kinit $enctype --enterprise --password-file=$PREFIX/tmppassfile --request-pac $USERNAME@$REALM || failed=`expr $failed + 1` +test_smbclient "Test login with user kerberos ccache" 'ls' -k yes || failed=`expr $failed + 1` + testit "kinit with password (windows style)" $samba4kinit $enctype --renewable --windows --password-file=$PREFIX/tmppassfile --request-pac $USERNAME@$REALM || failed=`expr $failed + 1` +test_smbclient "Test login with user kerberos ccache" 'ls' -k yes || failed=`expr $failed + 1` + testit "kinit renew ticket" $samba4kinit $enctype --request-pac -R test_smbclient "Test login with kerberos ccache" 'ls' -k yes || failed=`expr $failed + 1` @@ -99,17 +108,21 @@ dn: cn=nettestuser,cn=users,$BASEDN changetype: modify add: servicePrincipalName servicePrincipalName: host/nettestuser +replace: userPrincipalName +userPrincipalName: nettest@$REALM EOF -testit "modify servicePrincipalName" $VALGRIND $ldbmodify -H ldap://$SERVER $PREFIX/tmpldbmodify -k yes $@ || failed=`expr $failed + 1` +testit "modify servicePrincipalName and userPrincpalName" $VALGRIND $ldbmodify -H ldap://$SERVER $PREFIX/tmpldbmodify -k yes $@ || failed=`expr $failed + 1` testit "set user password with kerberos ccache" $VALGRIND $samba_tool user setpassword nettestuser --newpassword=$USERPASS $CONFIGURATION -k yes $@ || failed=`expr $failed + 1` testit "enable user with kerberos cache" $VALGRIND $enableaccount nettestuser -H ldap://$SERVER -k yes $@ || failed=`expr $failed + 1` -KRB5CCNAME="$PREFIX/tmpuserccache" +KRB5CCNAME_PATH="$PREFIX/tmpuserccache" +KRB5CCNAME="FILE:$KRB5CCNAME_PATH" export KRB5CCNAME +rm -f $KRB5CCNAME_PATH testit "kinit with user password" $samba4kinit $enctype --password-file=$PREFIX/tmpuserpassfile --request-pac nettestuser@$REALM || failed=`expr $failed + 1` test_smbclient "Test login with user kerberos ccache" 'ls' -k yes || failed=`expr $failed + 1` @@ -118,11 +131,25 @@ NEWUSERPASS=testPaSS@34% testit "change user password with 'samba-tool user password' (rpc)" $VALGRIND $samba_tool user password -W$DOMAIN -Unettestuser%$USERPASS $CONFIGURATION -k no --newpassword=$NEWUSERPASS $@ || failed=`expr $failed + 1` echo $NEWUSERPASS > $PREFIX/tmpuserpassfile +rm -f $KRB5CCNAME_PATH testit "kinit with user password" $samba4kinit $enctype --password-file=$PREFIX/tmpuserpassfile --request-pac nettestuser@$REALM || failed=`expr $failed + 1` test_smbclient "Test login with user kerberos ccache" 'ls' -k yes || failed=`expr $failed + 1` +rm -f $KRB5CCNAME_PATH +testit "kinit with password (NT-Principal style) using UPN" $samba4kinit $enctype --password-file=$PREFIX/tmpuserpassfile --request-pac nettest@$REALM || failed=`expr $failed + 1` +test_smbclient "Test login with user kerberos ccache from enterprise UPN" 'ls' -k yes || failed=`expr $failed + 1` + +rm -f $KRB5CCNAME_PATH +testit "kinit with password (enterprise style) using UPN" $samba4kinit $enctype --enterprise --password-file=$PREFIX/tmpuserpassfile --request-pac nettest@$REALM || failed=`expr $failed + 1` +test_smbclient "Test login with user kerberos ccache from enterprise UPN" 'ls' -k yes || failed=`expr $failed + 1` + +rm -f $KRB5CCNAME_PATH +testit "kinit with password (windows style) using UPN" $samba4kinit $enctype --renewable --windows --password-file=$PREFIX/tmpuserpassfile --request-pac nettest@$REALM || failed=`expr $failed + 1` +test_smbclient "Test login with user kerberos ccache from windows UPN" 'ls' -k yes || failed=`expr $failed + 1` + + USERPASS=$NEWUSERPASS NEWUSERPASS=testPaSS@56% echo $NEWUSERPASS > $PREFIX/tmpuserpassfile @@ -139,6 +166,7 @@ EOF testit "change user password with kpasswd" $texpect $PREFIX/tmpkpasswdscript $samba4kpasswd nettestuser@$REALM || failed=`expr $failed + 1` +rm -f $KRB5CCNAME_PATH testit "kinit with user password" $samba4kinit $enctype --password-file=$PREFIX/tmpuserpassfile --request-pac nettestuser@$REALM || failed=`expr $failed + 1` NEWUSERPASS=testPaSS@78% @@ -154,8 +182,9 @@ send ${NEWUSERPASS}\n expect Success EOF -testit "set user password with kpasswd" $texpect $PREFIX/tmpkpasswdscript $samba4kpasswd --cache=$PREFIX/tmpccache nettestuser@$REALM || failed=`expr $failed + 1` +testit "set user password with kpasswd" $texpect $PREFIX/tmpkpasswdscript $samba4kpasswd --cache=$ADMIN_KRB5CCNAME nettestuser@$REALM || failed=`expr $failed + 1` +rm -f $KRB5CCNAME_PATH testit "kinit with user password" $samba4kinit $enctype --password-file=$PREFIX/tmpuserpassfile --request-pac nettestuser@$REALM || failed=`expr $failed + 1` test_smbclient "Test login with user kerberos ccache" 'ls' -k yes || failed=`expr $failed + 1` @@ -210,16 +239,19 @@ testit "kinit with user password" $samba4kinit $enctype --password-file=$PREFIX/ test_smbclient "Test login with user kerberos ccache" 'ls' -k yes || failed=`expr $failed + 1` -KRB5CCNAME="$PREFIX/tmpccache" +KRB5CCNAME_PATH="$PREFIX/tmpccache" +KRB5CCNAME="FILE:$KRB5CCNAME_PATH" export KRB5CCNAME +rm -rf $KRB5CCNAME_PATH + lowerrealm=$(echo $REALM | tr '[A-Z]' '[a-z]') test_smbclient "Test login with user kerberos lowercase realm" 'ls' -k yes -Unettestuser@$lowerrealm%$NEWUSERPASS || failed=`expr $failed + 1` test_smbclient "Test login with user kerberos lowercase realm 2" 'ls' -k yes -Unettestuser@$REALM%$NEWUSERPASS --realm=$lowerrealm || failed=`expr $failed + 1` testit "del user with kerberos ccache" $VALGRIND $samba_tool user delete nettestuser $CONFIGURATION -k yes $@ || failed=`expr $failed + 1` -rm -f $KRB5CCNAME +rm -f $KRB5CCNAME_PATH testit "kinit with machineaccountccache script" $machineaccountccache $CONFIGURATION $KRB5CCNAME || failed=`expr $failed + 1` test_smbclient "Test machine account login with kerberos ccache" 'ls' -k yes || failed=`expr $failed + 1` -- 2.1.4 From 6c02b27098855471a3875e2a152afa47f2018eeb Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 5 Jan 2015 14:54:45 +1300 Subject: [PATCH 07/60] torture: Start a new testsuite for krb5 and KDC behaviour Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 9a0aa6f6f7217399eaac34aa8ac82b49d953175a) --- source4/auth/kerberos/krb5_init_context.c | 123 +++++++++++++++++++++--------- source4/auth/kerberos/krb5_init_context.h | 6 ++ source4/selftest/tests.py | 4 + source4/torture/krb5/kdc.c | 118 ++++++++++++++++++++++++++++ source4/torture/krb5/wscript_build | 11 +++ source4/torture/wscript_build | 1 + 6 files changed, 226 insertions(+), 37 deletions(-) create mode 100644 source4/torture/krb5/kdc.c create mode 100644 source4/torture/krb5/wscript_build diff --git a/source4/auth/kerberos/krb5_init_context.c b/source4/auth/kerberos/krb5_init_context.c index 4404b67..e8a1a6c 100644 --- a/source4/auth/kerberos/krb5_init_context.c +++ b/source4/auth/kerberos/krb5_init_context.c @@ -210,46 +210,31 @@ static void smb_krb5_socket_handler(struct tevent_context *ev, struct tevent_fd } } -krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, - void *data, - krb5_krbhst_info *hi, - time_t timeout, - const krb5_data *send_buf, - krb5_data *recv_buf) +static krb5_error_code smb_krb5_send_and_recv_func_int(krb5_context context, + struct tevent_context *ev, + krb5_krbhst_info *hi, + struct addrinfo *ai, + krb5_send_to_kdc_func func, + void *data, + time_t timeout, + const krb5_data *send_buf, + krb5_data *recv_buf) { krb5_error_code ret; NTSTATUS status; const char *name; - struct addrinfo *ai, *a; + struct addrinfo *a; struct smb_krb5_socket *smb_krb5; DATA_BLOB send_blob; - struct tevent_context *ev; TALLOC_CTX *tmp_ctx = talloc_new(NULL); if (!tmp_ctx) { return ENOMEM; } - if (!data) { - /* If no event context was available, then create one for this loop */ - ev = samba_tevent_context_init(tmp_ctx); - if (!ev) { - talloc_free(tmp_ctx); - return ENOMEM; - } - } else { - ev = talloc_get_type_abort(data, struct tevent_context); - } - send_blob = data_blob_const(send_buf->data, send_buf->length); - ret = krb5_krbhst_get_addrinfo(context, hi, &ai); - if (ret) { - talloc_free(tmp_ctx); - return ret; - } - for (a = ai; a; a = a->ai_next) { struct socket_address *remote_addr; smb_krb5 = talloc(tmp_ctx, struct smb_krb5_socket); @@ -359,18 +344,20 @@ krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, return EINVAL; } - /* After each and every event loop, reset the - * send_to_kdc pointers to what they were when - * we entered this loop. That way, if a - * nested event has invalidated them, we put - * it back before we return to the heimdal - * code */ - ret = krb5_set_send_to_kdc_func(context, - smb_krb5_send_and_recv_func, - data); - if (ret != 0) { - talloc_free(tmp_ctx); - return ret; + if (func) { + /* After each and every event loop, reset the + * send_to_kdc pointers to what they were when + * we entered this loop. That way, if a + * nested event has invalidated them, we put + * it back before we return to the heimdal + * code */ + ret = krb5_set_send_to_kdc_func(context, + func, + data); + if (ret != 0) { + talloc_free(tmp_ctx); + return ret; + } } } if (NT_STATUS_EQUAL(smb_krb5->status, NT_STATUS_IO_TIMEOUT)) { @@ -407,6 +394,68 @@ krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, } return KRB5_KDC_UNREACH; } + +krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, + void *data, + krb5_krbhst_info *hi, + time_t timeout, + const krb5_data *send_buf, + krb5_data *recv_buf) +{ + krb5_error_code ret; + struct addrinfo *ai; + + struct tevent_context *ev; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + if (!data) { + /* If no event context was available, then create one for this loop */ + ev = samba_tevent_context_init(tmp_ctx); + if (!ev) { + talloc_free(tmp_ctx); + return ENOMEM; + } + } else { + ev = talloc_get_type_abort(data, struct tevent_context); + } + + ret = krb5_krbhst_get_addrinfo(context, hi, &ai); + if (ret) { + talloc_free(tmp_ctx); + return ret; + } + return smb_krb5_send_and_recv_func_int(context, ev, hi, ai, smb_krb5_send_and_recv_func, data, timeout, send_buf, recv_buf); +} + +krb5_error_code smb_krb5_send_and_recv_func_forced(krb5_context context, + void *data, /* struct addrinfo */ + krb5_krbhst_info *hi, + time_t timeout, + const krb5_data *send_buf, + krb5_data *recv_buf) +{ + struct addrinfo *ai = data; + + struct tevent_context *ev; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + /* If no event context was available, then create one for this loop */ + ev = samba_tevent_context_init(tmp_ctx); + if (!ev) { + talloc_free(tmp_ctx); + return ENOMEM; + } + + /* No need to pass in send_and_recv functions, we won't nest on this private event loop */ + return smb_krb5_send_and_recv_func_int(context, ev, hi, ai, NULL, NULL, + timeout, send_buf, recv_buf); +} #endif krb5_error_code diff --git a/source4/auth/kerberos/krb5_init_context.h b/source4/auth/kerberos/krb5_init_context.h index 3c32069..6c997c5 100644 --- a/source4/auth/kerberos/krb5_init_context.h +++ b/source4/auth/kerberos/krb5_init_context.h @@ -45,6 +45,12 @@ krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, time_t timeout, const krb5_data *send_buf, krb5_data *recv_buf); +krb5_error_code smb_krb5_send_and_recv_func_forced(krb5_context context, + void *data, /* struct addrinfo */ + krb5_krbhst_info *hi, + time_t timeout, + const krb5_data *send_buf, + krb5_data *recv_buf); krb5_error_code smb_krb5_context_set_event_ctx(struct smb_krb5_context *smb_krb5_context, struct tevent_context *ev, struct tevent_context **previous_ev); diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 8d03b40..1b54d25 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -546,6 +546,10 @@ for env in ['vampire_dc', 'promoted_dc']: for env in ["dc", "s4member", "rodc", "promoted_dc", "plugin_s4_dc", "s3member"]: plantestsuite("samba.blackbox.wbinfo(%s:local)" % env, "%s:local" % env, [os.path.join(samba4srcdir, "../nsswitch/tests/test_wbinfo.sh"), '$DOMAIN', '$DC_USERNAME', '$DC_PASSWORD', env]) +for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc"]: + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER', "-k", "yes", '-U$USERNAME@$REALM%$PASSWORD', '--workgroup=$DOMAIN']) + + # TODO: Verifying the databases really should be a part of the # environment teardown. # check the databases are all OK. PLEASE LEAVE THIS AS THE LAST TEST diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c new file mode 100644 index 0000000..9dcee40 --- /dev/null +++ b/source4/torture/krb5/kdc.c @@ -0,0 +1,118 @@ +/* + Unix SMB/CIFS implementation. + + Validate the krb5 pac generation routines + + Copyright (C) Andrew Bartlett 2005-2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "torture/smbtorture.h" +#include "torture/winbind/proto.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/popt_common.h" +#include "source4/auth/kerberos/kerberos.h" +#include "source4/auth/kerberos/kerberos_util.h" +#include "lib/util/util_net.h" + +static bool torture_krb5_init_context(struct torture_context *tctx, + struct smb_krb5_context **smb_krb5_context) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + krb5_error_code k5ret; + bool ok; + struct addrinfo *server; + + k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context); + torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed"); + + ok = interpret_string_addr_internal(&server, host, AI_NUMERICHOST); + torture_assert(tctx, ok, "Failed to parse target server"); + + set_sockaddr_port(server->ai_addr, 88); + + k5ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context, + smb_krb5_send_and_recv_func_forced, + server); + torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed"); + return true; +} + +static bool torture_krb5_as_req_1(struct torture_context *tctx) +{ + krb5_error_code k5ret; + bool ok; + krb5_creds my_creds; + krb5_principal principal; + struct smb_krb5_context *smb_krb5_context; + enum credentials_obtained obtained; + const char *error_string; + const char *password = cli_credentials_get_password(cmdline_credentials); + + ok = torture_krb5_init_context(tctx, &smb_krb5_context); + torture_assert(tctx, ok, "torture_krb5_init_context failed"); + + k5ret = principal_from_credentials(tctx, cmdline_credentials, smb_krb5_context, &principal, &obtained, &error_string); + torture_assert_int_equal(tctx, k5ret, 0, error_string); + + k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal, + password, NULL, NULL, 0, + NULL, NULL); + torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed"); + + torture_assert_int_equal(tctx, + krb5_principal_get_type(smb_krb5_context->krb5_context, + my_creds.client), KRB5_NT_PRINCIPAL, + "smb_krb5_init_context gave incorrect client->name.name_type"); + + torture_assert(tctx, krb5_principal_compare(smb_krb5_context->krb5_context, + principal, my_creds.client), + "krb5_get_init_creds_password returned a different principal"); + + torture_assert_int_equal(tctx, + krb5_principal_get_type(smb_krb5_context->krb5_context, + my_creds.server), KRB5_NT_SRV_INST, + "smb_krb5_init_context gave incorrect client->name.name_type"); + + torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context, + my_creds.server, 0), + "krbtgt", + "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]"); + + k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds); + torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed"); + + return true; +} + +NTSTATUS torture_krb5_init(void); +NTSTATUS torture_krb5_init(void) +{ + struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "krb5"); + struct torture_suite *kdc_suite = torture_suite_create(suite, "kdc"); + suite->description = talloc_strdup(suite, "Kerberos tests"); + kdc_suite->description = talloc_strdup(kdc_suite, "Kerberos KDC tests"); + + torture_suite_add_simple_test(kdc_suite, "as-req-1", + torture_krb5_as_req_1); + + torture_suite_add_suite(suite, kdc_suite); + + torture_register_suite(suite); + return NT_STATUS_OK; +} diff --git a/source4/torture/krb5/wscript_build b/source4/torture/krb5/wscript_build new file mode 100644 index 0000000..963c3a3 --- /dev/null +++ b/source4/torture/krb5/wscript_build @@ -0,0 +1,11 @@ +#!/usr/bin/env python +if bld.CONFIG_SET('AD_DC_BUILD_IS_ENABLED'): + bld.SAMBA_MODULE('TORTURE_KRB5', + source='kdc.c', + autoproto='proto.h', + subsystem='smbtorture', + init_function='torture_krb5_init', + deps='authkrb5 popt POPT_CREDENTIALS torture KERBEROS_UTIL', + internal_module=True + ) + diff --git a/source4/torture/wscript_build b/source4/torture/wscript_build index c13e7d4..0ef2c23 100755 --- a/source4/torture/wscript_build +++ b/source4/torture/wscript_build @@ -90,6 +90,7 @@ bld.SAMBA_MODULE('TORTURE_AUTH', ) bld.RECURSE('local') +bld.RECURSE('krb5') bld.SAMBA_MODULE('TORTURE_NBENCH', source='nbench/nbio.c nbench/nbench.c', -- 2.1.4 From c2ecdf083d72bd49d1490444bb529b789086e10f Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 5 Jan 2015 16:07:42 +1300 Subject: [PATCH 08/60] torture: Run new testsuite for krb5 and KDC behaviour with machine account also Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 378bb04835a377699a8ff254c0ec633ac63a41de) --- source4/selftest/tests.py | 5 ++++- source4/torture/krb5/kdc.c | 16 +++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 1b54d25..cf44dc5 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -547,7 +547,10 @@ for env in ["dc", "s4member", "rodc", "promoted_dc", "plugin_s4_dc", "s3member"] plantestsuite("samba.blackbox.wbinfo(%s:local)" % env, "%s:local" % env, [os.path.join(samba4srcdir, "../nsswitch/tests/test_wbinfo.sh"), '$DOMAIN', '$DC_USERNAME', '$DC_PASSWORD', env]) for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc"]: - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER', "-k", "yes", '-U$USERNAME@$REALM%$PASSWORD', '--workgroup=$DOMAIN']) + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER', "-k", "yes", '-U$USERNAME@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], + "samba4.krb5.kdc with specified account") + plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER', "-k", "yes", '-P', '--workgroup=$DOMAIN'], + "samba4.krb5.kdc with machine account") # TODO: Verifying the databases really should be a part of the diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index 9dcee40..c9014cf 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -53,7 +53,8 @@ static bool torture_krb5_init_context(struct torture_context *tctx, return true; } -static bool torture_krb5_as_req_1(struct torture_context *tctx) +static bool torture_krb5_as_req_creds(struct torture_context *tctx, + struct cli_credentials *credentials) { krb5_error_code k5ret; bool ok; @@ -62,12 +63,12 @@ static bool torture_krb5_as_req_1(struct torture_context *tctx) struct smb_krb5_context *smb_krb5_context; enum credentials_obtained obtained; const char *error_string; - const char *password = cli_credentials_get_password(cmdline_credentials); + const char *password = cli_credentials_get_password(credentials); ok = torture_krb5_init_context(tctx, &smb_krb5_context); torture_assert(tctx, ok, "torture_krb5_init_context failed"); - k5ret = principal_from_credentials(tctx, cmdline_credentials, smb_krb5_context, &principal, &obtained, &error_string); + k5ret = principal_from_credentials(tctx, credentials, smb_krb5_context, &principal, &obtained, &error_string); torture_assert_int_equal(tctx, k5ret, 0, error_string); k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal, @@ -100,6 +101,11 @@ static bool torture_krb5_as_req_1(struct torture_context *tctx) return true; } +static bool torture_krb5_as_req_cmdline(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, cmdline_credentials); +} + NTSTATUS torture_krb5_init(void); NTSTATUS torture_krb5_init(void) { @@ -108,8 +114,8 @@ NTSTATUS torture_krb5_init(void) suite->description = talloc_strdup(suite, "Kerberos tests"); kdc_suite->description = talloc_strdup(kdc_suite, "Kerberos KDC tests"); - torture_suite_add_simple_test(kdc_suite, "as-req-1", - torture_krb5_as_req_1); + torture_suite_add_simple_test(kdc_suite, "as-req-cmdline", + torture_krb5_as_req_cmdline); torture_suite_add_suite(suite, kdc_suite); -- 2.1.4 From 5d66ea1d9b000aa283fd8f91a4474f6aebe5b05d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 5 Jan 2015 16:32:23 +1300 Subject: [PATCH 09/60] torture: Additionally run testsuite for krb5 and KDC behaviour with unprivileged accounts Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit ff240c84e471fb6e83f663fef6b0ec7f257832e2) --- selftest/target/Samba4.pm | 21 +++++++++++++++++++++ source4/selftest/tests.py | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 342de58..568d27a 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -773,6 +773,27 @@ sub provision_raw_step2($$$) return undef; } + my $samba_tool_cmd = Samba::bindir_path($self, "samba-tool") + . " user add --configfile=$ctx->{smb_conf} testallowed $ctx->{password}"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to add testallowed user: \n$samba_tool_cmd\n"); + return undef; + } + + $samba_tool_cmd = Samba::bindir_path($self, "samba-tool") + . " user add --configfile=$ctx->{smb_conf} testdenied $ctx->{password}"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to add testdenied user: \n$samba_tool_cmd\n"); + return undef; + } + + $samba_tool_cmd = Samba::bindir_path($self, "samba-tool") + . " group addmembers --configfile=$ctx->{smb_conf} 'Allowed RODC Password Replication Group' testallowed"; + unless (system($samba_tool_cmd) == 0) { + warn("Unable to add testallowed user to 'Allowed RODC Password Replication Group': \n$samba_tool_cmd\n"); + return undef; + } + return $ret; } diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index cf44dc5..d1b7bfd 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -549,6 +549,10 @@ for env in ["dc", "s4member", "rodc", "promoted_dc", "plugin_s4_dc", "s3member"] for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc"]: plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER', "-k", "yes", '-U$USERNAME@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], "samba4.krb5.kdc with specified account") + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER', "-k", "yes", '-Utestallowed@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], + "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER', "-k", "yes", '-Utestdenied@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], + "samba4.krb5.kdc with account DENIED permission to replicate to an RODC") plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER', "-k", "yes", '-P', '--workgroup=$DOMAIN'], "samba4.krb5.kdc with machine account") -- 2.1.4 From 2716687484aae7da179702ebf19767edb27ede5c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 5 Jan 2015 16:48:08 +1300 Subject: [PATCH 10/60] torture: Additionally run testsuite for krb5 and KDC behaviour against all the DC envs Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit fc84d35c4eaf50ca8139b1210201be12d89a0b3e) --- source4/selftest/tests.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index d1b7bfd..3b849f8 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -546,14 +546,14 @@ for env in ['vampire_dc', 'promoted_dc']: for env in ["dc", "s4member", "rodc", "promoted_dc", "plugin_s4_dc", "s3member"]: plantestsuite("samba.blackbox.wbinfo(%s:local)" % env, "%s:local" % env, [os.path.join(samba4srcdir, "../nsswitch/tests/test_wbinfo.sh"), '$DOMAIN', '$DC_USERNAME', '$DC_PASSWORD', env]) -for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc"]: - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER', "-k", "yes", '-U$USERNAME@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], +for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", "fl2008r2dc"]: + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-U$USERNAME@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], "samba4.krb5.kdc with specified account") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER', "-k", "yes", '-Utestallowed@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER', "-k", "yes", '-Utestdenied@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestdenied@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], "samba4.krb5.kdc with account DENIED permission to replicate to an RODC") - plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER', "-k", "yes", '-P', '--workgroup=$DOMAIN'], + plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN'], "samba4.krb5.kdc with machine account") -- 2.1.4 From aec9c6e7d81089fc7c882c74754875e088eed3a4 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 5 Jan 2015 17:48:50 +1300 Subject: [PATCH 11/60] torture: Decode expected packets and test KDC behaviour for wrong passwords Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 672ade3876877ad30e4367f0cd01e660b0def8cd) --- source4/torture/krb5/kdc.c | 173 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 164 insertions(+), 9 deletions(-) diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index c9014cf..c9a1a60 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -30,31 +30,170 @@ #include "source4/auth/kerberos/kerberos_util.h" #include "lib/util/util_net.h" +enum torture_krb5_test { + TORTURE_KRB5_TEST_PLAIN, + TORTURE_KRB5_TEST_BREAK_PW +}; + +struct torture_krb5_context { + struct torture_context *tctx; + struct addrinfo *server; + enum torture_krb5_test test; + int packet_count; + AS_REQ as_req; + AS_REP as_rep; +}; + +static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, const krb5_data *send_buf) +{ + size_t used; + switch (test_context->test) + { + case TORTURE_KRB5_TEST_PLAIN: + case TORTURE_KRB5_TEST_BREAK_PW: + torture_assert_int_equal(test_context->tctx, + decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0, + "decode_AS_REQ failed"); + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno"); + break; + } + return true; +} + +static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) +{ + KRB_ERROR error; + size_t used; + switch (test_context->test) + { + case TORTURE_KRB5_TEST_PLAIN: + if (test_context->packet_count == 0) { + torture_assert_int_equal(test_context->tctx, + decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + 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_REQUIRED - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) + && (test_context->packet_count == 1)) { + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else { + torture_assert_int_equal(test_context->tctx, + decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->as_rep.pvno, 5, "Got wrong as_rep->pvno"); + free_AS_REP(&test_context->as_rep); + } + torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); + free_AS_REQ(&test_context->as_req); + break; + case TORTURE_KRB5_TEST_BREAK_PW: + if (test_context->packet_count == 0) { + torture_assert_int_equal(test_context->tctx, + decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + 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_REQUIRED - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else if (test_context->packet_count == 1) { + torture_assert_int_equal(test_context->tctx, + decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + 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"); + free_KRB_ERROR(&error); + } + torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets"); + free_AS_REQ(&test_context->as_req); + break; + } + return true; +} + +static krb5_error_code smb_krb5_send_and_recv_func_override(krb5_context context, + void *data, /* struct torture_krb5_context */ + krb5_krbhst_info *hi, + time_t timeout, + const krb5_data *send_buf, + krb5_data *recv_buf) +{ + krb5_error_code k5ret; + bool ok; + + struct torture_krb5_context *test_context + = talloc_get_type_abort(data, struct torture_krb5_context); + + ok = torture_krb5_pre_send_test(test_context, send_buf); + if (ok == false) { + return EINVAL; + } + + k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server, + hi, timeout, send_buf, recv_buf); + + ok = torture_krb5_post_recv_test(test_context, recv_buf); + if (ok == false) { + return EINVAL; + } + + test_context->packet_count++; + + return k5ret; +} + +static int test_context_destructor(struct torture_krb5_context *test_context) +{ + freeaddrinfo(test_context->server); + return 0; +} + + static bool torture_krb5_init_context(struct torture_context *tctx, + enum torture_krb5_test test, struct smb_krb5_context **smb_krb5_context) { const char *host = torture_setting_string(tctx, "host", NULL); krb5_error_code k5ret; bool ok; - struct addrinfo *server; + + struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context); + torture_assert(tctx, test_context != NULL, "Failed to allocate"); + + test_context->test = test; + test_context->tctx = tctx; k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context); torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed"); - ok = interpret_string_addr_internal(&server, host, AI_NUMERICHOST); + ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST); torture_assert(tctx, ok, "Failed to parse target server"); - set_sockaddr_port(server->ai_addr, 88); + talloc_set_destructor(test_context, test_context_destructor); + + set_sockaddr_port(test_context->server->ai_addr, 88); k5ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context, - smb_krb5_send_and_recv_func_forced, - server); + smb_krb5_send_and_recv_func_override, + test_context); torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed"); return true; } static bool torture_krb5_as_req_creds(struct torture_context *tctx, - struct cli_credentials *credentials) + struct cli_credentials *credentials, + enum torture_krb5_test test) { krb5_error_code k5ret; bool ok; @@ -65,16 +204,24 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx, const char *error_string; const char *password = cli_credentials_get_password(credentials); - ok = torture_krb5_init_context(tctx, &smb_krb5_context); + ok = torture_krb5_init_context(tctx, test, &smb_krb5_context); torture_assert(tctx, ok, "torture_krb5_init_context failed"); k5ret = principal_from_credentials(tctx, credentials, smb_krb5_context, &principal, &obtained, &error_string); torture_assert_int_equal(tctx, k5ret, 0, error_string); + if (test == TORTURE_KRB5_TEST_BREAK_PW) { + password = "NOT the password"; + } k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal, password, NULL, NULL, 0, NULL, NULL); - torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed"); + if (test == TORTURE_KRB5_TEST_BREAK_PW) { + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_PREAUTH_FAILED, "krb5_get_init_creds_password should have failed"); + return true; + } else { + torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed"); + } torture_assert_int_equal(tctx, krb5_principal_get_type(smb_krb5_context->krb5_context, @@ -103,7 +250,12 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx, static bool torture_krb5_as_req_cmdline(struct torture_context *tctx) { - return torture_krb5_as_req_creds(tctx, cmdline_credentials); + return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_PLAIN); +} + +static bool torture_krb5_as_req_break_pw(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_BREAK_PW); } NTSTATUS torture_krb5_init(void); @@ -117,6 +269,9 @@ NTSTATUS torture_krb5_init(void) torture_suite_add_simple_test(kdc_suite, "as-req-cmdline", torture_krb5_as_req_cmdline); + torture_suite_add_simple_test(kdc_suite, "as-req-break-pw", + torture_krb5_as_req_break_pw); + torture_suite_add_suite(suite, kdc_suite); torture_register_suite(suite); -- 2.1.4 From 4fd2e7cccaf12f5f9f515fc6243c71089cc776b0 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 6 Jan 2015 13:24:04 +1300 Subject: [PATCH 12/60] torture: Extend KDC test to cover more options and modes Signed-off-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit a07598db9cefcad4accd9e189c748a5bed630cf6) --- selftest/knownfail | 4 ++ source4/torture/krb5/kdc.c | 154 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 151 insertions(+), 7 deletions(-) diff --git a/selftest/knownfail b/selftest/knownfail index af7e7fd..5fc05a0 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -296,3 +296,7 @@ ^samba.blackbox.wbinfo\(s3member:local\).wbinfo -G check for sane mapping\(s3member:local\) ^samba.ntlm_auth.\(dc:local\).ntlm_auth against winbindd with failed require-membership-of ^samba.ntlm_auth.\(dc:local\).ntlm_auth with NTLMSSP gss-spnego-client and gss-spnego server against winbind with failed require-membership-of +# +# Differences in our KDC compared to windows +# +^samba4.krb5.kdc .*.as-req-pac-request # We should reply to a request for a PAC over UDP with KRB5KRB_ERR_RESPONSE_TOO_BIG unconditionally \ No newline at end of file diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index c9a1a60..edf1ecd 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -32,7 +32,10 @@ enum torture_krb5_test { TORTURE_KRB5_TEST_PLAIN, - TORTURE_KRB5_TEST_BREAK_PW + TORTURE_KRB5_TEST_WIN2K, + TORTURE_KRB5_TEST_PAC_REQUEST, + TORTURE_KRB5_TEST_BREAK_PW, + TORTURE_KRB5_TEST_CLOCK_SKEW, }; struct torture_krb5_context { @@ -50,7 +53,10 @@ static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context switch (test_context->test) { case TORTURE_KRB5_TEST_PLAIN: + case TORTURE_KRB5_TEST_WIN2K: + case TORTURE_KRB5_TEST_PAC_REQUEST: case TORTURE_KRB5_TEST_BREAK_PW: + case TORTURE_KRB5_TEST_CLOCK_SKEW: torture_assert_int_equal(test_context->tctx, decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0, "decode_AS_REQ failed"); @@ -68,6 +74,7 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex switch (test_context->test) { case TORTURE_KRB5_TEST_PLAIN: + case TORTURE_KRB5_TEST_WIN2K: if (test_context->packet_count == 0) { torture_assert_int_equal(test_context->tctx, decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, @@ -95,6 +102,43 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); free_AS_REQ(&test_context->as_req); break; + case TORTURE_KRB5_TEST_PAC_REQUEST: + if (test_context->packet_count == 0) { + torture_assert_int_equal(test_context->tctx, + decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else if (test_context->packet_count == 1) { + torture_assert_int_equal(test_context->tctx, + decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + 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_REQUIRED - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) + && (test_context->packet_count == 2)) { + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else { + torture_assert_int_equal(test_context->tctx, + decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->as_rep.pvno, 5, "Got wrong as_rep->pvno"); + free_AS_REP(&test_context->as_rep); + } + torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); + free_AS_REQ(&test_context->as_req); + break; case TORTURE_KRB5_TEST_BREAK_PW: if (test_context->packet_count == 0) { torture_assert_int_equal(test_context->tctx, @@ -118,6 +162,29 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets"); free_AS_REQ(&test_context->as_req); break; + case TORTURE_KRB5_TEST_CLOCK_SKEW: + if (test_context->packet_count == 0) { + torture_assert_int_equal(test_context->tctx, + decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + 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_REQUIRED - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else if (test_context->packet_count == 1) { + torture_assert_int_equal(test_context->tctx, + decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_AP_ERR_SKEW - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } + torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets"); + free_AS_REQ(&test_context->as_req); + break; } return true; } @@ -203,24 +270,73 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx, enum credentials_obtained obtained; const char *error_string; const char *password = cli_credentials_get_password(credentials); + krb5_get_init_creds_opt *krb_options = NULL; ok = torture_krb5_init_context(tctx, test, &smb_krb5_context); torture_assert(tctx, ok, "torture_krb5_init_context failed"); - k5ret = principal_from_credentials(tctx, credentials, smb_krb5_context, &principal, &obtained, &error_string); + k5ret = principal_from_credentials(tctx, credentials, smb_krb5_context, + &principal, &obtained, &error_string); torture_assert_int_equal(tctx, k5ret, 0, error_string); - if (test == TORTURE_KRB5_TEST_BREAK_PW) { + switch (test) + { + case TORTURE_KRB5_TEST_PLAIN: + break; + + case TORTURE_KRB5_TEST_WIN2K: + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options), + 0, "krb5_get_init_creds_opt_alloc failed"); + + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context, krb_options, true), + 0, "krb5_get_init_creds_opt_set_win2k failed"); + break; + + case TORTURE_KRB5_TEST_PAC_REQUEST: + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options), + 0, "krb5_get_init_creds_opt_alloc failed"); + + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_set_pac_request(smb_krb5_context->krb5_context, krb_options, true), + 0, "krb5_get_init_creds_opt_set_pac_request failed"); + break; + + case TORTURE_KRB5_TEST_BREAK_PW: password = "NOT the password"; + break; + + case TORTURE_KRB5_TEST_CLOCK_SKEW: + torture_assert_int_equal(tctx, + krb5_set_real_time(smb_krb5_context->krb5_context, time(NULL) + 3600, 0), + 0, "krb5_set_real_time failed"); + break; + + break; } k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal, password, NULL, NULL, 0, - NULL, NULL); - if (test == TORTURE_KRB5_TEST_BREAK_PW) { + NULL, krb_options); + krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options); + + switch (test) + { + case TORTURE_KRB5_TEST_PLAIN: + case TORTURE_KRB5_TEST_WIN2K: + case TORTURE_KRB5_TEST_PAC_REQUEST: + torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed"); + break; + + case TORTURE_KRB5_TEST_BREAK_PW: torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_PREAUTH_FAILED, "krb5_get_init_creds_password should have failed"); return true; - } else { - torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed"); + + case TORTURE_KRB5_TEST_CLOCK_SKEW: + torture_assert_int_equal(tctx, k5ret, KRB5KRB_AP_ERR_SKEW, "krb5_get_init_creds_password should have failed"); + return true; + } torture_assert_int_equal(tctx, @@ -253,11 +369,26 @@ static bool torture_krb5_as_req_cmdline(struct torture_context *tctx) return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_PLAIN); } +static bool torture_krb5_as_req_win2k(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_WIN2K); +} + +static bool torture_krb5_as_req_pac_request(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_PAC_REQUEST); +} + static bool torture_krb5_as_req_break_pw(struct torture_context *tctx) { return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_BREAK_PW); } +static bool torture_krb5_as_req_clock_skew(struct torture_context *tctx) +{ + return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_CLOCK_SKEW); +} + NTSTATUS torture_krb5_init(void); NTSTATUS torture_krb5_init(void) { @@ -269,9 +400,18 @@ NTSTATUS torture_krb5_init(void) torture_suite_add_simple_test(kdc_suite, "as-req-cmdline", torture_krb5_as_req_cmdline); + torture_suite_add_simple_test(kdc_suite, "as-req-win2k", + torture_krb5_as_req_win2k); + + torture_suite_add_simple_test(kdc_suite, "as-req-pac-request", + torture_krb5_as_req_pac_request); + torture_suite_add_simple_test(kdc_suite, "as-req-break-pw", torture_krb5_as_req_break_pw); + torture_suite_add_simple_test(kdc_suite, "as-req-clock-skew", + torture_krb5_as_req_clock_skew); + torture_suite_add_suite(suite, kdc_suite); torture_register_suite(suite); -- 2.1.4 From d6c7df3ecf41cde4a2f903424b4461731c59cbe5 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Wed, 17 Dec 2014 16:55:34 +1300 Subject: [PATCH 13/60] heimdal: Fix bug in KDC handling of enterprise principals The useful change in Samba from this commit is that we gain validation of the enterprise principal name. (commit message by Andrew Bartlett) Cherry-pick of Heimdal commit c76ec8ec6a507a6f34ca80c11e5297146acff83f Reviewed-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Reviewed-by: Andrew Bartlett (cherry picked from commit fe99c420b21933e0dc11a5c4193e9af4cbfc574e) --- source4/heimdal/kdc/misc.c | 73 ++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/source4/heimdal/kdc/misc.c b/source4/heimdal/kdc/misc.c index 1b2c440..749c67c 100644 --- a/source4/heimdal/kdc/misc.c +++ b/source4/heimdal/kdc/misc.c @@ -48,41 +48,36 @@ _kdc_db_fetch(krb5_context context, krb5_error_code ret = HDB_ERR_NOENTRY; int i; unsigned kvno = 0; + krb5_principal enterprise_principal = NULL; + krb5_const_principal princ; + + *h = NULL; if (kvno_ptr) { kvno = *kvno_ptr; flags |= HDB_F_KVNO_SPECIFIED; } - ent = calloc (1, sizeof (*ent)); - if (ent == NULL) { - krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); - return ENOMEM; + ent = calloc(1, sizeof (*ent)); + if (ent == NULL) + return krb5_enomem(context); + + if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { + if (principal->name.name_string.len != 1) { + ret = KRB5_PARSE_MALFORMED; + krb5_set_error_message(context, ret, + "malformed request: " + "enterprise name with %d name components", + principal->name.name_string.len); + goto out; + } + ret = krb5_parse_name(context, principal->name.name_string.val[0], + &enterprise_principal); + if (ret) + goto out; } - for(i = 0; i < config->num_db; i++) { - krb5_principal enterprise_principal = NULL; - if (!(config->db[i]->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) - && principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { - if (principal->name.name_string.len != 1) { - ret = KRB5_PARSE_MALFORMED; - krb5_set_error_message(context, ret, - "malformed request: " - "enterprise name with %d name components", - principal->name.name_string.len); - free(ent); - return ret; - } - ret = krb5_parse_name(context, principal->name.name_string.val[0], - &enterprise_principal); - if (ret) { - free(ent); - return ret; - } - - principal = enterprise_principal; - } - + for (i = 0; i < config->num_db; i++) { ret = config->db[i]->hdb_open(context, config->db[i], O_RDONLY, 0); if (ret) { const char *msg = krb5_get_error_message(context, ret); @@ -91,26 +86,34 @@ _kdc_db_fetch(krb5_context context, continue; } + if (config->db[i]->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) + princ = principal; + else if (enterprise_principal) + princ = enterprise_principal; + ret = config->db[i]->hdb_fetch_kvno(context, config->db[i], - principal, + princ, flags | HDB_F_DECRYPT, kvno, ent); - - krb5_free_principal(context, enterprise_principal); - config->db[i]->hdb_close(context, config->db[i]); - if(ret == 0) { + + if (ret == 0) { if (db) *db = config->db[i]; *h = ent; - return 0; + ent = NULL; + goto out; } } + + ret = HDB_ERR_NOENTRY; + krb5_set_error_message(context, ret, "no such entry found in hdb"); + +out: + krb5_free_principal(context, enterprise_principal); free(ent); - krb5_set_error_message(context, ret, - "no such entry found in hdb"); return ret; } -- 2.1.4 From bee970edf66d4374f837c6f534ceff264b314a2a Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Wed, 17 Dec 2014 16:57:40 +1300 Subject: [PATCH 14/60] heimdal: Really bug in KDC handling of enterprise princs The value of this commit to Samba is to continue to match Heimdal's upstream code in this area. Because we set HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL there is no runtime difference. (commit message by Andrew Bartlett) Cherry-pick of Heimdal commit 9aa7883ff2efb3e0a60016c9090c577acfd0779f Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Reviewed-by: Andrew Bartlett (cherry picked from commit da4ac71eaba84fa6227b7d9f3adb204003ceaa70) --- source4/heimdal/kdc/misc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source4/heimdal/kdc/misc.c b/source4/heimdal/kdc/misc.c index 749c67c..869c676 100644 --- a/source4/heimdal/kdc/misc.c +++ b/source4/heimdal/kdc/misc.c @@ -86,9 +86,8 @@ _kdc_db_fetch(krb5_context context, continue; } - if (config->db[i]->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) - princ = principal; - else if (enterprise_principal) + princ = principal; + if (!(config->db[i]->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) && enterprise_principal) princ = enterprise_principal; ret = config->db[i]->hdb_fetch_kvno(context, -- 2.1.4 From ea85322e42e7c262bccb8dca113ec3947c0c60d5 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 21 Jan 2015 11:45:45 +1300 Subject: [PATCH 15/60] heimdal: Ensure that HDB_ERR_NOT_FOUND_HERE, critical for the RODC, is not overwritten This change ensures that our RODC will correctly proxy when asked to provide a ticket for a service or user where the keys are not on this RODC. Signed-off-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit 891c4c6a403cc0904c37caaf500bb3a4e3a646c7) --- source4/heimdal/kdc/misc.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/source4/heimdal/kdc/misc.c b/source4/heimdal/kdc/misc.c index 869c676..4ef5439 100644 --- a/source4/heimdal/kdc/misc.c +++ b/source4/heimdal/kdc/misc.c @@ -98,18 +98,33 @@ _kdc_db_fetch(krb5_context context, ent); config->db[i]->hdb_close(context, config->db[i]); - if (ret == 0) { + switch (ret) { + case 0: if (db) *db = config->db[i]; *h = ent; ent = NULL; goto out; + + case HDB_ERR_NOENTRY: + /* Check the other databases */ + continue; + + default: + /* + * This is really important, because errors like + * HDB_ERR_NOT_FOUND_HERE (used to indicate to Samba that + * the RODC on which this code is running does not have + * the key we need, and so a proxy to the KDC is required) + * have specific meaning, and need to be propogated up. + */ + goto out; } } - ret = HDB_ERR_NOENTRY; - krb5_set_error_message(context, ret, "no such entry found in hdb"); - + if (ret == HDB_ERR_NOENTRY) { + krb5_set_error_message(context, ret, "no such entry found in hdb"); + } out: krb5_free_principal(context, enterprise_principal); free(ent); -- 2.1.4 From 758bed64ce36e2e9ade0793f346c2665edbe3e70 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 17 Dec 2014 17:02:53 +1300 Subject: [PATCH 16/60] kdc: Fix enterpise principal name handling Based on a patch by Samuel Cabrero This ensures we write the correct (implict, samAccountName) based UPN into the ticket, rather than the userPrincipalName, which will have a different realm. Pair-programmed-with: Garming Sam Signed-off-by: Andrew Bartlett Signed-off-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 86021a081fa7973d00ac3665296ffcfc9e834fb0) --- source4/kdc/db-glue.c | 33 +++++++++++++++++++++++---------- source4/kdc/hdb-samba4.c | 2 +- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index 37e2f9e..fae2703 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -628,6 +628,8 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context, entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && principal == NULL) { krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL); + } else if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { + krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL); } else { ret = copy_Principal(principal, entry_ex->entry.principal); if (ret) { @@ -1216,18 +1218,29 @@ static krb5_error_code samba_kdc_lookup_client(krb5_context context, struct ldb_message **msg) { NTSTATUS nt_status; char *principal_string; - krb5_error_code ret; - ret = krb5_unparse_name(context, principal, &principal_string); - - if (ret != 0) { - return ret; + if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { + principal_string = smb_krb5_principal_get_comp_string(mem_ctx, context, + principal, 0); + if (principal_string == NULL) { + return ENOMEM; + } + nt_status = sam_get_results_principal(kdc_db_ctx->samdb, + mem_ctx, principal_string, attrs, + realm_dn, msg); + TALLOC_FREE(principal_string); + } else { + krb5_error_code ret; + ret = krb5_unparse_name(context, principal, &principal_string); + if (ret != 0) { + return ret; + } + nt_status = sam_get_results_principal(kdc_db_ctx->samdb, + mem_ctx, principal_string, attrs, + realm_dn, msg); + free(principal_string); } - nt_status = sam_get_results_principal(kdc_db_ctx->samdb, - mem_ctx, principal_string, attrs, - realm_dn, msg); - free(principal_string); if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) { return HDB_ERR_NOENTRY; } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) { @@ -1236,7 +1249,7 @@ static krb5_error_code samba_kdc_lookup_client(krb5_context context, return EINVAL; } - return ret; + return 0; } static krb5_error_code samba_kdc_fetch_client(krb5_context context, diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c index 47d59d4a..52ddb5e 100644 --- a/source4/kdc/hdb-samba4.c +++ b/source4/kdc/hdb-samba4.c @@ -207,7 +207,7 @@ NTSTATUS hdb_samba4_create_kdc(struct samba_kdc_base_context *base_ctx, (*db)->hdb_master_key_set = 0; (*db)->hdb_db = NULL; - (*db)->hdb_capability_flags = 0; + (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL; nt_status = samba_kdc_setup_db_ctx(*db, base_ctx, &kdc_db_ctx); if (!NT_STATUS_IS_OK(nt_status)) { -- 2.1.4 From af171d07de37ea53d9560fd36b16eb951cf53af8 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 18 Dec 2014 17:23:43 +1300 Subject: [PATCH 17/60] sefltest: Add test for enterprise UPN in a different domain Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit c1280569a97be772549debbecb374c53a6cdf796) --- testprogs/blackbox/test_kinit.sh | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/testprogs/blackbox/test_kinit.sh b/testprogs/blackbox/test_kinit.sh index cc235c2..db17e86 100755 --- a/testprogs/blackbox/test_kinit.sh +++ b/testprogs/blackbox/test_kinit.sh @@ -68,8 +68,8 @@ test_smbclient() { enctype="-e $ENCTYPE" -PWSETCONFIG="-H ldap://$SERVER -U$USERNAME%$PASSWORD" -export PWSETCONFIG +ADMIN_LDBMODIFY_CONFIG="-H ldap://$SERVER -U$USERNAME%$PASSWORD" +export ADMIN_LDBMODIFY_CONFIG KRB5CCNAME_PATH="$PREFIX/tmpccache" KRB5CCNAME="FILE:$KRB5CCNAME_PATH" @@ -77,7 +77,7 @@ ADMIN_KRB5CCNAME="FILE:$KRB5CCNAME_PATH" export KRB5CCNAME rm -rf $KRB5CCNAME_PATH -testit "reset password policies beside of minimum password age of 0 days" $VALGRIND $samba_tool domain passwordsettings $PWSETCONFIG set --complexity=default --history-length=default --min-pwd-length=default --min-pwd-age=0 --max-pwd-age=default || failed=`expr $failed + 1` +testit "reset password policies beside of minimum password age of 0 days" $VALGRIND $samba_tool domain passwordsettings $ADMIN_LDBMODIFY_CONFIG set --complexity=default --history-length=default --min-pwd-length=default --min-pwd-age=0 --max-pwd-age=default || failed=`expr $failed + 1` echo $PASSWORD > $PREFIX/tmppassfile #testit "kinit with keytab" $samba4kinit $enctype --keytab=$PREFIX/dc/private/secrets.keytab $SERVER\$@$REALM || failed=`expr $failed + 1` @@ -149,6 +149,19 @@ rm -f $KRB5CCNAME_PATH testit "kinit with password (windows style) using UPN" $samba4kinit $enctype --renewable --windows --password-file=$PREFIX/tmpuserpassfile --request-pac nettest@$REALM || failed=`expr $failed + 1` test_smbclient "Test login with user kerberos ccache from windows UPN" 'ls' -k yes || failed=`expr $failed + 1` +cat > $PREFIX/tmpldbmodify < $PREFIX/tmppasswordchange < Date: Wed, 21 Jan 2015 15:57:40 +1300 Subject: [PATCH 18/60] torture: Extend krb5.kdc test to confirm correct RODC proxy behaviour The RODC should answer some requests locally, and others it should defer to the main DC. We can tell which KDC we talk do by the KVNO of the encrypted parts that are returned to the KDC. Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 03d07ed58bb4ebad41260a35f8952a18c8cf3e6d) --- selftest/target/Samba4.pm | 11 +++++++++++ source4/selftest/tests.py | 12 ++++++++---- source4/torture/krb5/kdc.c | 19 ++++++++++++++++++- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 568d27a..721a7f5 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -1515,6 +1515,17 @@ sub provision_rodc($$$) return undef; } + # This ensures deterministic behaviour for tests that want to have the testallowed + # user password verified on the RODC + $cmd = "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" "; + $cmd .= "$samba_tool rodc preload testallowed $ret->{CONFIGURATION}"; + $cmd .= " --server=$dcvars->{DC_SERVER}"; + + unless (system($cmd) == 0) { + warn("RODC join failed\n$cmd"); + return undef; + } + # we overwrite the kdc after the RODC join # so that use the RODC as kdc and test # the proxy code diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 3b849f8..4c4c0b3 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -549,13 +549,17 @@ for env in ["dc", "s4member", "rodc", "promoted_dc", "plugin_s4_dc", "s3member"] for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", "fl2008r2dc"]: plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-U$USERNAME@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], "samba4.krb5.kdc with specified account") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], - "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestdenied@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], "samba4.krb5.kdc with account DENIED permission to replicate to an RODC") - plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN'], - "samba4.krb5.kdc with machine account") + if env == "rodc": + extra_options = ['--option=torture:expect_rodc=true'] + else: + extra_options = [] + plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN'] + extra_options, + "samba4.krb5.kdc with machine account") + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed@$REALM%$PASSWORD', '--workgroup=$DOMAIN'] + extra_options, + "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") # TODO: Verifying the databases really should be a part of the # environment teardown. diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index edf1ecd..4f76001 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -96,7 +96,24 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0, "decode_AS_REP failed"); torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); - torture_assert_int_equal(test_context->tctx, test_context->as_rep.pvno, 5, "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert(test_context->tctx, + test_context->as_rep.ticket.enc_part.kvno, + "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + if (torture_setting_bool(test_context->tctx, "expect_rodc", false)) { + torture_assert_int_not_equal(test_context->tctx, + *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Did not get a RODC number in the KVNO"); + } else { + torture_assert_int_equal(test_context->tctx, + *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Unexpecedly got a RODC number in the KVNO"); + } free_AS_REP(&test_context->as_rep); } torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); -- 2.1.4 From fd1c26426ed959991e3f4a08901fefc9286d1a56 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 21 Jan 2015 17:27:09 +1300 Subject: [PATCH 19/60] torture-krb5: Add tests for combinations of enterprise, cannon, and different input principals This combinational test confirms the interactions between a number of differnet kerberos flags and principal types. Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam (cherry picked from commit 170ee3071b7b51af0b6a89b7abf944ec3b08c014) --- selftest/target/Samba.pm | 8 +- source4/selftest/tests.py | 8 +- source4/torture/krb5/kdc-canon.c | 401 +++++++++++++++++++++++++++++++++++++ source4/torture/krb5/kdc.c | 3 +- source4/torture/krb5/wscript_build | 2 +- 5 files changed, 415 insertions(+), 7 deletions(-) create mode 100644 source4/torture/krb5/kdc-canon.c diff --git a/selftest/target/Samba.pm b/selftest/target/Samba.pm index ccc63f3..2b7343d 100644 --- a/selftest/target/Samba.pm +++ b/selftest/target/Samba.pm @@ -124,7 +124,8 @@ sub mk_krb5_conf($$) sub mk_realms_stanza($$$$) { my ($realm, $dnsname, $domain, $kdc_ipv4) = @_; - + my $lc_domain = lc($domain); + my $realms_stanza = " $realm = { kdc = $kdc_ipv4:88 @@ -141,6 +142,11 @@ sub mk_realms_stanza($$$$) admin_server = $kdc_ipv4:88 default_domain = $dnsname } + $lc_domain = { + kdc = $kdc_ipv4:88 + admin_server = $kdc_ipv4:88 + default_domain = $dnsname + } "; return $realms_stanza; diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 4c4c0b3..f207b1a 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -547,18 +547,18 @@ for env in ["dc", "s4member", "rodc", "promoted_dc", "plugin_s4_dc", "s3member"] plantestsuite("samba.blackbox.wbinfo(%s:local)" % env, "%s:local" % env, [os.path.join(samba4srcdir, "../nsswitch/tests/test_wbinfo.sh"), '$DOMAIN', '$DC_USERNAME', '$DC_PASSWORD', env]) for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", "fl2008r2dc"]: - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-U$USERNAME@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM'], "samba4.krb5.kdc with specified account") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestdenied@$REALM%$PASSWORD', '--workgroup=$DOMAIN'], + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestdenied%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM'], "samba4.krb5.kdc with account DENIED permission to replicate to an RODC") if env == "rodc": extra_options = ['--option=torture:expect_rodc=true'] else: extra_options = [] - plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN'] + extra_options, + plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN', '--realm=$REALM'] + extra_options, "samba4.krb5.kdc with machine account") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed@$REALM%$PASSWORD', '--workgroup=$DOMAIN'] + extra_options, + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM'] + extra_options, "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") # TODO: Verifying the databases really should be a part of the diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c new file mode 100644 index 0000000..53a3b6a --- /dev/null +++ b/source4/torture/krb5/kdc-canon.c @@ -0,0 +1,401 @@ +/* + Unix SMB/CIFS implementation. + + Validate the krb5 pac generation routines + + Copyright (C) Andrew Bartlett 2005-2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/kerberos.h" +#include "torture/smbtorture.h" +#include "torture/krb5/proto.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/popt_common.h" +#include "source4/auth/kerberos/kerberos.h" +#include "source4/auth/kerberos/kerberos_util.h" +#include "lib/util/util_net.h" + +#define TEST_CANONICALIZE 0x0000001 +#define TEST_ENTERPRISE 0x0000002 +#define TEST_UPPER_REALM 0x0000004 +#define TEST_UPPER_USERNAME 0x0000008 +#define TEST_NETBIOS_REALM 0x0000010 +#define TEST_ALL 0x000001F + +struct test_data { + struct smb_krb5_context *smb_krb5_context; + const char *realm; + const char *real_realm; + const char *username; + bool canonicalize; + bool enterprise; + bool upper_realm; + bool upper_username; +}; + +struct torture_krb5_context { + struct torture_context *tctx; + struct addrinfo *server; + struct test_data *test_data; + int packet_count; + AS_REQ as_req; + AS_REP as_rep; +}; + +static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf) +{ + krb5_error_code k5ret; + size_t used; + torture_assert_int_equal(test_context->tctx, + decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0, + "decode_AS_REQ failed"); + + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno"); + if (test_context->test_data->canonicalize || test_context->test_data->enterprise) { + torture_assert(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, "krb5 libs did not set canonicalize!"); + } else { + torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); + } + + if (test_context->test_data->enterprise) { + torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.cname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, "krb5 libs did not pass principal as enterprise!"); + } else { + torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.cname->name_type, KRB5_NT_PRINCIPAL, "krb5 libs unexpectedly set principal as enterprise!"); + } + + /* Force off canonicalize that was forced on by the krb5 libs */ + if (test_context->test_data->canonicalize == false && test_context->test_data->enterprise) { + test_context->as_req.req_body.kdc_options.canonicalize = false; + } + + ASN1_MALLOC_ENCODE(AS_REQ, modified_send_buf->data, modified_send_buf->length, + &test_context->as_req, &used, k5ret); + torture_assert_int_equal(test_context->tctx, + k5ret, 0, + "encode_AS_REQ failed"); + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "re-encode length mismatch"); + return true; +} + +static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) +{ + KRB_ERROR error; + size_t used; + if (test_context->packet_count == 0) { + torture_assert_int_equal(test_context->tctx, + decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + 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_REQUIRED - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) + && (test_context->packet_count == 1)) { + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + free_KRB_ERROR(&error); + } else { + torture_assert_int_equal(test_context->tctx, + decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert(test_context->tctx, + test_context->as_rep.ticket.enc_part.kvno, + "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + if (torture_setting_bool(test_context->tctx, "expect_rodc", false)) { + torture_assert_int_not_equal(test_context->tctx, + *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Did not get a RODC number in the KVNO"); + } else { + torture_assert_int_equal(test_context->tctx, + *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Unexpecedly got a RODC number in the KVNO"); + } + free_AS_REP(&test_context->as_rep); + } + torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); + free_AS_REQ(&test_context->as_req); + return true; +} + +static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context context, + void *data, /* struct torture_krb5_context */ + krb5_krbhst_info *hi, + time_t timeout, + const krb5_data *send_buf, + krb5_data *recv_buf) +{ + krb5_error_code k5ret; + bool ok; + krb5_data modified_send_buf; + + struct torture_krb5_context *test_context + = talloc_get_type_abort(data, struct torture_krb5_context); + + ok = torture_krb5_pre_send_test(test_context, send_buf, &modified_send_buf); + if (ok == false) { + return EINVAL; + } + + k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server, + hi, timeout, &modified_send_buf, recv_buf); + + ok = torture_krb5_post_recv_test(test_context, recv_buf); + if (ok == false) { + return EINVAL; + } + + test_context->packet_count++; + + return k5ret; +} + +static int test_context_destructor(struct torture_krb5_context *test_context) +{ + freeaddrinfo(test_context->server); + return 0; +} + + +static bool torture_krb5_init_context_canon(struct torture_context *tctx, + struct test_data *test_data, + struct smb_krb5_context **smb_krb5_context) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + krb5_error_code k5ret; + bool ok; + + struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context); + torture_assert(tctx, test_context != NULL, "Failed to allocate"); + + test_context->test_data = test_data; + test_context->tctx = tctx; + + k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context); + torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed"); + + ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST); + torture_assert(tctx, ok, "Failed to parse target server"); + + talloc_set_destructor(test_context, test_context_destructor); + + set_sockaddr_port(test_context->server->ai_addr, 88); + + k5ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context, + smb_krb5_send_and_recv_func_canon_override, + test_context); + torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed"); + return true; +} + + +static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void *tcase_data) +{ + krb5_error_code k5ret; + krb5_get_init_creds_opt *krb_options = NULL; + struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data); + char *realm; + char *upper_real_realm; + char *username; + krb5_principal principal; + krb5_principal expected_principal; + char *principal_string; + int principal_flags; + char *expected_principal_string; + int expected_principal_flags; + char *got_principal_string; + char *assertion_message; + const char *password = cli_credentials_get_password(cmdline_credentials); + struct smb_krb5_context *smb_krb5_context; + bool ok; + krb5_creds my_creds; + + ok = torture_krb5_init_context_canon(tctx, test_data, &smb_krb5_context); + torture_assert(tctx, ok, "torture_krb5_init_context failed"); + + if (test_data->upper_realm) { + realm = strupper_talloc(test_data, test_data->realm); + } else { + realm = strlower_talloc(test_data, test_data->realm); + } + if (test_data->upper_username) { + username = strupper_talloc(test_data, test_data->username); + } else { + username = talloc_strdup(test_data, test_data->username); + } + + principal_string = talloc_asprintf(test_data, "%s@%s", username, realm); + + upper_real_realm = strupper_talloc(test_data, test_data->real_realm); + + /* + * If we are set to canonicalize, we get back the fixed UPPER + * case realm, and the real username (ie matching LDAP + * samAccountName) + * + * Otherwise, if we are set to enterprise, we + * get back the whole principal as-sent + * + * Finally, if we are not set to canonicalize, we get back the + * fixed UPPER case realm, but the as-sent username + */ + if (test_data->canonicalize) { + expected_principal_string = talloc_asprintf(test_data, "%s@%s", test_data->username, upper_real_realm); + } else if (test_data->enterprise) { + expected_principal_string = principal_string; + } else { + expected_principal_string = talloc_asprintf(test_data, "%s@%s", username, upper_real_realm); + } + + if (test_data->enterprise) { + principal_flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE; + } else { + principal_flags = 0; + } + + if (test_data->canonicalize) { + expected_principal_flags = 0; + } else { + expected_principal_flags = principal_flags; + } + + torture_assert_int_equal(tctx, + krb5_parse_name_flags(smb_krb5_context->krb5_context, + principal_string, + principal_flags, + &principal), + 0, "krb5_parse_name_flags failed"); + torture_assert_int_equal(tctx, + krb5_parse_name_flags(smb_krb5_context->krb5_context, + expected_principal_string, + expected_principal_flags, + &expected_principal), + 0, "krb5_parse_name_flags failed"); + + /* + * Set the canonicalize flag if this test requires it + */ + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options), + 0, "krb5_get_init_creds_opt_alloc failed"); + + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context, krb_options, test_data->canonicalize), + 0, "krb5_get_init_creds_opt_set_canonicalize failed"); + + k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal, + password, NULL, NULL, 0, + NULL, krb_options); + krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options); + + assertion_message = talloc_asprintf(tctx, + "krb5_get_init_creds_password for %s failed: %s", + principal_string, + smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + + /* + * Assert that the reply was with the correct type of + * principal, depending on the flags we set + */ + if (test_data->canonicalize == false && test_data->enterprise) { + torture_assert_int_equal(tctx, + krb5_principal_get_type(smb_krb5_context->krb5_context, + my_creds.client), KRB5_NT_ENTERPRISE_PRINCIPAL, + "smb_krb5_init_context gave incorrect client->name.name_type"); + } else { + torture_assert_int_equal(tctx, + krb5_principal_get_type(smb_krb5_context->krb5_context, + my_creds.client), KRB5_NT_PRINCIPAL, + "smb_krb5_init_context gave incorrect client->name.name_type"); + } + + torture_assert_int_equal(tctx, + krb5_unparse_name(smb_krb5_context->krb5_context, + my_creds.client, &got_principal_string), 0, + "krb5_unparse_name failed"); + + assertion_message = talloc_asprintf(tctx, + "krb5_get_init_creds_password returned a different principal %s to what was expected %s", + got_principal_string, expected_principal_string); + krb5_free_unparsed_name(smb_krb5_context->krb5_context, got_principal_string); + + torture_assert(tctx, krb5_principal_compare(smb_krb5_context->krb5_context, + my_creds.client, expected_principal), + assertion_message); + + torture_assert_int_equal(tctx, + krb5_principal_get_type(smb_krb5_context->krb5_context, + my_creds.server), KRB5_NT_SRV_INST, + "smb_krb5_init_context gave incorrect client->name.name_type"); + + torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context, + my_creds.server, 0), + "krbtgt", + "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]"); + + krb5_free_principal(smb_krb5_context->krb5_context, principal); + + k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds); + torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed"); + + return true; +} + +struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx) +{ + unsigned int i; + struct torture_suite *suite = torture_suite_create(mem_ctx, "canon"); + suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests"); + + for (i = 0; i < TEST_ALL; i++) { + char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s", + (i & TEST_CANONICALIZE) ? "canon" : "no-canon", + (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise", + (i & TEST_UPPER_REALM) ? "uc-realm" : "lc-realm", + (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user", + (i & TEST_NETBIOS_REALM) ? "netbios-realm" : "krb5-realm"); + + struct test_data *test_data = talloc(suite, struct test_data); + if (i & TEST_NETBIOS_REALM) { + test_data->realm = cli_credentials_get_domain(cmdline_credentials); + } else { + test_data->realm = cli_credentials_get_realm(cmdline_credentials); + } + test_data->real_realm = cli_credentials_get_realm(cmdline_credentials); + test_data->username = cli_credentials_get_username(cmdline_credentials); + test_data->canonicalize = (i & TEST_CANONICALIZE) != 0; + test_data->enterprise = (i & TEST_ENTERPRISE) != 0; + test_data->upper_realm = (i & TEST_UPPER_REALM) != 0; + test_data->upper_username = (i & TEST_UPPER_USERNAME) != 0; + torture_suite_add_simple_tcase_const(suite, name, torture_krb5_as_req_canon, + test_data); + + } + return suite; +} diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index 4f76001..be4b245 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -24,6 +24,7 @@ #include "system/kerberos.h" #include "torture/smbtorture.h" #include "torture/winbind/proto.h" +#include "torture/krb5/proto.h" #include "auth/credentials/credentials.h" #include "lib/cmdline/popt_common.h" #include "source4/auth/kerberos/kerberos.h" @@ -406,7 +407,6 @@ static bool torture_krb5_as_req_clock_skew(struct torture_context *tctx) return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_CLOCK_SKEW); } -NTSTATUS torture_krb5_init(void); NTSTATUS torture_krb5_init(void) { struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "krb5"); @@ -429,6 +429,7 @@ NTSTATUS torture_krb5_init(void) torture_suite_add_simple_test(kdc_suite, "as-req-clock-skew", torture_krb5_as_req_clock_skew); + torture_suite_add_suite(kdc_suite, torture_krb5_canon(kdc_suite)); torture_suite_add_suite(suite, kdc_suite); torture_register_suite(suite); diff --git a/source4/torture/krb5/wscript_build b/source4/torture/krb5/wscript_build index 963c3a3..81a5b09 100644 --- a/source4/torture/krb5/wscript_build +++ b/source4/torture/krb5/wscript_build @@ -1,7 +1,7 @@ #!/usr/bin/env python if bld.CONFIG_SET('AD_DC_BUILD_IS_ENABLED'): bld.SAMBA_MODULE('TORTURE_KRB5', - source='kdc.c', + source='kdc.c kdc-canon.c', autoproto='proto.h', subsystem='smbtorture', init_function='torture_krb5_init', -- 2.1.4 From cad986aee6acb91abcf0b7490a8f70d68f452380 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 22 Jan 2015 14:11:52 +1300 Subject: [PATCH 20/60] kdc: Fix Samba's KDC to only change the principal in the right cases If we are set to canonicalize, we get back the fixed UPPER case realm, and the real username (ie matching LDAP samAccountName) Otherwise, if we are set to enterprise, we get back the whole principal as-sent Finally, if we are not set to canonicalize, we get back the fixed UPPER case realm, but the as-sent username Signed-off-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit 9fc3f1e3d6854f399e2b2322b8ab1a714353ba12) --- source4/kdc/db-glue.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index fae2703..ee84501 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -625,10 +625,22 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context, userAccountControl |= msDS_User_Account_Control_Computed; } + /* + * If we are set to canonicalize, we get back the fixed UPPER + * case realm, and the real username (ie matching LDAP + * samAccountName) + * + * Otherwise, if we are set to enterprise, we + * get back the whole principal as-sent + * + * Finally, if we are not set to canonicalize, we get back the + * fixed UPPER case realm, but the as-sent username + */ + entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && principal == NULL) { krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL); - } else if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { + } else if (flags & HDB_F_CANON) { krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL); } else { ret = copy_Principal(principal, entry_ex->entry.principal); @@ -637,14 +649,16 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context, goto out; } - /* While we have copied the client principal, tests - * show that Win2k3 returns the 'corrected' realm, not - * the client-specified realm. This code attempts to - * replace the client principal's realm with the one - * we determine from our records */ - - /* this has to be with malloc() */ - krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx)); + if (principal->name.name_type != KRB5_NT_ENTERPRISE_PRINCIPAL) { + /* While we have copied the client principal, tests + * show that Win2k3 returns the 'corrected' realm, not + * the client-specified realm. This code attempts to + * replace the client principal's realm with the one + * we determine from our records */ + + /* this has to be with malloc() */ + krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx)); + } } /* First try and figure out the flags based on the userAccountControl */ -- 2.1.4 From 8f6034a0fdad95b3197003da38f8b212c8874894 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 23 Jan 2015 17:39:45 +1300 Subject: [PATCH 21/60] kdc: Add TODO to remind us where we need to hook for RODC to get secrets Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 69fb2a7616fe3b67312904075fdb691b7fa510bb) --- source4/kdc/db-glue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index ee84501..ad52284 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -374,6 +374,7 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context, if (allocated_keys == 0) { if (kdc_db_ctx->rodc) { /* We are on an RODC, but don't have keys for this account. Signal this to the caller */ + /* TODO: We need to call a generalised version of auth_sam_trigger_repl_secret from here */ return HDB_ERR_NOT_FOUND_HERE; } -- 2.1.4 From 51107f20f870ce9b8f6a55abc400e04c85a52326 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 23 Jan 2015 14:07:41 +1300 Subject: [PATCH 22/60] torture-krb5: Add comments Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit d0751b576363a25ca67f485651b206677bf1d4b8) --- source4/torture/krb5/kdc-canon.c | 36 +++++++++++++++++++++++++++++++++ source4/torture/krb5/kdc.c | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index 53a3b6a..a20f9f9 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -57,6 +57,20 @@ struct torture_krb5_context { AS_REP as_rep; }; + +/* + * Confirm that the outgoing packet meets certain expectations. This + * should be extended to further assert the correct and expected + * behaviour of the krb5 libs, so we know what we are sending to the + * server. + * + * Additionally, this CHANGES the request to remove the canonicalize + * flag automatically added by the krb5 libs when an enterprise + * principal is used, so we can test what the server does in this + * combination. + * + */ + static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf) { krb5_error_code k5ret; @@ -93,6 +107,14 @@ static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context return true; } +/* + * Confirm that the incoming packet from the KDC meets certain + * expectations. This uses a packet count to work out what test we + * are in, and where in the test we are, so we can assert on the + * expected reply packets from the KDC. + * + */ + static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) { KRB_ERROR error; @@ -143,6 +165,20 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex return true; } +/* + * This function is set in torture_krb5_init_context_canon as krb5 + * send_and_recv function. This allows us to override what server the + * test is aimed at, and to inspect the packets just before they are + * sent to the network, and before they are processed on the recv + * side. + * + * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test() + * functions are implement the actual tests. + * + * When this asserts, the caller will get a spurious 'cannot contact + * any KDC' message. + * + */ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context context, void *data, /* struct torture_krb5_context */ krb5_krbhst_info *hi, diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index be4b245..eed5d8d 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -48,6 +48,14 @@ struct torture_krb5_context { AS_REP as_rep; }; +/* + * Confirm that the outgoing packet meets certain expectations. This + * should be extended to further assert the correct and expected + * behaviour of the krb5 libs, so we know what we are sending to the + * server. + * + */ + static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, const krb5_data *send_buf) { size_t used; @@ -68,6 +76,14 @@ static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context return true; } +/* + * Confirm that the incoming packet from the KDC meets certain + * expectations. This uses a switch and the packet count to work out + * what test we are in, and where in the test we are, so we can assert + * on the expected reply packets from the KDC. + * + */ + static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) { KRB_ERROR error; @@ -120,6 +136,10 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); free_AS_REQ(&test_context->as_req); break; + + /* + * Confirm correct error codes when we ask for the PAC. This behaviour is rather odd... + */ case TORTURE_KRB5_TEST_PAC_REQUEST: if (test_context->packet_count == 0) { torture_assert_int_equal(test_context->tctx, @@ -157,6 +177,10 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); free_AS_REQ(&test_context->as_req); break; + + /* + * Confirm correct error codes when we deliberatly send the wrong password + */ case TORTURE_KRB5_TEST_BREAK_PW: if (test_context->packet_count == 0) { torture_assert_int_equal(test_context->tctx, @@ -180,6 +204,10 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets"); free_AS_REQ(&test_context->as_req); break; + + /* + * Confirm correct error codes when we deliberatly skew the client clock + */ case TORTURE_KRB5_TEST_CLOCK_SKEW: if (test_context->packet_count == 0) { torture_assert_int_equal(test_context->tctx, @@ -207,6 +235,21 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex return true; } + +/* + * This function is set in torture_krb5_init_context as krb5 + * send_and_recv function. This allows us to override what server the + * test is aimed at, and to inspect the packets just before they are + * sent to the network, and before they are processed on the recv + * side. + * + * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test() + * functions are implement the actual tests. + * + * When this asserts, the caller will get a spurious 'cannot contact + * any KDC' message. + * + */ static krb5_error_code smb_krb5_send_and_recv_func_override(krb5_context context, void *data, /* struct torture_krb5_context */ krb5_krbhst_info *hi, -- 2.1.4 From dfb6ff68ae5bb8868f061012d30d0ce3553a0b8c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 23 Jan 2015 14:09:33 +1300 Subject: [PATCH 23/60] torture-kdc: Skip the request-pac behaviour for now against an RODC Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 89b868f67761fbcf1319229c2f09502bdf16086e) --- source4/torture/krb5/kdc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index eed5d8d..405b45f 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -437,6 +437,9 @@ static bool torture_krb5_as_req_win2k(struct torture_context *tctx) static bool torture_krb5_as_req_pac_request(struct torture_context *tctx) { + if (torture_setting_bool(test_context->tctx, "expect_rodc", false)) { + return torture_skip(tctx, "This test needs further investigation in the RODC case against a Windows DC, in particular with non-cached users"); + } return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_PAC_REQUEST); } -- 2.1.4 From 7a2beb7a2b41761d81ec5da587c3a4250e6b03a6 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 23 Jan 2015 14:28:28 +1300 Subject: [PATCH 24/60] torture-krb5: Split the expected behaviour of the RODC up The expectations of the cached accounts are different to those of the RODC in general. Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 62905cd6d21d457a54faa2a14e9713dcf280dbe5) --- source4/selftest/tests.py | 13 ++++++++++--- source4/torture/krb5/kdc-canon.c | 2 +- source4/torture/krb5/kdc.c | 6 +++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index f207b1a..b09fde2 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -547,12 +547,19 @@ for env in ["dc", "s4member", "rodc", "promoted_dc", "plugin_s4_dc", "s3member"] plantestsuite("samba.blackbox.wbinfo(%s:local)" % env, "%s:local" % env, [os.path.join(samba4srcdir, "../nsswitch/tests/test_wbinfo.sh"), '$DOMAIN', '$DC_USERNAME', '$DC_PASSWORD', env]) for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", "fl2008r2dc"]: - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM'], + if env == "rodc": + extra_options = ['--option=torture:expect_rodc=true'] + else: + extra_options = [] + + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM'] + extra_options, "samba4.krb5.kdc with specified account") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestdenied%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM'], + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestdenied%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM'] + extra_options, "samba4.krb5.kdc with account DENIED permission to replicate to an RODC") + + # These last two tests are for users cached at the RODC if env == "rodc": - extra_options = ['--option=torture:expect_rodc=true'] + extra_options = ['--option=torture:expect_rodc=true', '--option=torture:expect_cached_at_rodc=true'] else: extra_options = [] diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index a20f9f9..3103d94 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -149,7 +149,7 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex torture_assert(test_context->tctx, test_context->as_rep.ticket.enc_part.kvno, "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); - if (torture_setting_bool(test_context->tctx, "expect_rodc", false)) { + if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { torture_assert_int_not_equal(test_context->tctx, *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, 0, "Did not get a RODC number in the KVNO"); diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index 405b45f..cf8c39b 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -122,7 +122,7 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex torture_assert(test_context->tctx, test_context->as_rep.ticket.enc_part.kvno, "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); - if (torture_setting_bool(test_context->tctx, "expect_rodc", false)) { + if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { torture_assert_int_not_equal(test_context->tctx, *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, 0, "Did not get a RODC number in the KVNO"); @@ -437,8 +437,8 @@ static bool torture_krb5_as_req_win2k(struct torture_context *tctx) static bool torture_krb5_as_req_pac_request(struct torture_context *tctx) { - if (torture_setting_bool(test_context->tctx, "expect_rodc", false)) { - return torture_skip(tctx, "This test needs further investigation in the RODC case against a Windows DC, in particular with non-cached users"); + if (torture_setting_bool(tctx, "expect_rodc", false)) { + torture_skip(tctx, "This test needs further investigation in the RODC case against a Windows DC, in particular with non-cached users"); } return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_PAC_REQUEST); } -- 2.1.4 From 1c0217a611746e6d67cd64588462c8b94812625d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 23 Jan 2015 14:38:51 +1300 Subject: [PATCH 25/60] torture-krb5: Move test of krb5_get_init_creds_opt_set_win2k to krb5.kdc.canon This allows the impact of this to be verified with the other options we are setting This also removes duplication in the kdc.c testsuite. Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 9d7719b62ba0453b7c4e4b8a4c2062dc55ac4abd) --- source4/torture/krb5/kdc-canon.c | 14 +++++++++++--- source4/torture/krb5/kdc.c | 22 ---------------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index 3103d94..018e61d 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -35,7 +35,8 @@ #define TEST_UPPER_REALM 0x0000004 #define TEST_UPPER_USERNAME 0x0000008 #define TEST_NETBIOS_REALM 0x0000010 -#define TEST_ALL 0x000001F +#define TEST_WIN2K 0x0000020 +#define TEST_ALL 0x000003F struct test_data { struct smb_krb5_context *smb_krb5_context; @@ -46,6 +47,7 @@ struct test_data { bool enterprise; bool upper_realm; bool upper_username; + bool win2k; }; struct torture_krb5_context { @@ -344,6 +346,10 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context, krb_options, test_data->canonicalize), 0, "krb5_get_init_creds_opt_set_canonicalize failed"); + torture_assert_int_equal(tctx, + krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context, krb_options, test_data->win2k), + 0, "krb5_get_init_creds_opt_set_win2k failed"); + k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal, password, NULL, NULL, 0, NULL, krb_options); @@ -410,12 +416,13 @@ struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx) suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests"); for (i = 0; i < TEST_ALL; i++) { - char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s", + char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s.%s", (i & TEST_CANONICALIZE) ? "canon" : "no-canon", (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise", (i & TEST_UPPER_REALM) ? "uc-realm" : "lc-realm", (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user", - (i & TEST_NETBIOS_REALM) ? "netbios-realm" : "krb5-realm"); + (i & TEST_NETBIOS_REALM) ? "netbios-realm" : "krb5-realm", + (i & TEST_WIN2K) ? "win2k" : "no-win2k"); struct test_data *test_data = talloc(suite, struct test_data); if (i & TEST_NETBIOS_REALM) { @@ -429,6 +436,7 @@ struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx) test_data->enterprise = (i & TEST_ENTERPRISE) != 0; test_data->upper_realm = (i & TEST_UPPER_REALM) != 0; test_data->upper_username = (i & TEST_UPPER_USERNAME) != 0; + test_data->win2k = (i & TEST_WIN2K) != 0; torture_suite_add_simple_tcase_const(suite, name, torture_krb5_as_req_canon, test_data); diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index cf8c39b..3eec528 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -33,7 +33,6 @@ enum torture_krb5_test { TORTURE_KRB5_TEST_PLAIN, - TORTURE_KRB5_TEST_WIN2K, TORTURE_KRB5_TEST_PAC_REQUEST, TORTURE_KRB5_TEST_BREAK_PW, TORTURE_KRB5_TEST_CLOCK_SKEW, @@ -62,7 +61,6 @@ static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context switch (test_context->test) { case TORTURE_KRB5_TEST_PLAIN: - case TORTURE_KRB5_TEST_WIN2K: case TORTURE_KRB5_TEST_PAC_REQUEST: case TORTURE_KRB5_TEST_BREAK_PW: case TORTURE_KRB5_TEST_CLOCK_SKEW: @@ -91,7 +89,6 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex switch (test_context->test) { case TORTURE_KRB5_TEST_PLAIN: - case TORTURE_KRB5_TEST_WIN2K: if (test_context->packet_count == 0) { torture_assert_int_equal(test_context->tctx, decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, @@ -345,16 +342,6 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx, case TORTURE_KRB5_TEST_PLAIN: break; - case TORTURE_KRB5_TEST_WIN2K: - torture_assert_int_equal(tctx, - krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options), - 0, "krb5_get_init_creds_opt_alloc failed"); - - torture_assert_int_equal(tctx, - krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context, krb_options, true), - 0, "krb5_get_init_creds_opt_set_win2k failed"); - break; - case TORTURE_KRB5_TEST_PAC_REQUEST: torture_assert_int_equal(tctx, krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options), @@ -385,7 +372,6 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx, switch (test) { case TORTURE_KRB5_TEST_PLAIN: - case TORTURE_KRB5_TEST_WIN2K: case TORTURE_KRB5_TEST_PAC_REQUEST: torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed"); break; @@ -430,11 +416,6 @@ static bool torture_krb5_as_req_cmdline(struct torture_context *tctx) return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_PLAIN); } -static bool torture_krb5_as_req_win2k(struct torture_context *tctx) -{ - return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_WIN2K); -} - static bool torture_krb5_as_req_pac_request(struct torture_context *tctx) { if (torture_setting_bool(tctx, "expect_rodc", false)) { @@ -463,9 +444,6 @@ NTSTATUS torture_krb5_init(void) torture_suite_add_simple_test(kdc_suite, "as-req-cmdline", torture_krb5_as_req_cmdline); - torture_suite_add_simple_test(kdc_suite, "as-req-win2k", - torture_krb5_as_req_win2k); - torture_suite_add_simple_test(kdc_suite, "as-req-pac-request", torture_krb5_as_req_pac_request); -- 2.1.4 From 638936d0570f736e8c6ac91100bbe7e0b2f3f437 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 23 Jan 2015 14:28:56 +1300 Subject: [PATCH 26/60] torture-krb5: Move checking of server and client names to krb5.kdc.canon This keeps this test in one place, rather than duplicated between krb5.kdc and krb5.kdc.canon Signed-off-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit 157539c5ad9b819e43dceee6bb47d2027de1d982) --- source4/torture/krb5/kdc-canon.c | 26 +++++++++++++++++++++++++- source4/torture/krb5/kdc.c | 19 ------------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index 018e61d..a12ee86 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -391,16 +391,40 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * my_creds.client, expected_principal), assertion_message); + torture_assert_int_equal(tctx, krb5_principal_get_type(smb_krb5_context->krb5_context, my_creds.server), KRB5_NT_SRV_INST, - "smb_krb5_init_context gave incorrect client->name.name_type"); + "smb_krb5_init_context gave incorrect server->name.name_type"); + + torture_assert_int_equal(tctx, + krb5_principal_get_num_comp(smb_krb5_context->krb5_context, + my_creds.server), 2, + "smb_krb5_init_context gave incorrect number of components in my_creds.server->name"); torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context, my_creds.server, 0), "krbtgt", "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]"); + if (test_data->canonicalize || test_data->enterprise) { + torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context, + my_creds.server, 1), + test_data->real_realm, + + "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]"); + } else { + torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context, + my_creds.server, 1), + realm, + + "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]"); + } + torture_assert_str_equal(tctx, krb5_principal_get_realm(smb_krb5_context->krb5_context, + my_creds.server), + test_data->real_realm, + "smb_krb5_init_context gave incorrect my_creds.server->realm"); + krb5_free_principal(smb_krb5_context->krb5_context, principal); k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds); diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index 3eec528..a7b6ac8 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -386,25 +386,6 @@ static bool torture_krb5_as_req_creds(struct torture_context *tctx, } - torture_assert_int_equal(tctx, - krb5_principal_get_type(smb_krb5_context->krb5_context, - my_creds.client), KRB5_NT_PRINCIPAL, - "smb_krb5_init_context gave incorrect client->name.name_type"); - - torture_assert(tctx, krb5_principal_compare(smb_krb5_context->krb5_context, - principal, my_creds.client), - "krb5_get_init_creds_password returned a different principal"); - - torture_assert_int_equal(tctx, - krb5_principal_get_type(smb_krb5_context->krb5_context, - my_creds.server), KRB5_NT_SRV_INST, - "smb_krb5_init_context gave incorrect client->name.name_type"); - - torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context, - my_creds.server, 0), - "krbtgt", - "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]"); - k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds); torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed"); -- 2.1.4 From 114f9c17851d6b399050e3df23602d4b31161a89 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 23 Jan 2015 16:41:50 +1300 Subject: [PATCH 27/60] kdc: Correctly return the krbtgt/realm@REALM principal from our KDC This needs to vary depending on if the client requested the canonicalize flag This was found by our new krb5.kdc test Signed-off-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit c1819f5fd1eb690326a1fc547422544f5c834558) --- source4/kdc/db-glue.c | 56 ++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index ad52284..042abe6 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -639,7 +639,37 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context, */ entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); - if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && principal == NULL) { + if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT) { + ret = krb5_copy_principal(context, principal, &entry_ex->entry.principal); + if (ret) { + return ret; + } + + /* + * Windows seems to canonicalize the principal + * in a TGS REP even if the client did not specify + * the canonicalize flag. + */ + if (flags & (HDB_F_CANON|HDB_F_FOR_TGS_REQ)) { + /* When requested to do so, ensure that the + * both realm values in the principal are set + * to the upper case, canonical realm */ + free(entry_ex->entry.principal->name.name_string.val[1]); + entry_ex->entry.principal->name.name_string.val[1] = strdup(lpcfg_realm(lp_ctx)); + if (!entry_ex->entry.principal->name.name_string.val[1]) { + ret = ENOMEM; + krb5_set_error_message(context, ret, "samba_kdc_fetch: strdup() failed!"); + return ret; + } + } + /* + * this has to be with malloc(), and appears to be + * required regardless of the canonicalize flag from + * the client + */ + krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx)); + + } else if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && principal == NULL) { krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL); } else if (flags & HDB_F_CANON) { krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL); @@ -1368,30 +1398,6 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context, return HDB_ERR_NOENTRY; } - /* - * Windows seems to canonicalize the principal - * in a TGS REP even if the client did not specify - * the canonicalize flag. - */ - if (flags & (HDB_F_CANON|HDB_F_FOR_TGS_REQ)) { - ret = krb5_copy_principal(context, principal, &alloc_principal); - if (ret) { - return ret; - } - - /* When requested to do so, ensure that the - * both realm values in the principal are set - * to the upper case, canonical realm */ - free(alloc_principal->name.name_string.val[1]); - alloc_principal->name.name_string.val[1] = strdup(lpcfg_realm(lp_ctx)); - if (!alloc_principal->name.name_string.val[1]) { - ret = ENOMEM; - krb5_set_error_message(context, ret, "samba_kdc_fetch: strdup() failed!"); - return ret; - } - principal = alloc_principal; - } - ret = samba_kdc_message2entry(context, kdc_db_ctx, mem_ctx, principal, SAMBA_KDC_ENT_TYPE_KRBTGT, flags, realm_dn, msg, entry_ex); -- 2.1.4 From 76914172664a12128d9db643937bed4fc3db5a8b Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 23 Jan 2015 16:43:48 +1300 Subject: [PATCH 28/60] torture-krb5: Check for UPN hanlding in krb5.kdc.canon test This allows us to confirm correct behaviour when a UPN is in use, particularly with the canonicalize flag and with enterprise principal names Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 52526ee26555daff27cb11ca2f444c2534a4d8f2) --- source4/torture/krb5/kdc-canon.c | 108 ++++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 18 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index a12ee86..a0df97b 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -36,18 +36,24 @@ #define TEST_UPPER_USERNAME 0x0000008 #define TEST_NETBIOS_REALM 0x0000010 #define TEST_WIN2K 0x0000020 -#define TEST_ALL 0x000003F +#define TEST_UPN 0x0000040 +#define TEST_ALL 0x000007F struct test_data { struct smb_krb5_context *smb_krb5_context; const char *realm; const char *real_realm; + const char *real_domain; const char *username; + const char *real_username; bool canonicalize; bool enterprise; bool upper_realm; bool upper_username; + bool netbios_realm; bool win2k; + bool upn; + bool other_upn_suffix; }; struct torture_krb5_context { @@ -127,8 +133,13 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex "decode_AS_REP failed"); torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); 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_REQUIRED - KRB5KDC_ERR_NONE, - "Got wrong error.error_code"); + if (test_context->test_data->netbios_realm && test_context->test_data->upn) { + torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } else { + torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } free_KRB_ERROR(&error); } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) && (test_context->packet_count == 1)) { @@ -273,6 +284,57 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * bool ok; krb5_creds my_creds; + const char *upn = torture_setting_string(tctx, "krb5-upn", ""); + + /* + * If we have not passed a UPN on the command line, + * then skip the UPN tests. + */ + if (test_data->upn && upn[0] == '\0') { + torture_skip(tctx, "This test needs a UPN specified as --option=torture:krb5-upn=user@example.com to run"); + } + + if (test_data->netbios_realm) { + test_data->realm = test_data->real_domain; + } else { + test_data->realm = test_data->real_realm; + } + + if (test_data->upn) { + char *p; + test_data->username = talloc_strdup(test_data, upn); + p = strchr(test_data->username, '@'); + if (p) { + *p = '\0'; + p++; + } + /* + * Test the UPN behaviour carefully. We can + * test in two different modes, depending on + * what UPN has been set up for us. + * + * If the UPN is in our realm, then we do all the tests with this name also. + * + * If the UPN is not in our realm, then we + * expect the tests that replace the realm to + * fail (as it won't match) + */ + if (strcasecmp(p, test_data->real_realm) != 0) { + test_data->other_upn_suffix = true; + } else { + test_data->other_upn_suffix = false; + } + + /* + * This lets us test the combination of the UPN prefix + * with a valid domain, without adding even more + * combinations + */ + if (test_data->netbios_realm == false) { + test_data->realm = p; + } + } + ok = torture_krb5_init_context_canon(tctx, test_data, &smb_krb5_context); torture_assert(tctx, ok, "torture_krb5_init_context failed"); @@ -303,7 +365,7 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * * fixed UPPER case realm, but the as-sent username */ if (test_data->canonicalize) { - expected_principal_string = talloc_asprintf(test_data, "%s@%s", test_data->username, upper_real_realm); + expected_principal_string = talloc_asprintf(test_data, "%s@%s", test_data->real_username, upper_real_realm); } else if (test_data->enterprise) { expected_principal_string = principal_string; } else { @@ -313,6 +375,9 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * if (test_data->enterprise) { principal_flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE; } else { + if (test_data->upn && test_data->other_upn_suffix) { + torture_skip(tctx, "UPN test for UPN with other UPN suffix only runs with enterprise principals"); + } principal_flags = 0; } @@ -355,12 +420,18 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * NULL, krb_options); krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options); - assertion_message = talloc_asprintf(tctx, - "krb5_get_init_creds_password for %s failed: %s", - principal_string, - smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx)); - torture_assert_int_equal(tctx, k5ret, 0, assertion_message); - + if (test_data->netbios_realm && test_data->upn) { + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN, "Got wrong error_code from krb5_get_init_creds_password"); + /* We can't proceed with more checks */ + return true; + } else { + assertion_message = talloc_asprintf(tctx, + "krb5_get_init_creds_password for %s failed: %s", + principal_string, + smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + } + /* * Assert that the reply was with the correct type of * principal, depending on the flags we set @@ -440,27 +511,28 @@ struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx) suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests"); for (i = 0; i < TEST_ALL; i++) { - char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s.%s", + char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s.%s.%s", (i & TEST_CANONICALIZE) ? "canon" : "no-canon", (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise", (i & TEST_UPPER_REALM) ? "uc-realm" : "lc-realm", (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user", (i & TEST_NETBIOS_REALM) ? "netbios-realm" : "krb5-realm", - (i & TEST_WIN2K) ? "win2k" : "no-win2k"); + (i & TEST_WIN2K) ? "win2k" : "no-win2k", + (i & TEST_UPN) ? "upn" : "no-upn"); + + struct test_data *test_data = talloc_zero(suite, struct test_data); - struct test_data *test_data = talloc(suite, struct test_data); - if (i & TEST_NETBIOS_REALM) { - test_data->realm = cli_credentials_get_domain(cmdline_credentials); - } else { - test_data->realm = cli_credentials_get_realm(cmdline_credentials); - } test_data->real_realm = cli_credentials_get_realm(cmdline_credentials); + test_data->real_domain = cli_credentials_get_domain(cmdline_credentials); test_data->username = cli_credentials_get_username(cmdline_credentials); + test_data->real_username = cli_credentials_get_username(cmdline_credentials); test_data->canonicalize = (i & TEST_CANONICALIZE) != 0; test_data->enterprise = (i & TEST_ENTERPRISE) != 0; test_data->upper_realm = (i & TEST_UPPER_REALM) != 0; test_data->upper_username = (i & TEST_UPPER_USERNAME) != 0; + test_data->netbios_realm = (i & TEST_NETBIOS_REALM) != 0; test_data->win2k = (i & TEST_WIN2K) != 0; + test_data->upn = (i & TEST_UPN) != 0; torture_suite_add_simple_tcase_const(suite, name, torture_krb5_as_req_canon, test_data); -- 2.1.4 From 75bd67799b81e98e2d228bf91d9aac08fd918f48 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 23 Jan 2015 17:19:41 +1300 Subject: [PATCH 29/60] selftest: Run krb5.kdc test against users with a UPN This tests both a UPN in our own realm, and a UPN with a non-realm suffix. Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Fri Jan 23 08:10:07 CET 2015 on sn-devel-104 (cherry picked from commit fba69f4a89bedaf799b3a3c78cde43f4f1d1aba3) --- selftest/target/Samba4.pm | 22 ++++++++++++++++++++++ source4/selftest/tests.py | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 721a7f5..85a328c 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -780,6 +780,18 @@ sub provision_raw_step2($$$) return undef; } + my $ldbmodify = Samba::bindir_path($self, "ldbmodify"); + my $base_dn = "DC=".join(",DC=", split(/\./, $ctx->{realm})); + my $user_dn = "cn=testallowed,cn=users,$base_dn"; + open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); + print LDIF "dn: $user_dn +changetype: modify +replace: userPrincipalName +userPrincipalName: testallowed_upn\@$ctx->{realm} +- +"; + close(LDIF); + $samba_tool_cmd = Samba::bindir_path($self, "samba-tool") . " user add --configfile=$ctx->{smb_conf} testdenied $ctx->{password}"; unless (system($samba_tool_cmd) == 0) { @@ -787,6 +799,16 @@ sub provision_raw_step2($$$) return undef; } + my $user_dn = "cn=testdenied,cn=users,$base_dn"; + open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); + print LDIF "dn: $user_dn +changetype: modify +replace: userPrincipalName +userPrincipalName: testdenied_upn\@$ctx->{realm}.upn +- +"; + close(LDIF); + $samba_tool_cmd = Samba::bindir_path($self, "samba-tool") . " group addmembers --configfile=$ctx->{smb_conf} 'Allowed RODC Password Replication Group' testallowed"; unless (system($samba_tool_cmd) == 0) { diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index b09fde2..116367e 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -554,7 +554,7 @@ for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-U$USERNAME%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM'] + extra_options, "samba4.krb5.kdc with specified account") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestdenied%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM'] + extra_options, + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestdenied%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:krb5-upn=testdenied_upn@$REALM.upn'] + extra_options, "samba4.krb5.kdc with account DENIED permission to replicate to an RODC") # These last two tests are for users cached at the RODC @@ -565,7 +565,7 @@ for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN', '--realm=$REALM'] + extra_options, "samba4.krb5.kdc with machine account") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM'] + extra_options, + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:krb5-upn=testallowed_upn@$REALM'] + extra_options, "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") # TODO: Verifying the databases really should be a part of the -- 2.1.4 From 46f3ac400cc051d503b537554eb8937d706faa40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Deschner?= Date: Fri, 23 Jan 2015 13:01:27 +0100 Subject: [PATCH 30/60] s4-torture: the new krb5 kdc tests are heimdal, not dc specific. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guenther Signed-off-by: Günther Deschner Reviewed-by: Andreas Schneider BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 94cd324be9a24fcdcbf0b1c4a9dfc7a49d074b8a) --- source4/torture/krb5/wscript_build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/torture/krb5/wscript_build b/source4/torture/krb5/wscript_build index 81a5b09..3d3a840 100644 --- a/source4/torture/krb5/wscript_build +++ b/source4/torture/krb5/wscript_build @@ -1,5 +1,5 @@ #!/usr/bin/env python -if bld.CONFIG_SET('AD_DC_BUILD_IS_ENABLED'): +if bld.CONFIG_SET('SAMBA4_USES_HEIMDAL'): bld.SAMBA_MODULE('TORTURE_KRB5', source='kdc.c kdc-canon.c', autoproto='proto.h', -- 2.1.4 From ed05a505eee556b6a94c7991512fd4acc810c171 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sat, 7 Feb 2015 20:58:42 +1300 Subject: [PATCH 31/60] torture-krb5: Do not do post-recv checks if the packet recv failed This may be the cause of the flapping tests in this code previously, as the recv_buf would be 0 length. Signed-off-by: Andrew Bartlett Reviewed-By: Jelmer Vernooij Reviewed-by: Kamen Mazdrashki BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit d7752757c29d69547b361ed094f04ae4db4c9f8a) --- source4/torture/krb5/kdc-canon.c | 6 +++++- source4/torture/krb5/kdc.c | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index a0df97b..c14be00 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -212,7 +212,11 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c } k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server, - hi, timeout, &modified_send_buf, recv_buf); + hi, timeout, &modified_send_buf, + recv_buf); + if (k5ret != 0) { + return k5ret; + } ok = torture_krb5_post_recv_test(test_context, recv_buf); if (ok == false) { diff --git a/source4/torture/krb5/kdc.c b/source4/torture/krb5/kdc.c index a7b6ac8..a6fcde9 100644 --- a/source4/torture/krb5/kdc.c +++ b/source4/torture/krb5/kdc.c @@ -267,7 +267,9 @@ static krb5_error_code smb_krb5_send_and_recv_func_override(krb5_context context k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server, hi, timeout, send_buf, recv_buf); - + if (k5ret != 0) { + return k5ret; + } ok = torture_krb5_post_recv_test(test_context, recv_buf); if (ok == false) { return EINVAL; -- 2.1.4 From f15287f45e2b9707644a0ad5135271f204edaf4a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 2 Feb 2015 12:38:07 +1300 Subject: [PATCH 32/60] kdc: fixup KDC to use functions portable to MIT krb5 Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 01c6991d362d26c71604649ad7a2dd4e6b695918) --- source4/kdc/db-glue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index 042abe6..3cd425a 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -1264,7 +1264,7 @@ static krb5_error_code samba_kdc_lookup_client(krb5_context context, NTSTATUS nt_status; char *principal_string; - if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { + if (krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) { principal_string = smb_krb5_principal_get_comp_string(mem_ctx, context, principal, 0); if (principal_string == NULL) { -- 2.1.4 From dd6cc85b8cc29e4d578e5158ab69cd1c85ee2b3a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 30 Jan 2015 12:31:29 +1300 Subject: [PATCH 33/60] kdc: make Samba KDC pass new TGS-REQ and AS-REQ (to self) testing This also reverts 51b94ab3fd4d13ee38813eb7d20db11edaa667a8 as our testing shows Windows 2012R2 does not have this behaviour. Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit f32564d643a76b2618395096d26d99654b33dd98) --- source4/kdc/db-glue.c | 206 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 148 insertions(+), 58 deletions(-) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index 3cd425a..aa73641 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -640,47 +640,62 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context, entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT) { - ret = krb5_copy_principal(context, principal, &entry_ex->entry.principal); - if (ret) { - return ret; - } - - /* - * Windows seems to canonicalize the principal - * in a TGS REP even if the client did not specify - * the canonicalize flag. - */ - if (flags & (HDB_F_CANON|HDB_F_FOR_TGS_REQ)) { - /* When requested to do so, ensure that the + if (flags & (HDB_F_CANON)) { + /* + * When requested to do so, ensure that the * both realm values in the principal are set - * to the upper case, canonical realm */ - free(entry_ex->entry.principal->name.name_string.val[1]); - entry_ex->entry.principal->name.name_string.val[1] = strdup(lpcfg_realm(lp_ctx)); - if (!entry_ex->entry.principal->name.name_string.val[1]) { - ret = ENOMEM; - krb5_set_error_message(context, ret, "samba_kdc_fetch: strdup() failed!"); - return ret; + * to the upper case, canonical realm + */ + ret = krb5_make_principal(context, &entry_ex->entry.principal, + lpcfg_realm(lp_ctx), "krbtgt", + lpcfg_realm(lp_ctx), NULL); + if (ret) { + krb5_clear_error_message(context); + goto out; + } + krb5_principal_set_type(context, entry_ex->entry.principal, KRB5_NT_SRV_INST); + } else { + ret = krb5_copy_principal(context, principal, &entry_ex->entry.principal); + if (ret) { + krb5_clear_error_message(context); + goto out; + } + /* + * this appears to be required regardless of + * the canonicalize flag from the client + */ + ret = krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx)); + if (ret) { + krb5_clear_error_message(context); + goto out; } } - /* - * this has to be with malloc(), and appears to be - * required regardless of the canonicalize flag from - * the client - */ - krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx)); } else if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && principal == NULL) { - krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL); - } else if (flags & HDB_F_CANON) { - krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL); + ret = krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL); + if (ret) { + krb5_clear_error_message(context); + goto out; + } + } else if (flags & HDB_F_CANON && flags & HDB_F_FOR_AS_REQ) { + /* + * HDB_F_CANON maps from the canonicalize flag in the + * packet, and has a different meaning between AS-REQ + * and TGS-REQ. We only change the principal in the AS-REQ case + */ + ret = krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL); + if (ret) { + krb5_clear_error_message(context); + goto out; + } } else { - ret = copy_Principal(principal, entry_ex->entry.principal); + ret = krb5_copy_principal(context, principal, &entry_ex->entry.principal); if (ret) { krb5_clear_error_message(context); goto out; } - if (principal->name.name_type != KRB5_NT_ENTERPRISE_PRINCIPAL) { + if (krb5_principal_get_type(context, principal) != KRB5_NT_ENTERPRISE_PRINCIPAL) { /* While we have copied the client principal, tests * show that Win2k3 returns the 'corrected' realm, not * the client-specified realm. This code attempts to @@ -688,7 +703,11 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context, * we determine from our records */ /* this has to be with malloc() */ - krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx)); + ret = krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx)); + if (ret) { + krb5_clear_error_message(context); + goto out; + } } } @@ -706,7 +725,18 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context, entry_ex->entry.flags.server = 0; } } - + /* + * To give the correct type of error to the client, we must + * not just return the entry without .server set, we must + * pretend the principal does not exist. Otherwise we may + * return ERR_POLICY instead of + * KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN + */ + if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER && entry_ex->entry.flags.server == 0) { + ret = HDB_ERR_NOENTRY; + krb5_set_error_message(context, ret, "samba_kdc_message2entry: no servicePrincipalName present for this server, refusing with no-such-entry"); + goto out; + } if (flags & HDB_F_ADMIN_DATA) { /* These (created_by, modified_by) parts of the entry are not relevant for Samba4's use * of the Heimdal KDC. They are stored in a the traditional @@ -716,9 +746,13 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context, /* use 'whenCreated' */ entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0); /* use 'kadmin' for now (needed by mit_samba) */ - krb5_make_principal(context, - &entry_ex->entry.created_by.principal, - lpcfg_realm(lp_ctx), "kadmin", NULL); + ret = krb5_make_principal(context, + &entry_ex->entry.created_by.principal, + lpcfg_realm(lp_ctx), "kadmin", NULL); + if (ret) { + krb5_clear_error_message(context); + goto out; + } entry_ex->entry.modified_by = (Event *) malloc(sizeof(Event)); if (entry_ex->entry.modified_by == NULL) { @@ -730,9 +764,13 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context, /* use 'whenChanged' */ entry_ex->entry.modified_by->time = ldb_msg_find_krb5time_ldap_time(msg, "whenChanged", 0); /* use 'kadmin' for now (needed by mit_samba) */ - krb5_make_principal(context, - &entry_ex->entry.modified_by->principal, - lpcfg_realm(lp_ctx), "kadmin", NULL); + ret = krb5_make_principal(context, + &entry_ex->entry.modified_by->principal, + lpcfg_realm(lp_ctx), "kadmin", NULL); + if (ret) { + krb5_clear_error_message(context); + goto out; + } } @@ -948,9 +986,13 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, /* use 'whenCreated' */ entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0); /* use 'kadmin' for now (needed by mit_samba) */ - krb5_make_principal(context, + ret = krb5_make_principal(context, &entry_ex->entry.created_by.principal, realm, "kadmin", NULL); + if (ret) { + krb5_clear_error_message(context); + goto out; + } entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); if (entry_ex->entry.principal == NULL) { @@ -973,7 +1015,11 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context, * we determine from our records */ - krb5_principal_set_realm(context, entry_ex->entry.principal, realm); + ret = krb5_principal_set_realm(context, entry_ex->entry.principal, realm); + if (ret) { + krb5_clear_error_message(context); + goto out; + } entry_ex->entry.valid_start = NULL; @@ -1308,8 +1354,8 @@ static krb5_error_code samba_kdc_fetch_client(krb5_context context, struct ldb_message *msg = NULL; ret = samba_kdc_lookup_client(context, kdc_db_ctx, - mem_ctx, principal, user_attrs, - &realm_dn, &msg); + mem_ctx, principal, user_attrs, + &realm_dn, &msg); if (ret != 0) { return ret; } @@ -1460,15 +1506,17 @@ static krb5_error_code samba_kdc_fetch_krbtgt(krb5_context context, } static krb5_error_code samba_kdc_lookup_server(krb5_context context, - struct samba_kdc_db_context *kdc_db_ctx, - TALLOC_CTX *mem_ctx, - krb5_const_principal principal, - const char **attrs, - struct ldb_dn **realm_dn, - struct ldb_message **msg) + struct samba_kdc_db_context *kdc_db_ctx, + TALLOC_CTX *mem_ctx, + krb5_const_principal principal, + unsigned flags, + const char **attrs, + struct ldb_dn **realm_dn, + struct ldb_message **msg) { krb5_error_code ret; - if (principal->name.name_string.len >= 2) { + if ((smb_krb5_principal_get_type(context, principal) != KRB5_NT_ENTERPRISE_PRINCIPAL) + && krb5_princ_size(context, principal) >= 2) { /* 'normal server' case */ int ldb_ret; NTSTATUS nt_status; @@ -1503,14 +1551,53 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context, if (ldb_ret != LDB_SUCCESS) { return HDB_ERR_NOENTRY; } - + return 0; + } else if (!(flags & HDB_F_FOR_AS_REQ) + && smb_krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) { + /* + * The behaviour of accepting an + * KRB5_NT_ENTERPRISE_PRINCIPAL server principal + * containing a UPN only applies to TGS-REQ packets, + * not AS-REQ packets. + */ + return samba_kdc_lookup_client(context, kdc_db_ctx, + mem_ctx, principal, attrs, + realm_dn, msg); } else { + /* + * This case is for: + * - the AS-REQ, where we only accept + * samAccountName based lookups for the server, no + * matter if the name is an + * KRB5_NT_ENTERPRISE_PRINCIPAL or not + * - for the TGS-REQ when we are not given an + * KRB5_NT_ENTERPRISE_PRINCIPAL, which also must + * only lookup samAccountName based names. + */ int lret; char *short_princ; - /* const char *realm; */ + krb5_principal enterprise_prinicpal = NULL; + + if (smb_krb5_principal_get_type(context, principal) == KRB5_NT_ENTERPRISE_PRINCIPAL) { + /* Need to reparse the enterprise principal to find the real target */ + if (principal->name.name_string.len != 1) { + ret = KRB5_PARSE_MALFORMED; + krb5_set_error_message(context, ret, "samba_kdc_lookup_server: request for an " + "enterprise principal with wrong (%d) number of components", + principal->name.name_string.len); + return ret; + } + ret = krb5_parse_name(context, principal->name.name_string.val[0], + &enterprise_prinicpal); + if (ret) { + talloc_free(mem_ctx); + return ret; + } + principal = enterprise_prinicpal; + } + /* server as client principal case, but we must not lookup userPrincipalNames */ *realm_dn = ldb_get_default_basedn(kdc_db_ctx->samdb); - /* realm = krb5_principal_get_realm(context, principal); */ /* TODO: Check if it is our realm, otherwise give referral */ @@ -1540,11 +1627,13 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context, return HDB_ERR_NOENTRY; } free(short_princ); + return 0; } - - return 0; + return HDB_ERR_NOENTRY; } + + static krb5_error_code samba_kdc_fetch_server(krb5_context context, struct samba_kdc_db_context *kdc_db_ctx, TALLOC_CTX *mem_ctx, @@ -1557,7 +1646,7 @@ static krb5_error_code samba_kdc_fetch_server(krb5_context context, struct ldb_message *msg; ret = samba_kdc_lookup_server(context, kdc_db_ctx, mem_ctx, principal, - server_attrs, &realm_dn, &msg); + flags, server_attrs, &realm_dn, &msg); if (ret != 0) { return ret; } @@ -1787,7 +1876,8 @@ samba_kdc_check_s4u2self(krb5_context context, } ret = samba_kdc_lookup_server(context, kdc_db_ctx, mem_ctx, target_principal, - delegation_check_attrs, &realm_dn, &msg); + HDB_F_GET_CLIENT|HDB_F_GET_SERVER, + delegation_check_attrs, &realm_dn, &msg); krb5_free_principal(context, enterprise_prinicpal); @@ -1841,8 +1931,8 @@ samba_kdc_check_pkinit_ms_upn_match(krb5_context context, } ret = samba_kdc_lookup_client(context, kdc_db_ctx, - mem_ctx, certificate_principal, - ms_upn_check_attrs, &realm_dn, &msg); + mem_ctx, certificate_principal, + ms_upn_check_attrs, &realm_dn, &msg); if (ret != 0) { talloc_free(mem_ctx); -- 2.1.4 From 7e8461959213b7009a8307416f8728cc1ca9764d Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 30 Jan 2015 12:31:10 +1300 Subject: [PATCH 34/60] torture-krb5: add TGS-REQ testing to krb5.kdc.canon testsuite Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit bcd33c0dce793427b2c408ad592801c881770d4d) --- source4/selftest/tests.py | 2 +- source4/torture/krb5/kdc-canon.c | 319 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 295 insertions(+), 26 deletions(-) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 116367e..bddba41 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -563,7 +563,7 @@ for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", else: extra_options = [] - plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN', '--realm=$REALM'] + extra_options, + plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:expect_machine_account=true'] + extra_options, "samba4.krb5.kdc with machine account") plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:krb5-upn=testallowed_upn@$REALM'] + extra_options, "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index c14be00..408e356 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -40,6 +40,7 @@ #define TEST_ALL 0x000007F struct test_data { + const char *test_name; struct smb_krb5_context *smb_krb5_context; const char *realm; const char *real_realm; @@ -55,14 +56,25 @@ struct test_data { bool upn; bool other_upn_suffix; }; - + +enum test_stage { + TEST_AS_REQ = 0, + TEST_SELF_TRUST_TGS_REQ, + TEST_TGS_REQ, + TEST_TGS_REQ_KRBTGT, + TEST_DONE +}; + struct torture_krb5_context { struct torture_context *tctx; struct addrinfo *server; struct test_data *test_data; int packet_count; + enum test_stage test_stage; AS_REQ as_req; AS_REP as_rep; + TGS_REQ tgs_req; + TGS_REP tgs_rep; }; @@ -79,14 +91,9 @@ struct torture_krb5_context { * */ -static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf) +static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) { krb5_error_code k5ret; - size_t used; - torture_assert_int_equal(test_context->tctx, - decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0, - "decode_AS_REQ failed"); - torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno"); if (test_context->test_data->canonicalize || test_context->test_data->enterprise) { @@ -115,6 +122,87 @@ static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context return true; } +static bool torture_krb5_pre_send_self_trust_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) +{ + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); + + + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); + + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.len, 2, + "Mismatch in name between request and expected request, expected krbtgt/realm"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[0], "krbtgt", + "Mismatch in name between request and expected request, expected krbtgt"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[1], test_context->test_data->realm, + "Mismatch in realm part of cross-realm request principal between request and expected request"); + + *modified_send_buf = *send_buf; + + return true; +} + +static bool torture_krb5_pre_send_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) +{ + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); + + if (test_context->test_data->enterprise) { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_ENTERPRISE_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); + + } else { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, test_context->test_data->realm, + "Mismatch in realm between request and expected request"); + + } + *modified_send_buf = *send_buf; + + return true; +} + +static bool torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) +{ + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); + + if (test_context->test_data->canonicalize) { + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); + } else { + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->realm, + "Mismatch in realm between request and expected request"); + } + + *modified_send_buf = *send_buf; + + return true; +} + /* * Confirm that the incoming packet from the KDC meets certain * expectations. This uses a packet count to work out what test we @@ -123,7 +211,7 @@ static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context * */ -static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) +static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) { KRB_ERROR error; size_t used; @@ -178,6 +266,88 @@ static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_contex return true; } +static bool torture_krb5_post_recv_self_trust_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) +{ + size_t used; + torture_assert_int_equal(test_context->tctx, + decode_TGS_REP(recv_buf->data, recv_buf->length, &test_context->tgs_rep, &used), 0, + "decode_TGS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert(test_context->tctx, + test_context->tgs_rep.ticket.enc_part.kvno, + "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->tgs_rep.ticket.realm, "Mismatch in realm between request and ticket response"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, + test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, + test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response"); + if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { + torture_assert_int_not_equal(test_context->tctx, + *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Did not get a RODC number in the KVNO"); + } else { + torture_assert_int_equal(test_context->tctx, + *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Unexpecedly got a RODC number in the KVNO"); + } + free_TGS_REP(&test_context->tgs_rep); + torture_assert(test_context->tctx, test_context->packet_count == 0, "too many packets"); + test_context->packet_count = 0; + test_context->test_stage = TEST_TGS_REQ; + free_TGS_REQ(&test_context->tgs_req); + return true; +} + +static bool torture_krb5_post_recv_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) +{ + KRB_ERROR error; + size_t used; + if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) { + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + 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_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } else { + torture_assert_int_equal(test_context->tctx, + decode_TGS_REP(recv_buf->data, recv_buf->length, &test_context->tgs_rep, &used), 0, + "decode_TGS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert(test_context->tctx, + test_context->tgs_rep.ticket.enc_part.kvno, + "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.realm, + test_context->test_data->real_realm, + "Mismatch in realm between ticket response and expected upper case REALM"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, + test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response"); + torture_assert_int_equal(test_context->tctx, + *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO"); + free_TGS_REP(&test_context->tgs_rep); + } + torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets"); + free_TGS_REQ(&test_context->tgs_req); + return true; +} + /* * This function is set in torture_krb5_init_context_canon as krb5 * send_and_recv function. This allows us to override what server the @@ -200,17 +370,50 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c krb5_data *recv_buf) { krb5_error_code k5ret; - bool ok; + bool ok = false; krb5_data modified_send_buf; + size_t used; struct torture_krb5_context *test_context = talloc_get_type_abort(data, struct torture_krb5_context); - ok = torture_krb5_pre_send_test(test_context, send_buf, &modified_send_buf); + if (test_context->test_stage == TEST_DONE) { + torture_warning(test_context->tctx, "Unexpected outgoing packet from krb5 libs"); + return EINVAL; + } + k5ret = decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used); + + if (k5ret == 0) { + test_context->test_stage = TEST_AS_REQ; + ok = torture_krb5_pre_send_as_req_test(test_context, send_buf, used, &modified_send_buf); + } else { + k5ret = decode_TGS_REQ(send_buf->data, send_buf->length, &test_context->tgs_req, &used); + if (k5ret == 0) { + if (test_context->test_stage == TEST_AS_REQ) { + test_context->packet_count = 0; + if (test_context->test_data->canonicalize == false + || test_context->test_data->enterprise + || (test_context->test_data->upper_realm && test_context->test_data->netbios_realm == false)) { + test_context->test_stage = TEST_TGS_REQ; + } else { + test_context->test_stage = TEST_SELF_TRUST_TGS_REQ; + } + } + if (test_context->test_stage == TEST_SELF_TRUST_TGS_REQ) { + ok = torture_krb5_pre_send_self_trust_tgs_req_test(test_context, send_buf, used, &modified_send_buf); + } else if (test_context->test_stage == TEST_TGS_REQ) { + ok = torture_krb5_pre_send_tgs_req_test(test_context, send_buf, used, &modified_send_buf); + } else if (test_context->test_stage == TEST_TGS_REQ_KRBTGT) { + ok = torture_krb5_pre_send_tgs_req_krbtgt_test(test_context, send_buf, used, &modified_send_buf); + } + } else { + torture_warning(test_context->tctx, "Failed to parse outgoing packet from krb5 libs"); + } + } if (ok == false) { return EINVAL; } - + k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server, hi, timeout, &modified_send_buf, recv_buf); @@ -218,7 +421,15 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c return k5ret; } - ok = torture_krb5_post_recv_test(test_context, recv_buf); + if (test_context->test_stage == TEST_AS_REQ) { + ok = torture_krb5_post_recv_as_req_test(test_context, recv_buf); + } else if (test_context->test_stage == TEST_SELF_TRUST_TGS_REQ) { + ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf); + } else if (test_context->test_stage == TEST_TGS_REQ) { + ok = torture_krb5_post_recv_tgs_req_test(test_context, recv_buf); + } else if (test_context->test_stage == TEST_TGS_REQ_KRBTGT) { + ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf); + } if (ok == false) { return EINVAL; } @@ -272,8 +483,6 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * krb5_error_code k5ret; krb5_get_init_creds_opt *krb_options = NULL; struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data); - char *realm; - char *upper_real_realm; char *username; krb5_principal principal; krb5_principal expected_principal; @@ -287,6 +496,10 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * struct smb_krb5_context *smb_krb5_context; bool ok; krb5_creds my_creds; + krb5_ccache ccache; + krb5_auth_context auth_context; + char *cc_name; + krb5_data in_data, enc_ticket; const char *upn = torture_setting_string(tctx, "krb5-upn", ""); @@ -343,9 +556,9 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * torture_assert(tctx, ok, "torture_krb5_init_context failed"); if (test_data->upper_realm) { - realm = strupper_talloc(test_data, test_data->realm); + test_data->realm = strupper_talloc(test_data, test_data->realm); } else { - realm = strlower_talloc(test_data, test_data->realm); + test_data->realm = strlower_talloc(test_data, test_data->realm); } if (test_data->upper_username) { username = strupper_talloc(test_data, test_data->username); @@ -353,10 +566,8 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * username = talloc_strdup(test_data, test_data->username); } - principal_string = talloc_asprintf(test_data, "%s@%s", username, realm); + principal_string = talloc_asprintf(test_data, "%s@%s", username, test_data->realm); - upper_real_realm = strupper_talloc(test_data, test_data->real_realm); - /* * If we are set to canonicalize, we get back the fixed UPPER * case realm, and the real username (ie matching LDAP @@ -369,11 +580,11 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * * fixed UPPER case realm, but the as-sent username */ if (test_data->canonicalize) { - expected_principal_string = talloc_asprintf(test_data, "%s@%s", test_data->real_username, upper_real_realm); + expected_principal_string = talloc_asprintf(test_data, "%s@%s", test_data->real_username, test_data->real_realm); } else if (test_data->enterprise) { expected_principal_string = principal_string; } else { - expected_principal_string = talloc_asprintf(test_data, "%s@%s", username, upper_real_realm); + expected_principal_string = talloc_asprintf(test_data, "%s@%s", username, test_data->real_realm); } if (test_data->enterprise) { @@ -491,7 +702,7 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * } else { torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context, my_creds.server, 1), - realm, + test_data->realm, "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]"); } @@ -500,10 +711,67 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * test_data->real_realm, "smb_krb5_init_context gave incorrect my_creds.server->realm"); + /* Store the result of the 'kinit' above into a memory ccache */ + cc_name = talloc_asprintf(tctx, "MEMORY:%s", test_data->test_name); + torture_assert_int_equal(tctx, krb5_cc_resolve(smb_krb5_context->krb5_context, cc_name, + &ccache), + 0, "krb5_cc_resolve failed"); + + torture_assert_int_equal(tctx, krb5_cc_initialize(smb_krb5_context->krb5_context, + ccache, my_creds.client), + 0, "krb5_cc_initialize failed"); + + torture_assert_int_equal(tctx, krb5_cc_store_cred(smb_krb5_context->krb5_context, + ccache, &my_creds), + 0, "krb5_cc_store_cred failed"); + + /* Prepare a TGS-REQ */ + torture_assert_int_equal(tctx, krb5_auth_con_init(smb_krb5_context->krb5_context, &auth_context), + 0, "krb5_auth_con_init failed"); + + /* Confirm we can not ask for our own name as a server */ + in_data.length = 0; + k5ret = krb5_mk_req_exact(smb_krb5_context->krb5_context, + &auth_context, + 0, + principal, + &in_data, ccache, + &enc_ticket); + + assertion_message = talloc_asprintf(tctx, + "krb5_mk_req_exact for %s failed: %s", + principal_string, + smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx)); + if (torture_setting_bool(tctx, "expect_machine_account", false)) { + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + } else { + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, assertion_message); + } + + /* + * Ask for a ticket to the KDC krbtgt account itself. The + * value in my_creds.server varies (in the non-canonicalize + * case) per the AS-REQ tests above, so we cover the same + * variations + */ + in_data.length = 0; + k5ret = krb5_mk_req_exact(smb_krb5_context->krb5_context, + &auth_context, + 0, + my_creds.server, + &in_data, ccache, + &enc_ticket); + + assertion_message = talloc_asprintf(tctx, + "krb5_mk_req_exact for %s failed: %s", + principal_string, + smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + krb5_free_principal(smb_krb5_context->krb5_context, principal); - k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds); - torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed"); + torture_assert_int_equal(tctx, krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds), + 0, "krb5_free_cred_contents failed"); return true; } @@ -526,7 +794,8 @@ struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx) struct test_data *test_data = talloc_zero(suite, struct test_data); - test_data->real_realm = cli_credentials_get_realm(cmdline_credentials); + test_data->test_name = name; + test_data->real_realm = strupper_talloc(test_data, cli_credentials_get_realm(cmdline_credentials)); test_data->real_domain = cli_credentials_get_domain(cmdline_credentials); test_data->username = cli_credentials_get_username(cmdline_credentials); test_data->real_username = cli_credentials_get_username(cmdline_credentials); -- 2.1.4 From 21861306e0dfd4bbb627efb095dc4ca8a9547741 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 30 Jan 2015 18:17:16 +1300 Subject: [PATCH 35/60] torture-krb5: Add tests for the canonicalise TGS-REQ case Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam (cherry picked from commit 0a4374a93aced30245c0719e6a279f1dd7ea78f1) --- source4/torture/krb5/kdc-canon.c | 199 ++++++++++++++++++++++++++++++++++----- 1 file changed, 173 insertions(+), 26 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index 408e356..3c33884 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -59,6 +59,7 @@ struct test_data { enum test_stage { TEST_AS_REQ = 0, + TEST_TGS_REQ_CANON, TEST_SELF_TRUST_TGS_REQ, TEST_TGS_REQ, TEST_TGS_REQ_KRBTGT, @@ -122,18 +123,60 @@ static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_ return true; } -static bool torture_krb5_pre_send_self_trust_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) +static bool torture_krb5_pre_send_tgs_req_canon_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) { torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); - torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.kdc_options.canonicalize, true, "krb5 libs unexpectedly did not set canonicalize!"); + if (test_context->test_data->enterprise) { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_ENTERPRISE_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, - test_context->test_data->real_realm, + } else if (test_context->test_data->canonicalize) { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, test_context->test_data->real_realm, "Mismatch in realm between request and expected request"); + } else { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, test_context->test_data->realm, + "Mismatch in realm between request and expected request"); + + } + + *modified_send_buf = *send_buf; + + return true; +} + +static bool torture_krb5_pre_send_self_trust_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) +{ + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); + + if (test_context->test_data->canonicalize) { + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); + } else { + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->realm, + "Mismatch in realm between request and expected request"); + } torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); @@ -146,7 +189,6 @@ static bool torture_krb5_pre_send_self_trust_tgs_req_test(struct torture_krb5_co torture_assert_str_equal(test_context->tctx, test_context->tgs_req.req_body.sname->name_string.val[1], test_context->test_data->realm, "Mismatch in realm part of cross-realm request principal between request and expected request"); - *modified_send_buf = *send_buf; return true; @@ -156,7 +198,7 @@ static bool torture_krb5_pre_send_tgs_req_test(struct torture_krb5_context *test { torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); - torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); if (test_context->test_data->enterprise) { torture_assert_int_equal(test_context->tctx, @@ -175,6 +217,7 @@ static bool torture_krb5_pre_send_tgs_req_test(struct torture_krb5_context *test "Mismatch in realm between request and expected request"); } + *modified_send_buf = *send_buf; return true; @@ -184,7 +227,7 @@ static bool torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_contex { torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); - torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); if (test_context->test_data->canonicalize) { torture_assert_str_equal(test_context->tctx, @@ -266,6 +309,60 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test return true; } +static bool torture_krb5_post_recv_tgs_req_canon_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) +{ + KRB_ERROR error; + size_t used; + if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) { + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + 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_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } else { + torture_assert_int_equal(test_context->tctx, + decode_TGS_REP(recv_buf->data, recv_buf->length, &test_context->tgs_rep, &used), 0, + "decode_TGS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert(test_context->tctx, + test_context->tgs_rep.ticket.enc_part.kvno, + "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.realm, + test_context->test_data->real_realm, + "Mismatch in realm between ticket response and expected upper case REALM"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, + test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response"); + torture_assert_int_equal(test_context->tctx, + *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO"); + free_TGS_REP(&test_context->tgs_rep); + } + torture_assert(test_context->tctx, test_context->packet_count == 0, "too many packets"); + free_TGS_REQ(&test_context->tgs_req); + + /* + * This tries to guess when the krb5 + * libs will ask for a cross-realm + * ticket, and when they will just ask + * the KDC directly. We always ask directly if 'canonicalize == true' becuase we use different client calls in that case. + */ + if (test_context->test_data->canonicalize == false + || test_context->test_data->enterprise + || (test_context->test_data->upper_realm && test_context->test_data->netbios_realm == false)) { + test_context->test_stage = TEST_TGS_REQ; + } else { + test_context->test_stage = TEST_SELF_TRUST_TGS_REQ; + } + return true; +} + static bool torture_krb5_post_recv_self_trust_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) { size_t used; @@ -301,7 +398,7 @@ static bool torture_krb5_post_recv_self_trust_tgs_req_test(struct torture_krb5_c 0, "Unexpecedly got a RODC number in the KVNO"); } free_TGS_REP(&test_context->tgs_rep); - torture_assert(test_context->tctx, test_context->packet_count == 0, "too many packets"); + torture_assert(test_context->tctx, test_context->packet_count == 1, "too many packets"); test_context->packet_count = 0; test_context->test_stage = TEST_TGS_REQ; free_TGS_REQ(&test_context->tgs_req); @@ -343,7 +440,7 @@ static bool torture_krb5_post_recv_tgs_req_test(struct torture_krb5_context *tes 0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO"); free_TGS_REP(&test_context->tgs_rep); } - torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets"); + torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); free_TGS_REQ(&test_context->tgs_req); return true; } @@ -381,25 +478,31 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c torture_warning(test_context->tctx, "Unexpected outgoing packet from krb5 libs"); return EINVAL; } - k5ret = decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used); - if (k5ret == 0) { - test_context->test_stage = TEST_AS_REQ; + if (decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used) == 0) { ok = torture_krb5_pre_send_as_req_test(test_context, send_buf, used, &modified_send_buf); } else { k5ret = decode_TGS_REQ(send_buf->data, send_buf->length, &test_context->tgs_req, &used); if (k5ret == 0) { if (test_context->test_stage == TEST_AS_REQ) { test_context->packet_count = 0; - if (test_context->test_data->canonicalize == false - || test_context->test_data->enterprise - || (test_context->test_data->upper_realm && test_context->test_data->netbios_realm == false)) { - test_context->test_stage = TEST_TGS_REQ; + if (test_context->test_data->canonicalize == false && test_context->test_data->enterprise == false + && (test_context->test_data->upper_realm == false || test_context->test_data->netbios_realm == true)) { + if (test_context->test_data->canonicalize == false + || test_context->test_data->enterprise + || (test_context->test_data->upper_realm && test_context->test_data->netbios_realm == false)) { + test_context->test_stage = TEST_TGS_REQ; + } else { + test_context->test_stage = TEST_SELF_TRUST_TGS_REQ; + } } else { - test_context->test_stage = TEST_SELF_TRUST_TGS_REQ; + test_context->test_stage = TEST_TGS_REQ_CANON; } } - if (test_context->test_stage == TEST_SELF_TRUST_TGS_REQ) { + + if (test_context->test_stage == TEST_TGS_REQ_CANON) { + ok = torture_krb5_pre_send_tgs_req_canon_test(test_context, send_buf, used, &modified_send_buf); + } else if (test_context->test_stage == TEST_SELF_TRUST_TGS_REQ) { ok = torture_krb5_pre_send_self_trust_tgs_req_test(test_context, send_buf, used, &modified_send_buf); } else if (test_context->test_stage == TEST_TGS_REQ) { ok = torture_krb5_pre_send_tgs_req_test(test_context, send_buf, used, &modified_send_buf); @@ -423,6 +526,8 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c if (test_context->test_stage == TEST_AS_REQ) { ok = torture_krb5_post_recv_as_req_test(test_context, recv_buf); + } else if (test_context->test_stage == TEST_TGS_REQ_CANON) { + ok = torture_krb5_post_recv_tgs_req_canon_test(test_context, recv_buf); } else if (test_context->test_stage == TEST_SELF_TRUST_TGS_REQ) { ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf); } else if (test_context->test_stage == TEST_TGS_REQ) { @@ -496,10 +601,12 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * struct smb_krb5_context *smb_krb5_context; bool ok; krb5_creds my_creds; + krb5_creds *server_creds; krb5_ccache ccache; krb5_auth_context auth_context; char *cc_name; krb5_data in_data, enc_ticket; + krb5_get_creds_opt opt; const char *upn = torture_setting_string(tctx, "krb5-upn", ""); @@ -726,10 +833,56 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * 0, "krb5_cc_store_cred failed"); /* Prepare a TGS-REQ */ + torture_assert_int_equal(tctx, + krb5_get_creds_opt_alloc(smb_krb5_context->krb5_context, &opt), + 0, "krb5_get_creds_opt_alloc"); + + krb5_get_creds_opt_add_options(smb_krb5_context->krb5_context, + opt, + KRB5_GC_CANONICALIZE); + + /* Confirm if we can get a ticket to our own name */ + k5ret = krb5_get_creds(smb_krb5_context->krb5_context, opt, ccache, principal, &server_creds); + + if (test_data->canonicalize == false && test_data->enterprise == false + && (test_data->upper_realm == false || test_data->netbios_realm == true)) { + torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND, "krb5_get_creds should have failed with KRB5_CC_NOTFOUND"); + } else { + assertion_message = talloc_asprintf(tctx, + "krb5_get_creds for %s failed: %s", + principal_string, + smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx)); + + /* We should only be able to get a ticket to our own name if we are a machine account */ + if (torture_setting_bool(tctx, "expect_machine_account", false)) { + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + torture_assert_int_equal(tctx, krb5_cc_store_cred(smb_krb5_context->krb5_context, + ccache, server_creds), + 0, "krb5_cc_store_cred failed"); + + torture_assert_int_equal(tctx, + krb5_free_creds(smb_krb5_context->krb5_context, + server_creds), + 0, "krb5_free_cred_contents failed"); + + } else { + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, assertion_message); + } + } + /* + * Confirm gettting a ticket to pass to the server. + * + * This triggers the client to attempt to get a + * cross-realm ticket between the alternate names of + * the server, and we need to confirm that behaviour. + * + * The cache isn't used because we asked the above not to + * pollute the cache + */ + torture_assert_int_equal(tctx, krb5_auth_con_init(smb_krb5_context->krb5_context, &auth_context), 0, "krb5_auth_con_init failed"); - /* Confirm we can not ask for our own name as a server */ in_data.length = 0; k5ret = krb5_mk_req_exact(smb_krb5_context->krb5_context, &auth_context, @@ -748,12 +901,6 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, assertion_message); } - /* - * Ask for a ticket to the KDC krbtgt account itself. The - * value in my_creds.server varies (in the non-canonicalize - * case) per the AS-REQ tests above, so we cover the same - * variations - */ in_data.length = 0; k5ret = krb5_mk_req_exact(smb_krb5_context->krb5_context, &auth_context, -- 2.1.4 From fff1c45523ffc522cf3b901bcb035a77beeff723 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 2 Feb 2015 12:18:23 +1300 Subject: [PATCH 36/60] torture-krb5: Reformat and re-work test to be easier to follow The behaviour is the same as in the previous commit, but it is much easier to follow as the main test code now indicates to the send_and_recv callbacks what stage of the test we are at, and resets the packet counter between stages. This also re-orders the code so that the send and recv callbacks for each stage are next to each other, and uses a case statement in the main send_and_recv driver for clarity. Andrew Bartlett Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam (cherry picked from commit 11871c853a911b85ebb3f9ff5671ce1f9024188f) --- source4/torture/krb5/kdc-canon.c | 855 ++++++++++++++++++++++++++------------- 1 file changed, 573 insertions(+), 282 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index 3c33884..8c9543c 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -41,7 +41,6 @@ struct test_data { const char *test_name; - struct smb_krb5_context *smb_krb5_context; const char *realm; const char *real_realm; const char *real_domain; @@ -67,6 +66,7 @@ enum test_stage { }; struct torture_krb5_context { + struct smb_krb5_context *smb_krb5_context; struct torture_context *tctx; struct addrinfo *server; struct test_data *test_data; @@ -80,6 +80,8 @@ struct torture_krb5_context { /* + * TEST_AS_REQ - SEND + * * Confirm that the outgoing packet meets certain expectations. This * should be extended to further assert the correct and expected * behaviour of the krb5 libs, so we know what we are sending to the @@ -92,21 +94,39 @@ struct torture_krb5_context { * */ -static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) +static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_context, + const krb5_data *send_buf, + krb5_data *modified_send_buf) { krb5_error_code k5ret; + size_t used; + torture_assert_int_equal(test_context->tctx, decode_AS_REQ(send_buf->data, send_buf->length, + &test_context->as_req, &used), + 0, "decode_AS_REQ for TEST_AS_REQ failed"); torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); - torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, + 5, "Got wrong as_req->pvno"); if (test_context->test_data->canonicalize || test_context->test_data->enterprise) { - torture_assert(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, "krb5 libs did not set canonicalize!"); + torture_assert(test_context->tctx, + test_context->as_req.req_body.kdc_options.canonicalize, + "krb5 libs did not set canonicalize!"); } else { - torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); + torture_assert_int_equal(test_context->tctx, + test_context->as_req.req_body.kdc_options.canonicalize, + false, + "krb5 libs unexpectedly set canonicalize!"); } if (test_context->test_data->enterprise) { - torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.cname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, "krb5 libs did not pass principal as enterprise!"); + torture_assert_int_equal(test_context->tctx, + test_context->as_req.req_body.cname->name_type, + KRB5_NT_ENTERPRISE_PRINCIPAL, + "krb5 libs did not pass principal as enterprise!"); } else { - torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.cname->name_type, KRB5_NT_PRINCIPAL, "krb5 libs unexpectedly set principal as enterprise!"); + torture_assert_int_equal(test_context->tctx, + test_context->as_req.req_body.cname->name_type, + KRB5_NT_PRINCIPAL, + "krb5 libs unexpectedly set principal as enterprise!"); } /* Force off canonicalize that was forced on by the krb5 libs */ @@ -119,171 +139,86 @@ static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_ torture_assert_int_equal(test_context->tctx, k5ret, 0, "encode_AS_REQ failed"); - torture_assert_int_equal(test_context->tctx, used, send_buf->length, "re-encode length mismatch"); - return true; -} - -static bool torture_krb5_pre_send_tgs_req_canon_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) -{ - torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); - torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); - torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.kdc_options.canonicalize, true, "krb5 libs unexpectedly did not set canonicalize!"); - - if (test_context->test_data->enterprise) { - torture_assert_int_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, - "Mismatch in name type between request and expected request, expected KRB5_NT_ENTERPRISE_PRINCIPAL"); - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, test_context->test_data->real_realm, - "Mismatch in realm between request and expected request"); - - } else if (test_context->test_data->canonicalize) { - torture_assert_int_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, - "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, test_context->test_data->real_realm, - "Mismatch in realm between request and expected request"); - - } else { - torture_assert_int_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, - "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, test_context->test_data->realm, - "Mismatch in realm between request and expected request"); - - } - - *modified_send_buf = *send_buf; - - return true; -} - -static bool torture_krb5_pre_send_self_trust_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) -{ - torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); - torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); - torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); - - if (test_context->test_data->canonicalize) { - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, - test_context->test_data->real_realm, - "Mismatch in realm between request and expected request"); - } else { - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, - test_context->test_data->realm, - "Mismatch in realm between request and expected request"); - } - torture_assert_int_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, - "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); - torture_assert_int_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_string.len, 2, - "Mismatch in name between request and expected request, expected krbtgt/realm"); - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_string.val[0], "krbtgt", - "Mismatch in name between request and expected request, expected krbtgt"); - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_string.val[1], test_context->test_data->realm, - "Mismatch in realm part of cross-realm request principal between request and expected request"); - *modified_send_buf = *send_buf; - - return true; -} - -static bool torture_krb5_pre_send_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) -{ - torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); - torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); - torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); - - if (test_context->test_data->enterprise) { - torture_assert_int_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, - "Mismatch in name type between request and expected request, expected KRB5_NT_ENTERPRISE_PRINCIPAL"); - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, test_context->test_data->real_realm, - "Mismatch in realm between request and expected request"); - - } else { - torture_assert_int_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, - "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, test_context->test_data->realm, - "Mismatch in realm between request and expected request"); - - } - - *modified_send_buf = *send_buf; - - return true; -} - -static bool torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, size_t used, krb5_data *modified_send_buf) -{ - torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); - torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); - torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); - - if (test_context->test_data->canonicalize) { - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, - test_context->test_data->real_realm, - "Mismatch in realm between request and expected request"); - } else { - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, - test_context->test_data->realm, - "Mismatch in realm between request and expected request"); - } - - *modified_send_buf = *send_buf; - + torture_assert_int_equal(test_context->tctx, used, send_buf->length, + "re-encode length mismatch"); return true; } /* - * Confirm that the incoming packet from the KDC meets certain - * expectations. This uses a packet count to work out what test we - * are in, and where in the test we are, so we can assert on the - * expected reply packets from the KDC. + * TEST_AS_REQ - RECV + * + * Confirm that the reply packet from the KDC meets certain + * expectations as part of TEST_AS_REQ. This uses a packet count to + * work out what packet we are up to in the multiple exchanged + * triggerd by krb5_get_init_creds_password(). * */ -static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) +static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test_context, + const krb5_data *recv_buf) { KRB_ERROR error; size_t used; if (test_context->packet_count == 0) { + /* + * The client libs obtain the salt by attempting to + * authenticate without pre-authentication and getting + * the correct salt with the + * KRB5KDC_ERR_PREAUTH_REQUIRED error. If we are in + * the test (netbios_realm && upn) that deliberatly + * has an incorrect principal, we check we get the + * correct error. + */ torture_assert_int_equal(test_context->tctx, - decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0, + decode_KRB_ERROR(recv_buf->data, recv_buf->length, + &error, &used), 0, "decode_AS_REP failed"); - torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); - torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, error.pvno, 5, + "Got wrong error.pvno"); if (test_context->test_data->netbios_realm && test_context->test_data->upn) { - torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, "Got wrong error.error_code"); } else { - torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE, + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE, "Got wrong error.error_code"); } free_KRB_ERROR(&error); } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) && (test_context->packet_count == 1)) { - torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); - torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); - torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + /* + * The Windows 2012R2 KDC will always respond with + * KRB5KRB_ERR_RESPONSE_TOO_BIG over UDP as the ticket + * won't fit, because of the PAC. (It appears to do + * this always, even if it will). This triggers the + * client to try again over TCP. + */ + torture_assert_int_equal(test_context->tctx, + used, recv_buf->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, + error.pvno, 5, + "Got wrong error.pvno"); + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, "Got wrong error.error_code"); free_KRB_ERROR(&error); } else { + /* + * Finally the successful packet. + */ torture_assert_int_equal(test_context->tctx, - decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0, + decode_AS_REP(recv_buf->data, recv_buf->length, + &test_context->as_rep, &used), 0, "decode_AS_REP failed"); - torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, + "length mismatch"); torture_assert_int_equal(test_context->tctx, test_context->as_rep.pvno, 5, "Got wrong as_rep->pvno"); @@ -293,6 +228,17 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test torture_assert(test_context->tctx, test_context->as_rep.ticket.enc_part.kvno, "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + + /* + * We can confirm that the correct proxy behaviour is + * in use on the KDC by checking the KVNO of the + * krbtgt account returned in the reply. + * + * A packet passed to the full RW DC will not have a + * KVNO in the upper bits, while a packet processed + * locally on the RODC will have these bits filled in + * the msDS-SecondaryKrbTgtNumber + */ if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { torture_assert_int_not_equal(test_context->tctx, *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, @@ -309,20 +255,100 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test return true; } +/* + * TEST_TGS_REQ_CANON + * + * Confirm that the outgoing TGS-REQ packet from krb5_get_creds + * certain expectations, like that the canonicalize bit is set (this + * test is to force that handling) and that if an enterprise name was + * requested, that it was sent. + * + */ + +static bool torture_krb5_pre_send_tgs_req_canon_test(struct torture_krb5_context *test_context, + const krb5_data *send_buf, + krb5_data *modified_send_buf) +{ + size_t used; + torture_assert_int_equal(test_context->tctx, + decode_TGS_REQ(send_buf->data, send_buf->length, + &test_context->tgs_req, &used), + 0, "decode_TGS_REQ for TEST_TGS_REQ_CANON test failed"); + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.kdc_options.canonicalize, + true, "krb5 libs unexpectedly did not set canonicalize!"); + + if (test_context->test_data->enterprise) { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_ENTERPRISE_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); + + } else if (test_context->test_data->canonicalize) { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); + + } else { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, test_context->test_data->realm, + "Mismatch in realm between request and expected request"); + + } + + *modified_send_buf = *send_buf; + + return true; +} + +/* + * TEST_TGS_REQ_CANON - RECV + * + * Confirm that the reply TGS-REP or error packet from the KDC meets + * certain expectations as part of TEST_TGS_REQ_CANON. + * + * This is triggered by krb5_get_creds() + * + */ + static bool torture_krb5_post_recv_tgs_req_canon_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) { KRB_ERROR error; size_t used; + + /* + * If this account did not have a servicePrincipalName, then + * we expect a errro packet, not a TGS-REQ + */ if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) { torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); - 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_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, + 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_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, "Got wrong error.error_code"); } else { torture_assert_int_equal(test_context->tctx, - decode_TGS_REP(recv_buf->data, recv_buf->length, &test_context->tgs_rep, &used), 0, + decode_TGS_REP(recv_buf->data, recv_buf->length, + &test_context->tgs_rep, + &used), + 0, "decode_TGS_REP failed"); - torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, + used, recv_buf->length, + "length mismatch"); torture_assert_int_equal(test_context->tctx, test_context->tgs_rep.pvno, 5, "Got wrong as_rep->pvno"); @@ -337,8 +363,9 @@ static bool torture_krb5_post_recv_tgs_req_canon_test(struct torture_krb5_contex test_context->test_data->real_realm, "Mismatch in realm between ticket response and expected upper case REALM"); torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_type, test_context->tgs_req.req_body.sname->name_type, - test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response"); + "Mismatch in name_type between request and ticket response"); torture_assert_int_equal(test_context->tctx, *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000, 0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO"); @@ -347,27 +374,80 @@ static bool torture_krb5_post_recv_tgs_req_canon_test(struct torture_krb5_contex torture_assert(test_context->tctx, test_context->packet_count == 0, "too many packets"); free_TGS_REQ(&test_context->tgs_req); - /* - * This tries to guess when the krb5 - * libs will ask for a cross-realm - * ticket, and when they will just ask - * the KDC directly. We always ask directly if 'canonicalize == true' becuase we use different client calls in that case. - */ - if (test_context->test_data->canonicalize == false - || test_context->test_data->enterprise - || (test_context->test_data->upper_realm && test_context->test_data->netbios_realm == false)) { - test_context->test_stage = TEST_TGS_REQ; + return true; +} + +/* + * TEST_SELF_TRUST_TGS_REQ + * + * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact() + * certain expectations, like that the canonicalize bit is set (this + * test is to force that handling). + * + * This test is for the case where the name we ask for, while a valid + * alternate name for our own realm is used. The client acts as if + * this is cross-realm trust. + * + */ + +static bool torture_krb5_pre_send_self_trust_tgs_req_test(struct torture_krb5_context *test_context, + const krb5_data *send_buf, + krb5_data *modified_send_buf) +{ + size_t used; + torture_assert_int_equal(test_context->tctx, + decode_TGS_REQ(send_buf->data, send_buf->length, + &test_context->tgs_req, &used), + 0, "decode_TGS_REQ for TEST_SELF_TRUST_TGS_REQ test failed"); + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!"); + + if (test_context->test_data->canonicalize) { + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); } else { - test_context->test_stage = TEST_SELF_TRUST_TGS_REQ; + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->realm, + "Mismatch in realm between request and expected request"); } + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.len, 2, + "Mismatch in name between request and expected request, expected krbtgt/realm"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[0], "krbtgt", + "Mismatch in name between request and expected request, expected krbtgt"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[1], test_context->test_data->realm, + "Mismatch in realm part of cross-realm request principal between request and expected request"); + *modified_send_buf = *send_buf; + return true; } +/* + * TEST_SELF_TRUST_TGS_REQ and TEST_TGS_REQ_KRBTGT - RECV + * + * Confirm that the reply TGS-REP packet for krb5_mk_req_exact(), + * where the client is behaving as if this is a cross-realm trust due + * to case or netbios vs dns name differences meets certain + * expectations. + * + */ + static bool torture_krb5_post_recv_self_trust_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) { size_t used; torture_assert_int_equal(test_context->tctx, - decode_TGS_REP(recv_buf->data, recv_buf->length, &test_context->tgs_rep, &used), 0, + decode_TGS_REP(recv_buf->data, recv_buf->length, + &test_context->tgs_rep, &used), + 0, "decode_TGS_REP failed"); torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); torture_assert_int_equal(test_context->tctx, @@ -381,13 +461,23 @@ static bool torture_krb5_post_recv_self_trust_tgs_req_test(struct torture_krb5_c "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); torture_assert_str_equal(test_context->tctx, test_context->tgs_req.req_body.realm, - test_context->tgs_rep.ticket.realm, "Mismatch in realm between request and ticket response"); - torture_assert_int_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_type, - test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response"); + test_context->tgs_rep.ticket.realm, + "Mismatch in realm between request and ticket response"); torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_type, test_context->tgs_req.req_body.sname->name_type, - test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response"); + "Mismatch in name_type between request and ticket response"); + + /* + * We can confirm that the correct proxy behaviour is + * in use on the KDC by checking the KVNO of the + * krbtgt account returned in the reply. + * + * A packet passed to the full RW DC will not have a + * KVNO in the upper bits, while a packet processed + * locally on the RODC will have these bits filled in + * the msDS-SecondaryKrbTgtNumber + */ if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { torture_assert_int_not_equal(test_context->tctx, *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000, @@ -398,27 +488,102 @@ static bool torture_krb5_post_recv_self_trust_tgs_req_test(struct torture_krb5_c 0, "Unexpecedly got a RODC number in the KVNO"); } free_TGS_REP(&test_context->tgs_rep); - torture_assert(test_context->tctx, test_context->packet_count == 1, "too many packets"); + torture_assert_int_equal(test_context->tctx, + test_context->packet_count, 0, + "too many packets"); test_context->packet_count = 0; test_context->test_stage = TEST_TGS_REQ; free_TGS_REQ(&test_context->tgs_req); return true; } +/* + * TEST_TGS_REQ + * + * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact() + * certain expectations, like that the canonicalize bit is set (this + * test is to force that handling) and that if an enterprise name was + * requested, that it was sent. + * + */ + +static bool torture_krb5_pre_send_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf) +{ + size_t used; + torture_assert_int_equal(test_context->tctx, + decode_TGS_REQ(send_buf->data, send_buf->length, + &test_context->tgs_req, &used), + 0, "decode_TGS_REQ for TEST_TGS_REQ test failed"); + torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, + "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.kdc_options.canonicalize, + false, + "krb5 libs unexpectedly set canonicalize!"); + + if (test_context->test_data->enterprise) { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, + KRB5_NT_ENTERPRISE_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_ENTERPRISE_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); + + } else { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, + KRB5_NT_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->realm, + "Mismatch in realm between request and expected request"); + + } + + *modified_send_buf = *send_buf; + + return true; +} + +/* + * TEST_TGS_REQ - RECV + * + * Confirm that the reply TGS-REP packet for krb5_mk_req_exact(), for + * the actual target service. + * + */ + static bool torture_krb5_post_recv_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) { KRB_ERROR error; size_t used; + /* + * If this account did not have a servicePrincipalName, then + * we expect a errro packet, not a TGS-REQ + */ if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) { - torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); - 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_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, + torture_assert_int_equal(test_context->tctx, + used, recv_buf->length, + "length mismatch"); + 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_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, "Got wrong error.error_code"); } else { torture_assert_int_equal(test_context->tctx, - decode_TGS_REP(recv_buf->data, recv_buf->length, &test_context->tgs_rep, &used), 0, + decode_TGS_REP(recv_buf->data, recv_buf->length, + &test_context->tgs_rep, &used), + 0, "decode_TGS_REP failed"); - torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, + "length mismatch"); torture_assert_int_equal(test_context->tctx, test_context->tgs_rep.pvno, 5, "Got wrong as_rep->pvno"); @@ -442,6 +607,52 @@ static bool torture_krb5_post_recv_tgs_req_test(struct torture_krb5_context *tes } torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); free_TGS_REQ(&test_context->tgs_req); + test_context->test_stage = TEST_DONE; + return true; +} + +/* + * TEST_TGS_REQ_KRBTGT + * + * + * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact() + * for the krbtgt/realm principal meets certain expectations, like + * that the canonicalize bit is not set + * + */ + +static bool torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf) +{ + size_t used; + torture_assert_int_equal(test_context->tctx, + decode_TGS_REQ(send_buf->data, send_buf->length, + &test_context->tgs_req, &used), + 0, "decode_TGS_REQ for TEST_TGS_REQ test failed"); + torture_assert_int_equal(test_context->tctx, + used, send_buf->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.pvno, 5, + "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.kdc_options.canonicalize, + false, + "krb5 libs unexpectedly set canonicalize!"); + + if (test_context->test_data->canonicalize) { + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); + } else { + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->realm, + "Mismatch in realm between request and expected request"); + } + + *modified_send_buf = *send_buf; + test_context->test_stage = TEST_DONE; return true; } @@ -469,49 +680,34 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c krb5_error_code k5ret; bool ok = false; krb5_data modified_send_buf; - size_t used; - + struct torture_krb5_context *test_context = talloc_get_type_abort(data, struct torture_krb5_context); - if (test_context->test_stage == TEST_DONE) { + switch (test_context->test_stage) { + case TEST_DONE: torture_warning(test_context->tctx, "Unexpected outgoing packet from krb5 libs"); return EINVAL; - } - - if (decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used) == 0) { - ok = torture_krb5_pre_send_as_req_test(test_context, send_buf, used, &modified_send_buf); - } else { - k5ret = decode_TGS_REQ(send_buf->data, send_buf->length, &test_context->tgs_req, &used); - if (k5ret == 0) { - if (test_context->test_stage == TEST_AS_REQ) { - test_context->packet_count = 0; - if (test_context->test_data->canonicalize == false && test_context->test_data->enterprise == false - && (test_context->test_data->upper_realm == false || test_context->test_data->netbios_realm == true)) { - if (test_context->test_data->canonicalize == false - || test_context->test_data->enterprise - || (test_context->test_data->upper_realm && test_context->test_data->netbios_realm == false)) { - test_context->test_stage = TEST_TGS_REQ; - } else { - test_context->test_stage = TEST_SELF_TRUST_TGS_REQ; - } - } else { - test_context->test_stage = TEST_TGS_REQ_CANON; - } - } - - if (test_context->test_stage == TEST_TGS_REQ_CANON) { - ok = torture_krb5_pre_send_tgs_req_canon_test(test_context, send_buf, used, &modified_send_buf); - } else if (test_context->test_stage == TEST_SELF_TRUST_TGS_REQ) { - ok = torture_krb5_pre_send_self_trust_tgs_req_test(test_context, send_buf, used, &modified_send_buf); - } else if (test_context->test_stage == TEST_TGS_REQ) { - ok = torture_krb5_pre_send_tgs_req_test(test_context, send_buf, used, &modified_send_buf); - } else if (test_context->test_stage == TEST_TGS_REQ_KRBTGT) { - ok = torture_krb5_pre_send_tgs_req_krbtgt_test(test_context, send_buf, used, &modified_send_buf); - } - } else { - torture_warning(test_context->tctx, "Failed to parse outgoing packet from krb5 libs"); - } + case TEST_AS_REQ: + ok = torture_krb5_pre_send_as_req_test(test_context, send_buf, + &modified_send_buf); + break; + case TEST_TGS_REQ_CANON: + ok = torture_krb5_pre_send_tgs_req_canon_test(test_context, send_buf, + &modified_send_buf); + break; + case TEST_SELF_TRUST_TGS_REQ: + ok = torture_krb5_pre_send_self_trust_tgs_req_test(test_context, send_buf, + &modified_send_buf); + break; + case TEST_TGS_REQ: + ok = torture_krb5_pre_send_tgs_req_test(test_context, send_buf, + &modified_send_buf); + break; + case TEST_TGS_REQ_KRBTGT: + ok = torture_krb5_pre_send_tgs_req_krbtgt_test(test_context, send_buf, + &modified_send_buf); + break; } if (ok == false) { return EINVAL; @@ -524,16 +720,25 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c return k5ret; } - if (test_context->test_stage == TEST_AS_REQ) { + switch (test_context->test_stage) { + case TEST_DONE: + torture_warning(test_context->tctx, "Unexpected outgoing packet from krb5 libs"); + return EINVAL; + case TEST_AS_REQ: ok = torture_krb5_post_recv_as_req_test(test_context, recv_buf); - } else if (test_context->test_stage == TEST_TGS_REQ_CANON) { + break; + case TEST_TGS_REQ_CANON: ok = torture_krb5_post_recv_tgs_req_canon_test(test_context, recv_buf); - } else if (test_context->test_stage == TEST_SELF_TRUST_TGS_REQ) { + break; + case TEST_SELF_TRUST_TGS_REQ: ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf); - } else if (test_context->test_stage == TEST_TGS_REQ) { + break; + case TEST_TGS_REQ: ok = torture_krb5_post_recv_tgs_req_test(test_context, recv_buf); - } else if (test_context->test_stage == TEST_TGS_REQ_KRBTGT) { + break; + case TEST_TGS_REQ_KRBTGT: ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf); + break; } if (ok == false) { return EINVAL; @@ -553,7 +758,7 @@ static int test_context_destructor(struct torture_krb5_context *test_context) static bool torture_krb5_init_context_canon(struct torture_context *tctx, struct test_data *test_data, - struct smb_krb5_context **smb_krb5_context) + struct torture_krb5_context **torture_krb5_context) { const char *host = torture_setting_string(tctx, "host", NULL); krb5_error_code k5ret; @@ -565,7 +770,7 @@ static bool torture_krb5_init_context_canon(struct torture_context *tctx, test_context->test_data = test_data; test_context->tctx = tctx; - k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context); + k5ret = smb_krb5_init_context(test_context, tctx->lp_ctx, &test_context->smb_krb5_context); torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed"); ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST); @@ -575,10 +780,11 @@ static bool torture_krb5_init_context_canon(struct torture_context *tctx, set_sockaddr_port(test_context->server->ai_addr, 88); - k5ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context, + k5ret = krb5_set_send_to_kdc_func(test_context->smb_krb5_context->krb5_context, smb_krb5_send_and_recv_func_canon_override, test_context); torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed"); + *torture_krb5_context = test_context; return true; } @@ -598,7 +804,8 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * char *got_principal_string; char *assertion_message; const char *password = cli_credentials_get_password(cmdline_credentials); - struct smb_krb5_context *smb_krb5_context; + krb5_context k5_context; + struct torture_krb5_context *test_context; bool ok; krb5_creds my_creds; krb5_creds *server_creds; @@ -659,8 +866,9 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * } } - ok = torture_krb5_init_context_canon(tctx, test_data, &smb_krb5_context); + ok = torture_krb5_init_context_canon(tctx, test_data, &test_context); torture_assert(tctx, ok, "torture_krb5_init_context failed"); + k5_context = test_context->smb_krb5_context->krb5_context; if (test_data->upper_realm) { test_data->realm = strupper_talloc(test_data, test_data->realm); @@ -687,11 +895,17 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * * fixed UPPER case realm, but the as-sent username */ if (test_data->canonicalize) { - expected_principal_string = talloc_asprintf(test_data, "%s@%s", test_data->real_username, test_data->real_realm); + expected_principal_string = talloc_asprintf(test_data, + "%s@%s", + test_data->real_username, + test_data->real_realm); } else if (test_data->enterprise) { expected_principal_string = principal_string; } else { - expected_principal_string = talloc_asprintf(test_data, "%s@%s", username, test_data->real_realm); + expected_principal_string = talloc_asprintf(test_data, + "%s@%s", + username, + test_data->real_realm); } if (test_data->enterprise) { @@ -710,47 +924,61 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * } torture_assert_int_equal(tctx, - krb5_parse_name_flags(smb_krb5_context->krb5_context, + krb5_parse_name_flags(k5_context, principal_string, principal_flags, &principal), 0, "krb5_parse_name_flags failed"); torture_assert_int_equal(tctx, - krb5_parse_name_flags(smb_krb5_context->krb5_context, + krb5_parse_name_flags(k5_context, expected_principal_string, expected_principal_flags, &expected_principal), 0, "krb5_parse_name_flags failed"); + /* + * Prepare a AS-REQ and run the TEST_AS_REQ tests + * + */ + + test_context->test_stage = TEST_AS_REQ; + test_context->packet_count = 0; + /* * Set the canonicalize flag if this test requires it */ torture_assert_int_equal(tctx, - krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options), + krb5_get_init_creds_opt_alloc(k5_context, &krb_options), 0, "krb5_get_init_creds_opt_alloc failed"); torture_assert_int_equal(tctx, - krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context, krb_options, test_data->canonicalize), + krb5_get_init_creds_opt_set_canonicalize(k5_context, + krb_options, + test_data->canonicalize), 0, "krb5_get_init_creds_opt_set_canonicalize failed"); torture_assert_int_equal(tctx, - krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context, krb_options, test_data->win2k), + krb5_get_init_creds_opt_set_win2k(k5_context, + krb_options, + test_data->win2k), 0, "krb5_get_init_creds_opt_set_win2k failed"); - k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal, + k5ret = krb5_get_init_creds_password(k5_context, &my_creds, principal, password, NULL, NULL, 0, NULL, krb_options); - krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options); + krb5_get_init_creds_opt_free(k5_context, krb_options); if (test_data->netbios_realm && test_data->upn) { - torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN, "Got wrong error_code from krb5_get_init_creds_password"); + torture_assert_int_equal(tctx, k5ret, + KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN, + "Got wrong error_code from krb5_get_init_creds_password"); /* We can't proceed with more checks */ return true; } else { assertion_message = talloc_asprintf(tctx, "krb5_get_init_creds_password for %s failed: %s", principal_string, - smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx)); + smb_get_krb5_error_message(k5_context, k5ret, tctx)); torture_assert_int_equal(tctx, k5ret, 0, assertion_message); } @@ -760,131 +988,173 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * */ if (test_data->canonicalize == false && test_data->enterprise) { torture_assert_int_equal(tctx, - krb5_principal_get_type(smb_krb5_context->krb5_context, - my_creds.client), KRB5_NT_ENTERPRISE_PRINCIPAL, + krb5_principal_get_type(k5_context, + my_creds.client), + KRB5_NT_ENTERPRISE_PRINCIPAL, "smb_krb5_init_context gave incorrect client->name.name_type"); } else { torture_assert_int_equal(tctx, - krb5_principal_get_type(smb_krb5_context->krb5_context, - my_creds.client), KRB5_NT_PRINCIPAL, + krb5_principal_get_type(k5_context, + my_creds.client), + KRB5_NT_PRINCIPAL, "smb_krb5_init_context gave incorrect client->name.name_type"); } - + torture_assert_int_equal(tctx, - krb5_unparse_name(smb_krb5_context->krb5_context, + krb5_unparse_name(k5_context, my_creds.client, &got_principal_string), 0, "krb5_unparse_name failed"); assertion_message = talloc_asprintf(tctx, "krb5_get_init_creds_password returned a different principal %s to what was expected %s", got_principal_string, expected_principal_string); - krb5_free_unparsed_name(smb_krb5_context->krb5_context, got_principal_string); - - torture_assert(tctx, krb5_principal_compare(smb_krb5_context->krb5_context, + krb5_free_unparsed_name(k5_context, got_principal_string); + + torture_assert(tctx, krb5_principal_compare(k5_context, my_creds.client, expected_principal), assertion_message); - + torture_assert_int_equal(tctx, - krb5_principal_get_type(smb_krb5_context->krb5_context, + krb5_principal_get_type(k5_context, my_creds.server), KRB5_NT_SRV_INST, "smb_krb5_init_context gave incorrect server->name.name_type"); - + torture_assert_int_equal(tctx, - krb5_principal_get_num_comp(smb_krb5_context->krb5_context, + krb5_principal_get_num_comp(k5_context, my_creds.server), 2, "smb_krb5_init_context gave incorrect number of components in my_creds.server->name"); - torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context, - my_creds.server, 0), + torture_assert_str_equal(tctx, + krb5_principal_get_comp_string(k5_context, + my_creds.server, 0), "krbtgt", "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]"); if (test_data->canonicalize || test_data->enterprise) { - torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context, - my_creds.server, 1), + torture_assert_str_equal(tctx, + krb5_principal_get_comp_string(k5_context, + my_creds.server, 1), test_data->real_realm, - + "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]"); } else { - torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context, - my_creds.server, 1), + torture_assert_str_equal(tctx, + krb5_principal_get_comp_string(k5_context, + my_creds.server, 1), test_data->realm, - + "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]"); } - torture_assert_str_equal(tctx, krb5_principal_get_realm(smb_krb5_context->krb5_context, - my_creds.server), + torture_assert_str_equal(tctx, + krb5_principal_get_realm(k5_context, + my_creds.server), test_data->real_realm, "smb_krb5_init_context gave incorrect my_creds.server->realm"); /* Store the result of the 'kinit' above into a memory ccache */ cc_name = talloc_asprintf(tctx, "MEMORY:%s", test_data->test_name); - torture_assert_int_equal(tctx, krb5_cc_resolve(smb_krb5_context->krb5_context, cc_name, + torture_assert_int_equal(tctx, krb5_cc_resolve(k5_context, cc_name, &ccache), 0, "krb5_cc_resolve failed"); - torture_assert_int_equal(tctx, krb5_cc_initialize(smb_krb5_context->krb5_context, + torture_assert_int_equal(tctx, krb5_cc_initialize(k5_context, ccache, my_creds.client), 0, "krb5_cc_initialize failed"); - torture_assert_int_equal(tctx, krb5_cc_store_cred(smb_krb5_context->krb5_context, + torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context, ccache, &my_creds), 0, "krb5_cc_store_cred failed"); - /* Prepare a TGS-REQ */ + /* + * Prepare a TGS-REQ and run the TEST_TGS_REQ_CANON tests + * + * This tests krb5_get_creds behaviour, which allows us to set + * the KRB5_GC_CANONICALIZE option + */ + + test_context->test_stage = TEST_TGS_REQ_CANON; + test_context->packet_count = 0; + torture_assert_int_equal(tctx, - krb5_get_creds_opt_alloc(smb_krb5_context->krb5_context, &opt), + krb5_get_creds_opt_alloc(k5_context, &opt), 0, "krb5_get_creds_opt_alloc"); - krb5_get_creds_opt_add_options(smb_krb5_context->krb5_context, + krb5_get_creds_opt_add_options(k5_context, opt, KRB5_GC_CANONICALIZE); /* Confirm if we can get a ticket to our own name */ - k5ret = krb5_get_creds(smb_krb5_context->krb5_context, opt, ccache, principal, &server_creds); + k5ret = krb5_get_creds(k5_context, opt, ccache, principal, &server_creds); + /* + * In these situations, the code above does not store a + * principal in the credentials cache matching what + * krb5_get_creds() needs, so the test fails. + * + */ if (test_data->canonicalize == false && test_data->enterprise == false && (test_data->upper_realm == false || test_data->netbios_realm == true)) { - torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND, "krb5_get_creds should have failed with KRB5_CC_NOTFOUND"); + torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND, + "krb5_get_creds should have failed with KRB5_CC_NOTFOUND"); } else { assertion_message = talloc_asprintf(tctx, "krb5_get_creds for %s failed: %s", principal_string, - smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx)); + smb_get_krb5_error_message(k5_context, k5ret, + tctx)); - /* We should only be able to get a ticket to our own name if we are a machine account */ + /* + * Only machine accounts (strictly, accounts with a + * servicePrincipalName) can expect this test to succeed + */ if (torture_setting_bool(tctx, "expect_machine_account", false)) { torture_assert_int_equal(tctx, k5ret, 0, assertion_message); - torture_assert_int_equal(tctx, krb5_cc_store_cred(smb_krb5_context->krb5_context, + torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context, ccache, server_creds), 0, "krb5_cc_store_cred failed"); torture_assert_int_equal(tctx, - krb5_free_creds(smb_krb5_context->krb5_context, + krb5_free_creds(k5_context, server_creds), 0, "krb5_free_cred_contents failed"); } else { - torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, assertion_message); + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, + assertion_message); } } + /* - * Confirm gettting a ticket to pass to the server. + * Confirm gettting a ticket to pass to the server, running + * either the TEST_TGS_REQ or TEST_SELF_TRUST_TGS_REQ stage. * * This triggers the client to attempt to get a * cross-realm ticket between the alternate names of * the server, and we need to confirm that behaviour. * - * The cache isn't used because we asked the above not to - * pollute the cache */ - torture_assert_int_equal(tctx, krb5_auth_con_init(smb_krb5_context->krb5_context, &auth_context), + /* + * This tries to guess when the krb5 libs will ask for a + * cross-realm ticket, and when they will just ask the KDC + * directly. + */ + if (test_context->test_data->canonicalize == false + || test_context->test_data->enterprise + || (test_context->test_data->upper_realm + && test_context->test_data->netbios_realm == false)) { + test_context->test_stage = TEST_TGS_REQ; + } else { + test_context->test_stage = TEST_SELF_TRUST_TGS_REQ; + } + + test_context->packet_count = 0; + torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context), 0, "krb5_auth_con_init failed"); in_data.length = 0; - k5ret = krb5_mk_req_exact(smb_krb5_context->krb5_context, + k5ret = krb5_mk_req_exact(k5_context, &auth_context, 0, principal, @@ -894,15 +1164,35 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * assertion_message = talloc_asprintf(tctx, "krb5_mk_req_exact for %s failed: %s", principal_string, - smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx)); + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + + /* + * Only machine accounts (strictly, accounts with a + * servicePrincipalName) can expect this test to succeed + */ if (torture_setting_bool(tctx, "expect_machine_account", false)) { torture_assert_int_equal(tctx, k5ret, 0, assertion_message); } else { - torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, assertion_message); + torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, + assertion_message); } + /* + * Confirm gettting a ticket for the same krbtgt/realm that we + * got back with the initial ticket, running the + * TEST_TGS_REQ_KRBTGT stage. + * + * This triggers the client to attempt to get a + * cross-realm ticket between the alternate names of + * the server, and we need to confirm that behaviour. + * + */ + + test_context->test_stage = TEST_TGS_REQ_KRBTGT; + test_context->packet_count = 0; + in_data.length = 0; - k5ret = krb5_mk_req_exact(smb_krb5_context->krb5_context, + k5ret = krb5_mk_req_exact(k5_context, &auth_context, 0, my_creds.server, @@ -912,12 +1202,12 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * assertion_message = talloc_asprintf(tctx, "krb5_mk_req_exact for %s failed: %s", principal_string, - smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx)); + smb_get_krb5_error_message(k5_context, k5ret, tctx)); torture_assert_int_equal(tctx, k5ret, 0, assertion_message); - krb5_free_principal(smb_krb5_context->krb5_context, principal); - - torture_assert_int_equal(tctx, krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds), + krb5_free_principal(k5_context, principal); + + torture_assert_int_equal(tctx, krb5_free_cred_contents(k5_context, &my_creds), 0, "krb5_free_cred_contents failed"); return true; @@ -942,7 +1232,8 @@ struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx) struct test_data *test_data = talloc_zero(suite, struct test_data); test_data->test_name = name; - test_data->real_realm = strupper_talloc(test_data, cli_credentials_get_realm(cmdline_credentials)); + test_data->real_realm + = strupper_talloc(test_data, cli_credentials_get_realm(cmdline_credentials)); test_data->real_domain = cli_credentials_get_domain(cmdline_credentials); test_data->username = cli_credentials_get_username(cmdline_credentials); test_data->real_username = cli_credentials_get_username(cmdline_credentials); -- 2.1.4 From 67e8b462de1ce777688b9168bca02ff1ef18fe95 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 2 Feb 2015 13:55:25 +1300 Subject: [PATCH 37/60] torture-krb5: Improve the assertions in our KDC tests to be more explicit Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam (cherry picked from commit 4bafb45b096e7246d8186f379a4663b755fb0d37) --- source4/torture/krb5/kdc-canon.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index 8c9543c..ef00e70 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -367,6 +367,17 @@ static bool torture_krb5_post_recv_tgs_req_canon_test(struct torture_krb5_contex test_context->tgs_req.req_body.sname->name_type, "Mismatch in name_type between request and ticket response"); torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.len, + test_context->tgs_req.req_body.sname->name_string.len, + "Mismatch in name_string.len between request and ticket response"); + torture_assert(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.len >= 1, + "name_string.len should be >=1 in ticket response"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.val[0], + test_context->tgs_req.req_body.sname->name_string.val[0], + "Mismatch in name between request and expected request"); + torture_assert_int_equal(test_context->tctx, *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000, 0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO"); free_TGS_REP(&test_context->tgs_rep); -- 2.1.4 From 23900a2decf18e0b92457684c8c0fbb518d07aed Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 2 Feb 2015 15:01:40 +1300 Subject: [PATCH 38/60] torture-krb5: Add tests for AS-REQ to our own name This allows us to probe the behaviour of AS-REQ requests against a principal other than krbtgt/ This alos allows verification of behaviour of principals of type KRB5_NT_ENTERPRISE_PRINCIPAL Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 5fe76cc02a1f09213b4b4a65dc53ad4d2ce97c86) --- source4/torture/krb5/kdc-canon.c | 195 +++++++++++++++++++++++++++++++++------ 1 file changed, 166 insertions(+), 29 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index ef00e70..44f8d91 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -62,6 +62,7 @@ enum test_stage { TEST_SELF_TRUST_TGS_REQ, TEST_TGS_REQ, TEST_TGS_REQ_KRBTGT, + TEST_AS_REQ_SELF, TEST_DONE }; @@ -80,7 +81,7 @@ struct torture_krb5_context { /* - * TEST_AS_REQ - SEND + * TEST_AS_REQ and TEST_AS_REQ_SELF - SEND * * Confirm that the outgoing packet meets certain expectations. This * should be extended to further assert the correct and expected @@ -98,11 +99,13 @@ static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_ const krb5_data *send_buf, krb5_data *modified_send_buf) { + AS_REQ mod_as_req; krb5_error_code k5ret; size_t used; torture_assert_int_equal(test_context->tctx, decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0, "decode_AS_REQ for TEST_AS_REQ failed"); + mod_as_req = test_context->as_req; torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch"); torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno"); @@ -131,21 +134,33 @@ static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_ /* Force off canonicalize that was forced on by the krb5 libs */ if (test_context->test_data->canonicalize == false && test_context->test_data->enterprise) { - test_context->as_req.req_body.kdc_options.canonicalize = false; + mod_as_req.req_body.kdc_options.canonicalize = false; + } + + if (test_context->test_stage == TEST_AS_REQ_SELF) { + /* + * Force the server name to match the client name, + * including the name type. This isn't possible with + * the krb5 client libs alone + */ + mod_as_req.req_body.sname = test_context->as_req.req_body.cname; } ASN1_MALLOC_ENCODE(AS_REQ, modified_send_buf->data, modified_send_buf->length, - &test_context->as_req, &used, k5ret); + &mod_as_req, &used, k5ret); torture_assert_int_equal(test_context->tctx, k5ret, 0, "encode_AS_REQ failed"); - torture_assert_int_equal(test_context->tctx, used, send_buf->length, - "re-encode length mismatch"); + + if (test_context->test_stage != TEST_AS_REQ_SELF) { + torture_assert_int_equal(test_context->tctx, used, send_buf->length, + "re-encode length mismatch"); + } return true; } /* - * TEST_AS_REQ - RECV + * TEST_AS_REQ and TEST_AS_REQ_SELF- RECV * * Confirm that the reply packet from the KDC meets certain * expectations as part of TEST_AS_REQ. This uses a packet count to @@ -177,17 +192,37 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test "length mismatch"); torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); - if (test_context->test_data->netbios_realm && test_context->test_data->upn) { - torture_assert_int_equal(test_context->tctx, - error.error_code, - KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, - "Got wrong error.error_code"); - } else { - torture_assert_int_equal(test_context->tctx, - error.error_code, - KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE, - "Got wrong error.error_code"); + if (test_context->test_stage == TEST_AS_REQ) { + if (test_context->test_data->netbios_realm && test_context->test_data->upn) { + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } else { + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } + } else if (test_context->test_stage == TEST_AS_REQ_SELF) { + if ((torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false + || (test_context->test_data->upn == true)) + && error.error_code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE) { + /* + * IGNORE + * + * This case is because Samba's Heimdal KDC + * checks server and client accounts before + * checking for pre-authentication. + */ + } else { + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } } + free_KRB_ERROR(&error); } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) && (test_context->packet_count == 1)) { @@ -204,10 +239,19 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); - torture_assert_int_equal(test_context->tctx, - error.error_code, - KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, - "Got wrong error.error_code"); + if (test_context->test_stage != TEST_AS_REQ_SELF + || ((torture_setting_bool(test_context->tctx, "expect_machine_account", false) + && (test_context->test_data->upn == false)))) { + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } else { + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } free_KRB_ERROR(&error); } else { /* @@ -239,7 +283,8 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test * locally on the RODC will have these bits filled in * the msDS-SecondaryKrbTgtNumber */ - if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { + if (test_context->test_stage == TEST_AS_REQ + && torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { torture_assert_int_not_equal(test_context->tctx, *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, 0, "Did not get a RODC number in the KVNO"); @@ -719,6 +764,10 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c ok = torture_krb5_pre_send_tgs_req_krbtgt_test(test_context, send_buf, &modified_send_buf); break; + case TEST_AS_REQ_SELF: + ok = torture_krb5_pre_send_as_req_test(test_context, send_buf, + &modified_send_buf); + break; } if (ok == false) { return EINVAL; @@ -750,6 +799,9 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c case TEST_TGS_REQ_KRBTGT: ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf); break; + case TEST_AS_REQ_SELF: + ok = torture_krb5_post_recv_as_req_test(test_context, recv_buf); + break; } if (ok == false) { return EINVAL; @@ -805,7 +857,6 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * krb5_error_code k5ret; krb5_get_init_creds_opt *krb_options = NULL; struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data); - char *username; krb5_principal principal; krb5_principal expected_principal; char *principal_string; @@ -887,12 +938,12 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * test_data->realm = strlower_talloc(test_data, test_data->realm); } if (test_data->upper_username) { - username = strupper_talloc(test_data, test_data->username); + test_data->username = strupper_talloc(test_data, test_data->username); } else { - username = talloc_strdup(test_data, test_data->username); + test_data->username = talloc_strdup(test_data, test_data->username); } - principal_string = talloc_asprintf(test_data, "%s@%s", username, test_data->realm); + principal_string = talloc_asprintf(test_data, "%s@%s", test_data->username, test_data->realm); /* * If we are set to canonicalize, we get back the fixed UPPER @@ -915,7 +966,7 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * } else { expected_principal_string = talloc_asprintf(test_data, "%s@%s", - username, + test_data->username, test_data->real_realm); } @@ -977,7 +1028,6 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * k5ret = krb5_get_init_creds_password(k5_context, &my_creds, principal, password, NULL, NULL, 0, NULL, krb_options); - krb5_get_init_creds_opt_free(k5_context, krb_options); if (test_data->netbios_realm && test_data->upn) { torture_assert_int_equal(tctx, k5ret, @@ -1119,7 +1169,8 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * * Only machine accounts (strictly, accounts with a * servicePrincipalName) can expect this test to succeed */ - if (torture_setting_bool(tctx, "expect_machine_account", false)) { + if (torture_setting_bool(tctx, "expect_machine_account", false) + && (test_data->enterprise || test_data->upn == false)) { torture_assert_int_equal(tctx, k5ret, 0, assertion_message); torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context, ccache, server_creds), @@ -1181,7 +1232,7 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * * Only machine accounts (strictly, accounts with a * servicePrincipalName) can expect this test to succeed */ - if (torture_setting_bool(tctx, "expect_machine_account", false)) { + if (torture_setting_bool(tctx, "expect_machine_account", false) && (test_data->enterprise || test_data->upn == false)) { torture_assert_int_equal(tctx, k5ret, 0, assertion_message); } else { torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, @@ -1216,7 +1267,93 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * smb_get_krb5_error_message(k5_context, k5ret, tctx)); torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + /* + * Confirm gettting a ticket for our own principal that we + * got back with the initial ticket, running the + * TEST_AS_REQ_SELF stage. + * + */ + test_context->test_stage = TEST_AS_REQ_SELF; + test_context->packet_count = 0; + + k5ret = krb5_get_init_creds_password(k5_context, &my_creds, principal, + password, NULL, NULL, 0, + principal_string, krb_options); + + if (torture_setting_bool(test_context->tctx, "expect_machine_account", false) && (test_data->upn == false)) { + assertion_message = talloc_asprintf(tctx, + "krb5_get_init_creds_password for %s failed: %s", + principal_string, + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + } else { + assertion_message = talloc_asprintf(tctx, + "Got wrong error_code from krb5_get_init_creds_password, expected KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN trying to get a ticket to %s for %s", principal_string, principal_string); + torture_assert_int_equal(tctx, k5ret, + KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, + assertion_message); + /* We can't proceed with more checks */ + return true; + } + + /* + * Assert that the reply was with the correct type of + * principal, depending on the flags we set + */ + if (test_data->canonicalize == false && test_data->enterprise) { + torture_assert_int_equal(tctx, + krb5_principal_get_type(k5_context, + my_creds.client), + KRB5_NT_ENTERPRISE_PRINCIPAL, + "smb_krb5_init_context gave incorrect client->name.name_type"); + torture_assert_int_equal(tctx, + krb5_principal_get_type(k5_context, + my_creds.server), + KRB5_NT_ENTERPRISE_PRINCIPAL, + "smb_krb5_init_context gave incorrect server->name.name_type"); + } else { + torture_assert_int_equal(tctx, + krb5_principal_get_type(k5_context, + my_creds.client), + KRB5_NT_PRINCIPAL, + "smb_krb5_init_context gave incorrect client->name.name_type"); + torture_assert_int_equal(tctx, + krb5_principal_get_type(k5_context, + my_creds.server), + KRB5_NT_PRINCIPAL, + "smb_krb5_init_context gave incorrect server->name.name_type"); + } + + torture_assert_int_equal(tctx, + krb5_unparse_name(k5_context, + my_creds.client, &got_principal_string), 0, + "krb5_unparse_name failed"); + + assertion_message = talloc_asprintf(tctx, + "krb5_get_init_creds_password returned a different principal %s to what was expected %s", + got_principal_string, expected_principal_string); + krb5_free_unparsed_name(k5_context, got_principal_string); + + torture_assert(tctx, krb5_principal_compare(k5_context, + my_creds.client, expected_principal), + assertion_message); + + torture_assert_int_equal(tctx, + krb5_unparse_name(k5_context, + my_creds.client, &got_principal_string), 0, + "krb5_unparse_name failed"); + + assertion_message = talloc_asprintf(tctx, + "krb5_get_init_creds_password returned a different server principal %s to what was expected %s", + got_principal_string, expected_principal_string); + krb5_free_unparsed_name(k5_context, got_principal_string); + + torture_assert(tctx, krb5_principal_compare(k5_context, + my_creds.client, expected_principal), + assertion_message); + krb5_free_principal(k5_context, principal); + krb5_get_init_creds_opt_free(k5_context, krb_options); torture_assert_int_equal(tctx, krb5_free_cred_contents(k5_context, &my_creds), 0, "krb5_free_cred_contents failed"); -- 2.1.4 From 5f51cf17b8ed8a5e623446a1a4336b882da98811 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 3 Feb 2015 11:36:49 +1300 Subject: [PATCH 39/60] selftest: Run krb5.kdc with an account that has a UPN and an SPN Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam (cherry picked from commit 32e2b75a96b45d64d6059240ef2e8da924c6c84e) --- selftest/target/Samba4.pm | 2 ++ source4/selftest/tests.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 85a328c..3cd3c39 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -788,6 +788,8 @@ sub provision_raw_step2($$$) changetype: modify replace: userPrincipalName userPrincipalName: testallowed_upn\@$ctx->{realm} +replace: servicePrincipalName +servicePrincipalName: host/testallowed - "; close(LDIF); diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index bddba41..5f2d9f1 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -565,7 +565,7 @@ for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:expect_machine_account=true'] + extra_options, "samba4.krb5.kdc with machine account") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:krb5-upn=testallowed_upn@$REALM'] + extra_options, + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:expect_machine_account=true', '--option=torture:krb5-upn=testallowed_upn@$REALM'] + extra_options, "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") # TODO: Verifying the databases really should be a part of the -- 2.1.4 From f648bbfc4c69e274d9bc37b748ad00388e5ef18e Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 3 Feb 2015 15:22:52 +1300 Subject: [PATCH 40/60] torture-krb5: Further test improvements to cover KRB5_GC_CANONICALIZE on krbtgt/ This covers more of the protocol, and confirms which tests actually send network packets (and so actually run the assertions in the send_and_recv handlers. Signed-off-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit e05ad3500fb501f87c1eb77a1c13c5c237f02b3d) --- source4/torture/krb5/kdc-canon.c | 259 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 243 insertions(+), 16 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index 44f8d91..8dbfd93 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -58,6 +58,7 @@ struct test_data { enum test_stage { TEST_AS_REQ = 0, + TEST_TGS_REQ_KRBTGT_CANON, TEST_TGS_REQ_CANON, TEST_SELF_TRUST_TGS_REQ, TEST_TGS_REQ, @@ -301,6 +302,128 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test } /* + * TEST_TGS_REQ_KRBTGT_CANON + * + * + * Confirm that the outgoing TGS-REQ packet from krb5_get_creds() + * for the krbtgt/realm principal meets certain expectations, like + * that the canonicalize bit is not set + * + */ + +static bool torture_krb5_pre_send_tgs_req_krbtgt_canon_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf) +{ + size_t used; + torture_assert_int_equal(test_context->tctx, + decode_TGS_REQ(send_buf->data, send_buf->length, + &test_context->tgs_req, &used), + 0, "decode_TGS_REQ for TEST_TGS_REQ test failed"); + torture_assert_int_equal(test_context->tctx, + used, send_buf->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.pvno, 5, + "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.kdc_options.canonicalize, + true, + "krb5 libs unexpectedly did not set canonicalize!"); + + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, + KRB5_NT_PRINCIPAL, + "Mismatch in name_type between request and expected request"); + + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); + + *modified_send_buf = *send_buf; + return true; +} + +/* + * TEST_TGS_REQ_KRBTGT_CANON + * + * Confirm that the reply TGS-REP packet for krb5_get_creds() + * where the client is behaving as if this is a cross-realm trust due + * to case or netbios vs dns name differences meets certain + * expectations, while canonicalize is set + * + */ + +static bool torture_krb5_post_recv_tgs_req_krbtgt_canon_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) +{ + size_t used; + torture_assert_int_equal(test_context->tctx, + decode_TGS_REP(recv_buf->data, recv_buf->length, + &test_context->tgs_rep, &used), + 0, + "decode_TGS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert(test_context->tctx, + test_context->tgs_rep.ticket.enc_part.kvno, + "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->tgs_rep.ticket.realm, + "Mismatch in realm between request and ticket response"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.realm, + test_context->test_data->real_realm, + "Mismatch in realm between ticket response and expected ticket response"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_type, + KRB5_NT_SRV_INST, + "Mismatch in name_type between ticket response and expected value of KRB5_NT_SRV_INST"); + + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.len, + 2, + "Mismatch in name_type between ticket response and expected value, expected krbtgt/REALM@REALM"); + + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.val[0], "krbtgt", + "Mismatch in name between reponse and expected response, expected krbtgt"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.val[1], test_context->test_data->real_realm, + "Mismatch in realm part of krbtgt/ in expected response, expected krbtgt/REALM@REALM"); + + /* + * We can confirm that the correct proxy behaviour is + * in use on the KDC by checking the KVNO of the + * krbtgt account returned in the reply. + * + * A packet passed to the full RW DC will not have a + * KVNO in the upper bits, while a packet processed + * locally on the RODC will have these bits filled in + * the msDS-SecondaryKrbTgtNumber + */ + if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { + torture_assert_int_not_equal(test_context->tctx, + *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Did not get a RODC number in the KVNO"); + } else { + torture_assert_int_equal(test_context->tctx, + *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Unexpecedly got a RODC number in the KVNO"); + } + free_TGS_REP(&test_context->tgs_rep); + torture_assert(test_context->tctx, + test_context->packet_count < 2, + "too many packets"); + free_TGS_REQ(&test_context->tgs_req); + return true; +} + +/* * TEST_TGS_REQ_CANON * * Confirm that the outgoing TGS-REQ packet from krb5_get_creds @@ -695,17 +818,10 @@ static bool torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_contex false, "krb5 libs unexpectedly set canonicalize!"); - if (test_context->test_data->canonicalize) { - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, - test_context->test_data->real_realm, - "Mismatch in realm between request and expected request"); - } else { - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.realm, - test_context->test_data->realm, - "Mismatch in realm between request and expected request"); - } + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->realm, + "Mismatch in realm between request and expected request"); *modified_send_buf = *send_buf; test_context->test_stage = TEST_DONE; @@ -748,6 +864,10 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c ok = torture_krb5_pre_send_as_req_test(test_context, send_buf, &modified_send_buf); break; + case TEST_TGS_REQ_KRBTGT_CANON: + ok = torture_krb5_pre_send_tgs_req_krbtgt_canon_test(test_context, send_buf, + &modified_send_buf); + break; case TEST_TGS_REQ_CANON: ok = torture_krb5_pre_send_tgs_req_canon_test(test_context, send_buf, &modified_send_buf); @@ -787,6 +907,9 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c case TEST_AS_REQ: ok = torture_krb5_post_recv_as_req_test(test_context, recv_buf); break; + case TEST_TGS_REQ_KRBTGT_CANON: + ok = torture_krb5_post_recv_tgs_req_krbtgt_canon_test(test_context, recv_buf); + break; case TEST_TGS_REQ_CANON: ok = torture_krb5_post_recv_tgs_req_canon_test(test_context, recv_buf); break; @@ -858,8 +981,10 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * krb5_get_init_creds_opt *krb_options = NULL; struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data); krb5_principal principal; + krb5_principal krbtgt_other; krb5_principal expected_principal; char *principal_string; + char *krbtgt_other_string; int principal_flags; char *expected_principal_string; int expected_principal_flags; @@ -1043,6 +1168,10 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * torture_assert_int_equal(tctx, k5ret, 0, assertion_message); } + torture_assert(tctx, + test_context->packet_count > 1, + "Expected krb5_get_init_creds_password to send more packets"); + /* * Assert that the reply was with the correct type of * principal, depending on the flags we set @@ -1128,6 +1257,82 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * 0, "krb5_cc_store_cred failed"); /* + * Prepare a TGS-REQ and run the TEST_TGS_REQ_KRBTGT_CANON tests + * + * This tests krb5_get_creds behaviour, which allows us to set + * the KRB5_GC_CANONICALIZE option against the krbtgt/ principal + */ + + krbtgt_other_string = talloc_asprintf(test_data, "krbtgt/%s@%s", test_data->real_domain, test_data->real_realm); + torture_assert_int_equal(tctx, + krb5_make_principal(k5_context, &krbtgt_other, + test_data->real_realm, "krbtgt", + test_data->real_domain, NULL), + 0, "krb5_make_principal failed"); + + test_context->test_stage = TEST_TGS_REQ_KRBTGT_CANON; + test_context->packet_count = 0; + + torture_assert_int_equal(tctx, + krb5_get_creds_opt_alloc(k5_context, &opt), + 0, "krb5_get_creds_opt_alloc"); + + krb5_get_creds_opt_add_options(k5_context, + opt, + KRB5_GC_CANONICALIZE); + + krb5_get_creds_opt_add_options(k5_context, + opt, + KRB5_GC_NO_STORE); + + /* Confirm if we can get a ticket krbtgt/realm that we got back with the initial kinit */ + k5ret = krb5_get_creds(k5_context, opt, ccache, krbtgt_other, &server_creds); + + if (test_data->canonicalize == false && test_data->enterprise == false + && test_data->netbios_realm && test_data->upper_realm) { + /* + * In these situations, the code above does store a + * principal in the credentials cache matching what + * krb5_get_creds() needs, so the test succeds, with no packets. + * + */ + assertion_message = talloc_asprintf(tctx, + "krb5_get_creds for %s failed with: %s", + krbtgt_other_string, + smb_get_krb5_error_message(k5_context, k5ret, + tctx)); + + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + torture_assert_int_equal(tctx, + test_context->packet_count, + 0, "Expected krb5_get_creds not to send packets"); + } else if (test_data->canonicalize == false && test_data->enterprise == false + && (test_data->upper_realm == false || test_data->netbios_realm == true)) { + torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND, + "krb5_get_creds should have failed with KRB5_CC_NOTFOUND"); + } else { + + /* + * In these situations, the code above does not store a + * principal in the credentials cache matching what + * krb5_get_creds() needs without talking to the KDC, so the + * test fails with looping detected because when we set + * canonicalize we confuse the client libs. + * + */ + assertion_message = talloc_asprintf(tctx, + "krb5_get_creds for %s should have failed with looping detected: %s", + krbtgt_other_string, + smb_get_krb5_error_message(k5_context, k5ret, + tctx)); + + torture_assert_int_equal(tctx, k5ret, KRB5_GET_IN_TKT_LOOP, assertion_message); + torture_assert_int_equal(tctx, + test_context->packet_count, + 2, "Expected krb5_get_creds to send packets"); + } + + /* * Prepare a TGS-REQ and run the TEST_TGS_REQ_CANON tests * * This tests krb5_get_creds behaviour, which allows us to set @@ -1145,6 +1350,10 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * opt, KRB5_GC_CANONICALIZE); + krb5_get_creds_opt_add_options(k5_context, + opt, + KRB5_GC_NO_STORE); + /* Confirm if we can get a ticket to our own name */ k5ret = krb5_get_creds(k5_context, opt, ccache, principal, &server_creds); @@ -1185,6 +1394,10 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, assertion_message); } + + torture_assert_int_equal(tctx, + test_context->packet_count, + 1, "Expected krb5_get_creds to send packets"); } /* @@ -1222,7 +1435,6 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * principal, &in_data, ccache, &enc_ticket); - assertion_message = talloc_asprintf(tctx, "krb5_mk_req_exact for %s failed: %s", principal_string, @@ -1240,14 +1452,21 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * } /* + * Only in these cases would the above code have needed to + * send packets to the network + */ + if (test_data->canonicalize == false && test_data->enterprise == false + && (test_data->upper_realm == false || test_data->netbios_realm == true)) { + torture_assert(tctx, + test_context->packet_count > 0, + "Expected krb5_get_creds to send packets"); + } + + /* * Confirm gettting a ticket for the same krbtgt/realm that we * got back with the initial ticket, running the * TEST_TGS_REQ_KRBTGT stage. * - * This triggers the client to attempt to get a - * cross-realm ticket between the alternate names of - * the server, and we need to confirm that behaviour. - * */ test_context->test_stage = TEST_TGS_REQ_KRBTGT; @@ -1286,12 +1505,20 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * principal_string, smb_get_krb5_error_message(k5_context, k5ret, tctx)); torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + torture_assert(tctx, + test_context->packet_count >= 2, + "Expected krb5_get_init_creds_password to send more packets"); + } else { assertion_message = talloc_asprintf(tctx, "Got wrong error_code from krb5_get_init_creds_password, expected KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN trying to get a ticket to %s for %s", principal_string, principal_string); torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, assertion_message); + torture_assert(tctx, + test_context->packet_count >= 1, + "Expected krb5_get_init_creds_password to send more packets"); + /* We can't proceed with more checks */ return true; } -- 2.1.4 From 1d985528a0728f051d41fb3a7c7347bcc456b0fe Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 3 Feb 2015 15:51:41 +1300 Subject: [PATCH 41/60] torture-krb5: Add additional assertions for non-canon TGS-REP This confirms that the KDC does not modify the returned principal in a TGS-REP unconditionally. Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit 60c791339122b8b3f9be5bc085badd14e2ca6058) --- source4/torture/krb5/kdc-canon.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index 8dbfd93..d18905e 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -647,6 +647,15 @@ static bool torture_krb5_post_recv_self_trust_tgs_req_test(struct torture_krb5_c test_context->tgs_req.req_body.sname->name_type, "Mismatch in name_type between request and ticket response"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.len, 2, + "Mismatch in name between request and expected request, expected krbtgt/realm"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.val[0], "krbtgt", + "Mismatch in name between request and expected request, expected krbtgt"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.val[1], test_context->test_data->realm, + "Mismatch in realm part of cross-realm request principal between response and expected request"); /* * We can confirm that the correct proxy behaviour is * in use on the KDC by checking the KVNO of the -- 2.1.4 From 8c1f7094eebba9c8c6d4cadcc6047a5aaabdd57c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 5 Feb 2015 10:11:42 +1300 Subject: [PATCH 42/60] torture-krb5: Split out TEST_AS_REQ_SELF recv testing routine This duplicates more code, but re-using the callbacks makes it much, much harder to debug Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam (cherry picked from commit 0a4da2fc97de2ce81c168820f2f5a792388d5bc5) --- source4/torture/krb5/kdc-canon.c | 236 ++++++++++++++++++++++++++++++--------- 1 file changed, 186 insertions(+), 50 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index d18905e..c904a36 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -161,7 +161,7 @@ static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_ } /* - * TEST_AS_REQ and TEST_AS_REQ_SELF- RECV + * TEST_AS_REQ - RECV * * Confirm that the reply packet from the KDC meets certain * expectations as part of TEST_AS_REQ. This uses a packet count to @@ -176,6 +176,7 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test KRB_ERROR error; size_t used; if (test_context->packet_count == 0) { + krb5_error_code k5ret; /* * The client libs obtain the salt by attempting to * authenticate without pre-authentication and getting @@ -185,43 +186,48 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test * has an incorrect principal, we check we get the * correct error. */ - torture_assert_int_equal(test_context->tctx, - decode_KRB_ERROR(recv_buf->data, recv_buf->length, - &error, &used), 0, - "decode_AS_REP failed"); + k5ret = decode_KRB_ERROR(recv_buf->data, recv_buf->length, + &error, &used); + if (k5ret != 0) { + AS_REP as_rep; + k5ret = decode_AS_REP(recv_buf->data, recv_buf->length, + &as_rep, &used); + if (k5ret == 0) { + if (test_context->test_data->netbios_realm && test_context->test_data->upn) { + torture_assert(test_context->tctx, false, + "expected to get a KRB_ERROR packet with " + "KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN, got valid AS-REP"); + } else { + torture_assert(test_context->tctx, false, + "expected to get a KRB_ERROR packet with " + "KRB5KDC_ERR_PREAUTH_REQUIRED, got valid AS-REP"); + } + } else { + if (test_context->test_data->netbios_realm && test_context->test_data->upn) { + torture_assert(test_context->tctx, false, + "unable to decode as KRB-ERROR or AS-REP, " + "expected to get a KRB_ERROR packet with KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN"); + } else { + torture_assert(test_context->tctx, false, + "unable to decode as KRB-ERROR or AS-REP, " + "expected to get a KRB_ERROR packet with KRB5KDC_ERR_PREAUTH_REQUIRED"); + } + } + } torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch"); torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); - if (test_context->test_stage == TEST_AS_REQ) { - if (test_context->test_data->netbios_realm && test_context->test_data->upn) { - torture_assert_int_equal(test_context->tctx, - error.error_code, - KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, - "Got wrong error.error_code"); - } else { - torture_assert_int_equal(test_context->tctx, - error.error_code, - KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE, - "Got wrong error.error_code"); - } - } else if (test_context->test_stage == TEST_AS_REQ_SELF) { - if ((torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false - || (test_context->test_data->upn == true)) - && error.error_code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE) { - /* - * IGNORE - * - * This case is because Samba's Heimdal KDC - * checks server and client accounts before - * checking for pre-authentication. - */ - } else { - torture_assert_int_equal(test_context->tctx, - error.error_code, - KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE, - "Got wrong error.error_code"); - } + if (test_context->test_data->netbios_realm && test_context->test_data->upn) { + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } else { + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); } free_KRB_ERROR(&error); @@ -240,19 +246,10 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno"); - if (test_context->test_stage != TEST_AS_REQ_SELF - || ((torture_setting_bool(test_context->tctx, "expect_machine_account", false) - && (test_context->test_data->upn == false)))) { - torture_assert_int_equal(test_context->tctx, - error.error_code, - KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, - "Got wrong error.error_code"); - } else { - torture_assert_int_equal(test_context->tctx, - error.error_code, - KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, - "Got wrong error.error_code"); - } + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); free_KRB_ERROR(&error); } else { /* @@ -284,8 +281,7 @@ static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test * locally on the RODC will have these bits filled in * the msDS-SecondaryKrbTgtNumber */ - if (test_context->test_stage == TEST_AS_REQ - && torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { + if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) { torture_assert_int_not_equal(test_context->tctx, *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, 0, "Did not get a RODC number in the KVNO"); @@ -837,6 +833,146 @@ static bool torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_contex return true; } +/* + * TEST_AS_REQ_SELF - RECV + * + * Confirm that the reply packet from the KDC meets certain + * expectations as part of TEST_AS_REQ. This uses a packet count to + * work out what packet we are up to in the multiple exchanged + * triggerd by krb5_get_init_creds_password(). + * + */ + +static bool torture_krb5_post_recv_as_req_self_test(struct torture_krb5_context *test_context, + const krb5_data *recv_buf) +{ + KRB_ERROR error; + size_t used; + if (test_context->packet_count == 0) { + krb5_error_code k5ret; + /* + * The client libs obtain the salt by attempting to + * authenticate without pre-authentication and getting + * the correct salt with the + * KRB5KDC_ERR_PREAUTH_REQUIRED error. If we are in + * the test (netbios_realm && upn) that deliberatly + * has an incorrect principal, we check we get the + * correct error. + */ + k5ret = decode_KRB_ERROR(recv_buf->data, recv_buf->length, + &error, &used); + if (k5ret != 0) { + AS_REP as_rep; + k5ret = decode_AS_REP(recv_buf->data, recv_buf->length, + &as_rep, &used); + if (k5ret == 0) { + if (torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false + || (test_context->test_data->upn == true)) { + torture_assert(test_context->tctx, false, + "expected to get a KRB_ERROR packet with " + "KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN or KRB5KDC_ERR_PREAUTH_REQUIRED, got valid AS-REP"); + } else { + torture_assert(test_context->tctx, false, + "expected to get a KRB_ERROR packet with " + "KRB5KDC_ERR_PREAUTH_REQUIRED, got valid AS-REP"); + } + } else { + if (torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false + || (test_context->test_data->upn == true)) { + torture_assert(test_context->tctx, false, + "unable to decode as KRB-ERROR or AS-REP, " + "expected to get a KRB_ERROR packet with KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN or KRB5KDC_ERR_PREAUTH_REQUIRED"); + } else { + torture_assert(test_context->tctx, false, + "unable to decode as KRB-ERROR or AS-REP, " + "expected to get a KRB_ERROR packet with KRB5KDC_ERR_PREAUTH_REQUIRED"); + } + } + } + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, error.pvno, 5, + "Got wrong error.pvno"); + if ((torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false + || (test_context->test_data->upn == true)) + && error.error_code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE) { + /* + * IGNORE + * + * This case is because Samba's Heimdal KDC + * checks server and client accounts before + * checking for pre-authentication. + */ + } else { + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } + + free_KRB_ERROR(&error); + } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) + && (test_context->packet_count == 1)) { + /* + * The Windows 2012R2 KDC will always respond with + * KRB5KRB_ERR_RESPONSE_TOO_BIG over UDP as the ticket + * won't fit, because of the PAC. (It appears to do + * this always, even if it will). This triggers the + * client to try again over TCP. + */ + torture_assert_int_equal(test_context->tctx, + used, recv_buf->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, + error.pvno, 5, + "Got wrong error.pvno"); + if ((torture_setting_bool(test_context->tctx, "expect_machine_account", false) + && (test_context->test_data->upn == false))) { + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } else { + torture_assert_int_equal(test_context->tctx, + error.error_code, + KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE, + "Got wrong error.error_code"); + } + free_KRB_ERROR(&error); + } else { + /* + * Finally the successful packet. + */ + torture_assert_int_equal(test_context->tctx, + decode_AS_REP(recv_buf->data, recv_buf->length, + &test_context->as_rep, &used), 0, + "decode_AS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->as_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert(test_context->tctx, + test_context->as_rep.ticket.enc_part.kvno, + "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + + /* + * We do not expect an RODC number here in the KVNO, + * as this is a ticket to the user's own account. + */ + torture_assert_int_equal(test_context->tctx, + *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Unexpecedly got a RODC number in the KVNO"); + free_AS_REP(&test_context->as_rep); + } + torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets"); + free_AS_REQ(&test_context->as_req); + return true; +} + /* * This function is set in torture_krb5_init_context_canon as krb5 * send_and_recv function. This allows us to override what server the @@ -932,7 +1068,7 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf); break; case TEST_AS_REQ_SELF: - ok = torture_krb5_post_recv_as_req_test(test_context, recv_buf); + ok = torture_krb5_post_recv_as_req_self_test(test_context, recv_buf); break; } if (ok == false) { -- 2.1.4 From 5e398255547382c7111145009cc06057abecd246 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 5 Feb 2015 15:49:40 +1300 Subject: [PATCH 43/60] torture-krb5: Add test in for normal TGS-REQ For example, host/server Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam (cherry picked from commit 52b74a4eaf82b892bddabcd70a3bc38c4fdc8410) --- source4/torture/krb5/kdc-canon.c | 173 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 1 deletion(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index c904a36..f6b7815 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -54,6 +54,8 @@ struct test_data { bool win2k; bool upn; bool other_upn_suffix; + const char *krb5_service; + const char *krb5_hostname; }; enum test_stage { @@ -63,6 +65,7 @@ enum test_stage { TEST_SELF_TRUST_TGS_REQ, TEST_TGS_REQ, TEST_TGS_REQ_KRBTGT, + TEST_TGS_REQ_HOST, TEST_AS_REQ_SELF, TEST_DONE }; @@ -834,6 +837,113 @@ static bool torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_contex } /* + * TEST_TGS_REQ_HOST + * + * + * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact() + * for the krbtgt/realm principal meets certain expectations, like + * that the canonicalize bit is not set + * + */ + +static bool torture_krb5_pre_send_tgs_req_host_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf) +{ + size_t used; + torture_assert_int_equal(test_context->tctx, + decode_TGS_REQ(send_buf->data, send_buf->length, + &test_context->tgs_req, &used), + 0, "decode_TGS_REQ for TEST_TGS_REQ test failed"); + torture_assert_int_equal(test_context->tctx, + used, send_buf->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.pvno, 5, + "Got wrong as_req->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.kdc_options.canonicalize, + true, + "krb5 libs unexpectedly did not set canonicalize!"); + + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.len, 2, + "Mismatch in name between request and expected request, expected krbtgt/realm"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[0], + test_context->test_data->krb5_service, + "Mismatch in name between request and expected request, expected krbtgt"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[1], + test_context->test_data->krb5_hostname, + "Mismatch in realm part of cross-realm request principal between request and expected request"); + + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.realm, + test_context->test_data->real_realm, + "Mismatch in realm between request and expected request"); + + *modified_send_buf = *send_buf; + return true; +} + +/* + * TEST_TGS_REQ_HOST - RECV + * + * Confirm that the reply TGS-REP packet for krb5_mk_req(), for + * the actual target service, as a SPN, not a any other name type. + * + */ + +static bool torture_krb5_post_recv_tgs_req_host_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf) +{ + size_t used; + torture_assert_int_equal(test_context->tctx, + decode_TGS_REP(recv_buf->data, recv_buf->length, + &test_context->tgs_rep, &used), + 0, + "decode_TGS_REP failed"); + torture_assert_int_equal(test_context->tctx, used, recv_buf->length, + "length mismatch"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.pvno, 5, + "Got wrong as_rep->pvno"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.tkt_vno, 5, + "Got wrong as_rep->ticket.tkt_vno"); + torture_assert(test_context->tctx, + test_context->tgs_rep.ticket.enc_part.kvno, + "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.realm, + test_context->test_data->real_realm, + "Mismatch in realm between ticket response and expected upper case REALM"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, + test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.len, 2, + "Mismatch in name between request and expected request, expected service/hostname"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.val[0], + test_context->test_data->krb5_service, + "Mismatch in name between request and expected request, expected service/hostname"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_rep.ticket.sname.name_string.val[1], + test_context->test_data->krb5_hostname, + "Mismatch in name between request and expected request, expected service/hostname"); + + torture_assert_int_equal(test_context->tctx, + *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000, + 0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO"); + free_TGS_REP(&test_context->tgs_rep); + + torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets"); + return true; +} + +/* * TEST_AS_REQ_SELF - RECV * * Confirm that the reply packet from the KDC meets certain @@ -1029,6 +1139,10 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c ok = torture_krb5_pre_send_tgs_req_krbtgt_test(test_context, send_buf, &modified_send_buf); break; + case TEST_TGS_REQ_HOST: + ok = torture_krb5_pre_send_tgs_req_host_test(test_context, send_buf, + &modified_send_buf); + break; case TEST_AS_REQ_SELF: ok = torture_krb5_pre_send_as_req_test(test_context, send_buf, &modified_send_buf); @@ -1067,6 +1181,9 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c case TEST_TGS_REQ_KRBTGT: ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf); break; + case TEST_TGS_REQ_HOST: + ok = torture_krb5_post_recv_tgs_req_host_test(test_context, recv_buf); + break; case TEST_AS_REQ_SELF: ok = torture_krb5_post_recv_as_req_self_test(test_context, recv_buf); break; @@ -1148,6 +1265,8 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * krb5_get_creds_opt opt; const char *upn = torture_setting_string(tctx, "krb5-upn", ""); + test_data->krb5_service = torture_setting_string(tctx, "krb5-service", "host"); + test_data->krb5_hostname = torture_setting_string(tctx, "krb5-hostname", ""); /* * If we have not passed a UPN on the command line, @@ -1604,7 +1723,59 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * && (test_data->upper_realm == false || test_data->netbios_realm == true)) { torture_assert(tctx, test_context->packet_count > 0, - "Expected krb5_get_creds to send packets"); + "Expected krb5_mk_req_exact to send packets"); + } + + /* + * Confirm gettting a ticket to pass to the server, running + * the TEST_TGS_REQ_HOST stage + * + * This triggers the client to attempt to get a + * cross-realm ticket between the alternate names of + * the server, and we need to confirm that behaviour. + * + */ + + if (*test_data->krb5_service && *test_data->krb5_hostname) { + /* + * This tries to guess when the krb5 libs will ask for a + * cross-realm ticket, and when they will just ask the KDC + * directly. + */ + test_context->test_stage = TEST_TGS_REQ_HOST; + + test_context->packet_count = 0; + torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context), + 0, "krb5_auth_con_init failed"); + + in_data.length = 0; + k5ret = krb5_mk_req(k5_context, + &auth_context, + 0, + test_data->krb5_service, + test_data->krb5_hostname, + &in_data, ccache, + &enc_ticket); + + if (test_data->canonicalize == false && test_data->enterprise == false + && (test_data->upper_realm == false || test_data->netbios_realm == true)) { + torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND, + "krb5_get_creds should have failed with KRB5_CC_NOTFOUND"); + } else { + assertion_message = talloc_asprintf(tctx, + "krb5_mk_req for %s failed: %s", + principal_string, + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + /* + * Only in these cases would the above code have needed to + * send packets to the network + */ + torture_assert(tctx, + test_context->packet_count > 0, + "Expected krb5_get_creds to send packets"); + } } /* -- 2.1.4 From dded3181248a888d72fde304b98e050e99c4eda1 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 5 Feb 2015 16:44:23 +1300 Subject: [PATCH 44/60] torture-krb5: Add test for TGS-REQ with type KRB5_NT_PRINCIPAL, KRB5_NT_SRV_INST, KRB5_NT_SRV_HST Signed-off-by: Andrew Bartlett BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Pair-programmed-with: Garming Sam Signed-off-by: Garming Sam (cherry picked from commit 3c89b25e4fbc86981ec2cfaebbd5e119a5fc965d) --- source4/selftest/tests.py | 8 +- source4/torture/krb5/kdc-canon.c | 176 +++++++++++++++++++++++++++++++++------ 2 files changed, 158 insertions(+), 26 deletions(-) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 5f2d9f1..9dccb92 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -563,9 +563,13 @@ for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", else: extra_options = [] - plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:expect_machine_account=true'] + extra_options, + plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:krb5-hostname=$SERVER', '--option=torture:expect_machine_account=true'] + extra_options, "samba4.krb5.kdc with machine account") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:expect_machine_account=true', '--option=torture:krb5-upn=testallowed_upn@$REALM'] + extra_options, + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed%$PASSWORD', + '--workgroup=$DOMAIN', '--realm=$REALM', + '--option=torture:expect_machine_account=true', + '--option=torture:krb5-upn=testallowed_upn@$REALM', + '--option=torture:krb5-hostname=testallowed'] + extra_options, "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") # TODO: Verifying the databases really should be a part of the diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index f6b7815..e745fe5 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -66,6 +66,8 @@ enum test_stage { TEST_TGS_REQ, TEST_TGS_REQ_KRBTGT, TEST_TGS_REQ_HOST, + TEST_TGS_REQ_HOST_SRV_INST, + TEST_TGS_REQ_HOST_SRV_HST, TEST_AS_REQ_SELF, TEST_DONE }; @@ -837,7 +839,7 @@ static bool torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_contex } /* - * TEST_TGS_REQ_HOST + * TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST and TEST_TGS_REQ_HOST_SRV_HST * * * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact() @@ -860,25 +862,54 @@ static bool torture_krb5_pre_send_tgs_req_host_test(struct torture_krb5_context test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno"); torture_assert_int_equal(test_context->tctx, - test_context->tgs_req.req_body.kdc_options.canonicalize, - true, - "krb5 libs unexpectedly did not set canonicalize!"); - - torture_assert_int_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, - "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); - torture_assert_int_equal(test_context->tctx, test_context->tgs_req.req_body.sname->name_string.len, 2, "Mismatch in name between request and expected request, expected krbtgt/realm"); - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_string.val[0], - test_context->test_data->krb5_service, - "Mismatch in name between request and expected request, expected krbtgt"); - torture_assert_str_equal(test_context->tctx, - test_context->tgs_req.req_body.sname->name_string.val[1], - test_context->test_data->krb5_hostname, - "Mismatch in realm part of cross-realm request principal between request and expected request"); + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.kdc_options.canonicalize, + true, + "krb5 libs unexpectedly did not set canonicalize!"); + + if (test_context->test_stage == TEST_TGS_REQ_HOST_SRV_INST) { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_SRV_INST, + "Mismatch in name type between request and expected request, expected KRB5_NT_SRV_INST"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[0], + strupper_talloc(test_context, test_context->test_data->krb5_service), + "Mismatch in name between request and expected request, expected service"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[1], + test_context->test_data->krb5_hostname, + "Mismatch in hostname part between request and expected request"); + + } else if (test_context->test_stage == TEST_TGS_REQ_HOST_SRV_HST) { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_SRV_HST, + "Mismatch in name type between request and expected request, expected KRB5_NT_SRV_HST"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[0], + test_context->test_data->krb5_service, + "Mismatch in name between request and expected request, expected service"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[1], + strupper_talloc(test_context, test_context->test_data->krb5_hostname), + "Mismatch in hostname part between request and expected request"); + + } else { + torture_assert_int_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL, + "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[0], + test_context->test_data->krb5_service, + "Mismatch in name between request and expected request, expected service"); + torture_assert_str_equal(test_context->tctx, + test_context->tgs_req.req_body.sname->name_string.val[1], + test_context->test_data->krb5_hostname, + "Mismatch in hostname part between request and expected request"); + + } torture_assert_str_equal(test_context->tctx, test_context->tgs_req.req_body.realm, test_context->test_data->real_realm, @@ -889,7 +920,7 @@ static bool torture_krb5_pre_send_tgs_req_host_test(struct torture_krb5_context } /* - * TEST_TGS_REQ_HOST - RECV + * TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST, TEST_TGS_REQ_HOST_SRV_HST - RECV * * Confirm that the reply TGS-REP packet for krb5_mk_req(), for * the actual target service, as a SPN, not a any other name type. @@ -927,11 +958,11 @@ static bool torture_krb5_post_recv_tgs_req_host_test(struct torture_krb5_context "Mismatch in name between request and expected request, expected service/hostname"); torture_assert_str_equal(test_context->tctx, test_context->tgs_rep.ticket.sname.name_string.val[0], - test_context->test_data->krb5_service, + test_context->tgs_req.req_body.sname->name_string.val[0], "Mismatch in name between request and expected request, expected service/hostname"); torture_assert_str_equal(test_context->tctx, test_context->tgs_rep.ticket.sname.name_string.val[1], - test_context->test_data->krb5_hostname, + test_context->tgs_req.req_body.sname->name_string.val[1], "Mismatch in name between request and expected request, expected service/hostname"); torture_assert_int_equal(test_context->tctx, @@ -1140,6 +1171,8 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c &modified_send_buf); break; case TEST_TGS_REQ_HOST: + case TEST_TGS_REQ_HOST_SRV_INST: + case TEST_TGS_REQ_HOST_SRV_HST: ok = torture_krb5_pre_send_tgs_req_host_test(test_context, send_buf, &modified_send_buf); break; @@ -1182,6 +1215,8 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf); break; case TEST_TGS_REQ_HOST: + case TEST_TGS_REQ_HOST_SRV_INST: + case TEST_TGS_REQ_HOST_SRV_HST: ok = torture_krb5_post_recv_tgs_req_host_test(test_context, recv_buf); break; case TEST_AS_REQ_SELF: @@ -1728,7 +1763,7 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * /* * Confirm gettting a ticket to pass to the server, running - * the TEST_TGS_REQ_HOST stage + * the TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST, TEST_TGS_REQ_HOST_SRV_HST stage * * This triggers the client to attempt to get a * cross-realm ticket between the alternate names of @@ -1737,13 +1772,13 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * */ if (*test_data->krb5_service && *test_data->krb5_hostname) { + krb5_principal host_principal_srv_inst; /* * This tries to guess when the krb5 libs will ask for a * cross-realm ticket, and when they will just ask the KDC * directly. */ test_context->test_stage = TEST_TGS_REQ_HOST; - test_context->packet_count = 0; torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context), 0, "krb5_auth_con_init failed"); @@ -1763,8 +1798,101 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * "krb5_get_creds should have failed with KRB5_CC_NOTFOUND"); } else { assertion_message = talloc_asprintf(tctx, - "krb5_mk_req for %s failed: %s", - principal_string, + "krb5_mk_req for %s/%s failed: %s", + test_data->krb5_hostname, + test_data->krb5_service, + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + /* + * Only in these cases would the above code have needed to + * send packets to the network + */ + torture_assert(tctx, + test_context->packet_count > 0, + "Expected krb5_get_creds to send packets"); + } + + + test_context->test_stage = TEST_TGS_REQ_HOST_SRV_INST; + test_context->packet_count = 0; + + torture_assert_int_equal(tctx, + krb5_make_principal(k5_context, &host_principal_srv_inst, + test_data->real_realm, + strupper_talloc(tctx, test_data->krb5_service), + test_data->krb5_hostname, + NULL), + 0, "krb5_make_principal failed"); + + krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_INST); + + torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context), + 0, "krb5_auth_con_init failed"); + + in_data.length = 0; + k5ret = krb5_mk_req_exact(k5_context, + &auth_context, + 0, + host_principal_srv_inst, + &in_data, ccache, + &enc_ticket); + krb5_free_principal(k5_context, host_principal_srv_inst); + if (test_data->canonicalize == false && test_data->enterprise == false + && (test_data->upper_realm == false || test_data->netbios_realm == true)) { + torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND, + "krb5_get_creds should have failed with KRB5_CC_NOTFOUND"); + } else { + assertion_message = talloc_asprintf(tctx, + "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s", + test_data->krb5_service, + test_data->krb5_hostname, + smb_get_krb5_error_message(k5_context, k5ret, tctx)); + + torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + /* + * Only in these cases would the above code have needed to + * send packets to the network + */ + torture_assert(tctx, + test_context->packet_count > 0, + "Expected krb5_get_creds to send packets"); + } + + + test_context->test_stage = TEST_TGS_REQ_HOST_SRV_HST; + test_context->packet_count = 0; + + torture_assert_int_equal(tctx, + krb5_make_principal(k5_context, &host_principal_srv_inst, + test_data->real_realm, + test_data->krb5_service, + strupper_talloc(tctx, test_data->krb5_hostname), + NULL), + 0, "krb5_make_principal failed"); + + krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_HST); + + torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context), + 0, "krb5_auth_con_init failed"); + + in_data.length = 0; + k5ret = krb5_mk_req_exact(k5_context, + &auth_context, + 0, + host_principal_srv_inst, + &in_data, ccache, + &enc_ticket); + krb5_free_principal(k5_context, host_principal_srv_inst); + if (test_data->canonicalize == false && test_data->enterprise == false + && (test_data->upper_realm == false || test_data->netbios_realm == true)) { + torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND, + "krb5_get_creds should have failed with KRB5_CC_NOTFOUND"); + } else { + assertion_message = talloc_asprintf(tctx, + "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s", + test_data->krb5_service, + test_data->krb5_hostname, smb_get_krb5_error_message(k5_context, k5ret, tctx)); torture_assert_int_equal(tctx, k5ret, 0, assertion_message); -- 2.1.4 From c0a343d6cc6a53cd9e02d0e2a601a3dd23f0d8b9 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 6 Feb 2015 08:53:21 +1300 Subject: [PATCH 45/60] auth/kerberos: Use talloc_stackframe to avoid memory and FD leak of event context The smb_krb5_send_and_recv_func_forced and smb_krb5_send_and_recv_func functions could leak an event context including an epoll FD and some memory. This may explain a flapping test in krb5.kdc Andrew Bartlett Signed-off-by: Andrew Bartlett Reviewed-By: Jelmer Vernooij Reviewed-by: Kamen Mazdrashki BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 (cherry picked from commit bdde51b26f4f5bcd6b0dcb5557fee40d7bc40207) --- source4/auth/kerberos/krb5_init_context.c | 60 +++++++++++++++++-------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/source4/auth/kerberos/krb5_init_context.c b/source4/auth/kerberos/krb5_init_context.c index e8a1a6c..7fcc8a6 100644 --- a/source4/auth/kerberos/krb5_init_context.c +++ b/source4/auth/kerberos/krb5_init_context.c @@ -228,8 +228,8 @@ static krb5_error_code smb_krb5_send_and_recv_func_int(krb5_context context, DATA_BLOB send_blob; - TALLOC_CTX *tmp_ctx = talloc_new(NULL); - if (!tmp_ctx) { + TALLOC_CTX *frame = talloc_stackframe(); + if (frame == NULL) { return ENOMEM; } @@ -237,9 +237,9 @@ static krb5_error_code smb_krb5_send_and_recv_func_int(krb5_context context, for (a = ai; a; a = a->ai_next) { struct socket_address *remote_addr; - smb_krb5 = talloc(tmp_ctx, struct smb_krb5_socket); + smb_krb5 = talloc(frame, struct smb_krb5_socket); if (!smb_krb5) { - talloc_free(tmp_ctx); + TALLOC_FREE(frame); return ENOMEM; } smb_krb5->hi = hi; @@ -254,7 +254,7 @@ static krb5_error_code smb_krb5_send_and_recv_func_int(krb5_context context, break; #endif default: - talloc_free(tmp_ctx); + TALLOC_FREE(frame); return EINVAL; } @@ -267,7 +267,7 @@ static krb5_error_code smb_krb5_send_and_recv_func_int(krb5_context context, status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0); break; case KRB5_KRBHST_HTTP: - talloc_free(tmp_ctx); + TALLOC_FREE(frame); return EINVAL; } if (!NT_STATUS_IS_OK(status)) { @@ -335,12 +335,12 @@ static krb5_error_code smb_krb5_send_and_recv_func_int(krb5_context context, packet_send(smb_krb5->packet, smb_krb5->request); break; case KRB5_KRBHST_HTTP: - talloc_free(tmp_ctx); + TALLOC_FREE(frame); return EINVAL; } while ((NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) { if (tevent_loop_once(ev) != 0) { - talloc_free(tmp_ctx); + TALLOC_FREE(frame); return EINVAL; } @@ -355,7 +355,7 @@ static krb5_error_code smb_krb5_send_and_recv_func_int(krb5_context context, func, data); if (ret != 0) { - talloc_free(tmp_ctx); + TALLOC_FREE(frame); return ret; } } @@ -381,14 +381,14 @@ static krb5_error_code smb_krb5_send_and_recv_func_int(krb5_context context, ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length); if (ret) { - talloc_free(tmp_ctx); + TALLOC_FREE(frame); return ret; } talloc_free(smb_krb5); break; } - talloc_free(tmp_ctx); + TALLOC_FREE(frame); if (a) { return 0; } @@ -406,16 +406,16 @@ krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, struct addrinfo *ai; struct tevent_context *ev; - TALLOC_CTX *tmp_ctx = talloc_new(NULL); - if (!tmp_ctx) { + TALLOC_CTX *frame = talloc_stackframe(); + if (frame == NULL) { return ENOMEM; } - if (!data) { + if (data == NULL) { /* If no event context was available, then create one for this loop */ - ev = samba_tevent_context_init(tmp_ctx); - if (!ev) { - talloc_free(tmp_ctx); + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + TALLOC_FREE(frame); return ENOMEM; } } else { @@ -424,10 +424,13 @@ krb5_error_code smb_krb5_send_and_recv_func(krb5_context context, ret = krb5_krbhst_get_addrinfo(context, hi, &ai); if (ret) { - talloc_free(tmp_ctx); + TALLOC_FREE(frame); return ret; } - return smb_krb5_send_and_recv_func_int(context, ev, hi, ai, smb_krb5_send_and_recv_func, data, timeout, send_buf, recv_buf); + + ret = smb_krb5_send_and_recv_func_int(context, ev, hi, ai, smb_krb5_send_and_recv_func, data, timeout, send_buf, recv_buf); + TALLOC_FREE(frame); + return ret; } krb5_error_code smb_krb5_send_and_recv_func_forced(krb5_context context, @@ -437,24 +440,27 @@ krb5_error_code smb_krb5_send_and_recv_func_forced(krb5_context context, const krb5_data *send_buf, krb5_data *recv_buf) { + krb5_error_code k5ret; struct addrinfo *ai = data; struct tevent_context *ev; - TALLOC_CTX *tmp_ctx = talloc_new(NULL); - if (!tmp_ctx) { + TALLOC_CTX *frame = talloc_stackframe(); + if (frame == NULL) { return ENOMEM; } - /* If no event context was available, then create one for this loop */ - ev = samba_tevent_context_init(tmp_ctx); - if (!ev) { - talloc_free(tmp_ctx); + /* no event context is passed in, create one for this loop */ + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + TALLOC_FREE(frame); return ENOMEM; } /* No need to pass in send_and_recv functions, we won't nest on this private event loop */ - return smb_krb5_send_and_recv_func_int(context, ev, hi, ai, NULL, NULL, - timeout, send_buf, recv_buf); + k5ret = smb_krb5_send_and_recv_func_int(context, ev, hi, ai, NULL, NULL, + timeout, send_buf, recv_buf); + TALLOC_FREE(frame); + return k5ret; } #endif -- 2.1.4 From 094405b30b7e0821e09fd0e1ca96fbbec6e25402 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sat, 7 Feb 2015 19:45:24 +1300 Subject: [PATCH 46/60] torture-krb5: Provide a generic handler to catch and print unexpected KRB_ERROR packets This may aid debugging in the future. Signed-off-by: Andrew Bartlett Reviewed-By: Jelmer Vernooij Reviewed-by: Kamen Mazdrashki BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Sun Feb 8 10:37:23 CET 2015 on sn-devel-104 (cherry picked from commit bfccf0abf8a11788b59edab1983d14114906c7f4) --- source4/torture/krb5/kdc-canon.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index e745fe5..312c7b5 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -60,16 +60,16 @@ struct test_data { enum test_stage { TEST_AS_REQ = 0, - TEST_TGS_REQ_KRBTGT_CANON, - TEST_TGS_REQ_CANON, - TEST_SELF_TRUST_TGS_REQ, - TEST_TGS_REQ, - TEST_TGS_REQ_KRBTGT, - TEST_TGS_REQ_HOST, - TEST_TGS_REQ_HOST_SRV_INST, - TEST_TGS_REQ_HOST_SRV_HST, - TEST_AS_REQ_SELF, - TEST_DONE + TEST_TGS_REQ_KRBTGT_CANON = 1, + TEST_TGS_REQ_CANON = 2, + TEST_SELF_TRUST_TGS_REQ = 3, + TEST_TGS_REQ = 4, + TEST_TGS_REQ_KRBTGT = 5, + TEST_TGS_REQ_HOST = 6, + TEST_TGS_REQ_HOST_SRV_INST = 7, + TEST_TGS_REQ_HOST_SRV_HST = 8, + TEST_AS_REQ_SELF = 9, + TEST_DONE = 10 }; struct torture_krb5_context { @@ -1224,6 +1224,18 @@ static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context c break; } if (ok == false) { + KRB_ERROR error; + size_t used; + torture_warning(test_context->tctx, "Packet of length %llu failed post-recv checks in test stage %d", (unsigned long long)recv_buf->length, test_context->test_stage); + if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) { + torture_warning(test_context->tctx, + "STAGE: %d Unexpectedly got a KRB-ERROR packet " + "with error code %d (%s)", + test_context->test_stage, + error.error_code, + error_message(error.error_code + KRB5KDC_ERR_NONE)); + free_KRB_ERROR(&error); + } return EINVAL; } -- 2.1.4 From 175f4b7bcca393a7f7de89078fbdb2e8995b6ba8 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 9 Mar 2015 16:00:56 +1300 Subject: [PATCH 47/60] kdc: Fix S4U2Self handling with KRB5_NT_ENTERPRISE_PRINCIPAL containing a UPN This is now handled properly by samba_kdc_lookup_server() and this wrapper actually breaks things. Andrew Bartlett Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher (cherry picked from commit a1ddee8d2f9e58e04f3203db9afa576354dd2079) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 --- source4/kdc/db-glue.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index aa73641..0bc907e 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -1839,7 +1839,6 @@ samba_kdc_check_s4u2self(krb5_context context, krb5_const_principal target_principal) { krb5_error_code ret; - krb5_principal enterprise_prinicpal = NULL; struct ldb_dn *realm_dn; struct ldb_message *msg; struct dom_sid *orig_sid; @@ -1857,30 +1856,10 @@ samba_kdc_check_s4u2self(krb5_context context, return ret; } - if (target_principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { - /* Need to reparse the enterprise principal to find the real target */ - if (target_principal->name.name_string.len != 1) { - ret = KRB5_PARSE_MALFORMED; - krb5_set_error_message(context, ret, "samba_kdc_check_s4u2self: request for delegation to enterprise principal with wrong (%d) number of components", - target_principal->name.name_string.len); - talloc_free(mem_ctx); - return ret; - } - ret = krb5_parse_name(context, target_principal->name.name_string.val[0], - &enterprise_prinicpal); - if (ret) { - talloc_free(mem_ctx); - return ret; - } - target_principal = enterprise_prinicpal; - } - ret = samba_kdc_lookup_server(context, kdc_db_ctx, mem_ctx, target_principal, HDB_F_GET_CLIENT|HDB_F_GET_SERVER, delegation_check_attrs, &realm_dn, &msg); - krb5_free_principal(context, enterprise_prinicpal); - if (ret != 0) { talloc_free(mem_ctx); return ret; -- 2.1.4 From 07ae5beddd22c9449bc1ad5c535a5d9d952d5e35 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 9 Mar 2015 11:12:01 +1300 Subject: [PATCH 48/60] torture-krb5: Add an initial test for s4u2self behaviour This test only checks for S4U2Self of the same user, but shows that a user account is not a valid service for this purpose. Andrew Bartlett Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Mon Mar 9 12:10:09 CET 2015 on sn-devel-104 (cherry picked from commit 02f6cfd14c8ac15b5d8a55783bb98a87557394d5) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 --- source4/torture/krb5/kdc-canon.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index 312c7b5..49c6c26 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -37,7 +37,8 @@ #define TEST_NETBIOS_REALM 0x0000010 #define TEST_WIN2K 0x0000020 #define TEST_UPN 0x0000040 -#define TEST_ALL 0x000007F +#define TEST_S4U2SELF 0x0000080 +#define TEST_ALL 0x00000FF struct test_data { const char *test_name; @@ -54,6 +55,7 @@ struct test_data { bool win2k; bool upn; bool other_upn_suffix; + bool s4u2self; const char *krb5_service; const char *krb5_hostname; }; @@ -1665,6 +1667,14 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * opt, KRB5_GC_NO_STORE); + if (test_data->s4u2self) { + torture_assert_int_equal(tctx, + krb5_get_creds_opt_set_impersonate(k5_context, + opt, + principal), + 0, "krb5_get_creds_opt_set_impersonate failed"); + } + /* Confirm if we can get a ticket to our own name */ k5ret = krb5_get_creds(k5_context, opt, ccache, principal, &server_creds); @@ -2051,14 +2061,15 @@ struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx) suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests"); for (i = 0; i < TEST_ALL; i++) { - char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s.%s.%s", + char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s.%s.%s.%s", (i & TEST_CANONICALIZE) ? "canon" : "no-canon", (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise", (i & TEST_UPPER_REALM) ? "uc-realm" : "lc-realm", (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user", (i & TEST_NETBIOS_REALM) ? "netbios-realm" : "krb5-realm", (i & TEST_WIN2K) ? "win2k" : "no-win2k", - (i & TEST_UPN) ? "upn" : "no-upn"); + (i & TEST_UPN) ? "upn" : "no-upn", + (i & TEST_S4U2SELF) ? "s4u2self" : "normal"); struct test_data *test_data = talloc_zero(suite, struct test_data); @@ -2075,6 +2086,7 @@ struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx) test_data->netbios_realm = (i & TEST_NETBIOS_REALM) != 0; test_data->win2k = (i & TEST_WIN2K) != 0; test_data->upn = (i & TEST_UPN) != 0; + test_data->s4u2self = (i & TEST_S4U2SELF) != 0; torture_suite_add_simple_tcase_const(suite, name, torture_krb5_as_req_canon, test_data); -- 2.1.4 From 240955131cc487faa6bac0b9910eb2cd7c8a3690 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Mar 2015 15:57:06 +1300 Subject: [PATCH 49/60] auth/kerberos: Do a string comparison in kerberos_decode_pac() not a principal comparison This ensures that if an enterprise principal is used, we do the comparison properly This matters as in the enterprise case, which can be triggered by MIT kinit -E, does not use canonicalization, and so the enterprise name, with the @ in it, is in the logon name. Otherwise, we get errors like: Name in PAC [TESTALLOWED@WIN2012R2] does not match principal name in ticket BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher Reviewed-by: Guenther Deschner (cherry picked from commit e48d136e3a5c89c9bab8ea898775fad1449d2f96) --- auth/kerberos/kerberos_pac.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/auth/kerberos/kerberos_pac.c b/auth/kerberos/kerberos_pac.c index 8f55c8f..32d9d7f 100644 --- a/auth/kerberos/kerberos_pac.c +++ b/auth/kerberos/kerberos_pac.c @@ -106,7 +106,6 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, DATA_BLOB modified_pac_blob; NTTIME tgs_authtime_nttime; - krb5_principal client_principal_pac = NULL; int i; struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL; @@ -357,28 +356,30 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, } if (client_principal) { - ret = smb_krb5_parse_name_norealm(context, - logon_name->account_name, - &client_principal_pac); + char *client_principal_string; + ret = krb5_unparse_name_flags(context, client_principal, + KRB5_PRINCIPAL_UNPARSE_NO_REALM|KRB5_PRINCIPAL_UNPARSE_DISPLAY, + &client_principal_string); if (ret) { - DEBUG(2, ("Could not parse name from PAC: [%s]:%s\n", + DEBUG(2, ("Could not unparse name from ticket to match with name from PAC: [%s]:%s\n", logon_name->account_name, error_message(ret))); talloc_free(tmp_ctx); return NT_STATUS_INVALID_PARAMETER; } - bool_ret = smb_krb5_principal_compare_any_realm(context, - client_principal, - client_principal_pac); - - krb5_free_principal(context, client_principal_pac); + bool_ret = strcmp(client_principal_string, logon_name->account_name) == 0; if (!bool_ret) { DEBUG(2, ("Name in PAC [%s] does not match principal name " - "in ticket\n", logon_name->account_name)); + "in ticket [%s]\n", + logon_name->account_name, + client_principal_string)); + SAFE_FREE(client_principal_string); talloc_free(tmp_ctx); return NT_STATUS_ACCESS_DENIED; } + SAFE_FREE(client_principal_string); + } DEBUG(3,("Found account name from PAC: %s [%s]\n", -- 2.1.4 From 82e7e3c19c398fffd551578216a796549e6ada62 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 12 Mar 2015 11:27:57 +1300 Subject: [PATCH 50/60] auth/kerberos: Use KRB5_PRINCIPAL_UNPARSE_DISPLAY in kerberos_create_pac() This ensures that in the all-Samba PAC creation code, we do not escape a space character if present in the logon name. This matches what we do in the Heimdal code in the KDC. Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher Reviewed-by: Guenther Deschner (cherry picked from commit bc8b580659d429690f6b54f17368526fc8c845e3) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 --- source4/auth/kerberos/kerberos_pac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source4/auth/kerberos/kerberos_pac.c b/source4/auth/kerberos/kerberos_pac.c index 5d65461..20cfe88 100644 --- a/source4/auth/kerberos/kerberos_pac.c +++ b/source4/auth/kerberos/kerberos_pac.c @@ -251,7 +251,9 @@ LOGON_INFO->info3 = *sam3; ret = krb5_unparse_name_flags(context, client_principal, - KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name); + KRB5_PRINCIPAL_UNPARSE_NO_REALM | + KRB5_PRINCIPAL_UNPARSE_DISPLAY, + &name); if (ret) { return ret; } -- 2.1.4 From c335302e6c044b317df70982605144a30657217e Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 18 Dec 2014 11:03:44 +1300 Subject: [PATCH 51/60] gensec_krb5: Match behaviour of gensec_gssapi for password-based keytabs This allows the winbind.pac.krb5 test to pass against the s3member environment, which uses the password from secrets.tdb. Andrew Bartlett Reviewed-by: Garming Sam Signed-off-by: Andrew Bartlett (cherry picked from commit 121bbc01842db03570623eadcbb97edab30ca651) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 --- source4/auth/gensec/gensec_krb5.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c index c34c434..a81dfc3 100644 --- a/source4/auth/gensec/gensec_krb5.c +++ b/source4/auth/gensec/gensec_krb5.c @@ -591,6 +591,16 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } + if (keytab->password_based || obtained < CRED_SPECIFIED) { + /* + * Use match-by-key in this case (matches + * cli_credentials_get_server_gss_creds() + * behaviour). No need to free the memory, + * this is handled with a talloc destructor. + */ + server_in_keytab = NULL; + } + /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */ if (gensec_krb5_state->gssapi && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { -- 2.1.4 From cb4344b6a49eb439ac9cb0cfd40c3cd54ab57186 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Mar 2015 15:58:36 +1300 Subject: [PATCH 52/60] torture-krb5: Test accepting the ticket to ensure PAC is well-formed A future test will ask for impersonation to a different user, and validate returned principal and the PAC matches that user. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher Reviewed-by: Guenther Deschner (cherry picked from commit a1b4a5d977862bda48819d3f0b33eccbd10ca4fd) --- source4/torture/krb5/kdc-canon.c | 135 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/source4/torture/krb5/kdc-canon.c b/source4/torture/krb5/kdc-canon.c index 49c6c26..20f0cf1 100644 --- a/source4/torture/krb5/kdc-canon.c +++ b/source4/torture/krb5/kdc-canon.c @@ -29,6 +29,10 @@ #include "source4/auth/kerberos/kerberos.h" #include "source4/auth/kerberos/kerberos_util.h" #include "lib/util/util_net.h" +#include "auth/auth.h" +#include "auth/auth_sam_reply.h" +#include "auth/gensec/gensec.h" +#include "param/param.h" #define TEST_CANONICALIZE 0x0000001 #define TEST_ENTERPRISE 0x0000002 @@ -87,6 +91,121 @@ struct torture_krb5_context { TGS_REP tgs_rep; }; +struct pac_data { + const char *principal_name; +}; + +/* + * A helper function which avoids touching the local databases to + * generate the session info, as we just want to verify the principal + * name that we found in the ticket not the full local token + */ +static NTSTATUS test_generate_session_info_pac(struct auth4_context *auth_ctx, + TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + DATA_BLOB *pac_blob, + const char *principal_name, + const struct tsocket_address *remote_address, + uint32_t session_info_flags, + struct auth_session_info **session_info) +{ + NTSTATUS nt_status; + struct auth_user_info_dc *user_info_dc; + TALLOC_CTX *tmp_ctx; + struct pac_data *pac_data; + + tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context"); + NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); + + auth_ctx->private_data = pac_data = talloc_zero(auth_ctx, struct pac_data); + + pac_data->principal_name = talloc_strdup(pac_data, principal_name); + if (!pac_data->principal_name) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + nt_status = kerberos_pac_blob_to_user_info_dc(tmp_ctx, + *pac_blob, + smb_krb5_context->krb5_context, + &user_info_dc, NULL, NULL); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + + if (user_info_dc->info->authenticated) { + session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED; + } + + session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES; + nt_status = auth_generate_session_info(mem_ctx, + NULL, + NULL, + user_info_dc, session_info_flags, + session_info); + if (!NT_STATUS_IS_OK(nt_status)) { + talloc_free(tmp_ctx); + return nt_status; + } + + talloc_free(tmp_ctx); + return NT_STATUS_OK; +} + +/* Check to see if we can pass the PAC across to the NETLOGON server for validation */ + +/* Also happens to be a really good one-step verfication of our Kerberos stack */ + +static bool test_accept_ticket(struct torture_context *tctx, + struct cli_credentials *credentials, + const char *principal, + DATA_BLOB client_to_server) +{ + NTSTATUS status; + struct gensec_security *gensec_server_context; + DATA_BLOB server_to_client; + struct auth4_context *auth_context; + struct auth_session_info *session_info; + struct pac_data *pac_data; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + + torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed"); + + auth_context = talloc_zero(tmp_ctx, struct auth4_context); + torture_assert(tctx, auth_context != NULL, "talloc_new() failed"); + + auth_context->generate_session_info_pac = test_generate_session_info_pac; + + status = gensec_server_start(tctx, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + auth_context, &gensec_server_context); + torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed"); + + status = gensec_set_credentials(gensec_server_context, credentials); + torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed"); + + status = gensec_start_mech_by_name(gensec_server_context, "krb5"); + torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_name (server) failed"); + + server_to_client = data_blob(NULL, 0); + + /* Do a client-server update dance */ + status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client); + torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed"); + + /* Extract the PAC using Samba's code */ + + status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info); + torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed"); + + pac_data = talloc_get_type(auth_context->private_data, struct pac_data); + + torture_assert(tctx, pac_data != NULL, "gensec_update failed to fill in pac_data in auth_context"); + torture_assert(tctx, pac_data->principal_name != NULL, "principal_name not present"); + torture_assert_str_equal(tctx, pac_data->principal_name, principal, "wrong principal name"); + return true; +} /* * TEST_AS_REQ and TEST_AS_REQ_SELF - SEND @@ -1298,6 +1417,7 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * char *krbtgt_other_string; int principal_flags; char *expected_principal_string; + char *expected_unparse_principal_string; int expected_principal_flags; char *got_principal_string; char *assertion_message; @@ -1436,6 +1556,11 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * &expected_principal), 0, "krb5_parse_name_flags failed"); + torture_assert_int_equal(tctx, + krb5_unparse_name(k5_context, + expected_principal, + &expected_unparse_principal_string), + 0, "krb5_unparse_name failed"); /* * Prepare a AS-REQ and run the TEST_AS_REQ tests * @@ -1752,7 +1877,7 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * in_data.length = 0; k5ret = krb5_mk_req_exact(k5_context, &auth_context, - 0, + AP_OPTS_USE_SUBKEY, principal, &in_data, ccache, &enc_ticket); @@ -1766,7 +1891,15 @@ static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void * * servicePrincipalName) can expect this test to succeed */ if (torture_setting_bool(tctx, "expect_machine_account", false) && (test_data->enterprise || test_data->upn == false)) { + DATA_BLOB client_to_server; torture_assert_int_equal(tctx, k5ret, 0, assertion_message); + client_to_server = data_blob_const(enc_ticket.data, enc_ticket.length); + torture_assert(tctx, + test_accept_ticket(tctx, cmdline_credentials, + expected_unparse_principal_string, + client_to_server), + "test_accept_ticket failed - failed to accept the ticket we just created"); + krb5_data_free(&enc_ticket); } else { torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, assertion_message); -- 2.1.4 From ccff536a590e96f5585996a52b971e9a19f95483 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 10 Mar 2015 15:36:01 +0100 Subject: [PATCH 53/60] heimdal:lib/krb5: allow enterprise principals in verify_logonname() BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Guenther Deschner (cherry picked from commit b7cc8c1187ff967e44587cd0d09185330378f366) --- source4/heimdal/lib/krb5/pac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c index 91f68d5..a28dc82 100644 --- a/source4/heimdal/lib/krb5/pac.c +++ b/source4/heimdal/lib/krb5/pac.c @@ -677,7 +677,9 @@ verify_logonname(krb5_context context, return ret; } } - ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); + ret = krb5_parse_name_flags(context, s, + KRB5_PRINCIPAL_PARSE_NO_REALM | + KRB5_PRINCIPAL_PARSE_ENTERPRISE, &p2); free(s); if (ret) return ret; -- 2.1.4 From 9d35654544e58e18566008a9f0db288d6e685307 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 10 Mar 2015 15:33:14 +0100 Subject: [PATCH 54/60] heimdal:lib/krb5: let build_logon_name() use KRB5_PRINCIPAL_UNPARSE_DISPLAY An ENTERPRISE principal should result in 'administrator@S4XDOM.BASE' instead of 'administrator\@S4XDOM.BASE'. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Reviewed-by: Guenther Deschner (cherry picked from commit da99f8a5b9e492406b5d64bb53f090de3fd93957) --- source4/heimdal/lib/krb5/pac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c index a28dc82..9328647 100644 --- a/source4/heimdal/lib/krb5/pac.c +++ b/source4/heimdal/lib/krb5/pac.c @@ -724,7 +724,9 @@ build_logon_name(krb5_context context, CHECK(ret, krb5_store_uint32(sp, t >> 32), out); ret = krb5_unparse_name_flags(context, principal, - KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s); + KRB5_PRINCIPAL_UNPARSE_NO_REALM | + KRB5_PRINCIPAL_UNPARSE_DISPLAY, + &s); if (ret) goto out; -- 2.1.4 From 5ecadbf3ce9c0cb4fb00c6cee8311aef65be2f49 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 12 Mar 2015 12:50:23 +1300 Subject: [PATCH 55/60] dsdb: Allow spaces in userPrincipalName values This is needed to enable a kinit with a UPN that has a space in it Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher Reviewed-by: Guenther Deschner (cherry picked from commit 3cd871321667045635d8236d91386070e84770a4) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 --- source4/dsdb/samdb/cracknames.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/source4/dsdb/samdb/cracknames.c b/source4/dsdb/samdb/cracknames.c index 0d1a800..a03b03d 100644 --- a/source4/dsdb/samdb/cracknames.c +++ b/source4/dsdb/samdb/cracknames.c @@ -680,8 +680,18 @@ WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, domain_filter = NULL; - /* By getting the unparsed name here, we ensure the escaping is correct (and trust the client less) */ - ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name); + /* + * By getting the unparsed name here, we ensure the + * escaping is removed correctly (and trust the client + * less). The important thing here is that a + * userPrincipalName may have a space in it, and this + * must not be kerberos escaped to match this filter, + * so we specify KRB5_PRINCIPAL_UNPARSE_DISPLAY + */ + ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, + principal, + KRB5_PRINCIPAL_UNPARSE_DISPLAY, + &unparsed_name); if (ret) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; -- 2.1.4 From 35660801483650f8be714bb81997a39c36f8b6f3 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 12 Mar 2015 10:43:57 +0100 Subject: [PATCH 56/60] selftest: fix the basedn for local accounts in non-DC environments e.g. s4member open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); doesn't generate an error if the command fails... 'testallowed' is a local account here, with a dn of CN=testallowed,CN=Users,DC=S4MEMBER instead of domain user CN=testallowed,CN=Users,DC=samba,DC=example,DC=com Signed-off-by: Stefan Metzmacher Reviewed-by: Michael Adam Reviewed-by: Guenther Deschner (cherry picked from commit 979385cd0fd20957d552e64edc07ea2fa0edc0fc) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 --- selftest/target/Samba4.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 3cd3c39..5cb98cc 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -782,6 +782,11 @@ sub provision_raw_step2($$$) my $ldbmodify = Samba::bindir_path($self, "ldbmodify"); my $base_dn = "DC=".join(",DC=", split(/\./, $ctx->{realm})); + + if ($ctx->{server_role} ne "domain controller") { + $base_dn = "DC=$ctx->{netbiosname}"; + } + my $user_dn = "cn=testallowed,cn=users,$base_dn"; open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); print LDIF "dn: $user_dn -- 2.1.4 From c15bde69d9a3ef34c4373be3a7f27dd11cec504c Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 12 Mar 2015 12:56:56 +1300 Subject: [PATCH 57/60] selftest: Change testsuite to use a UPN with a space in it This shows that the previous patch is correct Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher Reviewed-by: Guenther Deschner (cherry picked from commit d3e0d7e2b0ee9fb72a8c602c86aee1d2f2755236) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 --- selftest/target/Samba4.pm | 2 +- source4/selftest/tests.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index 5cb98cc..eb06d67 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -792,7 +792,7 @@ sub provision_raw_step2($$$) print LDIF "dn: $user_dn changetype: modify replace: userPrincipalName -userPrincipalName: testallowed_upn\@$ctx->{realm} +userPrincipalName: testallowed upn\@$ctx->{realm} replace: servicePrincipalName servicePrincipalName: host/testallowed - diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 9dccb92..b6d1e8a 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -568,7 +568,7 @@ for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:expect_machine_account=true', - '--option=torture:krb5-upn=testallowed_upn@$REALM', + '--option=torture:krb5-upn=testallowed\ upn@$REALM', '--option=torture:krb5-hostname=testallowed'] + extra_options, "samba4.krb5.kdc with account ALLOWED permission to replicate to an RODC") -- 2.1.4 From f2c053e603553b0600214b8836c2c051df74de48 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 12 Mar 2015 13:29:56 +1300 Subject: [PATCH 58/60] dsdb: Ensure we cope with a samAccountName with a space in it in DsCrackName() Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher Reviewed-by: Guenther Deschner (cherry picked from commit 7ed24924d2917556a03c51eadcb65b3e3c1e8af6) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 --- source4/dsdb/samdb/cracknames.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/source4/dsdb/samdb/cracknames.c b/source4/dsdb/samdb/cracknames.c index a03b03d..ae334b4 100644 --- a/source4/dsdb/samdb/cracknames.c +++ b/source4/dsdb/samdb/cracknames.c @@ -323,8 +323,16 @@ static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, return WERR_OK; } + /* + * The important thing here is that a samAccountName may have + * a space in it, and this must not be kerberos escaped to + * match this filter, so we specify + * KRB5_PRINCIPAL_UNPARSE_DISPLAY + */ ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, - KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short); + KRB5_PRINCIPAL_UNPARSE_NO_REALM | + KRB5_PRINCIPAL_UNPARSE_DISPLAY, + &unparsed_name_short); krb5_free_principal(smb_krb5_context->krb5_context, principal); if (ret) { -- 2.1.4 From 17432d41862fd2760e48c296074f8784ae72cb71 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 12 Mar 2015 13:29:56 +1300 Subject: [PATCH 59/60] kdc: Ensure we cope with a samAccountName with a space in it Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher Reviewed-by: Guenther Deschner (cherry picked from commit 7f5740f34226301e2172c7e2024fd8c6c4ededf5) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 --- source4/kdc/db-glue.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c index 0bc907e..8f2b361 100644 --- a/source4/kdc/db-glue.c +++ b/source4/kdc/db-glue.c @@ -1601,7 +1601,10 @@ static krb5_error_code samba_kdc_lookup_server(krb5_context context, /* TODO: Check if it is our realm, otherwise give referral */ - ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &short_princ); + ret = krb5_unparse_name_flags(context, principal, + KRB5_PRINCIPAL_UNPARSE_NO_REALM | + KRB5_PRINCIPAL_UNPARSE_DISPLAY, + &short_princ); if (ret != 0) { krb5_set_error_message(context, ret, "samba_kdc_lookup_principal: could not parse principal"); -- 2.1.4 From 24faf8a6d5e2d40b04280597e0072c46a048fb28 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 12 Mar 2015 13:43:49 +1300 Subject: [PATCH 60/60] selftest: Change testsuite to use a samAccountName with a space in it This shows that the previous patch is correct Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher Reviewed-by: Guenther Deschner (cherry picked from commit 4bebab21463825c22cced6e8c59b99c525172911) BUG: https://bugzilla.samba.org/show_bug.cgi?id=11142 --- selftest/target/Samba4.pm | 17 +++++++++++++---- source4/selftest/tests.py | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index eb06d67..11c2148 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -791,6 +791,15 @@ sub provision_raw_step2($$$) open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); print LDIF "dn: $user_dn changetype: modify +replace: samAccountName +samAccountName: test allowed +- +"; + close(LDIF); + + open(LDIF, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb"); + print LDIF "dn: $user_dn +changetype: modify replace: userPrincipalName userPrincipalName: testallowed upn\@$ctx->{realm} replace: servicePrincipalName @@ -817,9 +826,9 @@ userPrincipalName: testdenied_upn\@$ctx->{realm}.upn close(LDIF); $samba_tool_cmd = Samba::bindir_path($self, "samba-tool") - . " group addmembers --configfile=$ctx->{smb_conf} 'Allowed RODC Password Replication Group' testallowed"; + . " group addmembers --configfile=$ctx->{smb_conf} 'Allowed RODC Password Replication Group' 'test allowed'"; unless (system($samba_tool_cmd) == 0) { - warn("Unable to add testallowed user to 'Allowed RODC Password Replication Group': \n$samba_tool_cmd\n"); + warn("Unable to add 'test allowed' user to 'Allowed RODC Password Replication Group': \n$samba_tool_cmd\n"); return undef; } @@ -1544,10 +1553,10 @@ sub provision_rodc($$$) return undef; } - # This ensures deterministic behaviour for tests that want to have the testallowed + # This ensures deterministic behaviour for tests that want to have the 'test allowed' # user password verified on the RODC $cmd = "KRB5_CONFIG=\"$ret->{KRB5_CONFIG}\" "; - $cmd .= "$samba_tool rodc preload testallowed $ret->{CONFIGURATION}"; + $cmd .= "$samba_tool rodc preload 'test allowed' $ret->{CONFIGURATION}"; $cmd .= " --server=$dcvars->{DC_SERVER}"; unless (system($cmd) == 0) { diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index b6d1e8a..ea0afd6 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -565,7 +565,7 @@ for env in ["dc", "rodc", "promoted_dc", "plugin_s4_dc", "fl2000dc", "fl2003dc", plansmbtorture4testsuite('krb5.kdc', "%s:local" % env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-P', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:krb5-hostname=$SERVER', '--option=torture:expect_machine_account=true'] + extra_options, "samba4.krb5.kdc with machine account") - plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utestallowed%$PASSWORD', + plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '-Utest\ allowed%$PASSWORD', '--workgroup=$DOMAIN', '--realm=$REALM', '--option=torture:expect_machine_account=true', '--option=torture:krb5-upn=testallowed\ upn@$REALM', -- 2.1.4