📄 pac.c
字号:
/* * Copyright (c) 2006 - 2007 Kungliga Tekniska H鰃skolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include "krb5_locl.h"#include <wind.h>RCSID("$Id: pac.c 22562 2008-02-03 17:38:35Z lha $");struct PAC_INFO_BUFFER { uint32_t type; uint32_t buffersize; uint32_t offset_hi; uint32_t offset_lo;};struct PACTYPE { uint32_t numbuffers; uint32_t version; struct PAC_INFO_BUFFER buffers[1];};struct krb5_pac_data { struct PACTYPE *pac; krb5_data data; struct PAC_INFO_BUFFER *server_checksum; struct PAC_INFO_BUFFER *privsvr_checksum; struct PAC_INFO_BUFFER *logon_name;};#define PAC_ALIGNMENT 8#define PACTYPE_SIZE 8#define PAC_INFO_BUFFER_SIZE 16#define PAC_SERVER_CHECKSUM 6#define PAC_PRIVSVR_CHECKSUM 7#define PAC_LOGON_NAME 10#define PAC_CONSTRAINED_DELEGATION 11#define CHECK(r,f,l) \ do { \ if (((r) = f ) != 0) { \ krb5_clear_error_string(context); \ goto l; \ } \ } while(0)static const char zeros[PAC_ALIGNMENT] = { 0 };/* * */krb5_error_codekrb5_pac_parse(krb5_context context, const void *ptr, size_t len, krb5_pac *pac){ krb5_error_code ret; krb5_pac p; krb5_storage *sp = NULL; uint32_t i, tmp, tmp2, header_end; p = calloc(1, sizeof(*p)); if (p == NULL) { ret = ENOMEM; krb5_set_error_string(context, "out of memory"); goto out; } sp = krb5_storage_from_readonly_mem(ptr, len); if (sp == NULL) { ret = ENOMEM; krb5_set_error_string(context, "out of memory"); goto out; } krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &tmp), out); CHECK(ret, krb5_ret_uint32(sp, &tmp2), out); if (tmp < 1) { krb5_set_error_string(context, "PAC have too few buffer"); ret = EINVAL; /* Too few buffers */ goto out; } if (tmp2 != 0) { krb5_set_error_string(context, "PAC have wrong version"); ret = EINVAL; /* Wrong version */ goto out; } p->pac = calloc(1, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1))); if (p->pac == NULL) { krb5_set_error_string(context, "out of memory"); ret = ENOMEM; goto out; } p->pac->numbuffers = tmp; p->pac->version = tmp2; header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); if (header_end > len) { ret = EINVAL; goto out; } for (i = 0; i < p->pac->numbuffers; i++) { CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out); /* consistency checks */ if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) { krb5_set_error_string(context, "PAC out of allignment"); ret = EINVAL; goto out; } if (p->pac->buffers[i].offset_hi) { krb5_set_error_string(context, "PAC high offset set"); ret = EINVAL; goto out; } if (p->pac->buffers[i].offset_lo > len) { krb5_set_error_string(context, "PAC offset off end"); ret = EINVAL; goto out; } if (p->pac->buffers[i].offset_lo < header_end) { krb5_set_error_string(context, "PAC offset inside header: %d %d", p->pac->buffers[i].offset_lo, header_end); ret = EINVAL; goto out; } if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){ krb5_set_error_string(context, "PAC length off end"); ret = EINVAL; goto out; } /* let save pointer to data we need later */ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { if (p->server_checksum) { krb5_set_error_string(context, "PAC have two server checksums"); ret = EINVAL; goto out; } p->server_checksum = &p->pac->buffers[i]; } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { if (p->privsvr_checksum) { krb5_set_error_string(context, "PAC have two KDC checksums"); ret = EINVAL; goto out; } p->privsvr_checksum = &p->pac->buffers[i]; } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { if (p->logon_name) { krb5_set_error_string(context, "PAC have two logon names"); ret = EINVAL; goto out; } p->logon_name = &p->pac->buffers[i]; } } ret = krb5_data_copy(&p->data, ptr, len); if (ret) goto out; krb5_storage_free(sp); *pac = p; return 0;out: if (sp) krb5_storage_free(sp); if (p) { if (p->pac) free(p->pac); free(p); } *pac = NULL; return ret;}krb5_error_codekrb5_pac_init(krb5_context context, krb5_pac *pac){ krb5_error_code ret; krb5_pac p; p = calloc(1, sizeof(*p)); if (p == NULL) { krb5_set_error_string(context, "out of memory"); return ENOMEM; } p->pac = calloc(1, sizeof(*p->pac)); if (p->pac == NULL) { free(p); krb5_set_error_string(context, "out of memory"); return ENOMEM; } ret = krb5_data_alloc(&p->data, PACTYPE_SIZE); if (ret) { free (p->pac); free(p); krb5_set_error_string(context, "out of memory"); return ret; } *pac = p; return 0;}krb5_error_codekrb5_pac_add_buffer(krb5_context context, krb5_pac p, uint32_t type, const krb5_data *data){ krb5_error_code ret; void *ptr; size_t len, offset, header_end, old_end; uint32_t i; len = p->pac->numbuffers; ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len)); if (ptr == NULL) { krb5_set_error_string(context, "out of memory"); return ENOMEM; } p->pac = ptr; for (i = 0; i < len; i++) p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE; offset = p->data.length + PAC_INFO_BUFFER_SIZE; p->pac->buffers[len].type = type; p->pac->buffers[len].buffersize = data->length; p->pac->buffers[len].offset_lo = offset; p->pac->buffers[len].offset_hi = 0; old_end = p->data.length; len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE; if (len < p->data.length) { krb5_set_error_string(context, "integer overrun"); return EINVAL; } /* align to PAC_ALIGNMENT */ len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; ret = krb5_data_realloc(&p->data, len); if (ret) { krb5_set_error_string(context, "out of memory"); return ret; } /* * make place for new PAC INFO BUFFER header */ header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE, (unsigned char *)p->data.data + header_end , old_end - header_end); memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE); /* * copy in new data part */ memcpy((unsigned char *)p->data.data + offset, data->data, data->length); memset((unsigned char *)p->data.data + offset + data->length, 0, p->data.length - offset - data->length); p->pac->numbuffers += 1; return 0;}krb5_error_codekrb5_pac_get_buffer(krb5_context context, krb5_pac p, uint32_t type, krb5_data *data){ krb5_error_code ret; uint32_t i; /* * Hide the checksums from external consumers */ if (type == PAC_PRIVSVR_CHECKSUM || type == PAC_SERVER_CHECKSUM) { ret = krb5_data_alloc(data, 16); if (ret) { krb5_set_error_string(context, "out of memory"); return ret; } memset(data->data, 0, data->length); return 0; } for (i = 0; i < p->pac->numbuffers; i++) { size_t len = p->pac->buffers[i].buffersize; size_t offset = p->pac->buffers[i].offset_lo; if (p->pac->buffers[i].type != type) continue; ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len); if (ret) { krb5_set_error_string(context, "Out of memory"); return ret; } return 0; } krb5_set_error_string(context, "No PAC buffer of type %lu was found", (unsigned long)type); return ENOENT;}/* * */krb5_error_codekrb5_pac_get_types(krb5_context context, krb5_pac p, size_t *len, uint32_t **types){ size_t i; *types = calloc(p->pac->numbuffers, sizeof(*types)); if (*types == NULL) { *len = 0; krb5_set_error_string(context, "out of memory"); return ENOMEM; } for (i = 0; i < p->pac->numbuffers; i++) (*types)[i] = p->pac->buffers[i].type; *len = p->pac->numbuffers; return 0;}/* * */voidkrb5_pac_free(krb5_context context, krb5_pac pac){ krb5_data_free(&pac->data); free(pac->pac); free(pac);}/* * */static krb5_error_codeverify_checksum(krb5_context context, const struct PAC_INFO_BUFFER *sig, const krb5_data *data, void *ptr, size_t len, const krb5_keyblock *key){ krb5_crypto crypto = NULL; krb5_storage *sp = NULL; uint32_t type; krb5_error_code ret; Checksum cksum; memset(&cksum, 0, sizeof(cksum)); sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo, sig->buffersize); if (sp == NULL) { krb5_set_error_string(context, "out of memory"); return ENOMEM; } krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &type), out); cksum.cksumtype = type; cksum.checksum.length = sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR); cksum.checksum.data = malloc(cksum.checksum.length); if (cksum.checksum.data == NULL) { krb5_set_error_string(context, "out of memory"); ret = ENOMEM; goto out; } ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length); if (ret != cksum.checksum.length) { krb5_set_error_string(context, "PAC checksum missing checksum"); ret = EINVAL; goto out; } if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) { krb5_set_error_string (context, "Checksum type %d not keyed", cksum.cksumtype); ret = EINVAL; goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) goto out; ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, ptr, len, &cksum); free(cksum.checksum.data); krb5_crypto_destroy(context, crypto); krb5_storage_free(sp); return ret;out: if (cksum.checksum.data) free(cksum.checksum.data); if (sp) krb5_storage_free(sp); if (crypto) krb5_crypto_destroy(context, crypto); return ret;}static krb5_error_codecreate_checksum(krb5_context context, const krb5_keyblock *key, void *data, size_t datalen, void *sig, size_t siglen){ krb5_crypto crypto = NULL; krb5_error_code ret; Checksum cksum; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) return ret; ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, data, datalen, &cksum); krb5_crypto_destroy(context, crypto); if (ret) return ret; if (cksum.checksum.length != siglen) { krb5_set_error_string(context, "pac checksum wrong length"); free_Checksum(&cksum); return EINVAL; } memcpy(sig, cksum.checksum.data, siglen); free_Checksum(&cksum); return 0;}/* * */#define NTTIME_EPOCH 0x019DB1DED53E8000LLstatic uint64_tunix2nttime(time_t unix_time){ long long wt; wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH; return wt;}static krb5_error_codeverify_logonname(krb5_context context, const struct PAC_INFO_BUFFER *logon_name, const krb5_data *data, time_t authtime,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -