2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/krb5/krb/pac.c
2N/A *
2N/A * Copyright 2008 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A */
2N/A/*
2N/A * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "k5-int.h"
2N/A#include "k5-utf8.h"
2N/A#include "authdata.h"
2N/A
2N/A/* draft-brezak-win2k-krb-authz-00 */
2N/A
2N/A/*
2N/A * A PAC consists of a sequence of PAC_INFO_BUFFERs, preceeded by
2N/A * a PACTYPE header. Decoding the contents of the buffers is left
2N/A * to the application (notwithstanding signature verification).
2N/A */
2N/A
2N/Atypedef struct _PAC_INFO_BUFFER {
2N/A krb5_ui_4 ulType;
2N/A krb5_ui_4 cbBufferSize;
2N/A krb5_ui_8 Offset;
2N/A} PAC_INFO_BUFFER;
2N/A
2N/A#define PAC_INFO_BUFFER_LENGTH 16
2N/A
2N/A/* ulType */
2N/A#define PAC_LOGON_INFO 1
2N/A#define PAC_CREDENTIALS_INFO 2
2N/A#define PAC_SERVER_CHECKSUM 6
2N/A#define PAC_PRIVSVR_CHECKSUM 7
2N/A#define PAC_CLIENT_INFO 10
2N/A#define PAC_DELEGATION_INFO 11
2N/A#define PAC_UPN_DNS_INFO 12
2N/A
2N/Atypedef struct _PACTYPE {
2N/A krb5_ui_4 cBuffers;
2N/A krb5_ui_4 Version;
2N/A PAC_INFO_BUFFER Buffers[1];
2N/A} PACTYPE;
2N/A
2N/A#define PAC_ALIGNMENT 8
2N/A#define PACTYPE_LENGTH 8U
2N/A#define PAC_SIGNATURE_DATA_LENGTH 4U
2N/A#define PAC_CLIENT_INFO_LENGTH 10U
2N/A
2N/A#define NT_TIME_EPOCH 11644473600LL
2N/A
2N/Astruct krb5_pac_data {
2N/A PACTYPE *pac; /* PAC header + info buffer array */
2N/A krb5_data data; /* PAC data (including uninitialised header) */
2N/A krb5_boolean verified;
2N/A};
2N/A
2N/Astatic krb5_error_code
2N/Ak5_pac_locate_buffer(krb5_context context,
2N/A const krb5_pac pac,
2N/A krb5_ui_4 type,
2N/A krb5_data *data);
2N/A
2N/A/*
2N/A * Add a buffer to the provided PAC and update header.
2N/A */
2N/Astatic krb5_error_code
2N/Ak5_pac_add_buffer(krb5_context context,
2N/A krb5_pac pac,
2N/A krb5_ui_4 type,
2N/A const krb5_data *data,
2N/A krb5_boolean zerofill,
2N/A krb5_data *out_data)
2N/A{
2N/A PACTYPE *header;
2N/A size_t header_len, i, pad = 0;
2N/A char *pac_data;
2N/A
2N/A assert((data->data == NULL) == zerofill);
2N/A
2N/A /* Check there isn't already a buffer of this type */
2N/A if (k5_pac_locate_buffer(context, pac, type, NULL) == 0) {
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, EINVAL,
2N/A "Duplicate PAC buffer of type %d",
2N/A type);
2N/A return EEXIST;
2N/A }
2N/A
2N/A header = (PACTYPE *)realloc(pac->pac,
2N/A sizeof(PACTYPE) +
2N/A (pac->pac->cBuffers * sizeof(PAC_INFO_BUFFER)));
2N/A if (header == NULL) {
2N/A return ENOMEM;
2N/A }
2N/A pac->pac = header;
2N/A
2N/A header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
2N/A
2N/A if (data->length % PAC_ALIGNMENT)
2N/A pad = PAC_ALIGNMENT - (data->length % PAC_ALIGNMENT);
2N/A
2N/A pac_data = realloc(pac->data.data,
2N/A pac->data.length + PAC_INFO_BUFFER_LENGTH + data->length + pad);
2N/A if (pac_data == NULL) {
2N/A return ENOMEM;
2N/A }
2N/A pac->data.data = pac_data;
2N/A
2N/A /* Update offsets of existing buffers */
2N/A for (i = 0; i < pac->pac->cBuffers; i++)
2N/A pac->pac->Buffers[i].Offset += PAC_INFO_BUFFER_LENGTH;
2N/A
2N/A /* Make room for new PAC_INFO_BUFFER */
2N/A memmove(pac->data.data + header_len + PAC_INFO_BUFFER_LENGTH,
2N/A pac->data.data + header_len,
2N/A pac->data.length - header_len);
2N/A memset(pac->data.data + header_len, 0, PAC_INFO_BUFFER_LENGTH);
2N/A
2N/A /* Initialise new PAC_INFO_BUFFER */
2N/A pac->pac->Buffers[i].ulType = type;
2N/A pac->pac->Buffers[i].cbBufferSize = data->length;
2N/A pac->pac->Buffers[i].Offset = pac->data.length + PAC_INFO_BUFFER_LENGTH;
2N/A assert((pac->pac->Buffers[i].Offset % PAC_ALIGNMENT) == 0);
2N/A
2N/A /* Copy in new PAC data and zero padding bytes */
2N/A if (zerofill)
2N/A memset(pac->data.data + pac->pac->Buffers[i].Offset, 0, data->length);
2N/A else
2N/A memcpy(pac->data.data + pac->pac->Buffers[i].Offset, data->data, data->length);
2N/A
2N/A memset(pac->data.data + pac->pac->Buffers[i].Offset + data->length, 0, pad);
2N/A
2N/A pac->pac->cBuffers++;
2N/A pac->data.length += PAC_INFO_BUFFER_LENGTH + data->length + pad;
2N/A
2N/A if (out_data != NULL) {
2N/A out_data->data = pac->data.data + pac->pac->Buffers[i].Offset;
2N/A out_data->length = data->length;
2N/A }
2N/A
2N/A pac->verified = FALSE;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_pac_add_buffer(krb5_context context,
2N/A krb5_pac pac,
2N/A krb5_ui_4 type,
2N/A const krb5_data *data)
2N/A{
2N/A return k5_pac_add_buffer(context, pac, type, data, FALSE, NULL);
2N/A}
2N/A
2N/A/*
2N/A * Free a PAC
2N/A */
2N/Avoid KRB5_CALLCONV
2N/Akrb5_pac_free(krb5_context context,
2N/A krb5_pac pac)
2N/A{
2N/A if (pac != NULL) {
2N/A if (pac->data.data != NULL) {
2N/A memset(pac->data.data, 0, pac->data.length);
2N/A free(pac->data.data);
2N/A }
2N/A if (pac->pac != NULL)
2N/A free(pac->pac);
2N/A memset(pac, 0, sizeof(*pac));
2N/A free(pac);
2N/A }
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5_pac_locate_buffer(krb5_context context,
2N/A const krb5_pac pac,
2N/A krb5_ui_4 type,
2N/A krb5_data *data)
2N/A{
2N/A PAC_INFO_BUFFER *buffer = NULL;
2N/A size_t i;
2N/A
2N/A if (pac == NULL) {
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, EINVAL,
2N/A "Invalid argument 'pac' is NULL");
2N/A return EINVAL;
2N/A }
2N/A
2N/A for (i = 0; i < pac->pac->cBuffers; i++) {
2N/A if (pac->pac->Buffers[i].ulType == type) {
2N/A if (buffer == NULL)
2N/A buffer = &pac->pac->Buffers[i];
2N/A else {
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, EINVAL,
2N/A "Invalid buffer found looping thru "
2N/A "PAC buffers (type=%d, i=%zd)",
2N/A type, i);
2N/A return EINVAL;
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (buffer == NULL) {
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, ENOENT,
2N/A "No PAC buffer found (type=%d)",
2N/A type);
2N/A return ENOENT;
2N/A }
2N/A
2N/A assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
2N/A
2N/A if (data != NULL) {
2N/A data->length = buffer->cbBufferSize;
2N/A data->data = pac->data.data + buffer->Offset;
2N/A }
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Find a buffer and copy data into output
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_pac_get_buffer(krb5_context context,
2N/A krb5_pac pac,
2N/A krb5_ui_4 type,
2N/A krb5_data *data)
2N/A{
2N/A krb5_data d;
2N/A krb5_error_code ret;
2N/A
2N/A ret = k5_pac_locate_buffer(context, pac, type, &d);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A data->data = malloc(d.length);
2N/A if (data->data == NULL)
2N/A return ENOMEM;
2N/A
2N/A data->length = d.length;
2N/A memcpy(data->data, d.data, d.length);
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Return an array of the types of data in the PAC
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_pac_get_types(krb5_context context,
2N/A krb5_pac pac,
2N/A size_t *len,
2N/A krb5_ui_4 **types)
2N/A{
2N/A size_t i;
2N/A
2N/A *types = (krb5_ui_4 *)malloc(pac->pac->cBuffers * sizeof(krb5_ui_4));
2N/A if (*types == NULL)
2N/A return ENOMEM;
2N/A
2N/A *len = pac->pac->cBuffers;
2N/A
2N/A for (i = 0; i < pac->pac->cBuffers; i++)
2N/A (*types)[i] = pac->pac->Buffers[i].ulType;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Initialize PAC
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_pac_init(krb5_context context,
2N/A krb5_pac *ppac)
2N/A{
2N/A krb5_pac pac;
2N/A
2N/A pac = (krb5_pac)malloc(sizeof(*pac));
2N/A if (pac == NULL)
2N/A return ENOMEM;
2N/A
2N/A pac->pac = (PACTYPE *)malloc(sizeof(PACTYPE));
2N/A if (pac->pac == NULL) {
2N/A free(pac);
2N/A return ENOMEM;
2N/A }
2N/A
2N/A pac->pac->cBuffers = 0;
2N/A pac->pac->Version = 0;
2N/A
2N/A pac->data.length = PACTYPE_LENGTH;
2N/A pac->data.data = calloc(1, pac->data.length);
2N/A if (pac->data.data == NULL) {
2N/A krb5_pac_free(context, pac);
2N/A return ENOMEM;
2N/A }
2N/A
2N/A pac->verified = FALSE;
2N/A
2N/A *ppac = pac;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5_pac_copy(krb5_context context,
2N/A krb5_pac src,
2N/A krb5_pac *dst)
2N/A{
2N/A size_t header_len;
2N/A krb5_ui_4 cbuffers;
2N/A krb5_error_code code;
2N/A krb5_pac pac;
2N/A
2N/A cbuffers = src->pac->cBuffers;
2N/A if (cbuffers != 0)
2N/A cbuffers--;
2N/A
2N/A header_len = sizeof(PACTYPE) + cbuffers * sizeof(PAC_INFO_BUFFER);
2N/A
2N/A pac = (krb5_pac)malloc(sizeof(*pac));
2N/A if (pac == NULL)
2N/A return ENOMEM;
2N/A
2N/A pac->pac = (PACTYPE *)malloc(header_len);
2N/A if (pac->pac == NULL) {
2N/A free(pac);
2N/A return ENOMEM;
2N/A }
2N/A
2N/A memcpy(pac->pac, src->pac, header_len);
2N/A
2N/A code = krb5int_copy_data_contents(context, &src->data, &pac->data);
2N/A if (code != 0) {
2N/A free(pac->pac);
2N/A free(pac);
2N/A return ENOMEM;
2N/A }
2N/A
2N/A pac->verified = src->verified;
2N/A *dst = pac;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Parse the supplied data into the PAC allocated by this function
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_pac_parse(krb5_context context,
2N/A const void *ptr,
2N/A size_t len,
2N/A krb5_pac *ppac)
2N/A{
2N/A krb5_error_code ret;
2N/A size_t i;
2N/A const unsigned char *p = (const unsigned char *)ptr;
2N/A krb5_pac pac;
2N/A size_t header_len;
2N/A krb5_ui_4 cbuffers, version;
2N/A
2N/A *ppac = NULL;
2N/A
2N/A if (len < PACTYPE_LENGTH) {
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, ERANGE,
2N/A "PAC type length is out of range (len=%zd)",
2N/A len);
2N/A return ERANGE;
2N/A }
2N/A
2N/A cbuffers = load_32_le(p);
2N/A p += 4;
2N/A version = load_32_le(p);
2N/A p += 4;
2N/A
2N/A if (version != 0) {
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, EINVAL,
2N/A "Invalid PAC version is %d, should be 0",
2N/A version);
2N/A return EINVAL;
2N/A }
2N/A
2N/A header_len = PACTYPE_LENGTH + (cbuffers * PAC_INFO_BUFFER_LENGTH);
2N/A if (len < header_len) {
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, ERANGE,
2N/A "PAC header len (%zd) out of range",
2N/A len);
2N/A return ERANGE;
2N/A }
2N/A
2N/A ret = krb5_pac_init(context, &pac);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A pac->pac = (PACTYPE *)realloc(pac->pac,
2N/A sizeof(PACTYPE) + ((cbuffers - 1) * sizeof(PAC_INFO_BUFFER)));
2N/A if (pac->pac == NULL) {
2N/A krb5_pac_free(context, pac);
2N/A return ENOMEM;
2N/A }
2N/A
2N/A pac->pac->cBuffers = cbuffers;
2N/A pac->pac->Version = version;
2N/A
2N/A for (i = 0; i < pac->pac->cBuffers; i++) {
2N/A PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
2N/A
2N/A buffer->ulType = load_32_le(p);
2N/A p += 4;
2N/A buffer->cbBufferSize = load_32_le(p);
2N/A p += 4;
2N/A buffer->Offset = load_64_le(p);
2N/A p += 8;
2N/A
2N/A if (buffer->Offset % PAC_ALIGNMENT) {
2N/A krb5_pac_free(context, pac);
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, EINVAL,
2N/A "PAC buffer offset mis-aligned");
2N/A return EINVAL;
2N/A }
2N/A if (buffer->Offset < header_len ||
2N/A buffer->Offset + buffer->cbBufferSize > len) {
2N/A krb5_pac_free(context, pac);
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, ERANGE,
2N/A "PAC offset is out of range");
2N/A return ERANGE;
2N/A }
2N/A }
2N/A
2N/A pac->data.data = realloc(pac->data.data, len);
2N/A if (pac->data.data == NULL) {
2N/A krb5_pac_free(context, pac);
2N/A return ENOMEM;
2N/A }
2N/A memcpy(pac->data.data, ptr, len);
2N/A
2N/A pac->data.length = len;
2N/A
2N/A *ppac = pac;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5_time_to_seconds_since_1970(krb5_int64 ntTime,
2N/A krb5_timestamp *elapsedSeconds)
2N/A{
2N/A krb5_ui_8 abstime;
2N/A
2N/A ntTime /= 10000000;
2N/A
2N/A abstime = ntTime > 0 ? ntTime - NT_TIME_EPOCH : -ntTime;
2N/A
2N/A if (abstime > KRB5_INT32_MAX)
2N/A return ERANGE;
2N/A
2N/A *elapsedSeconds = abstime;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds,
2N/A krb5_ui_8 *ntTime)
2N/A{
2N/A *ntTime = elapsedSeconds;
2N/A
2N/A if (elapsedSeconds > 0)
2N/A *ntTime += NT_TIME_EPOCH;
2N/A
2N/A *ntTime *= 10000000;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5_pac_validate_client(krb5_context context,
2N/A const krb5_pac pac,
2N/A krb5_timestamp authtime,
2N/A krb5_const_principal principal)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_data client_info;
2N/A char *pac_princname;
2N/A unsigned char *p;
2N/A krb5_timestamp pac_authtime;
2N/A krb5_ui_2 pac_princname_length;
2N/A krb5_int64 pac_nt_authtime;
2N/A krb5_principal pac_principal;
2N/A
2N/A ret = k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A if (client_info.length < PAC_CLIENT_INFO_LENGTH) {
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, ERANGE,
2N/A "PAC client info length out of range "
2N/A "(info len=%d)", client_info.length);
2N/A return ERANGE;
2N/A }
2N/A
2N/A p = (unsigned char *)client_info.data;
2N/A pac_nt_authtime = load_64_le(p);
2N/A p += 8;
2N/A pac_princname_length = load_16_le(p);
2N/A p += 2;
2N/A
2N/A ret = k5_time_to_seconds_since_1970(pac_nt_authtime, &pac_authtime);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
2N/A pac_princname_length % 2) {
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, ERANGE,
2N/A "PAC client info length is out of range");
2N/A return ERANGE;
2N/A }
2N/A
2N/A ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2,
2N/A &pac_princname, NULL);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A ret = krb5_parse_name_flags(context, pac_princname, 0, &pac_principal);
2N/A if (ret != 0) {
2N/A free(pac_princname);
2N/A return ret;
2N/A }
2N/A
2N/A if (pac_authtime != authtime) {
2N/A /* Solaris Kerberos */
2N/A char timestring[17];
2N/A char pac_timestring[17];
2N/A char fill = ' ';
2N/A int err, pac_err;
2N/A /* Need better ret code here but don't see one */
2N/A ret = KRB5KRB_AP_WRONG_PRINC;
2N/A err = krb5_timestamp_to_sfstring(pac_authtime,
2N/A timestring,
2N/A sizeof (timestring), &fill);
2N/A pac_err = krb5_timestamp_to_sfstring(pac_authtime,
2N/A pac_timestring,
2N/A sizeof (pac_timestring), &fill);
2N/A if (pac_princname && !err && !pac_err) {
2N/A krb5_set_error_message(context, ret,
2N/A "PAC verify fail: PAC authtime '%s' does "
2N/A "not match authtime '%s'. PAC principal is"
2N/A " '%s'", pac_timestring, timestring,
2N/A pac_princname);
2N/A }
2N/A } else if (!krb5_principal_compare_flags(context,
2N/A pac_principal,
2N/A principal,
2N/A KRB5_PRINCIPAL_COMPARE_IGNORE_REALM)) {
2N/A /* Solaris Kerberos */
2N/A char *p_name = NULL;
2N/A krb5_error_code perr;
2N/A ret = KRB5KRB_AP_WRONG_PRINC;
2N/A perr = krb5_unparse_name(context, principal, &p_name);
2N/A if (pac_princname && !perr) {
2N/A krb5_set_error_message(context, ret,
2N/A "Wrong principal in request: PAC verify: "
2N/A "Principal in PAC is '%s' and does not "
2N/A "match '%s'", pac_princname, p_name);
2N/A }
2N/A if (p_name)
2N/A krb5_free_unparsed_name(context, p_name);
2N/A }
2N/A
2N/A free(pac_princname);
2N/A krb5_free_principal(context, pac_principal);
2N/A
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5_pac_zero_signature(krb5_context context,
2N/A const krb5_pac pac,
2N/A krb5_ui_4 type,
2N/A krb5_data *data)
2N/A{
2N/A PAC_INFO_BUFFER *buffer = NULL;
2N/A size_t i;
2N/A
2N/A assert(type == PAC_SERVER_CHECKSUM || type == PAC_PRIVSVR_CHECKSUM);
2N/A assert(data->length >= pac->data.length);
2N/A
2N/A for (i = 0; i < pac->pac->cBuffers; i++) {
2N/A if (pac->pac->Buffers[i].ulType == type) {
2N/A buffer = &pac->pac->Buffers[i];
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (buffer == NULL) {
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, ENOENT,
2N/A "No PAC buffer found (type=%d)",
2N/A type);
2N/A return ENOENT;
2N/A }
2N/A
2N/A if (buffer->Offset + buffer->cbBufferSize > pac->data.length)
2N/A return ERANGE;
2N/A
2N/A if (buffer->cbBufferSize < PAC_SIGNATURE_DATA_LENGTH)
2N/A return KRB5_BAD_MSIZE;
2N/A
2N/A /* Zero out the data portion of the checksum only */
2N/A memset(data->data + buffer->Offset + PAC_SIGNATURE_DATA_LENGTH,
2N/A 0,
2N/A buffer->cbBufferSize - PAC_SIGNATURE_DATA_LENGTH);
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5_pac_verify_server_checksum(krb5_context context,
2N/A const krb5_pac pac,
2N/A const krb5_keyblock *server)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_data pac_data; /* PAC with zeroed checksums */
2N/A krb5_checksum checksum;
2N/A krb5_data checksum_data;
2N/A krb5_boolean valid;
2N/A krb5_octet *p;
2N/A
2N/A ret = k5_pac_locate_buffer(context, pac,
2N/A PAC_SERVER_CHECKSUM, &checksum_data);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
2N/A return KRB5_BAD_MSIZE;
2N/A
2N/A p = (krb5_octet *)checksum_data.data;
2N/A checksum.checksum_type = load_32_le(p);
2N/A checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
2N/A checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
2N/A if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
2N/A return KRB5KRB_AP_ERR_INAPP_CKSUM;
2N/A
2N/A pac_data.length = pac->data.length;
2N/A pac_data.data = malloc(pac->data.length);
2N/A if (pac_data.data == NULL)
2N/A return ENOMEM;
2N/A
2N/A memcpy(pac_data.data, pac->data.data, pac->data.length);
2N/A
2N/A /* Zero out both checksum buffers */
2N/A ret = k5_pac_zero_signature(context, pac,
2N/A PAC_SERVER_CHECKSUM, &pac_data);
2N/A if (ret != 0) {
2N/A free(pac_data.data);
2N/A return ret;
2N/A }
2N/A
2N/A ret = k5_pac_zero_signature(context, pac,
2N/A PAC_PRIVSVR_CHECKSUM, &pac_data);
2N/A if (ret != 0) {
2N/A free(pac_data.data);
2N/A return ret;
2N/A }
2N/A
2N/A ret = krb5_c_verify_checksum(context, server,
2N/A KRB5_KEYUSAGE_APP_DATA_CKSUM,
2N/A &pac_data, &checksum, &valid);
2N/A
2N/A free(pac_data.data);
2N/A
2N/A if (ret != 0) {
2N/A return ret;
2N/A }
2N/A
2N/A if (valid == FALSE) {
2N/A ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, ret,
2N/A "Decrypt integrity check failed for PAC");
2N/A }
2N/A
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5_pac_verify_kdc_checksum(krb5_context context,
2N/A const krb5_pac pac,
2N/A const krb5_keyblock *privsvr)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_data server_checksum, privsvr_checksum;
2N/A krb5_checksum checksum;
2N/A krb5_boolean valid;
2N/A krb5_octet *p;
2N/A
2N/A ret = k5_pac_locate_buffer(context, pac,
2N/A PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
2N/A return KRB5_BAD_MSIZE;
2N/A
2N/A ret = k5_pac_locate_buffer(context, pac,
2N/A PAC_SERVER_CHECKSUM, &server_checksum);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
2N/A return KRB5_BAD_MSIZE;
2N/A
2N/A p = (krb5_octet *)privsvr_checksum.data;
2N/A checksum.checksum_type = load_32_le(p);
2N/A checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
2N/A checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
2N/A
2N/A server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
2N/A server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
2N/A
2N/A ret = krb5_c_verify_checksum(context, privsvr,
2N/A KRB5_KEYUSAGE_APP_DATA_CKSUM,
2N/A &server_checksum, &checksum, &valid);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A if (valid == FALSE) {
2N/A ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
2N/A /* Solaris Kerberos */
2N/A krb5_set_error_message(context, ret,
2N/A "Decrypt integrity check failed for PAC");
2N/A }
2N/A
2N/A return ret;
2N/A}
2N/A
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5_pac_verify(krb5_context context,
2N/A const krb5_pac pac,
2N/A krb5_timestamp authtime,
2N/A krb5_const_principal principal,
2N/A const krb5_keyblock *server,
2N/A const krb5_keyblock *privsvr)
2N/A{
2N/A krb5_error_code ret;
2N/A
2N/A if (server == NULL)
2N/A return EINVAL;
2N/A
2N/A ret = k5_pac_verify_server_checksum(context, pac, server);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A if (privsvr != NULL) {
2N/A ret = k5_pac_verify_kdc_checksum(context, pac, privsvr);
2N/A if (ret != 0)
2N/A return ret;
2N/A }
2N/A
2N/A if (principal != NULL) {
2N/A ret = k5_pac_validate_client(context, pac, authtime, principal);
2N/A if (ret != 0)
2N/A return ret;
2N/A }
2N/A
2N/A pac->verified = TRUE;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5_insert_client_info(krb5_context context,
2N/A krb5_pac pac,
2N/A krb5_timestamp authtime,
2N/A krb5_const_principal principal)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_data client_info;
2N/A char *princ_name_utf8 = NULL;
2N/A unsigned char *princ_name_ucs2 = NULL, *p;
2N/A size_t princ_name_ucs2_len = 0;
2N/A krb5_ui_8 nt_authtime;
2N/A
2N/A /* If we already have a CLIENT_INFO buffer, then just validate it */
2N/A if (k5_pac_locate_buffer(context, pac,
2N/A PAC_CLIENT_INFO, &client_info) == 0) {
2N/A return k5_pac_validate_client(context, pac, authtime, principal);
2N/A }
2N/A
2N/A ret = krb5_unparse_name_flags(context, principal,
2N/A KRB5_PRINCIPAL_UNPARSE_NO_REALM,
2N/A &princ_name_utf8);
2N/A if (ret != 0)
2N/A goto cleanup;
2N/A
2N/A ret = krb5int_utf8s_to_ucs2les(princ_name_utf8,
2N/A &princ_name_ucs2,
2N/A &princ_name_ucs2_len);
2N/A if (ret != 0)
2N/A goto cleanup;
2N/A
2N/A client_info.length = PAC_CLIENT_INFO_LENGTH + princ_name_ucs2_len;
2N/A client_info.data = NULL;
2N/A
2N/A ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO,
2N/A &client_info, TRUE, &client_info);
2N/A if (ret != 0)
2N/A goto cleanup;
2N/A
2N/A p = (unsigned char *)client_info.data;
2N/A
2N/A /* copy in authtime converted to a 64-bit NT time */
2N/A k5_seconds_since_1970_to_time(authtime, &nt_authtime);
2N/A store_64_le(nt_authtime, p);
2N/A p += 8;
2N/A
2N/A /* copy in number of UCS-2 characters in principal name */
2N/A store_16_le(princ_name_ucs2_len, p);
2N/A p += 2;
2N/A
2N/A /* copy in principal name */
2N/A memcpy(p, princ_name_ucs2, princ_name_ucs2_len);
2N/A
2N/Acleanup:
2N/A if (princ_name_ucs2 != NULL)
2N/A free(princ_name_ucs2);
2N/A krb5_free_unparsed_name(context, princ_name_utf8);
2N/A
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5_insert_checksum(krb5_context context,
2N/A krb5_pac pac,
2N/A krb5_ui_4 type,
2N/A const krb5_keyblock *key,
2N/A krb5_cksumtype *cksumtype)
2N/A{
2N/A krb5_error_code ret;
2N/A size_t len;
2N/A krb5_data cksumdata;
2N/A
2N/A ret = krb5int_c_mandatory_cksumtype(context, key->enctype, cksumtype);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A ret = krb5_c_checksum_length(context, *cksumtype, &len);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A ret = k5_pac_locate_buffer(context, pac, type, &cksumdata);
2N/A if (ret == 0) {
2N/A /*
2N/A * If we're resigning PAC, make sure we can fit checksum
2N/A * into existing buffer
2N/A */
2N/A if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len)
2N/A return ERANGE;
2N/A
2N/A memset(cksumdata.data, 0, cksumdata.length);
2N/A } else {
2N/A /* Add a zero filled buffer */
2N/A cksumdata.length = PAC_SIGNATURE_DATA_LENGTH + len;
2N/A cksumdata.data = NULL;
2N/A
2N/A ret = k5_pac_add_buffer(context, pac,
2N/A type, &cksumdata,
2N/A TRUE, &cksumdata);
2N/A if (ret != 0)
2N/A return ret;
2N/A }
2N/A
2N/A /* Encode checksum type into buffer */
2N/A store_32_le((krb5_ui_4)*cksumtype, cksumdata.data);
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/* in-place encoding of PAC header */
2N/Astatic krb5_error_code
2N/Ak5_pac_encode_header(krb5_context context, krb5_pac pac)
2N/A{
2N/A size_t i;
2N/A unsigned char *p;
2N/A size_t header_len;
2N/A
2N/A header_len = PACTYPE_LENGTH +
2N/A (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
2N/A assert(pac->data.length >= header_len);
2N/A
2N/A p = (unsigned char *)pac->data.data;
2N/A
2N/A store_32_le(pac->pac->cBuffers, p);
2N/A p += 4;
2N/A store_32_le(pac->pac->Version, p);
2N/A p += 4;
2N/A
2N/A for (i = 0; i < pac->pac->cBuffers; i++) {
2N/A PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
2N/A
2N/A store_32_le(buffer->ulType, p);
2N/A p += 4;
2N/A store_32_le(buffer->cbBufferSize, p);
2N/A p += 4;
2N/A store_64_le(buffer->Offset, p);
2N/A p += 8;
2N/A
2N/A assert((buffer->Offset % PAC_ALIGNMENT) == 0);
2N/A assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
2N/A assert(buffer->Offset >= header_len);
2N/A
2N/A if (buffer->Offset % PAC_ALIGNMENT ||
2N/A buffer->Offset + buffer->cbBufferSize > pac->data.length ||
2N/A buffer->Offset < header_len)
2N/A return ERANGE;
2N/A }
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A#if 0
2N/A/*
2N/A * Solaris Kerberos
2N/A * We don't have the new MIT iov interfaces yet and don't need them yet.
2N/A * We'll need this for full 1.7 resync.
2N/A */
2N/Akrb5_error_code KRB5_CALLCONV
2N/Akrb5int_pac_sign(krb5_context context,
2N/A krb5_pac pac,
2N/A krb5_timestamp authtime,
2N/A krb5_const_principal principal,
2N/A const krb5_keyblock *server_key,
2N/A const krb5_keyblock *privsvr_key,
2N/A krb5_data *data)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_data server_cksum, privsvr_cksum;
2N/A krb5_cksumtype server_cksumtype, privsvr_cksumtype;
2N/A krb5_crypto_iov iov[2];
2N/A
2N/A data->length = 0;
2N/A data->data = NULL;
2N/A
2N/A if (principal != NULL) {
2N/A ret = k5_insert_client_info(context, pac, authtime, principal);
2N/A if (ret != 0)
2N/A return ret;
2N/A }
2N/A
2N/A /* Create zeroed buffers for both checksums */
2N/A ret = k5_insert_checksum(context, pac, PAC_SERVER_CHECKSUM,
2N/A server_key, &server_cksumtype);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A ret = k5_insert_checksum(context, pac, PAC_PRIVSVR_CHECKSUM,
2N/A privsvr_key, &privsvr_cksumtype);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A /* Now, encode the PAC header so that the checksums will include it */
2N/A ret = k5_pac_encode_header(context, pac);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A /* Generate the server checksum over the entire PAC */
2N/A ret = k5_pac_locate_buffer(context, pac,
2N/A PAC_SERVER_CHECKSUM, &server_cksum);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A assert(server_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
2N/A
2N/A iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
2N/A iov[0].data = pac->data;
2N/A
2N/A iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
2N/A iov[1].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
2N/A iov[1].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
2N/A
2N/A ret = krb5_c_make_checksum_iov(context, server_cksumtype,
2N/A server_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
2N/A iov, sizeof(iov)/sizeof(iov[0]));
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A /* Generate the privsvr checksum over the server checksum buffer */
2N/A ret = k5_pac_locate_buffer(context, pac,
2N/A PAC_PRIVSVR_CHECKSUM, &privsvr_cksum);
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A assert(privsvr_cksum.length > PAC_SIGNATURE_DATA_LENGTH);
2N/A
2N/A iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
2N/A iov[0].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
2N/A iov[0].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
2N/A
2N/A iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
2N/A iov[1].data.data = privsvr_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
2N/A iov[1].data.length = privsvr_cksum.length - PAC_SIGNATURE_DATA_LENGTH;
2N/A
2N/A ret = krb5_c_make_checksum_iov(context, privsvr_cksumtype,
2N/A privsvr_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
2N/A iov, sizeof(iov)/sizeof(iov[0]));
2N/A if (ret != 0)
2N/A return ret;
2N/A
2N/A data->data = malloc(pac->data.length);
2N/A if (data->data == NULL)
2N/A return ENOMEM;
2N/A
2N/A data->length = pac->data.length;
2N/A
2N/A memcpy(data->data, pac->data.data, pac->data.length);
2N/A memset(pac->data.data, 0,
2N/A PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH));
2N/A
2N/A return 0;
2N/A}
2N/A#endif
2N/A
2N/A/*
2N/A * PAC auth data attribute backend
2N/A */
2N/Astruct mspac_context {
2N/A krb5_pac pac;
2N/A};
2N/A
2N/Astatic krb5_error_code
2N/Amspac_init(krb5_context kcontext, void **plugin_context)
2N/A{
2N/A *plugin_context = NULL;
2N/A return 0;
2N/A}
2N/A
2N/Astatic void
2N/Amspac_flags(krb5_context kcontext,
2N/A void *plugin_context,
2N/A krb5_authdatatype ad_type,
2N/A krb5_flags *flags)
2N/A{
2N/A *flags = AD_USAGE_KDC_ISSUED;
2N/A}
2N/A
2N/Astatic void
2N/Amspac_fini(krb5_context kcontext, void *plugin_context)
2N/A{
2N/A return;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_request_init(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void **request_context)
2N/A{
2N/A struct mspac_context *pacctx;
2N/A
2N/A pacctx = (struct mspac_context *)malloc(sizeof(*pacctx));
2N/A if (pacctx == NULL)
2N/A return ENOMEM;
2N/A
2N/A pacctx->pac = NULL;
2N/A
2N/A *request_context = pacctx;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_import_authdata(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A krb5_authdata **authdata,
2N/A krb5_boolean kdc_issued,
2N/A krb5_const_principal kdc_issuer)
2N/A{
2N/A krb5_error_code code;
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A
2N/A if (kdc_issued)
2N/A return EINVAL;
2N/A
2N/A if (pacctx->pac != NULL) {
2N/A krb5_pac_free(kcontext, pacctx->pac);
2N/A pacctx->pac = NULL;
2N/A }
2N/A
2N/A assert(authdata[0] != NULL);
2N/A assert((authdata[0]->ad_type & AD_TYPE_FIELD_TYPE_MASK) ==
2N/A KRB5_AUTHDATA_WIN2K_PAC);
2N/A
2N/A code = krb5_pac_parse(kcontext, authdata[0]->contents,
2N/A authdata[0]->length, &pacctx->pac);
2N/A
2N/A return code;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_export_authdata(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A krb5_flags usage,
2N/A krb5_authdata ***out_authdata)
2N/A{
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A krb5_error_code code;
2N/A krb5_authdata **authdata;
2N/A krb5_data data;
2N/A
2N/A if (pacctx->pac == NULL)
2N/A return 0;
2N/A
2N/A authdata = calloc(2, sizeof(krb5_authdata *));
2N/A if (authdata == NULL)
2N/A return ENOMEM;
2N/A
2N/A authdata[0] = calloc(1, sizeof(krb5_authdata));
2N/A if (authdata[0] == NULL) {
2N/A free(authdata);
2N/A return ENOMEM;
2N/A }
2N/A authdata[1] = NULL;
2N/A
2N/A code = krb5int_copy_data_contents(kcontext, &pacctx->pac->data, &data);
2N/A if (code != 0) {
2N/A krb5_free_authdata(kcontext, authdata);
2N/A return code;
2N/A }
2N/A
2N/A authdata[0]->magic = KV5M_AUTHDATA;
2N/A authdata[0]->ad_type = KRB5_AUTHDATA_WIN2K_PAC;
2N/A authdata[0]->length = data.length;
2N/A authdata[0]->contents = (krb5_octet *)data.data;
2N/A
2N/A authdata[1] = NULL;
2N/A
2N/A *out_authdata = authdata;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_verify(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A const krb5_auth_context *auth_context,
2N/A const krb5_keyblock *key,
2N/A const krb5_ap_req *req)
2N/A{
2N/A krb5_error_code code;
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A
2N/A if (pacctx->pac == NULL)
2N/A return EINVAL;
2N/A
2N/A code = krb5_pac_verify(kcontext,
2N/A pacctx->pac,
2N/A req->ticket->enc_part2->times.authtime,
2N/A req->ticket->enc_part2->client,
2N/A key,
2N/A NULL);
2N/A
2N/A#if 0
2N/A /*
2N/A * Now, we could return 0 and just set pac->verified to FALSE.
2N/A * Thoughts?
2N/A */
2N/A if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
2N/A assert(pacctx->pac->verified == FALSE);
2N/A code = 0;
2N/A }
2N/A#endif
2N/A
2N/A return code;
2N/A}
2N/A
2N/Astatic void
2N/Amspac_request_fini(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context)
2N/A{
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A
2N/A if (pacctx != NULL) {
2N/A if (pacctx->pac != NULL)
2N/A krb5_pac_free(kcontext, pacctx->pac);
2N/A
2N/A free(pacctx);
2N/A }
2N/A}
2N/A
2N/A#define STRLENOF(x) (sizeof((x)) - 1)
2N/A
2N/Astatic struct {
2N/A krb5_ui_4 type;
2N/A krb5_data attribute;
2N/A} mspac_attribute_types[] = {
2N/A { (krb5_ui_4)-1, { KV5M_DATA, STRLENOF("urn:mspac:"), "urn:mspac:" } },
2N/A { PAC_LOGON_INFO, { KV5M_DATA, STRLENOF("urn:mspac:logon-info"), "urn:mspac:logon-info" } },
2N/A { PAC_CREDENTIALS_INFO, { KV5M_DATA, STRLENOF("urn:mspac:credentials-info"), "urn:mspac:credentials-info" } },
2N/A { PAC_SERVER_CHECKSUM, { KV5M_DATA, STRLENOF("urn:mspac:server-checksum"), "urn:mspac:server-checksum" } },
2N/A { PAC_PRIVSVR_CHECKSUM, { KV5M_DATA, STRLENOF("urn:mspac:privsvr-checksum"), "urn:mspac:privsvr-checksum" } },
2N/A { PAC_CLIENT_INFO, { KV5M_DATA, STRLENOF("urn:mspac:client-info"), "urn:mspac:client-info" } },
2N/A { PAC_DELEGATION_INFO, { KV5M_DATA, STRLENOF("urn:mspac:delegation-info"), "urn:mspac:delegation-info" } },
2N/A { PAC_UPN_DNS_INFO, { KV5M_DATA, STRLENOF("urn:mspac:upn-dns-info"), "urn:mspac:upn-dns-info" } },
2N/A};
2N/A
2N/A#define MSPAC_ATTRIBUTE_COUNT (sizeof(mspac_attribute_types)/sizeof(mspac_attribute_types[0]))
2N/A
2N/Astatic krb5_error_code
2N/Amspac_type2attr(krb5_ui_4 type, krb5_data *attr)
2N/A{
2N/A unsigned int i;
2N/A
2N/A for (i = 0; i < MSPAC_ATTRIBUTE_COUNT; i++) {
2N/A if (mspac_attribute_types[i].type == type) {
2N/A *attr = mspac_attribute_types[i].attribute;
2N/A return 0;
2N/A }
2N/A }
2N/A
2N/A return ENOENT;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_attr2type(const krb5_data *attr, krb5_ui_4 *type)
2N/A{
2N/A unsigned int i;
2N/A
2N/A for (i = 0; i < MSPAC_ATTRIBUTE_COUNT; i++) {
2N/A if (attr->length == mspac_attribute_types[i].attribute.length &&
2N/A strncasecmp(attr->data, mspac_attribute_types[i].attribute.data, attr->length) == 0) {
2N/A *type = mspac_attribute_types[i].type;
2N/A return 0;
2N/A }
2N/A }
2N/A
2N/A if (attr->length > STRLENOF("urn:mspac:") &&
2N/A strncasecmp(attr->data, "urn:mspac:", STRLENOF("urn:mspac:")) == 0)
2N/A {
2N/A char *p = &attr->data[STRLENOF("urn:mspac:")];
2N/A char *endptr;
2N/A
2N/A *type = strtoul(p, &endptr, 10);
2N/A if (*type != 0 && *endptr == '\0')
2N/A return 0;
2N/A }
2N/A
2N/A return ENOENT;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_get_attribute_types(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A krb5_data **out_attrs)
2N/A{
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A unsigned int i, j;
2N/A krb5_data *attrs;
2N/A krb5_error_code code;
2N/A
2N/A if (pacctx->pac == NULL)
2N/A return ENOENT;
2N/A
2N/A attrs = calloc(1 + pacctx->pac->pac->cBuffers + 1, sizeof(krb5_data));
2N/A if (attrs == NULL)
2N/A return ENOMEM;
2N/A
2N/A j = 0;
2N/A
2N/A /* The entire PAC */
2N/A code = krb5int_copy_data_contents(kcontext,
2N/A &mspac_attribute_types[0].attribute,
2N/A &attrs[j++]);
2N/A if (code != 0) {
2N/A free(attrs);
2N/A return code;
2N/A }
2N/A
2N/A /* PAC buffers */
2N/A for (i = 0; i < pacctx->pac->pac->cBuffers; i++) {
2N/A krb5_data attr;
2N/A
2N/A code = mspac_type2attr(pacctx->pac->pac->Buffers[i].ulType, &attr);
2N/A if (code == 0) {
2N/A code = krb5int_copy_data_contents(kcontext, &attr, &attrs[j++]);
2N/A if (code != 0) {
2N/A krb5int_free_data_list(kcontext, attrs);
2N/A return code;
2N/A }
2N/A } else {
2N/A int length;
2N/A
2N/A length = asprintf(&attrs[j].data, "urn:mspac:%d",
2N/A pacctx->pac->pac->Buffers[i].ulType);
2N/A if (length < 0) {
2N/A krb5int_free_data_list(kcontext, attrs);
2N/A return ENOMEM;
2N/A }
2N/A attrs[j++].length = length;
2N/A }
2N/A }
2N/A attrs[j].data = NULL;
2N/A attrs[j].length = 0;
2N/A
2N/A *out_attrs = attrs;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_get_attribute(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A const krb5_data *attribute,
2N/A krb5_boolean *authenticated,
2N/A krb5_boolean *complete,
2N/A krb5_data *value,
2N/A krb5_data *display_value,
2N/A int *more)
2N/A{
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A krb5_error_code code;
2N/A krb5_ui_4 type;
2N/A
2N/A if (display_value != NULL) {
2N/A display_value->data = NULL;
2N/A display_value->length = 0;
2N/A }
2N/A
2N/A if (*more != -1 || pacctx->pac == NULL)
2N/A return ENOENT;
2N/A
2N/A code = mspac_attr2type(attribute, &type);
2N/A if (code != 0)
2N/A return code;
2N/A
2N/A /* -1 is a magic type that refers to the entire PAC */
2N/A if (type == (krb5_ui_4)-1) {
2N/A if (value != NULL)
2N/A code = krb5int_copy_data_contents(kcontext,
2N/A &pacctx->pac->data,
2N/A value);
2N/A else
2N/A code = 0;
2N/A } else {
2N/A if (value != NULL)
2N/A code = krb5_pac_get_buffer(kcontext, pacctx->pac, type, value);
2N/A else
2N/A code = k5_pac_locate_buffer(kcontext, pacctx->pac, type, NULL);
2N/A }
2N/A if (code == 0) {
2N/A *authenticated = pacctx->pac->verified;
2N/A *complete = TRUE;
2N/A }
2N/A
2N/A *more = 0;
2N/A
2N/A return code;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_set_attribute(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A krb5_boolean complete,
2N/A const krb5_data *attribute,
2N/A const krb5_data *value)
2N/A{
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A krb5_error_code code;
2N/A krb5_ui_4 type;
2N/A
2N/A if (pacctx->pac == NULL)
2N/A return ENOENT;
2N/A
2N/A code = mspac_attr2type(attribute, &type);
2N/A if (code != 0)
2N/A return code;
2N/A
2N/A /* -1 is a magic type that refers to the entire PAC */
2N/A if (type == (krb5_ui_4)-1) {
2N/A krb5_pac newpac;
2N/A
2N/A code = krb5_pac_parse(kcontext, value->data, value->length, &newpac);
2N/A if (code != 0)
2N/A return code;
2N/A
2N/A krb5_pac_free(kcontext, pacctx->pac);
2N/A pacctx->pac = newpac;
2N/A } else {
2N/A code = krb5_pac_add_buffer(kcontext, pacctx->pac, type, value);
2N/A }
2N/A
2N/A return code;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_export_internal(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A krb5_boolean restrict_authenticated,
2N/A void **ptr)
2N/A{
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A krb5_error_code code;
2N/A krb5_pac pac;
2N/A
2N/A *ptr = NULL;
2N/A
2N/A if (pacctx->pac == NULL)
2N/A return 0;
2N/A
2N/A if (restrict_authenticated && (pacctx->pac->verified) == FALSE)
2N/A return 0;
2N/A
2N/A code = krb5_pac_parse(kcontext, pacctx->pac->data.data,
2N/A pacctx->pac->data.length, &pac);
2N/A if (code == 0) {
2N/A pac->verified = pacctx->pac->verified;
2N/A *ptr = pac;
2N/A }
2N/A
2N/A return code;
2N/A}
2N/A
2N/Astatic void
2N/Amspac_free_internal(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A void *ptr)
2N/A{
2N/A if (ptr != NULL)
2N/A krb5_pac_free(kcontext, (krb5_pac)ptr);
2N/A
2N/A return;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_size(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A size_t *sizep)
2N/A{
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A
2N/A *sizep += sizeof(krb5_int32);
2N/A
2N/A if (pacctx->pac != NULL)
2N/A *sizep += pacctx->pac->data.length;
2N/A
2N/A *sizep += sizeof(krb5_int32);
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_externalize(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A krb5_octet **buffer,
2N/A size_t *lenremain)
2N/A{
2N/A krb5_error_code code = 0;
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A size_t required = 0;
2N/A krb5_octet *bp;
2N/A size_t remain;
2N/A
2N/A bp = *buffer;
2N/A remain = *lenremain;
2N/A
2N/A if (pacctx->pac != NULL) {
2N/A mspac_size(kcontext, context, plugin_context,
2N/A request_context, &required);
2N/A
2N/A if (required <= remain) {
2N/A krb5_ser_pack_int32((krb5_int32)pacctx->pac->data.length,
2N/A &bp, &remain);
2N/A krb5_ser_pack_bytes((krb5_octet *)pacctx->pac->data.data,
2N/A (size_t)pacctx->pac->data.length,
2N/A &bp, &remain);
2N/A krb5_ser_pack_int32((krb5_int32)pacctx->pac->verified,
2N/A &bp, &remain);
2N/A } else {
2N/A code = ENOMEM;
2N/A }
2N/A } else {
2N/A krb5_ser_pack_int32(0, &bp, &remain); /* length */
2N/A krb5_ser_pack_int32(0, &bp, &remain); /* verified */
2N/A }
2N/A
2N/A *buffer = bp;
2N/A *lenremain = remain;
2N/A
2N/A return code;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_internalize(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A krb5_octet **buffer,
2N/A size_t *lenremain)
2N/A{
2N/A struct mspac_context *pacctx = (struct mspac_context *)request_context;
2N/A krb5_error_code code;
2N/A krb5_int32 ibuf;
2N/A krb5_octet *bp;
2N/A size_t remain;
2N/A krb5_pac pac = NULL;
2N/A
2N/A bp = *buffer;
2N/A remain = *lenremain;
2N/A
2N/A /* length */
2N/A code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
2N/A if (code != 0)
2N/A return code;
2N/A
2N/A if (ibuf != 0) {
2N/A code = krb5_pac_parse(kcontext, bp, ibuf, &pac);
2N/A if (code != 0)
2N/A return code;
2N/A
2N/A bp += ibuf;
2N/A remain -= ibuf;
2N/A }
2N/A
2N/A /* verified */
2N/A code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
2N/A if (code != 0) {
2N/A krb5_pac_free(kcontext, pac);
2N/A return code;
2N/A }
2N/A
2N/A if (pac != NULL) {
2N/A pac->verified = (ibuf != 0);
2N/A }
2N/A
2N/A if (pacctx->pac != NULL) {
2N/A krb5_pac_free(kcontext, pacctx->pac);
2N/A }
2N/A
2N/A pacctx->pac = pac;
2N/A
2N/A *buffer = bp;
2N/A *lenremain = remain;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Amspac_copy(krb5_context kcontext,
2N/A krb5_authdata_context context,
2N/A void *plugin_context,
2N/A void *request_context,
2N/A void *dst_plugin_context,
2N/A void *dst_request_context)
2N/A{
2N/A struct mspac_context *srcctx = (struct mspac_context *)request_context;
2N/A struct mspac_context *dstctx = (struct mspac_context *)dst_request_context;
2N/A krb5_error_code code = 0;
2N/A
2N/A assert(dstctx != NULL);
2N/A assert(dstctx->pac == NULL);
2N/A
2N/A if (srcctx->pac != NULL)
2N/A code = k5_pac_copy(kcontext, srcctx->pac, &dstctx->pac);
2N/A
2N/A return code;
2N/A}
2N/A
2N/Astatic krb5_authdatatype mspac_ad_types[] = { KRB5_AUTHDATA_WIN2K_PAC, 0 };
2N/A
2N/Akrb5plugin_authdata_client_ftable_v0 krb5int_mspac_authdata_client_ftable = {
2N/A "mspac",
2N/A mspac_ad_types,
2N/A mspac_init,
2N/A mspac_fini,
2N/A mspac_flags,
2N/A mspac_request_init,
2N/A mspac_request_fini,
2N/A mspac_get_attribute_types,
2N/A mspac_get_attribute,
2N/A mspac_set_attribute,
2N/A NULL, /* delete_attribute_proc */
2N/A mspac_export_authdata,
2N/A mspac_import_authdata,
2N/A mspac_export_internal,
2N/A mspac_free_internal,
2N/A mspac_verify,
2N/A mspac_size,
2N/A mspac_externalize,
2N/A mspac_internalize,
2N/A mspac_copy
2N/A};