r23792: convert Samba4 to GPLv3
[kai/samba-autobuild/.git] / source4 / torture / auth / pac.c
index 21c3119e31679bade56383873402db778ef86a83..30bb53587d6117b5d5006a2a59a4e2e56b7ec70b 100644 (file)
@@ -7,7 +7,7 @@
 
    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 2 of the License, or
+   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,
@@ -17,8 +17,7 @@
 
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "auth/auth.h"
 #include "auth/kerberos/kerberos.h"
 #include "librpc/gen_ndr/ndr_krb5pac.h"
-#include "librpc/gen_ndr/ndr_samr.h"
+#include "samba3/samba3.h"
+#include "libcli/security/security.h"
+#include "torture/torture.h"
 
-#ifdef HAVE_KRB5
-
-static BOOL torture_pac_self_check(void) 
+static bool torture_pac_self_check(struct torture_context *tctx)
 {
        NTSTATUS nt_status;
-       TALLOC_CTX *mem_ctx = talloc_named(NULL, 0, "PAC self check");
        DATA_BLOB tmp_blob;
-       struct PAC_LOGON_INFO *pac_info;
+       struct PAC_DATA *pac_data;
+       struct PAC_LOGON_INFO *logon_info;
+       union netr_Validation validation;
 
        /* Generate a nice, arbitary keyblock */
        uint8_t server_bytes[16];
@@ -48,13 +48,17 @@ static BOOL torture_pac_self_check(void)
        struct smb_krb5_context *smb_krb5_context;
 
        struct auth_serversupplied_info *server_info;
+       struct auth_serversupplied_info *server_info_out;
 
-       ret = smb_krb5_init_context(mem_ctx, &smb_krb5_context);
+       krb5_principal client_principal;
+       time_t logon_time = time(NULL);
 
-       if (ret) {
-               talloc_free(mem_ctx);
-               return False;
-       }
+       TALLOC_CTX *mem_ctx = tctx;
+
+       torture_assert(tctx, 0 == smb_krb5_init_context(mem_ctx, 
+                                                       NULL,
+                                                       &smb_krb5_context), 
+                      "smb_krb5_init_context");
 
        generate_random_buffer(server_bytes, 16);
        generate_random_buffer(krbtgt_bytes, 16);
@@ -63,28 +67,24 @@ static BOOL torture_pac_self_check(void)
                                 ENCTYPE_ARCFOUR_HMAC,
                                 server_bytes, sizeof(server_bytes),
                                 &server_keyblock);
-       if (ret) {
-               DEBUG(1, ("Server Keyblock encoding failed: %s\n", 
-                         smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
-                                                    ret, mem_ctx)));
-
-               talloc_free(mem_ctx);
-               return False;
-       }
+       torture_assert(tctx, !ret, talloc_asprintf(tctx, 
+                                                  "(self test) Server Keyblock encoding failed: %s", 
+                                                  smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
+                                                                             ret, mem_ctx)));
 
        ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
                                 ENCTYPE_ARCFOUR_HMAC,
                                 krbtgt_bytes, sizeof(krbtgt_bytes),
                                 &krbtgt_keyblock);
        if (ret) {
-               DEBUG(1, ("KRBTGT Keyblock encoding failed: %s\n", 
-                         smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
-                                                    ret, mem_ctx)));
-
+               char *err = smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
+                                                      ret, mem_ctx);
+       
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &server_keyblock);
-               talloc_free(mem_ctx);
-               return False;
+
+               torture_fail(tctx, talloc_asprintf(tctx, 
+                                                  "(self test) KRBTGT Keyblock encoding failed: %s", err));
        }
 
        /* We need an input, and this one requires no underlying database */
@@ -95,61 +95,128 @@ static BOOL torture_pac_self_check(void)
                                            &server_keyblock);
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &krbtgt_keyblock);
-               talloc_free(mem_ctx);
-               return False;
+               torture_fail(tctx, "auth_anonymous_server_info");
        }
