Heimdal provides Kerberos PAC parsing routines. Use them.
[kai/samba.git] / source4 / torture / auth / pac.c
index 5218d73d8f21c49a797cfccd4e67c05822a127f9..42901f1eff155d9fdaf48920c92d6810f316d885 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"
+#include "auth/session_proto.h"
 
-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_DATA *pac_data;
        struct PAC_LOGON_INFO *logon_info;
@@ -50,12 +51,16 @@ static BOOL torture_pac_self_check(void)
        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,
+                                                       tctx->lp_ctx,
+                                                       &smb_krb5_context), 
+                      "smb_krb5_init_context");
 
        generate_random_buffer(server_bytes, 16);
        generate_random_buffer(krbtgt_bytes, 16);
@@ -64,137 +69,185 @@ static BOOL torture_pac_self_check(void)
                                 ENCTYPE_ARCFOUR_HMAC,
                                 server_bytes, sizeof(server_bytes),
                                 &server_keyblock);
-       if (ret) {
-               printf("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) {
-               printf("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 */
-       nt_status = auth_anonymous_server_info(mem_ctx, &server_info);
+       nt_status = auth_anonymous_server_info(mem_ctx, lp_netbios_name(tctx->lp_ctx), &server_info);
 
        if (!NT_STATUS_IS_OK(nt_status)) {
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &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_create_pac(mem_ctx, server_info, 
+       ret = kerberos_create_pac(mem_ctx, 
+                                 lp_iconv_convenience(tctx->lp_ctx),
+                                 server_info, 
                                  smb_krb5_context->krb5_context,  
                                  &krbtgt_keyblock,
                                  &server_keyblock,
+                                 client_principal,
+                                 logon_time,
                                  &tmp_blob);
        
        if (ret) {
-               printf("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_data,
+       /* Now check that we can read it back (using full decode and validate) */
+       nt_status = kerberos_decode_pac(mem_ctx, 
+                                       lp_iconv_convenience(tctx->lp_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);
-               DEBUG(1, ("PAC decoding failed: %s\n", 
-                         nt_errstr(nt_status)));
+               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 we can read it back (using Heimdal's pac parsing) */
+       nt_status = kerberos_pac_blob_to_server_info(mem_ctx, 
+                                                    lp_iconv_convenience(tctx->lp_ctx),
+                                                    tmp_blob, 
+                                                    smb_krb5_context->krb5_context,
+                                                    &server_info_out);
 
-               talloc_free(mem_ctx);
-               return False;
+       if (!dom_sid_equal(server_info->account_sid, 
+                          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, 
+                                            "(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)));
        }
+       talloc_free(server_info_out);
 
-       /* Now check that we can read it back */
-       nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info,
+       /* Now check that we can read it back (yet again) */
+       nt_status = kerberos_pac_logon_info(mem_ctx, 
+                                           lp_iconv_convenience(tctx->lp_ctx),
+                                           &logon_info,
                                            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);
-               printf("PAC decoding (for logon info) failed: %s\n"
-                      nt_errstr(nt_status));
+               krb5_free_principal(smb_krb5_context->krb5_context
+                                   client_principal);
                
-               talloc_free(mem_ctx);
-               return False;
+               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);
+       krb5_free_principal(smb_krb5_context->krb5_context, 
+                           client_principal);
 
+       /* And make a server info from the samba-parsed PAC */
        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)) {
-               printf("PAC decoding (make server info) failed: %s\n", 
-                      nt_errstr(nt_status));
-               
-               talloc_free(mem_ctx);
-               return False;
+               torture_fail(tctx, 
+                            talloc_asprintf(tctx, 
+                                            "(self test) PAC decoding (make server info) failed: %s", 
+                                            nt_errstr(nt_status)));
        }
        
        if (!dom_sid_equal(server_info->account_sid, 
                           server_info_out->account_sid)) {
-               printf("PAC Decode resulted in *different* domain SID: %s != %s\n",
-                      dom_sid_string(mem_ctx, server_info->account_sid)
-                      dom_sid_string(mem_ctx, server_info_out->account_sid));
-               talloc_free(mem_ctx);
-               return False;
+               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)));
        }
-       
-       talloc_free(mem_ctx);
-       return True;
+       return true;
 }
 
 
 /* This is the PAC generated on my test network, by my test Win2k3 server.
    -- abartlet 2005-07-04
- */
+*/
 
 static const uint8_t saved_pac[] = {
        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00, 
@@ -239,137 +292,201 @@ static const uint8_t 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");
+       enum ndr_err_code ndr_err;
        DATA_BLOB tmp_blob, validate_blob;
-       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;
+       krb5_keyblock krbtgt_keyblock, *krbtgt_keyblock_p;
        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,
+                                                       tctx->lp_ctx,
+                                                       &smb_krb5_context),
+                      "smb_krb5_init_context");
 
-       pac_kdc_key = lp_parm_string(-1,"torture","pac_kdc_key");
-       if (pac_kdc_key == NULL) {
-               pac_kdc_key = "B286757148AF7FD252C53603A150B7E7";
-       }
+       pac_kdc_key = torture_setting_string(tctx, "pac_kdc_key", 
+                                            "B286757148AF7FD252C53603A150B7E7");
 
-       pac_member_key = lp_parm_string(-1,"torture","pac_member_key");
-       if (pac_member_key == NULL) {
-               pac_member_key = "D217FAEAE5E6B5F95CCC94077AB8A5FC";
-       }
+       pac_member_key = torture_setting_string(tctx, "pac_member_key", 
+                                               "D217FAEAE5E6B5F95CCC94077AB8A5FC");
 
-       printf("Using pac_kdc_key '%s'\n", pac_kdc_key);
-       printf("Using pac_member_key '%s'\n", pac_member_key);
+       torture_comment(tctx, "Using pac_kdc_key '%s'\n", pac_kdc_key);
+       torture_comment(tctx, "Using pac_member_key '%s'\n", pac_member_key);
 
        /* 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;
+       if (*pac_kdc_key == 0) {
+               krbtgt_bytes = NULL;
+       } else {
+               krbtgt_bytes = smbpasswd_gethexpwd(mem_ctx, pac_kdc_key);
+               if (!krbtgt_bytes) {
+                       torture_fail(tctx, "(saved test) Could not interpret krbtgt key");
+               }
        }
 
        krbsrv_bytes = smbpasswd_gethexpwd(mem_ctx, pac_member_key);
        if (!krbsrv_bytes) {
-               DEBUG(0, ("Could not interpret krbsrv key"));
-               talloc_free(mem_ctx);
-               return False;
+               torture_fail(tctx, "(saved test) Could not interpret krbsrv key");
        }
 
        ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
                                 ENCTYPE_ARCFOUR_HMAC,
                                 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;
-       }
-
-       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_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)));
+
+       if (krbtgt_bytes) {
+               ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
+                                        ENCTYPE_ARCFOUR_HMAC,
+                                        krbtgt_bytes->hash, sizeof(krbtgt_bytes->hash),
+                                        &krbtgt_keyblock);
+               if (ret) {
+                       krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                                   &server_keyblock);
+                       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)));
+               }
+               krbtgt_keyblock_p = &krbtgt_keyblock;
+       } else {
+               krbtgt_keyblock_p = NULL;
        }
 
-       pac_file = lp_parm_string(-1,"torture","pac_file");
+       pac_file = torture_setting_string(tctx, "pac_file", NULL);
        if (pac_file) {
-               tmp_blob.data = file_load(pac_file, &tmp_blob.length, mem_ctx);
-               printf("Loaded pac of size %d from %s\n", tmp_blob.length, 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(saved_pac, sizeof(saved_pac));
-               file_save("x.dat", tmp_blob.data, tmp_blob.length);
+               tmp_blob = data_blob_talloc(mem_ctx, saved_pac, sizeof(saved_pac));
        }
        
        dump_data(10,tmp_blob.data,tmp_blob.length);
 
+       principal_string = torture_setting_string(tctx, "pac_client_principal", 
+                                                 "w2003final$@WIN2K3.THINKER.LOCAL");
+
+       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_p);
+               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_data,
+       nt_status = kerberos_decode_pac(mem_ctx, 
+                                       lp_iconv_convenience(tctx->lp_ctx),
+                                       &pac_data,
                                        tmp_blob,
-                                       smb_krb5_context,
-                                       &krbtgt_keyblock,
-                                       &server_keyblock);
+                                       smb_krb5_context->krb5_context,
+                                       krbtgt_keyblock_p,
+                                       &server_keyblock, 
+                                       client_principal, authtime, NULL);
        if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(1, ("PAC decoding failed: %s\n", 
-                         nt_errstr(nt_status)));
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           krbtgt_keyblock_p);
+               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)));
+       }
 
+       /* Now check we can read it back (using Heimdal's pac parsing) */
+       nt_status = kerberos_pac_blob_to_server_info(mem_ctx, 
+                                                    lp_iconv_convenience(tctx->lp_ctx),
+                                                    tmp_blob, 
+                                                    smb_krb5_context->krb5_context,
+                                                    &server_info_out);
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
-                                           &krbtgt_keyblock);
+                                           krbtgt_keyblock_p);
                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, 
