s4-kdc: Implement mit_samba_reget_pac()
authorAndreas Schneider <asn@samba.org>
Fri, 30 Sep 2016 05:43:31 +0000 (07:43 +0200)
committerAndreas Schneider <asn@cryptomilk.org>
Sat, 29 Apr 2017 21:31:12 +0000 (23:31 +0200)
Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Andrew Bartlet <abartlet@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source4/kdc/mit_samba.c
source4/kdc/mit_samba.h

index 82a6dfddbe12a72bf3ee0ed7975b850d50708d90..333714e010956af77340263e654f3c0131f19f25 100644 (file)
@@ -30,6 +30,7 @@
 #include "kdc/sdb.h"
 #include "kdc/sdb_kdb.h"
 #include "auth/kerberos/kerberos.h"
 #include "kdc/sdb.h"
 #include "kdc/sdb_kdb.h"
 #include "auth/kerberos/kerberos.h"
+#include "auth/kerberos/pac_utils.h"
 #include "kdc/samba_kdc.h"
 #include "kdc/pac-glue.h"
 #include "kdc/db-glue.h"
 #include "kdc/samba_kdc.h"
 #include "kdc/pac-glue.h"
 #include "kdc/db-glue.h"
@@ -468,6 +469,388 @@ done:
        return ret;
 }
 
        return ret;
 }
 
+krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
+                                   krb5_context context,
+                                   int flags,
+                                   krb5_const_principal client_principal,
+                                   krb5_db_entry *client,
+                                   krb5_db_entry *server,
+                                   krb5_db_entry *krbtgt,
+                                   krb5_keyblock *krbtgt_keyblock,
+                                   krb5_pac *pac)
+{
+       TALLOC_CTX *tmp_ctx;
+       krb5_error_code code;
+       NTSTATUS nt_status;
+       DATA_BLOB *pac_blob = NULL;
+       DATA_BLOB *upn_blob = NULL;
+       DATA_BLOB *deleg_blob = NULL;
+       struct samba_kdc_entry *client_skdc_entry = NULL;
+       struct samba_kdc_entry *krbtgt_skdc_entry;
+       bool is_in_db = false;
+       bool is_untrusted = false;
+       size_t num_types = 0;
+       uint32_t *types = NULL;
+       uint32_t forced_next_type = 0;
+       size_t i = 0;
+       ssize_t logon_info_idx = -1;
+       ssize_t delegation_idx = -1;
+       ssize_t logon_name_idx = -1;
+       ssize_t upn_dns_info_idx = -1;
+       ssize_t srv_checksum_idx = -1;
+       ssize_t kdc_checksum_idx = -1;
+       krb5_pac new_pac = NULL;
+       bool ok;
+
+       if (client != NULL) {
+               client_skdc_entry =
+                       talloc_get_type_abort(client->e_data,
+                                             struct samba_kdc_entry);
+
+               /* The user account may be set not to want the PAC */
+               ok = samba_princ_needs_pac(client_skdc_entry);
+               if (!ok) {
+                       return EINVAL;
+               }
+       }
+
+       if (krbtgt == NULL) {
+               return EINVAL;
+       }
+       krbtgt_skdc_entry =
+               talloc_get_type_abort(krbtgt->e_data,
+                                     struct samba_kdc_entry);
+
+       tmp_ctx = talloc_named(ctx, 0, "mit_samba_reget_pac context");
+       if (tmp_ctx == NULL) {
+               return ENOMEM;
+       }
+
+       code = samba_krbtgt_is_in_db(krbtgt_skdc_entry,
+                                    &is_in_db,
+                                    &is_untrusted);
+       if (code != 0) {
+               goto done;
+       }
+
+       if (is_untrusted) {
+               if (client == NULL) {
+                       code = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+                       goto done;
+               }
+
+               nt_status = samba_kdc_get_pac_blobs(tmp_ctx,
+                                                   client_skdc_entry,
+                                                   &pac_blob,
+                                                   NULL,
+                                                   &upn_blob);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       code = EINVAL;
+                       goto done;
+               }
+       } else {
+               struct PAC_SIGNATURE_DATA *pac_srv_sig;
+               struct PAC_SIGNATURE_DATA *pac_kdc_sig;
+
+               pac_blob = talloc_zero(tmp_ctx, DATA_BLOB);
+               if (pac_blob == NULL) {
+                       code = ENOMEM;
+                       goto done;
+               }
+
+               pac_srv_sig = talloc_zero(tmp_ctx, struct PAC_SIGNATURE_DATA);
+               if (pac_srv_sig == NULL) {
+                       code = ENOMEM;
+                       goto done;
+               }
+
+               pac_kdc_sig = talloc_zero(tmp_ctx, struct PAC_SIGNATURE_DATA);
+               if (pac_kdc_sig == NULL) {
+                       code = ENOMEM;
+                       goto done;
+               }
+
+               nt_status = samba_kdc_update_pac_blob(tmp_ctx,
+                                                     context,
+                                                     *pac,
+                                                     pac_blob,
+                                                     pac_srv_sig,
+                                                     pac_kdc_sig);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       DEBUG(0, ("Update PAC blob failed: %s\n",
+                                 nt_errstr(nt_status)));
+                       code = EINVAL;
+                       goto done;
+               }
+
+               if (is_in_db) {
+                       /*
+                        * Now check the KDC signature, fetching the correct
+                        * key based on the enc type.
+                        */
+                       code = check_pac_checksum(pac_srv_sig->signature,
+                                                 pac_kdc_sig,
+                                                 context,
+                                                 krbtgt_keyblock);
+                       if (code != 0) {
+                               DBG_INFO("PAC KDC signature failed to verify\n");
+                               goto done;
+                       }
+               }
+       }
+
+       if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
+               deleg_blob = talloc_zero(tmp_ctx, DATA_BLOB);
+               if (deleg_blob == NULL) {
+                       code = ENOMEM;
+                       goto done;
+               }
+
+               nt_status = samba_kdc_update_delegation_info_blob(tmp_ctx,
+                                                                 context,
+                                                                 *pac,
+                                                                 server->princ,
+                                                                 discard_const(client_principal),
+                                                                 deleg_blob);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       DEBUG(0, ("Update delegation info failed: %s\n",
+                                 nt_errstr(nt_status)));
+                       code = EINVAL;
+                       goto done;
+               }
+       }
+
+       /* Check the types of the given PAC */
+       code = krb5_pac_get_types(context, *pac, &num_types, &types);
+       if (code != 0) {
+               goto done;
+       }
+
+       for (i = 0; i < num_types; i++) {
+               switch (types[i]) {
+               case PAC_TYPE_LOGON_INFO:
+                       if (logon_info_idx != -1) {
+                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
+                                           types[i],
+                                           logon_info_idx,
+                                           i);
+                               SAFE_FREE(types);
+                               code = EINVAL;
+                               goto done;
+                       }
+                       logon_info_idx = i;
+                       break;
+               case PAC_TYPE_CONSTRAINED_DELEGATION:
+                       if (delegation_idx != -1) {
+                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
+                                           types[i],
+                                           delegation_idx,
+                                           i);
+                               SAFE_FREE(types);
+                               code = EINVAL;
+                               goto done;
+                       }
+                       delegation_idx = i;
+                       break;
+               case PAC_TYPE_LOGON_NAME:
+                       if (logon_name_idx != -1) {
+                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
+                                           types[i],
+                                           logon_name_idx,
+                                           i);
+                               SAFE_FREE(types);
+                               code = EINVAL;
+                               goto done;
+                       }
+                       logon_name_idx = i;
+                       break;
+               case PAC_TYPE_UPN_DNS_INFO:
+                       if (upn_dns_info_idx != -1) {
+                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
+                                           types[i],
+                                           upn_dns_info_idx,
+                                           i);
+                               SAFE_FREE(types);
+                               code = EINVAL;
+                               goto done;
+                       }
+                       upn_dns_info_idx = i;
+                       break;
+               case PAC_TYPE_SRV_CHECKSUM:
+                       if (srv_checksum_idx != -1) {
+                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
+                                           types[i],
+                                           srv_checksum_idx,
+                                           i);
+                               SAFE_FREE(types);
+                               code = EINVAL;
+                               goto done;
+                       }
+                       srv_checksum_idx = i;
+                       break;
+               case PAC_TYPE_KDC_CHECKSUM:
+                       if (kdc_checksum_idx != -1) {
+                               DBG_WARNING("logon type[%u] twice [%zd] and [%zu]: \n",
+                                           types[i],
+                                           kdc_checksum_idx,
+                                           i);
+                               SAFE_FREE(types);
+                               code = EINVAL;
+                               goto done;
+                       }
+                       kdc_checksum_idx = i;
+                       break;
+               default:
+                       continue;
+               }
+       }
+
+       if (logon_info_idx == -1) {
+               DEBUG(1, ("PAC_TYPE_LOGON_INFO missing\n"));
+               SAFE_FREE(types);
+               code = EINVAL;
+               goto done;
+       }
+       if (logon_name_idx == -1) {
+               DEBUG(1, ("PAC_TYPE_LOGON_NAME missing\n"));
+               SAFE_FREE(types);
+               code = EINVAL;
+               goto done;
+       }
+       if (srv_checksum_idx == -1) {
+               DEBUG(1, ("PAC_TYPE_SRV_CHECKSUM missing\n"));
+               SAFE_FREE(types);
+               code = EINVAL;
+               goto done;
+       }
+       if (kdc_checksum_idx == -1) {
+               DEBUG(1, ("PAC_TYPE_KDC_CHECKSUM missing\n"));
+               SAFE_FREE(types);
+               code = EINVAL;
+               goto done;
+       }
+
+       /* Build an updated PAC */
+       code = krb5_pac_init(context, &new_pac);
+       if (code != 0) {
+               SAFE_FREE(types);
+               goto done;
+       }
+
+       for (i = 0;;) {
+               krb5_data type_data;
+               DATA_BLOB type_blob = data_blob_null;
+               uint32_t type;
+
+               if (forced_next_type != 0) {
+                       /*
+                        * We need to inject possible missing types
+                        */
+                       type = forced_next_type;
+                       forced_next_type = 0;
+               } else if (i < num_types) {
+                       type = types[i];
+                       i++;
+               } else {
+                       break;
+               }
+
+               switch (type) {
+               case PAC_TYPE_LOGON_INFO:
+                       type_blob = *pac_blob;
+
+                       if (delegation_idx == -1 && deleg_blob != NULL) {
+                               /* inject CONSTRAINED_DELEGATION behind */
+                               forced_next_type = PAC_TYPE_CONSTRAINED_DELEGATION;
+                       }
+                       break;
+               case PAC_TYPE_CONSTRAINED_DELEGATION:
+                       if (deleg_blob != NULL) {
+                               type_blob = *deleg_blob;
+                       }
+                       break;
+               case PAC_TYPE_CREDENTIAL_INFO:
+                       /*
+                        * Note that we copy the credential blob,
+                        * as it's only usable with the PKINIT based
+                        * AS-REP reply key, it's only available on the
+                        * host which did the AS-REQ/AS-REP exchange.
+                        *
+                        * This matches Windows 2008R2...
+                        */
+                       break;
+               case PAC_TYPE_LOGON_NAME:
+                       /*
+                        * This is generated in the main KDC code
+                        */
+                       continue;
+               case PAC_TYPE_UPN_DNS_INFO:
+                       /*
+                        * Replace in the RODC case, otherwise
+                        * upn_blob is NULL and we just copy.
+                        */
+                       if (upn_blob != NULL) {
+                               type_blob = *upn_blob;
+                       }
+                       break;
+               case PAC_TYPE_SRV_CHECKSUM:
+                       /*
+                        * This is generated in the main KDC code
+                        */
+                       continue;
+               case PAC_TYPE_KDC_CHECKSUM:
+                       /*
+                        * This is generated in the main KDC code
+                        */
+                       continue;
+               default:
+                       /* just copy... */
+                       break;
+               }
+
+               if (type_blob.length != 0) {
+                       code = smb_krb5_copy_data_contents(&type_data,
+                                                          type_blob.data,
+                                                          type_blob.length);
+                       if (code != 0) {
+                               SAFE_FREE(types);
+                               krb5_pac_free(context, new_pac);
+                               goto done;
+                       }
+               } else {
+                       code = krb5_pac_get_buffer(context,
+                                                  *pac,
+                                                  type,
+                                                  &type_data);
+                       if (code != 0) {
+                               SAFE_FREE(types);
+                               krb5_pac_free(context, new_pac);
+                               goto done;
+                       }
+               }
+
+               code = krb5_pac_add_buffer(context,
+                                          new_pac,
+                                          type,
+                                          &type_data);
+               smb_krb5_free_data_contents(context, &type_data);
+               if (code != 0) {
+                       SAFE_FREE(types);
+                       krb5_pac_free(context, new_pac);
+                       goto done;
+               }
+       }
+
+       SAFE_FREE(types);
+
+       /* We now replace the pac */
+       krb5_pac_free(context, *pac);
+       *pac = new_pac;
+done:
+       talloc_free(tmp_ctx);
+       return code;
+}
+
 /* provide header, function is exported but there are no public headers */
 
 krb5_error_code encode_krb5_padata_sequence(krb5_pa_data *const *rep, krb5_data **code);
 /* provide header, function is exported but there are no public headers */
 
 krb5_error_code encode_krb5_padata_sequence(krb5_pa_data *const *rep, krb5_data **code);
index 37e7d8d5126476f8931b4a9f23abeebbb0cd4c2f..036e77a5299ffd0a66fe8d67bde5ed2655fd4006 100644 (file)
@@ -53,6 +53,16 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
                      krb5_keyblock *client_key,
                      krb5_pac *pac);
 
                      krb5_keyblock *client_key,
                      krb5_pac *pac);
 
+krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
+                                   krb5_context context,
+                                   int flags,
+                                   krb5_const_principal client_principal,
+                                   krb5_db_entry *client,
+                                   krb5_db_entry *server,
+                                   krb5_db_entry *krbtgt,
+                                   krb5_keyblock *krbtgt_keyblock,
+                                   krb5_pac *pac);
+
 int mit_samba_update_pac_data(struct mit_samba_context *ctx,
                              krb5_db_entry *client,
                              DATA_BLOB *pac_data,
 int mit_samba_update_pac_data(struct mit_samba_context *ctx,
                              krb5_db_entry *client,
                              DATA_BLOB *pac_data,