-       
+
+       ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, 
+                                   server_info->account_name, 
+                                   KRB5_PRINCIPAL_PARSE_NO_REALM, 
+                                   &client_principal);
+       if (ret) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               torture_fail(tctx, "krb5_parse_name_flags(norealm)");
+       }
+
        /* OK, go ahead and make a PAC */
-       ret = kerberos_encode_pac(mem_ctx, server_info, 
+       ret = kerberos_create_pac(mem_ctx, server_info, 
                                  smb_krb5_context->krb5_context,  
                                  &krbtgt_keyblock,
                                  &server_keyblock,
+                                 client_principal,
+                                 logon_time,
                                  &tmp_blob);
        
        if (ret) {
-               DEBUG(1, ("PAC encoding failed: %s\n", 
-                         smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
-                                                    ret, mem_ctx)));
-
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &krbtgt_keyblock);
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &server_keyblock);
-               talloc_free(mem_ctx);
-               return False;
+               krb5_free_principal(smb_krb5_context->krb5_context, 
+                                   client_principal);
+
+               torture_fail(tctx, talloc_asprintf(tctx,
+                                                  "(self test) PAC encoding failed: %s", 
+                                                  smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
+                                                                             ret, mem_ctx)));
        }
 
        dump_data(10,tmp_blob.data,tmp_blob.length);
 
        /* Now check that we can read it back */
-       nt_status = kerberos_decode_pac(mem_ctx, &pac_info,
+       nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
                                        tmp_blob,
-                                       smb_krb5_context,
+                                       smb_krb5_context->krb5_context,
                                        &krbtgt_keyblock,
-                                       &server_keyblock);
+                                       &server_keyblock,
+                                       client_principal, 
+                                       logon_time, NULL);
 
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, 
+                                   client_principal);
+
+               torture_fail(tctx, talloc_asprintf(tctx,
+                                                  "(self test) PAC decoding failed: %s", 
+                                                  nt_errstr(nt_status)));
+       }
+
+       /* Now check that we can read it back */
+       nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info,
+                                           tmp_blob,
+                                           smb_krb5_context->krb5_context,
+                                           &krbtgt_keyblock,
+                                           &server_keyblock,
+                                           client_principal, 
+                                           logon_time, 
+                                           NULL);
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, 
+                                   client_principal);
+               
+               torture_fail(tctx,  
+                            talloc_asprintf(tctx, 
+                                            "(self test) PAC decoding (for logon info) failed: %s", 
+                                            nt_errstr(nt_status)));
+       }
+       
        krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                    &krbtgt_keyblock);
        krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                    &server_keyblock);
-       if (ret) {
-               DEBUG(1, ("PAC decoding failed: %s\n", 
-                         nt_errstr(nt_status)));
-
-               talloc_free(mem_ctx);
-               return False;
+       krb5_free_principal(smb_krb5_context->krb5_context, 
+                           client_principal);
+
+       validation.sam3 = &logon_info->info3;
+       nt_status = make_server_info_netlogon_validation(mem_ctx,
+                                                        "",
+                                                        3, &validation,
+                                                        &server_info_out); 
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               torture_fail(tctx, 
+                            talloc_asprintf(tctx, 
+                                            "(self test) PAC decoding (make server info) failed: %s", 
+                                            nt_errstr(nt_status)));
        }
-
-       talloc_free(mem_ctx);
-       return True;
+       
+       if (!dom_sid_equal(server_info->account_sid, 
+                          server_info_out->account_sid)) {
+               torture_fail(tctx,  
+                            talloc_asprintf(tctx, 
+                                            "(self test) PAC Decode resulted in *different* domain SID: %s != %s",
+                                            dom_sid_string(mem_ctx, server_info->account_sid), 
+                                            dom_sid_string(mem_ctx, server_info_out->account_sid)));
+       }
+       return true;
 }
 
 
 /* This is the PAC generated on my test network, by my test Win2k3 server.
    -- abartlet 2005-07-04
- */
+*/
 
-static const char saved_pac[] = {
+static const uint8_t saved_pac[] = {
        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, 
        0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
        0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
@@ -192,148 +259,390 @@ static const char saved_pac[] = {
 };
 
 /* Check with a known 'well formed' PAC, from my test server */
-static BOOL torture_pac_saved_check(void) 
+static bool torture_pac_saved_check(struct torture_context *tctx)
 {
        NTSTATUS nt_status;
-       TALLOC_CTX *mem_ctx = talloc_named(NULL, 0, "PAC saved check");
        DATA_BLOB tmp_blob, validate_blob;
-       struct PAC_LOGON_INFO *pac_info;
-       struct PAC_DATA pac_data;
+       struct PAC_DATA *pac_data, pac_data2;
+       struct PAC_LOGON_INFO *logon_info;
+       union netr_Validation validation;
+       const char *pac_file, *pac_kdc_key, *pac_member_key;
+       struct auth_serversupplied_info *server_info_out;
+
        krb5_keyblock server_keyblock;
        krb5_keyblock krbtgt_keyblock;
-       uint8_t server_bytes[16];
-       struct samr_Password *krbtgt_bytes;
+       struct samr_Password *krbtgt_bytes, *krbsrv_bytes;
        
        krb5_error_code ret;
-
        struct smb_krb5_context *smb_krb5_context;
 
-       ret = smb_krb5_init_context(mem_ctx, &smb_krb5_context);
+       const char *principal_string;
+       char *broken_principal_string;
+       krb5_principal client_principal;
+       const char *authtime_string;
+       time_t authtime;
+       TALLOC_CTX *mem_ctx = tctx;
 
-       if (ret) {
-               talloc_free(mem_ctx);
-               return False;
-       }
+       torture_assert(tctx, 0 == smb_krb5_init_context(mem_ctx, NULL,
+                                                       &smb_krb5_context),
+                      "smb_krb5_init_context");
+
+       pac_kdc_key = torture_setting_string(tctx, "pac_kdc_key", 
+                                            "B286757148AF7FD252C53603A150B7E7");
+
+       pac_member_key = torture_setting_string(tctx, "pac_member_key", 
+                                               "D217FAEAE5E6B5F95CCC94077AB8A5FC");
+
+       torture_comment(tctx, "Using pac_kdc_key '%s'\n", pac_kdc_key);
+       torture_comment(tctx, "Using pac_member_key '%s'\n", pac_member_key);
 
-       krbtgt_bytes = smbpasswd_gethexpwd(mem_ctx, "B286757148AF7FD252C53603A150B7E7");
+       /* The krbtgt key in use when the above PAC was generated.
+        * This is an arcfour-hmac-md5 key, extracted with our 'net
+        * samdump' tool. */
+       krbtgt_bytes = smbpasswd_gethexpwd(mem_ctx, pac_kdc_key);
        if (!krbtgt_bytes) {
-               DEBUG(0, ("Could not interpret krbtgt key"));
-               talloc_free(mem_ctx);
-               return False;
+               torture_fail(tctx, "(saved test) Could not interpret krbtgt key");
        }
 
-       /* The machine trust account in use when the above PAC 
-          was generated.  It used arcfour-hmac-md5, so this is easy */
-       E_md4hash("iqvwmii8CuEkyY", server_bytes);
+       krbsrv_bytes = smbpasswd_gethexpwd(mem_ctx, pac_member_key);
+       if (!krbsrv_bytes) {
+               torture_fail(tctx, "(saved test) Could not interpret krbsrv key");
+       }
 
        ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
                                 ENCTYPE_ARCFOUR_HMAC,
-                                server_bytes, sizeof(server_bytes),
+                                krbsrv_bytes->hash, sizeof(krbsrv_bytes->hash),
                                 &server_keyblock);
-       if (ret) {
-               DEBUG(1, ("Server Keyblock encoding failed: %s\n", 
-                         smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
-                                                    ret, mem_ctx)));
-
-               talloc_free(mem_ctx);
-               return False;
-       }
+       torture_assert(tctx, !ret,
+                      talloc_asprintf(tctx,
+                                      "(saved test) Server Keyblock encoding failed: %s", 
+                                      smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
+                                                                 ret, mem_ctx)));
 
        ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
                                 ENCTYPE_ARCFOUR_HMAC,
                                 krbtgt_bytes->hash, sizeof(krbtgt_bytes->hash),
                                 &krbtgt_keyblock);
        if (ret) {
-               DEBUG(1, ("Server Keyblock encoding failed: %s\n", 
-                         smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
-                                                    ret, mem_ctx)));
-
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &server_keyblock);
-               talloc_free(mem_ctx);
-               return False;
+               torture_fail(tctx, 
+                            talloc_asprintf(tctx, 
+                                            "(saved test) Server Keyblock encoding failed: %s", 
+                                            smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
+                                                                       ret, mem_ctx)));
        }
 