+                                                  "(saved test) Heimdal PAC decoding failed: %s", 
+                                                  nt_errstr(nt_status)));
        }
 
-       /* 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,
-                                           &krbtgt_keyblock,
+       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_p);
+               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) Heimdal 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)));
+       }
+
+       talloc_free(server_info_out);
+
+       /* Parse the PAC again, for the logon info this time (using Samba4's parsing) */
+       nt_status = kerberos_pac_logon_info(mem_ctx, 
+                                           lp_iconv_convenience(tctx->lp_ctx),
+                                           &logon_info,
+                                           tmp_blob,
+                                           smb_krb5_context->krb5_context,
+                                           krbtgt_keyblock_p,
+                                           &server_keyblock,
+                                           client_principal, authtime, NULL);
 
        if (!NT_STATUS_IS_OK(nt_status)) {
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
-                                           &krbtgt_keyblock);
+                                           krbtgt_keyblock_p);
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &server_keyblock);
-               printf("PAC decoding (for logon info) failed: %s\n", 
-                         nt_errstr(nt_status));
-
-               talloc_free(mem_ctx);
-               return False;
+               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)));
        }
 
        validation.sam3 = &logon_info->info3;
@@ -379,48 +496,111 @@ static BOOL torture_pac_saved_check(void)
                                                         &server_info_out); 
        if (!NT_STATUS_IS_OK(nt_status)) {
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
-                                           &krbtgt_keyblock);
+                                           krbtgt_keyblock_p);
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
-                                   &server_keyblock);
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
 
