Heimdal provides Kerberos PAC parsing routines. Use them.
[metze/samba/wip.git] / source / auth / kerberos / kerberos_pac.c
index 705f66a64b0e37651a2067d0ca7610704bb7b0b1..9ebace32cb5b188675a9821177b75150be95772f 100644 (file)
@@ -3,7 +3,7 @@
 
    Create and parse the krb5 PAC
    
-   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005,2008
    Copyright (C) Andrew Tridgell 2001
    Copyright (C) Luke Howard 2002-2003
    Copyright (C) Stefan Metzmacher 2004-2005
@@ -25,6 +25,7 @@
 
 #include "includes.h"
 #include "system/kerberos.h"
+#include "auth/auth.h"
 #include "auth/kerberos/kerberos.h"
 #include "librpc/gen_ndr/ndr_krb5pac.h"
 #include "lib/ldb/include/ldb.h"
@@ -66,6 +67,7 @@ static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
 }
 
  NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
+                             struct smb_iconv_convenience *iconv_convenience,
                              struct PAC_DATA **pac_data_out,
                              DATA_BLOB blob,
                              krb5_context context,
@@ -86,7 +88,6 @@ static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
        struct PAC_LOGON_NAME *logon_name = NULL;
        struct PAC_DATA *pac_data;
        struct PAC_DATA_RAW *pac_data_raw;
-       struct smb_iconv_convenience *iconv_convenience = lp_iconv_convenience(global_loadparm);
 
        DATA_BLOB *srv_sig_blob = NULL;
        DATA_BLOB *kdc_sig_blob = NULL;
@@ -113,8 +114,9 @@ static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
                return NT_STATUS_NO_MEMORY;
        }
 