-       tmp_blob = data_blob_const(saved_pac, sizeof(saved_pac));
+       pac_file = torture_setting_string(tctx, "pac_file", NULL);
+       if (pac_file) {
+               tmp_blob.data = (uint8_t *)file_load(pac_file, &tmp_blob.length, mem_ctx);
+               torture_comment(tctx, "(saved test) Loaded pac of size %ld from %s\n", (long)tmp_blob.length, pac_file);
+       } else {
+               tmp_blob = data_blob_talloc(mem_ctx, saved_pac, sizeof(saved_pac));
+       }
+       
+       dump_data(10,tmp_blob.data,tmp_blob.length);
 
-       /*tmp_blob.data = file_load(lp_parm_string(-1,"torture","pac_file"), &tmp_blob.length);*/
+       principal_string = torture_setting_string(tctx, "pac_client_principal", 
+                                                 "w2003final$@WIN2K3.THINKER.LOCAL");
 
-       dump_data(10,tmp_blob.data,tmp_blob.length);
+       authtime_string = torture_setting_string(tctx, "pac_authtime", "1120440609");
+       authtime = strtoull(authtime_string, NULL, 0);
+
+       ret = krb5_parse_name(smb_krb5_context->krb5_context, principal_string, 
+                             &client_principal);
+       if (ret) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               torture_fail(tctx,  
+                            talloc_asprintf(tctx, 
+                                            "(saved test) parsing of client principal [%s] failed: %s", 
+                                            principal_string, 
+                                            smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx)));
+       }
 
        /* Decode and verify the signaure on the PAC */
-       nt_status = kerberos_decode_pac(mem_ctx, &pac_info,
+       nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
                                        tmp_blob,
-                                       smb_krb5_context,
+                                       smb_krb5_context->krb5_context,
                                        &krbtgt_keyblock,
-                                       &server_keyblock);
-       krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
-                                   &krbtgt_keyblock);
-       krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
-                                   &server_keyblock);
+                                       &server_keyblock, 
+                                       client_principal, authtime, NULL);
        if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(1, ("PAC decoding failed: %s\n", 
-                         nt_errstr(nt_status)));
-
-               talloc_free(mem_ctx);
-               return False;
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+               
+               torture_fail(tctx, talloc_asprintf(tctx, 
+                                                  "(saved test) PAC decoding failed: %s", 
+                                                  nt_errstr(nt_status)));
        }
 
-       nt_status = ndr_pull_struct_blob(&tmp_blob, mem_ctx, &pac_data,
-                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+       /* Parse the PAC again, for the logon info this time */
+       nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info,
+                                           tmp_blob,
+                                           smb_krb5_context->krb5_context,
+                                           &krbtgt_keyblock,
+                                           &server_keyblock,
+                                           client_principal, authtime, NULL);
+
        if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(0,("can't parse the PAC\n"));
-               talloc_free(mem_ctx);
-               return False;
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+       
+               torture_fail(tctx,  
+                            talloc_asprintf(tctx, 
+                                            "(saved test) PAC decoding (for logon info) failed: %s", 
+                                            nt_errstr(nt_status)));
        }
 