-               printf("PAC decoding (make server info) failed: %s\n", 
-                      nt_errstr(nt_status));
-               
-               talloc_free(mem_ctx);
-               return False;
+               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"), 
+           !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);
+                                           krbtgt_keyblock_p);
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
 
-               printf("PAC Decode resulted in *different* domain SID: %s != %s\n",
-                      "S-1-5-21-3048156945-3961193616-3706469200-1005", 
-                      dom_sid_string(mem_ctx, server_info_out->account_sid));
-               talloc_free(mem_ctx);
-               return False;
+               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)));
+       }
+
+       if (krbtgt_bytes == NULL) {
+               torture_comment(tctx, "skipping PAC encoding tests as non kdc key\n");
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+               return true;
        }
 
        ret = kerberos_encode_pac(mem_ctx, 
+                                 lp_iconv_convenience(tctx->lp_ctx),
                                  pac_data,
                                  smb_krb5_context->krb5_context,
-                                 &krbtgt_keyblock,
+                                 krbtgt_keyblock_p,
                                  &server_keyblock,
                                  &validate_blob);
 
        if (ret != 0) {
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
-                                           &krbtgt_keyblock);
+                                           krbtgt_keyblock_p);
+               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_p);
+               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_p);
+               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, 
+                                 lp_iconv_convenience(tctx->lp_ctx),
+                                 server_info_out,
+                                 smb_krb5_context->krb5_context,
+                                 krbtgt_keyblock_p,
+                                 &server_keyblock,
+                                 client_principal, authtime,
+                                 &validate_blob);
+
+       if (ret != 0) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           krbtgt_keyblock_p);
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
 
