/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
*/
/*
* COPYRIGHT (C) 2006,2007
* THE REGENTS OF THE UNIVERSITY OF MICHIGAN
* ALL RIGHTS RESERVED
*
* Permission is granted to use, copy, create derivative works
* and redistribute this software and such derivative works
* for any purpose, so long as the name of The University of
* Michigan is not used in any advertising or publicity
* pertaining to the use of distribution of this software
* without specific, written prior authorization. If the
* above copyright notice or any other identification of the
* University of Michigan is included in any copy of any
* portion of this software, then the disclaimer below must
* also be included.
*
* THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
* FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
* PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
* MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
* WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
* REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
* FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
* CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
* OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
* IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGES.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
/* Solaris Kerberos */
#include <k5-int.h>
#include "pkinit.h"
/* Remove when FAST PKINIT is settled. */
#include "../fast_factor.h"
static krb5_error_code
static void
static void
static void
static pkinit_kdc_context
static krb5_error_code
{
pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n",
switch(err_code) {
break;
break;
break;
default:
pkiDebug("no edata needed for error %d (%s)\n",
retval = 0;
goto cleanup;
}
return retval;
}
static krb5_error_code
struct _krb5_db_entry_new * client,
struct _krb5_db_entry_new * server,
void *pa_plugin_context,
krb5_pa_data * data)
{
pkiDebug("pkinit_server_get_edata: entered!\n");
/* Remove (along with armor_key) when FAST PKINIT is settled. */
/* Don't advertise PKINIT if the client used FAST. */
return EINVAL;
}
/*
* If we don't have a realm context for the given realm,
* don't tell the client that we support pkinit!
*/
return retval;
}
static krb5_error_code
int *valid_san)
{
int i;
#ifdef DEBUG_SAN_INFO
#endif
&princs,
NULL);
if (retval) {
goto out;
}
/* XXX Verify this is consistent with client side XXX */
#if 0
pkiDebug("%s: call_san_checking_plugins() returned retval %d\n",
if (retval) {
goto cleanup;
}
pkiDebug("%s: call_san_checking_plugins() returned decision %d\n",
if (plugin_decision != NO_DECISION) {
goto out;
}
#endif
#ifdef DEBUG_SAN_INFO
#endif
#ifdef DEBUG_SAN_INFO
pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n",
#endif
*valid_san = 1;
retval = 0;
goto out;
}
}
/*
* XXX if cert has names but none match, should we
* be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here?
*/
pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n",
goto out;
}
#ifdef DEBUG_SAN_INFO
pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n",
#endif
*valid_san = 1;
retval = 0;
goto out;
}
}
/* We found no match */
*valid_san = 0;
/* XXX ??? If there was one or more name in the cert, but
* none matched the client name, then return mismatch? */
}
retval = 0;
out:
}
}
#ifdef DEBUG_SAN_INFO
if (client_string != NULL)
#endif
pkiDebug("%s: returning retval %d, valid_san %d\n",
return retval;
}
static krb5_error_code
int *eku_accepted)
{
*eku_accepted = 0;
*eku_accepted = 1;
retval = 0;
goto out;
}
0, /* kdc cert */
if (retval) {
pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n",
goto out;
}
out:
pkiDebug("%s: returning retval %d, eku_accepted %d\n",
return retval;
}
static krb5_error_code
struct _krb5_db_entry_new * client,
krb5_pa_data * data,
void *pa_plugin_context,
void **pa_request_context,
{
pkiDebug("pkinit_verify_padata: entered!\n");
/* Solaris Kerberos: length is unsigned */
return 0;
/* Remove (along with armor_key) when FAST PKINIT is settled. */
/* Don't allow PKINIT if the client used FAST. */
return EINVAL;
}
return EINVAL;
return 0;
#ifdef DEBUG_ASN1
#endif
/* create a per-request context */
if (retval)
goto cleanup;
case KRB5_PADATA_PK_AS_REQ:
pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n");
if (retval) {
pkiDebug("decode_krb5_pa_pk_as_req failed\n");
goto cleanup;
}
#ifdef DEBUG_ASN1
"/tmp/kdc_signed_data");
#endif
break;
pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n");
if (retval) {
pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n");
goto cleanup;
}
#ifdef DEBUG_ASN1
#endif
break;
default:
goto cleanup;
}
if (retval) {
pkiDebug("pkcs7_signeddata_verify failed\n");
goto cleanup;
}
if (is_signed) {
&valid_san);
if (retval)
goto cleanup;
if (!valid_san) {
pkiDebug("%s: did not find an acceptable SAN in user "
"certificate\n", __FUNCTION__);
goto cleanup;
}
if (retval)
goto cleanup;
if (!valid_eku) {
pkiDebug("%s: did not find an acceptable EKU in user "
"certificate\n", __FUNCTION__);
goto cleanup;
}
} else { /* !is_signed */
krb5_anonymous_principal())) {
"signed, but client not anonymous.");
goto cleanup;
}
}
#ifdef DEBUG_ASN1
#endif
case KRB5_PADATA_PK_AS_REQ:
if (retval) {
pkiDebug("failed to decode krb5_auth_pack\n");
goto cleanup;
}
/* check dh parameters */
if (retval) {
pkiDebug("bad dh parameters\n");
goto cleanup;
}
} else if (!is_signed) {
/*Anonymous pkinit requires DH*/
goto cleanup;
}
/*
* The KDC may have modified the request after decoding it.
* We need to compute the checksum on the data that
* came from the client. Therefore, we use the original
* packet contents.
*/
if (retval) {
goto cleanup;
}
if (retval) {
goto cleanup;
}
if (retval) {
pkiDebug("unable to calculate AS REQ checksum\n");
goto cleanup;
}
pkiDebug("failed to match the checksum\n");
#ifdef DEBUG_CKSUM
pkiDebug("calculating checksum on buf size (%d)\n",
pkiDebug("received checksum type=%d size=%d ",
pkiDebug("expected checksum type=%d size=%d ",
#endif
goto cleanup;
}
/* check if kdcPkId present and match KDC's subjectIdentifier */
int valid_kdcPkId = 0;
if (retval)
goto cleanup;
if (!valid_kdcPkId)
pkiDebug("kdcPkId in AS_REQ does not match KDC's cert"
"RFC says to ignore and proceed\n");
}
/* remember the decoded auth_pack for verify_padata routine */
break;
if (retval) {
pkiDebug("failed to decode krb5_auth_pack_draft9\n");
goto cleanup;
}
if (retval) {
pkiDebug("bad dh parameters\n");
goto cleanup;
}
}
/* remember the decoded auth_pack for verify_padata routine */
auth_pack9 = NULL;
break;
}
/*
* This code used to generate ad-initial-verified-cas authorization data.
* However that has been removed until the ad-kdc-issued discussion can
* happen in the working group. Dec 2009
*/
/* return authorization data to be included in the ticket */
default:
*authz_data = NULL;
}
/* remember to set the PREAUTH flag in the reply */
pkiDebug("pkinit_verify_padata failed: creating e-data\n");
pkiDebug("pkinit_create_edata failed\n");
}
case KRB5_PADATA_PK_AS_REQ:
break;
}
if (tmp_as_req != NULL)
if (auth_pack9 != NULL)
return retval;
}
static krb5_error_code
{
*out_padata = NULL;
return 0;
/*
* The KDC contribution key needs to be a fresh key of an enctype supported
* by the client and server. The existing session key meets these
* requirements so we use it.
*/
encrypting_key, "KEYEXCHANGE",
&new_session);
if (ret)
goto cleanup;
if (ret)
goto cleanup;
if (ret)
goto cleanup;
if (ret)
goto cleanup;
goto cleanup;
}
*out_padata = pa;
*session = *new_session;
return ret;
}
static krb5_error_code
struct _krb5_db_entry_new * client,
struct _krb5_key_data * client_key,
krb5_pa_data ** send_pa,
void *pa_plugin_context,
void **pa_request_context)
{
int i = 0;
unsigned int subjectPublicKey_len = 0;
int fixed_keypack = 0;
}
/* Solaris Kerberos: length is unsigned */
return 0;
pkiDebug("missing request context \n");
return EINVAL;
}
pkiDebug("Unable to locate correct realm context\n");
return ENOENT;
}
pkiDebug("pkinit_return_padata: entered!\n");
if (encrypting_key->contents) {
encrypting_key->length = 0;
}
if (!krb5_c_valid_enctype(enctype))
continue;
else {
break;
}
}
goto cleanup;
}
case KRB5_PADATA_PK_AS_REQ:
goto cleanup;
}
/* let's assume it's RSA. we'll reset it to DH if needed */
break;
goto cleanup;
}
break;
default:
goto cleanup;
}
}
/* if this DH, then process finish computing DH key */
pkiDebug("received DH key delivery AS REQ\n");
&server_key, &server_key_len);
if (retval) {
goto cleanup;
}
}
if (retval) {
pkiDebug("pkinit_octetstring2key failed: %s\n",
goto cleanup;
}
if (retval) {
pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
goto cleanup;
}
#ifdef DEBUG_ASN1
"/tmp/kdc_dh_key_info");
#endif
case KRB5_PADATA_PK_AS_REQ:
(unsigned char *)encoded_dhkey_info->data,
if (retval) {
pkiDebug("failed to create pkcs7 signed data\n");
goto cleanup;
}
break;
(unsigned char *)encoded_dhkey_info->data,
if (retval) {
pkiDebug("failed to create pkcs7 signed data\n");
goto cleanup;
}
break;
}
} else {
pkiDebug("received RSA key delivery AS REQ\n");
if (retval) {
pkiDebug("unable to make a session key\n");
goto cleanup;
}
/* check if PA_TYPE of 132 is present which means the client is
* requesting that a checksum is send back instead of the nonce
*/
pkiDebug("%s: Checking pa_type 0x%08x\n",
fixed_keypack = 1;
}
pkiDebug("%s: return checksum instead of nonce = %d\n",
/* if this is an RFC reply or draft9 client requested a checksum
* in the reply instead of the nonce, create an RFC-style keypack
*/
goto cleanup;
}
switch (encrypting_key->enctype) {
case ENCTYPE_DES_CBC_MD4:
break;
case ENCTYPE_DES_CBC_MD5:
case ENCTYPE_DES_CBC_CRC:
break;
default:
&cksum_type);
if (retval)
goto cleanup;
break;
}
if (retval) {
pkiDebug("unable to calculate AS REQ checksum\n");
goto cleanup;
}
#ifdef DEBUG_CKSUM
#endif
if (retval) {
pkiDebug("failed to encode reply_key_pack\n");
goto cleanup;
}
}
case KRB5_PADATA_PK_AS_REQ:
(unsigned char *)encoded_key_pack->data,
break;
/* if the request is from the broken draft9 client that
* expects back a nonce, create it now
*/
if (!fixed_keypack) {
goto cleanup;
}
if (retval) {
pkiDebug("failed to encode reply_key_pack\n");
goto cleanup;
}
}
(unsigned char *)encoded_key_pack->data,
break;
}
if (retval) {
pkiDebug("failed to create pkcs7 enveloped data: %s\n",
goto cleanup;
}
#ifdef DEBUG_ASN1
"/tmp/kdc_key_pack");
case KRB5_PADATA_PK_AS_REQ:
"/tmp/kdc_enc_key_pack");
break;
"/tmp/kdc_enc_key_pack");
break;
}
#endif
}
case KRB5_PADATA_PK_AS_REQ:
break;
break;
}
if (retval) {
pkiDebug("failed to encode AS_REP\n");
goto cleanup;
}
#ifdef DEBUG_ASN1
"/tmp/kdc_as_rep");
#endif
goto cleanup;
}
case KRB5_PADATA_PK_AS_REQ:
break;
break;
}
if (encoded_dhkey_info != NULL)
if (encoded_key_pack != NULL)
case KRB5_PADATA_PK_AS_REQ:
break;
if (!fixed_keypack)
else
break;
}
if (retval)
pkiDebug("pkinit_verify_padata failure");
return retval;
}
static int
{
if (patype == KRB5_PADATA_PKINIT_KX)
return PA_PSEUDO;
return PA_SUFFICIENT | PA_REPLACES_KEY;
}
0
};
static void
{
/*
* There is nothing currently allocated by pkinit_init_kdc_profile()
* which needs to be freed here.
*/
}
static krb5_error_code
{
"No pkinit_identity supplied for realm %s",
goto errout;
}
"No pkinit_anchors supplied for realm %s",
goto errout;
}
pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, "
"using default value (%d) instead\n", __FUNCTION__,
}
&eku_string);
if (eku_string != NULL) {
} else {
pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n",
}
}
return 0;
return retval;
}
static pkinit_kdc_context
{
int i;
if (pa_plugin_context == NULL)
return NULL;
for (i = 0; realm_contexts[i] != NULL; i++) {
pkinit_kdc_context p = realm_contexts[i];
pkiDebug("%s: returning context at %p for realm '%s'\n",
__FUNCTION__, p, p->realmname);
return p;
}
}
pkiDebug("%s: unable to find realm context for realm '%.*s'\n",
return NULL;
}
static int
{
goto errout;
pkiDebug("%s: initializing context at %p for realm '%s'\n",
goto errout;
if (retval)
goto errout;
if (retval)
goto errout;
if (retval)
goto errout;
if (retval)
goto errout;
if (retval)
goto errout;
/*
* Solaris Kerberos:
* Some methods of storing key information (PKCS11, PKCS12,...) may
* require interactive prompting.
*/
NULL);
if (retval)
goto errout;
if (retval)
goto errout;
pkiDebug("%s: returning context at %p for realm '%s'\n",
retval = 0;
if (retval)
return retval;
}
static int
const char **realmnames)
{
size_t i, j;
if (retval)
return retval;
/* Determine how many realms we may need to support */
for (i = 0; realmnames[i] != NULL; i++) {};
numrealms = i;
if (realm_contexts == NULL)
return ENOMEM;
for (i = 0, j = 0; i < numrealms; i++) {
realm_contexts[j++] = plgctx;
}
if (j == 0) {
/*
* Solaris Kerberos
* Improve error messages for the common case of a single realm
*/
if (numrealms != 1) {
"correctly for pkinit support");
}
goto errout;
}
*blob = realm_contexts;
retval = 0;
if (retval)
return retval;
}
static void
{
return;
}
static void
{
int i;
if (realm_contexts == NULL)
return;
for (i = 0; realm_contexts[i] != NULL; i++) {
}
}
static krb5_error_code
{
return retval;
if (retval)
goto cleanup;
retval = 0;
if (retval)
return retval;
}
static void
{
return;
}
}
/* Only necessary for static plugin linking support. */
#include "k5-plugin.h"
"pkinit", /* name */
supported_server_pa_types, /* pa_type_list */
pkinit_server_plugin_init, /* (*init_proc) */
pkinit_server_plugin_fini, /* (*fini_proc) */
pkinit_server_get_flags, /* (*flags_proc) */
pkinit_server_get_edata, /* (*edata_proc) */
pkinit_server_verify_padata,/* (*verify_proc) */
pkinit_server_return_padata,/* (*return_proc) */
NULL, /* (*freepa_reqcontext_proc) */
};