/* Unix SMB/CIFS implementation. PAC Glue between Samba and the KDC Copyright (C) Catalyst.Net Ltd 2023 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "source4/kdc/pac-blobs.h" #include "lib/util/debug.h" #include "lib/util/samba_util.h" void pac_blobs_init(struct pac_blobs *pac_blobs) { size_t i; for (i = 0; i < ARRAY_SIZE(pac_blobs->type_index); ++i) { pac_blobs->type_index[i] = SIZE_MAX; } pac_blobs->type_blobs = NULL; pac_blobs->num_types = 0; } void pac_blobs_destroy(struct pac_blobs *pac_blobs) { TALLOC_FREE(pac_blobs->type_blobs); } static inline size_t *pac_blobs_get_index(struct pac_blobs *pac_blobs, size_t type) { /* Ensure the type is valid. */ SMB_ASSERT(type >= PAC_TYPE_BEGIN); SMB_ASSERT(type < PAC_TYPE_END); return &pac_blobs->type_index[type - PAC_TYPE_BEGIN]; } static inline struct type_data *pac_blobs_get(struct pac_blobs *pac_blobs, size_t type) { size_t index = *pac_blobs_get_index(pac_blobs, type); SMB_ASSERT(index < pac_blobs->num_types); return &pac_blobs->type_blobs[index]; } krb5_error_code pac_blobs_from_krb5_pac(struct pac_blobs *pac_blobs, TALLOC_CTX *mem_ctx, krb5_context context, const krb5_const_pac pac) { krb5_error_code code; uint32_t *types = NULL; size_t i; code = krb5_pac_get_types(context, pac, &pac_blobs->num_types, &types); if (code != 0) { DBG_ERR("krb5_pac_get_types failed\n"); return code; } pac_blobs->type_blobs = talloc_array(mem_ctx, struct type_data, pac_blobs->num_types); if (pac_blobs->type_blobs == NULL) { DBG_ERR("Out of memory\n"); SAFE_FREE(types); return ENOMEM; } for (i = 0; i < pac_blobs->num_types; ++i) { uint32_t type = types[i]; size_t *type_index = NULL; pac_blobs->type_blobs[i] = (struct type_data) { .type = type, .data = NULL, }; switch (type) { /* PAC buffer types that we support. */ case PAC_TYPE_LOGON_INFO: case PAC_TYPE_CREDENTIAL_INFO: case PAC_TYPE_SRV_CHECKSUM: case PAC_TYPE_KDC_CHECKSUM: case PAC_TYPE_LOGON_NAME: case PAC_TYPE_CONSTRAINED_DELEGATION: case PAC_TYPE_UPN_DNS_INFO: case PAC_TYPE_CLIENT_CLAIMS_INFO: case PAC_TYPE_DEVICE_INFO: case PAC_TYPE_DEVICE_CLAIMS_INFO: case PAC_TYPE_TICKET_CHECKSUM: case PAC_TYPE_ATTRIBUTES_INFO: case PAC_TYPE_REQUESTER_SID: case PAC_TYPE_FULL_CHECKSUM: type_index = pac_blobs_get_index(pac_blobs, type); if (*type_index != SIZE_MAX) { DBG_WARNING("PAC buffer type[%"PRIu32"] twice\n", type); pac_blobs_destroy(pac_blobs); SAFE_FREE(types); return EINVAL; } *type_index = i; break; default: break; } } SAFE_FREE(types); return 0; } krb5_error_code _pac_blobs_ensure_exists(struct pac_blobs *pac_blobs, const uint32_t type, const char *name, const char *location, const char *function) { if (*pac_blobs_get_index(pac_blobs, type) == SIZE_MAX) { DEBUGLF(DBGLVL_ERR, ("%s: %s missing\n", function, name), location, function); return EINVAL; } return 0; } krb5_error_code _pac_blobs_replace_existing(struct pac_blobs *pac_blobs, const uint32_t type, const char *name, const DATA_BLOB *blob, const char *location, const char *function) { krb5_error_code code; code = _pac_blobs_ensure_exists(pac_blobs, type, name, location, function); if (code != 0) { return code; } pac_blobs_get(pac_blobs, type)->data = blob; return 0; } krb5_error_code pac_blobs_add_blob(struct pac_blobs *pac_blobs, TALLOC_CTX *mem_ctx, const uint32_t type, const DATA_BLOB *blob) { size_t *index = NULL; if (blob == NULL) { return 0; } index = pac_blobs_get_index(pac_blobs, type); if (*index == SIZE_MAX) { pac_blobs->type_blobs = talloc_realloc(mem_ctx, pac_blobs->type_blobs, struct type_data, pac_blobs->num_types + 1); if (pac_blobs->type_blobs == NULL) { DBG_ERR("Out of memory\n"); return ENOMEM; } *index = pac_blobs->num_types++; } *pac_blobs_get(pac_blobs, type) = (struct type_data) { .type = type, .data = blob, }; return 0; } krb5_error_code pac_blobs_remove_blob(struct pac_blobs *pac_blobs, TALLOC_CTX *mem_ctx, const uint32_t type) { size_t found_index; size_t i; /* Get the index of this PAC buffer type. */ found_index = *pac_blobs_get_index(pac_blobs, type); if (found_index == SIZE_MAX) { /* We don't have a PAC buffer of this type, so we're done. */ return 0; } /* Since the PAC buffer is present, there will be at least one type in the array. */ SMB_ASSERT(pac_blobs->num_types > 0); /* The index should be valid. */ SMB_ASSERT(found_index < pac_blobs->num_types); /* * Even though a consistent ordering of PAC buffers is not to be relied * upon, we must still maintain the ordering we are given. */ for (i = found_index; i < pac_blobs->num_types - 1; ++i) { size_t moved_type; /* Shift each following element backwards by one. */ pac_blobs->type_blobs[i] = pac_blobs->type_blobs[i + 1]; /* Mark the new position of the moved element in the index. */ moved_type = pac_blobs->type_blobs[i].type; if (moved_type >= PAC_TYPE_BEGIN && moved_type < PAC_TYPE_END) { *pac_blobs_get_index(pac_blobs, moved_type) = i; } } /* Mark the removed element as no longer present. */ *pac_blobs_get_index(pac_blobs, type) = SIZE_MAX; /* We do not free the removed data blob, as it may be statically allocated (e.g., a null blob). */ /* Remove the last element from the array. */ pac_blobs->type_blobs = talloc_realloc(mem_ctx, pac_blobs->type_blobs, struct type_data, --pac_blobs->num_types); if (pac_blobs->type_blobs == NULL) { DBG_ERR("Out of memory\n"); return ENOMEM; } return 0; }