-               DEBUG(0, ("PAC push failed\n"));
-               talloc_free(mem_ctx);
-               return False;
+               torture_fail(tctx, "(saved test) regnerated PAC create failed");
        }
 
        dump_data(10,validate_blob.data,validate_blob.length);
@@ -430,61 +610,143 @@ static BOOL torture_pac_saved_check(void)
         * pointer, padding etc algorithms as win2k3.
         */
        if (tmp_blob.length != validate_blob.length) {
+               ndr_err = ndr_pull_struct_blob(&validate_blob, mem_ctx, 
+                                              lp_iconv_convenience(tctx->lp_ctx), &pac_data2,
+                                              (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+               nt_status = ndr_map_error2ntstatus(ndr_err);
+               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);
+                                           krbtgt_keyblock_p);
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &server_keyblock);
+               krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
 
-               DEBUG(0, ("PAC push failed: original buffer length[%u] != created buffer length[%u]\n",
-                               (unsigned)tmp_blob.length, (unsigned)validate_blob.length));
-               talloc_free(mem_ctx);
-               return False;
+               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) {
+               ndr_err = ndr_pull_struct_blob(&validate_blob, mem_ctx, 
+                                              lp_iconv_convenience(tctx->lp_ctx), &pac_data2,
+                                              (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+               nt_status = ndr_map_error2ntstatus(ndr_err);
+               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);
+                                           krbtgt_keyblock_p);
                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);
 
-               DEBUG(0, ("PAC push failed: length[%u] matches, but data does not\n",
-                         (unsigned)tmp_blob.length));
-               talloc_free(mem_ctx);
-               return False;
+               torture_fail(tctx, talloc_asprintf(tctx, 
+                                                  "(saved test) PAC regenerate failed: length[%u] matches, but data does not", (unsigned)tmp_blob.length));
+       }
+
+       /* 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, 
+                                       lp_iconv_convenience(tctx->lp_ctx),
+                                       &pac_data,
+                                       tmp_blob,
+                                       smb_krb5_context->krb5_context,
+                                       krbtgt_keyblock_p,
+                                       &server_keyblock,
+                                       client_principal, 
+                                       authtime + 1, NULL);
+       if (NT_STATUS_IS_OK(nt_status)) {
+
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           krbtgt_keyblock_p);
+               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)");
+       }
+
+       /* Break the client principal */
+       krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
+
+       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_p);
+               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, 
+                                       lp_iconv_convenience(tctx->lp_ctx),
+                                       &pac_data,
+                                       tmp_blob,
+                                       smb_krb5_context->krb5_context,
+                                       krbtgt_keyblock_p,
+                                       &server_keyblock,
+                                       client_principal, 
+                                       authtime, NULL);
+       if (NT_STATUS_IS_OK(nt_status)) {
+               krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
+                                           krbtgt_keyblock_p);
+               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,
+       nt_status = kerberos_decode_pac(mem_ctx, 
+                                       lp_iconv_convenience(tctx->lp_ctx),
+                                       &pac_data,
                                        tmp_blob,
-                                       smb_krb5_context,
-                                       &krbtgt_keyblock,
-                                       &server_keyblock);
+                                       smb_krb5_context->krb5_context,
+                                       krbtgt_keyblock_p,
+                                       &server_keyblock,
+                                       client_principal, 
+                                       authtime, NULL);
        if (NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(1, ("PAC decoding DID NOT fail on broken checksum\n"));
-
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
-                                           &krbtgt_keyblock);
+                                           krbtgt_keyblock_p);
                krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                            &server_keyblock);
-               talloc_free(mem_ctx);
-               return False;
+               torture_fail(tctx, "(saved test) PAC decoding DID NOT fail on broken checksum");
        }
 
        krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
-                                   &krbtgt_keyblock);
+                                   krbtgt_keyblock_p);
        krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
                                    &server_keyblock);
-
-       talloc_free(mem_ctx);
-       return True;
+       return true;
 }
 
-BOOL torture_pac(void) 
+struct torture_suite *torture_pac(TALLOC_CTX *mem_ctx)
 {
-       BOOL ret = True;
-       ret &= torture_pac_self_check();
-       ret &= torture_pac_saved_check();
-       return ret;
+       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;
 }