-       nt_status = ndr_push_struct_blob(&validate_blob, mem_ctx, &pac_data,
-                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
+       validation.sam3 = &logon_info->info3;
+       nt_status = make_server_info_netlogon_validation(mem_ctx,
+                                                        "",
+                                                        3, &validation,
+                                                        &server_info_out); 
        if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(0, ("PAC push failed: %s\n", nt_errstr(nt_status)));
-               talloc_free(mem_ctx);
-               return False;
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+
+               torture_fail(tctx,  
+                            talloc_asprintf(tctx, 
+                                            "(saved test) PAC decoding (make server info) failed: %s", 
+                                            nt_errstr(nt_status)));
+       }
+
+       if (!pac_file &&
+           !dom_sid_equal(dom_sid_parse_talloc(mem_ctx, 
+                                               "S-1-5-21-3048156945-3961193616-3706469200-1005"), 
+                          server_info_out->account_sid)) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+
+               torture_fail(tctx,  
+                            talloc_asprintf(tctx, 
+                                            "(saved test) PAC Decode resulted in *different* domain SID: %s != %s",
+                                            "S-1-5-21-3048156945-3961193616-3706469200-1005", 
+                                            dom_sid_string(mem_ctx, server_info_out->account_sid)));
+       }
+
+       ret = kerberos_encode_pac(mem_ctx, 
+                                 pac_data,
+                                 smb_krb5_context->krb5_context,
+                                 &krbtgt_keyblock,
+                                 &server_keyblock,
+                                 &validate_blob);
+
+       if (ret != 0) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+
+               torture_fail(tctx, "(saved test) PAC push failed");
+       }
+
+       dump_data(10, validate_blob.data, validate_blob.length);
+
+       /* compare both the length and the data bytes after a
+        * pull/push cycle.  This ensures we use the exact same
+        * pointer, padding etc algorithms as win2k3.
+        */
+       if (tmp_blob.length != validate_blob.length) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+
+               torture_fail(tctx, 
+                            talloc_asprintf(tctx, 
+                                            "(saved test) PAC push failed: original buffer length[%u] != created buffer length[%u]",
+                                            (unsigned)tmp_blob.length, (unsigned)validate_blob.length));
+       }
+
+       if (memcmp(tmp_blob.data, validate_blob.data, tmp_blob.length) != 0) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+
+               DEBUG(0, ("tmp_data:\n"));
+               dump_data(0, tmp_blob.data, tmp_blob.length);
+               DEBUG(0, ("validate_blob:\n"));
+               dump_data(0, validate_blob.data, validate_blob.length);
+
+               torture_fail(tctx, talloc_asprintf(tctx, "(saved test) PAC push failed: length[%u] matches, but data does not", (unsigned)tmp_blob.length));
+       }
+
+       ret = kerberos_create_pac(mem_ctx, 
+                                 server_info_out,
+                                 smb_krb5_context->krb5_context,
+                                 &krbtgt_keyblock,
+                                 &server_keyblock,
+                                 client_principal, authtime,
+                                 &validate_blob);
+
+       if (ret != 0) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+
+               torture_fail(tctx, "(saved test) regnerated PAC create failed");
        }
 
        dump_data(10,validate_blob.data,validate_blob.length);
 