-       ndr_err = ndr_pull_struct_blob(&blob, pac_data, pac_data,
-                                      (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+       ndr_err = ndr_pull_struct_blob(&blob, pac_data, 
+                       iconv_convenience, pac_data,
+                      (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                status = ndr_map_error2ntstatus(ndr_err);
                DEBUG(0,("can't parse the PAC: %s\n",
@@ -128,7 +130,8 @@ static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       ndr_err = ndr_pull_struct_blob(&blob, pac_data_raw, pac_data_raw,
+       ndr_err = ndr_pull_struct_blob(&blob, pac_data_raw, 
+                                      iconv_convenience, pac_data_raw,
                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                status = ndr_map_error2ntstatus(ndr_err);
@@ -208,7 +211,8 @@ static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
        /* Find and zero out the signatures, as required by the signing algorithm */
 
        /* We find the data blobs above, now we parse them to get at the exact portion we should zero */
-       ndr_err = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe, kdc_sig_wipe,
+       ndr_err = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe, 
+                                      iconv_convenience, kdc_sig_wipe,
                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                status = ndr_map_error2ntstatus(ndr_err);
@@ -217,7 +221,8 @@ static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
                return status;
        }
        
-       ndr_err = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe, srv_sig_wipe,
+       ndr_err = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe, 
+                                      iconv_convenience, srv_sig_wipe,
                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                status = ndr_map_error2ntstatus(ndr_err);
@@ -336,6 +341,7 @@ static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
 }
 
 _PUBLIC_  NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
+                                 struct smb_iconv_convenience *iconv_convenience,
                                  struct PAC_LOGON_INFO **logon_info,
                                  DATA_BLOB blob,
                                  krb5_context context,
@@ -348,7 +354,9 @@ _PUBLIC_  NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
        NTSTATUS nt_status;
        struct PAC_DATA *pac_data;
        int i;
-       nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
+       nt_status = kerberos_decode_pac(mem_ctx, 
+                                       iconv_convenience,
+                                       &pac_data,
                                        blob,
                                        context,
                                        krbtgt_keyblock,
@@ -419,6 +427,7 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
 }
 
  krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
+                                    struct smb_iconv_convenience *iconv_convenience,
                                    struct PAC_DATA *pac_data,
                                    krb5_context context,
                                    const krb5_keyblock *krbtgt_keyblock,
@@ -481,7 +490,7 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
        memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
 
        ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx, 
-                                      lp_iconv_convenience(global_loadparm),
+                                      iconv_convenience,
                                       pac_data,
                                       (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -506,7 +515,7 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
 
        /* And push it out again, this time to the world.  This relies on determanistic pointer values */
        ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx, 
-                                      lp_iconv_convenience(global_loadparm),
+                                      iconv_convenience,
                                       pac_data,
                                       (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -523,6 +532,7 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
 
 
  krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
+                                    struct smb_iconv_convenience *iconv_convenience,
                                     struct auth_serversupplied_info *server_info,
                                     krb5_context context,
                                     const krb5_keyblock *krbtgt_keyblock,
@@ -635,6 +645,7 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
        unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
 
        ret = kerberos_encode_pac(mem_ctx, 
+                                 iconv_convenience,
                                  pac_data, 
                                  context,
                                  krbtgt_keyblock,
@@ -644,3 +655,123 @@ static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
        return ret;
 }
 
+krb5_error_code kerberos_pac_to_server_info(TALLOC_CTX *mem_ctx,
+                                               struct smb_iconv_convenience *iconv_convenience,
+                                               krb5_pac pac,
+                                               krb5_context context,
+                                               struct auth_serversupplied_info **server_info) 
+{
+       NTSTATUS nt_status;
+       enum ndr_err_code ndr_err;
+       krb5_error_code ret;
+
+       DATA_BLOB pac_logon_info_in, pac_srv_checksum_in, pac_kdc_checksum_in;
+       krb5_data k5pac_logon_info_in, k5pac_srv_checksum_in, k5pac_kdc_checksum_in;
+
+       union PAC_INFO info;
+       union netr_Validation validation;
+       struct auth_serversupplied_info *server_info_out;
+
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+       if (!tmp_ctx) {
+               return ENOMEM;
+       }
+
+       ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_LOGON_INFO, &k5pac_logon_info_in);
+       if (ret != 0) {
+               talloc_free(tmp_ctx);
+               return EINVAL;
+       }
+
+       pac_logon_info_in = data_blob_const(k5pac_logon_info_in.data, k5pac_logon_info_in.length);
+
+       ndr_err = ndr_pull_union_blob(&pac_logon_info_in, tmp_ctx, iconv_convenience, &info,
+                                     PAC_TYPE_LOGON_INFO,
+                                     (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
+       krb5_data_free(&k5pac_logon_info_in);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err) || !info.logon_info.info) {
+               nt_status = ndr_map_error2ntstatus(ndr_err);
+               DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
+               talloc_free(tmp_ctx);
+               return EINVAL;
+       }
+
+       /* Pull this right into the normal auth sysstem structures */
+       validation.sam3 = &info.logon_info.info->info3;
+       nt_status = make_server_info_netlogon_validation(mem_ctx,
+                                                        "",
+                                                        3, &validation,
+                                                        &server_info_out); 
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               talloc_free(tmp_ctx);
+               return EINVAL;
+       }
+       
+       ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_SRV_CHECKSUM, &k5pac_srv_checksum_in);
+       if (ret != 0) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       pac_srv_checksum_in = data_blob_const(k5pac_srv_checksum_in.data, k5pac_srv_checksum_in.length);
+               
+       ndr_err = ndr_pull_struct_blob(&pac_srv_checksum_in, server_info_out, 
+                                      iconv_convenience, &server_info_out->pac_srv_sig,
+                                      (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
+       krb5_data_free(&k5pac_srv_checksum_in);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               nt_status = ndr_map_error2ntstatus(ndr_err);
+               DEBUG(0,("can't parse the KDC signature: %s\n",
+                       nt_errstr(nt_status)));
+               return EINVAL;
+       }
+
+       ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_KDC_CHECKSUM, &k5pac_kdc_checksum_in);
+       if (ret != 0) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       pac_kdc_checksum_in = data_blob_const(k5pac_kdc_checksum_in.data, k5pac_kdc_checksum_in.length);
+               
+       ndr_err = ndr_pull_struct_blob(&pac_kdc_checksum_in, server_info_out, 
+                                      iconv_convenience, &server_info_out->pac_kdc_sig,
+                                      (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
+       krb5_data_free(&k5pac_kdc_checksum_in);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               nt_status = ndr_map_error2ntstatus(ndr_err);
+               DEBUG(0,("can't parse the KDC signature: %s\n",
+                       nt_errstr(nt_status)));
+               return EINVAL;
+       }
+
+       *server_info = server_info_out;
+       
+       return 0;
+}
+
+
+NTSTATUS kerberos_pac_blob_to_server_info(TALLOC_CTX *mem_ctx,
+                                                    struct smb_iconv_convenience *iconv_convenience,
+                                                    DATA_BLOB pac_blob, 
+                                                    krb5_context context,
+                                                    struct auth_serversupplied_info **server_info) 
+{
+       krb5_error_code ret;
+       krb5_pac pac;
+       ret = krb5_pac_parse(context, 
+                            pac_blob.data, pac_blob.length, 
+                            &pac);
+       if (ret) {
+               return map_nt_error_from_unix(ret);
+       }
+
+
+       ret = kerberos_pac_to_server_info(mem_ctx, iconv_convenience, pac, context, server_info);
+       krb5_pac_free(context, pac);
+       if (ret) {
+               return map_nt_error_from_unix(ret);
+       }
+       return NT_STATUS_OK;
+}