-       /* all we can check is the length of the buffers,
-        * to check that the alignment and padding is ok,
-        * we can't compare the bytes, because we use a different algorithm
-        * to create the pointer values
+       /* compare both the length and the data bytes after a
+        * pull/push cycle.  This ensures we use the exact same
+        * pointer, padding etc algorithms as win2k3.
         */
        if (tmp_blob.length != validate_blob.length) {
-               DEBUG(0, ("PAC push failed: orignial buffer length[%u] != created buffer length[%u]\n",
-                               (unsigned)tmp_blob.length, (unsigned)validate_blob.length));
-               talloc_free(mem_ctx);
-               return False;
+               nt_status = ndr_pull_struct_blob(&validate_blob, mem_ctx, &pac_data2,
+                                                (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+               torture_assert_ntstatus_ok(tctx, nt_status, "can't parse the PAC");
+               
+               NDR_PRINT_DEBUG(PAC_DATA, pac_data);
+
+               NDR_PRINT_DEBUG(PAC_DATA, &pac_data2);
+
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+
+               torture_fail(tctx, talloc_asprintf(tctx, 
+                                                  "(saved test) PAC regenerate failed: original buffer length[%u] != created buffer length[%u]",
+                                                  (unsigned)tmp_blob.length, (unsigned)validate_blob.length));
        }
 
        if (memcmp(tmp_blob.data, validate_blob.data, tmp_blob.length) != 0) {
-               DEBUG(0, ("PAC push failed: length[%u] matches, but data does not\n",
-                         (unsigned)tmp_blob.length));
-               talloc_free(mem_ctx);
-               return False;
+               nt_status = ndr_pull_struct_blob(&validate_blob, mem_ctx, &pac_data2,
+                                                (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+               torture_assert_ntstatus_ok(tctx, nt_status, "can't parse the PAC");
+               
+               NDR_PRINT_DEBUG(PAC_DATA, pac_data);
+
+               NDR_PRINT_DEBUG(PAC_DATA, &pac_data2);
+
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+
+               DEBUG(0, ("tmp_data:\n"));
+               dump_data(0, tmp_blob.data, tmp_blob.length);
+               DEBUG(0, ("validate_blob:\n"));
+               dump_data(0, validate_blob.data, validate_blob.length);
+
+               torture_fail(tctx, talloc_asprintf(tctx, 
+                                                  "(saved test) PAC regenerate failed: length[%u] matches, but data does not", (unsigned)tmp_blob.length));
        }
 
-       talloc_free(mem_ctx);
-       return True;
-}
+       /* Break the auth time, to ensure we check this vital detail (not setting this caused all the pain in the first place... */
+       nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
+                                       tmp_blob,
+                                       smb_krb5_context->krb5_context,
+                                       &krbtgt_keyblock,
+                                       &server_keyblock,
+                                       client_principal, 
+                                       authtime + 1, NULL);
+       if (NT_STATUS_IS_OK(nt_status)) {
 
-BOOL torture_pac(void) 
-{
-       BOOL ret = True;
-       ret &= torture_pac_self_check();
-       ret &= torture_pac_saved_check();
-       return ret;
-}
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+               torture_fail(tctx, "(saved test) PAC decoding DID NOT fail on broken auth time (time + 1)");
+       }
 
-#else 
+       /* Break the client principal */
+       krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
 
-BOOL torture_pac(void) 
-{
-       printf("Cannot do PAC test without Krb5\n");
-       return False;
+       broken_principal_string = talloc_strdup(mem_ctx, principal_string);
+       broken_principal_string[0]++;
+
+       ret = krb5_parse_name(smb_krb5_context->krb5_context,
+                             broken_principal_string, &client_principal);
+       if (ret) {
+
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               torture_fail(tctx, talloc_asprintf(tctx, 
+                                                  "(saved test) parsing of broken client principal failed: %s", 
+                                                  smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx)));
+       }
+
+       nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
+                                       tmp_blob,
+                                       smb_krb5_context->krb5_context,
+                                       &krbtgt_keyblock,
+                                       &server_keyblock,
+                                       client_principal, 
+                                       authtime, NULL);
+       if (NT_STATUS_IS_OK(nt_status)) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               torture_fail(tctx, "(saved test) PAC decoding DID NOT fail on modified principal");
+       }
+
+       /* Finally...  Bugger up the signature, and check we fail the checksum */
+       tmp_blob.data[tmp_blob.length - 2]++;
+
+       nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
+                                       tmp_blob,
+                                       smb_krb5_context->krb5_context,
+                                       &krbtgt_keyblock,
+                                       &server_keyblock,
+                                       client_principal, 
+                                       authtime, NULL);
+       if (NT_STATUS_IS_OK(nt_status)) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &krbtgt_keyblock);
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               torture_fail(tctx, "(saved test) PAC decoding DID NOT fail on broken checksum");
+       }
+
+       krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                   &krbtgt_keyblock);
+       krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                   &server_keyblock);
+       return true;
 }
 
-#endif
+_PUBLIC_ struct torture_suite *torture_pac(TALLOC_CTX *mem_ctx)
+{
+       struct torture_suite *suite = torture_suite_create(mem_ctx, "PAC");
+
+       torture_suite_add_simple_test(suite, "self check", 
+                                     torture_pac_self_check);
+
+       torture_suite_add_simple_test(suite, "saved check",
+                                     torture_pac_saved_check);
 
+       return suite;
+}