blob: 1dc79baa0a52a00fa272f727b54f5d468182f461 [file] [log] [blame]
/*
* 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.
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, OmniTI Computer Consulting, Inc. All rights reserved.
* Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
* Copyright 2018 RackTop Systems.
*/
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <unistd.h>
#include <dirent.h>
/* Solaris Kerberos */
#include <libintl.h>
#include <assert.h>
#include <security/pam_appl.h>
#include <ctype.h>
#include "k5-int.h"
#include <ctype.h>
/*
* Q: What is this SILLYDECRYPT stuff about?
* A: When using the ActivCard Linux pkcs11 library (v2.0.1),
* the decrypt function fails. By inserting an extra
* function call, which serves nothing but to change the
* stack, we were able to work around the issue. If the
* ActivCard library is fixed in the future, this
* definition and related code can be removed.
*/
#define SILLYDECRYPT
#include "pkinit_crypto_openssl.h"
/*
* Solaris Kerberos:
* Changed to a switch statement so gettext() can be used
* for internationization.
* Use defined constants rather than raw numbers for error codes.
*/
static char *
pkcs11_error_table(short code) {
switch (code) {
case CKR_OK:
return (gettext("ok"));
case CKR_CANCEL:
return (gettext("cancel"));
case CKR_HOST_MEMORY:
return (gettext("host memory"));
case CKR_SLOT_ID_INVALID:
return (gettext("slot id invalid"));
case CKR_GENERAL_ERROR:
return (gettext("general error"));
case CKR_FUNCTION_FAILED:
return (gettext("function failed"));
case CKR_ARGUMENTS_BAD:
return (gettext("arguments bad"));
case CKR_NO_EVENT:
return (gettext("no event"));
case CKR_NEED_TO_CREATE_THREADS:
return (gettext("need to create threads"));
case CKR_CANT_LOCK:
return (gettext("cant lock"));
case CKR_ATTRIBUTE_READ_ONLY:
return (gettext("attribute read only"));
case CKR_ATTRIBUTE_SENSITIVE:
return (gettext("attribute sensitive"));
case CKR_ATTRIBUTE_TYPE_INVALID:
return (gettext("attribute type invalid"));
case CKR_ATTRIBUTE_VALUE_INVALID:
return (gettext("attribute value invalid"));
case CKR_DATA_INVALID:
return (gettext("data invalid"));
case CKR_DATA_LEN_RANGE:
return (gettext("data len range"));
case CKR_DEVICE_ERROR:
return (gettext("device error"));
case CKR_DEVICE_MEMORY:
return (gettext("device memory"));
case CKR_DEVICE_REMOVED:
return (gettext("device removed"));
case CKR_ENCRYPTED_DATA_INVALID:
return (gettext("encrypted data invalid"));
case CKR_ENCRYPTED_DATA_LEN_RANGE:
return (gettext("encrypted data len range"));
case CKR_FUNCTION_CANCELED:
return (gettext("function canceled"));
case CKR_FUNCTION_NOT_PARALLEL:
return (gettext("function not parallel"));
case CKR_FUNCTION_NOT_SUPPORTED:
return (gettext("function not supported"));
case CKR_KEY_HANDLE_INVALID:
return (gettext("key handle invalid"));
case CKR_KEY_SIZE_RANGE:
return (gettext("key size range"));
case CKR_KEY_TYPE_INCONSISTENT:
return (gettext("key type inconsistent"));
case CKR_KEY_NOT_NEEDED:
return (gettext("key not needed"));
case CKR_KEY_CHANGED:
return (gettext("key changed"));
case CKR_KEY_NEEDED:
return (gettext("key needed"));
case CKR_KEY_INDIGESTIBLE:
return (gettext("key indigestible"));
case CKR_KEY_FUNCTION_NOT_PERMITTED:
return (gettext("key function not permitted"));
case CKR_KEY_NOT_WRAPPABLE:
return (gettext("key not wrappable"));
case CKR_KEY_UNEXTRACTABLE:
return (gettext("key unextractable"));
case CKR_MECHANISM_INVALID:
return (gettext("mechanism invalid"));
case CKR_MECHANISM_PARAM_INVALID:
return (gettext("mechanism param invalid"));
case CKR_OBJECT_HANDLE_INVALID:
return (gettext("object handle invalid"));
case CKR_OPERATION_ACTIVE:
return (gettext("operation active"));
case CKR_OPERATION_NOT_INITIALIZED:
return (gettext("operation not initialized"));
case CKR_PIN_INCORRECT:
return (gettext("pin incorrect"));
case CKR_PIN_INVALID:
return (gettext("pin invalid"));
case CKR_PIN_LEN_RANGE:
return (gettext("pin len range"));
case CKR_PIN_EXPIRED:
return (gettext("pin expired"));
case CKR_PIN_LOCKED:
return (gettext("pin locked"));
case CKR_SESSION_CLOSED:
return (gettext("session closed"));
case CKR_SESSION_COUNT:
return (gettext("session count"));
case CKR_SESSION_HANDLE_INVALID:
return (gettext("session handle invalid"));
case CKR_SESSION_PARALLEL_NOT_SUPPORTED:
return (gettext("session parallel not supported"));
case CKR_SESSION_READ_ONLY:
return (gettext("session read only"));
case CKR_SESSION_EXISTS:
return (gettext("session exists"));
case CKR_SESSION_READ_ONLY_EXISTS:
return (gettext("session read only exists"));
case CKR_SESSION_READ_WRITE_SO_EXISTS:
return (gettext("session read write so exists"));
case CKR_SIGNATURE_INVALID:
return (gettext("signature invalid"));
case CKR_SIGNATURE_LEN_RANGE:
return (gettext("signature len range"));
case CKR_TEMPLATE_INCOMPLETE:
return (gettext("template incomplete"));
case CKR_TEMPLATE_INCONSISTENT:
return (gettext("template inconsistent"));
case CKR_TOKEN_NOT_PRESENT:
return (gettext("token not present"));
case CKR_TOKEN_NOT_RECOGNIZED:
return (gettext("token not recognized"));
case CKR_TOKEN_WRITE_PROTECTED:
return (gettext("token write protected"));
case CKR_UNWRAPPING_KEY_HANDLE_INVALID:
return (gettext("unwrapping key handle invalid"));
case CKR_UNWRAPPING_KEY_SIZE_RANGE:
return (gettext("unwrapping key size range"));
case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT:
return (gettext("unwrapping key type inconsistent"));
case CKR_USER_ALREADY_LOGGED_IN:
return (gettext("user already logged in"));
case CKR_USER_NOT_LOGGED_IN:
return (gettext("user not logged in"));
case CKR_USER_PIN_NOT_INITIALIZED:
return (gettext("user pin not initialized"));
case CKR_USER_TYPE_INVALID:
return (gettext("user type invalid"));
case CKR_USER_ANOTHER_ALREADY_LOGGED_IN:
return (gettext("user another already logged in"));
case CKR_USER_TOO_MANY_TYPES:
return (gettext("user too many types"));
case CKR_WRAPPED_KEY_INVALID:
return (gettext("wrapped key invalid"));
case CKR_WRAPPED_KEY_LEN_RANGE:
return (gettext("wrapped key len range"));
case CKR_WRAPPING_KEY_HANDLE_INVALID:
return (gettext("wrapping key handle invalid"));
case CKR_WRAPPING_KEY_SIZE_RANGE:
return (gettext("wrapping key size range"));
case CKR_WRAPPING_KEY_TYPE_INCONSISTENT:
return (gettext("wrapping key type inconsistent"));
case CKR_RANDOM_SEED_NOT_SUPPORTED:
return (gettext("random seed not supported"));
case CKR_RANDOM_NO_RNG:
return (gettext("random no rng"));
case CKR_DOMAIN_PARAMS_INVALID:
return (gettext("domain params invalid"));
case CKR_BUFFER_TOO_SMALL:
return (gettext("buffer too small"));
case CKR_SAVED_STATE_INVALID:
return (gettext("saved state invalid"));
case CKR_INFORMATION_SENSITIVE:
return (gettext("information sensitive"));
case CKR_STATE_UNSAVEABLE:
return (gettext("state unsaveable"));
case CKR_CRYPTOKI_NOT_INITIALIZED:
return (gettext("cryptoki not initialized"));
case CKR_CRYPTOKI_ALREADY_INITIALIZED:
return (gettext("cryptoki already initialized"));
case CKR_MUTEX_BAD:
return (gettext("mutex bad"));
case CKR_MUTEX_NOT_LOCKED:
return (gettext("mutex not locked"));
case CKR_FUNCTION_REJECTED:
return (gettext("function rejected"));
default:
return (gettext("unknown error"));
}
}
/* DH parameters */
unsigned char pkinit_1024_dhprime[128] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
unsigned char pkinit_2048_dhprime[2048/8] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
unsigned char pkinit_4096_dhprime[4096/8] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
/*
* Many things have changed in OpenSSL 1.1. The code in this file has been
* updated to use the v1.1 APIs but some are new and require emulation
* for older OpenSSL versions.
*/
/* EVP_MD_CTX construct and destructor names have changed */
#define EVP_MD_CTX_new EVP_MD_CTX_create
#define EVP_MD_CTX_free EVP_MD_CTX_destroy
/* ASN1_STRING_data is deprecated */
#define ASN1_STRING_get0_data ASN1_STRING_data
/* X509_STORE_CTX_trusted_stack is deprecated */
#define X509_STORE_CTX_set0_trusted_stack X509_STORE_CTX_trusted_stack
/* get_rfc2409_prime_1024() has been renamed. */
#define BN_get_rfc2409_prime_1024 get_rfc2409_prime_1024
#define OBJ_get0_data(o) ((o)->data)
#define OBJ_length(o) ((o)->length)
/* Some new DH functions that aren't in OpenSSL 1.0.x */
#define DH_bits(dh) BN_num_bits((dh)->p);
#define DH_set0_pqg(dh, p, q, g) __DH_set0_pqg(dh, p, q, g)
static int
__DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
{
if ((dh->p == NULL && p == NULL) || (dh->g == NULL && g == NULL))
return 0;
if (p != NULL) {
BN_free(dh->p);
dh->p = p;
}
if (q != NULL) {
BN_free(dh->q);
dh->q = q;
}
if (g != NULL) {
BN_free(dh->g);
dh->g = g;
}
if (q != NULL) {
dh->length = BN_num_bits(q);
}
return 1;
}
#define DH_get0_pqg(dh, p, q, g) __DH_get0_pqg(dh, p, q, g)
static void
__DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q,
const BIGNUM **g)
{
if (p != NULL)
*p = dh->p;
if (q != NULL)
*q = dh->q;
if (g != NULL)
*g = dh->g;
}
#define DH_set0_key(dh, pub, priv) __DH_set0_key(dh, pub, priv)
static int
__DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
{
if (pub_key != NULL) {
BN_free(dh->pub_key);
dh->pub_key = pub_key;
}
if (priv_key != NULL) {
BN_free(dh->priv_key);
dh->priv_key = priv_key;
}
return 1;
}
#define DH_get0_key(dh, pub, priv) __DH_get0_key(dh, pub, priv)
static void
__DH_get0_key(const DH *dh, const BIGNUM **pub, const BIGNUM **priv)
{
if (pub != NULL)
*pub = dh->pub_key;
if (priv != NULL)
*priv = dh->priv_key;
}
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L || LIBRESSL_VERSION_NUMBER */
krb5_error_code
pkinit_init_plg_crypto(pkinit_plg_crypto_context *cryptoctx) {
krb5_error_code retval = ENOMEM;
pkinit_plg_crypto_context ctx = NULL;
/* initialize openssl routines */
/* Solaris Kerberos */
retval = openssl_init();
if (retval != 0)
goto out;
ctx = (pkinit_plg_crypto_context)malloc(sizeof(*ctx));
if (ctx == NULL)
goto out;
(void) memset(ctx, 0, sizeof(*ctx));
pkiDebug("%s: initializing openssl crypto context at %p\n",
__FUNCTION__, ctx);
retval = pkinit_init_pkinit_oids(ctx);
if (retval)
goto out;
retval = pkinit_init_dh_params(ctx);
if (retval)
goto out;
*cryptoctx = ctx;
out:
if (retval && ctx != NULL)
pkinit_fini_plg_crypto(ctx);
return retval;
}
void
pkinit_fini_plg_crypto(pkinit_plg_crypto_context cryptoctx)
{
pkiDebug("%s: freeing context at %p\n", __FUNCTION__, cryptoctx);
if (cryptoctx == NULL)
return;
pkinit_fini_pkinit_oids(cryptoctx);
pkinit_fini_dh_params(cryptoctx);
free(cryptoctx);
}
krb5_error_code
pkinit_init_identity_crypto(pkinit_identity_crypto_context *idctx)
{
krb5_error_code retval = ENOMEM;
pkinit_identity_crypto_context ctx = NULL;
ctx = (pkinit_identity_crypto_context)malloc(sizeof(*ctx));
if (ctx == NULL)
goto out;
(void) memset(ctx, 0, sizeof(*ctx));
retval = pkinit_init_certs(ctx);
if (retval)
goto out;
retval = pkinit_init_pkcs11(ctx);
if (retval)
goto out;
pkiDebug("%s: returning ctx at %p\n", __FUNCTION__, ctx);
*idctx = ctx;
out:
if (retval) {
if (ctx)
pkinit_fini_identity_crypto(ctx);
}
return retval;
}
void
pkinit_fini_identity_crypto(pkinit_identity_crypto_context idctx)
{
if (idctx == NULL)
return;
pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, idctx);
pkinit_fini_certs(idctx);
pkinit_fini_pkcs11(idctx);
free(idctx);
}
krb5_error_code
pkinit_init_req_crypto(pkinit_req_crypto_context *cryptoctx)
{
pkinit_req_crypto_context ctx = NULL;
/* Solaris Kerberos */
if (cryptoctx == NULL)
return EINVAL;
ctx = (pkinit_req_crypto_context)malloc(sizeof(*ctx));
if (ctx == NULL)
return ENOMEM;
(void) memset(ctx, 0, sizeof(*ctx));
ctx->dh = NULL;
ctx->received_cert = NULL;
*cryptoctx = ctx;
pkiDebug("%s: returning ctx at %p\n", __FUNCTION__, ctx);
return 0;
}
void
pkinit_fini_req_crypto(pkinit_req_crypto_context req_cryptoctx)
{
if (req_cryptoctx == NULL)
return;
pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, req_cryptoctx);
if (req_cryptoctx->dh != NULL)
DH_free(req_cryptoctx->dh);
if (req_cryptoctx->received_cert != NULL)
X509_free(req_cryptoctx->received_cert);
free(req_cryptoctx);
}
static krb5_error_code
pkinit_init_pkinit_oids(pkinit_plg_crypto_context ctx)
{
ctx->id_pkinit_san = OBJ_txt2obj("1.3.6.1.5.2.2", 1);
if (ctx->id_pkinit_san == NULL)
return ENOMEM;
ctx->id_pkinit_authData = OBJ_txt2obj("1.3.6.1.5.2.3.1", 1);
if (ctx->id_pkinit_authData == NULL)
return ENOMEM;
ctx->id_pkinit_DHKeyData = OBJ_txt2obj("1.3.6.1.5.2.3.2", 1);
if (ctx->id_pkinit_DHKeyData == NULL)
return ENOMEM;
ctx->id_pkinit_rkeyData = OBJ_txt2obj("1.3.6.1.5.2.3.3", 1);
if (ctx->id_pkinit_rkeyData == NULL)
return ENOMEM;
ctx->id_pkinit_KPClientAuth = OBJ_txt2obj("1.3.6.1.5.2.3.4", 1);
if (ctx->id_pkinit_KPClientAuth == NULL)
return ENOMEM;
ctx->id_pkinit_KPKdc = OBJ_txt2obj("1.3.6.1.5.2.3.5", 1);
if (ctx->id_pkinit_KPKdc == NULL)
return ENOMEM;
ctx->id_ms_kp_sc_logon = OBJ_txt2obj("1.3.6.1.4.1.311.20.2.2", 1);
if (ctx->id_ms_kp_sc_logon == NULL)
return ENOMEM;
ctx->id_ms_san_upn = OBJ_txt2obj("1.3.6.1.4.1.311.20.2.3", 1);
if (ctx->id_ms_san_upn == NULL)
return ENOMEM;
ctx->id_kp_serverAuth = OBJ_txt2obj("1.3.6.1.5.5.7.3.1", 1);
if (ctx->id_kp_serverAuth == NULL)
return ENOMEM;
return 0;
}
static krb5_error_code
get_cert(char *filename, X509 **retcert)
{
X509 *cert = NULL;
BIO *tmp = NULL;
int code;
krb5_error_code retval;
if (filename == NULL || retcert == NULL)
return EINVAL;
*retcert = NULL;
tmp = BIO_new(BIO_s_file());
if (tmp == NULL)
return ENOMEM;
code = BIO_read_filename(tmp, filename);
if (code == 0) {
retval = errno;
goto cleanup;
}
cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL);
if (cert == NULL) {
retval = EIO;
pkiDebug("failed to read certificate from %s\n", filename);
goto cleanup;
}
*retcert = cert;
retval = 0;
cleanup:
if (tmp != NULL)
BIO_free(tmp);
return retval;
}
static krb5_error_code
get_key(char *filename, EVP_PKEY **retkey)
{
EVP_PKEY *pkey = NULL;
BIO *tmp = NULL;
int code;
krb5_error_code retval;
if (filename == NULL || retkey == NULL)
return EINVAL;
tmp = BIO_new(BIO_s_file());
if (tmp == NULL)
return ENOMEM;
code = BIO_read_filename(tmp, filename);
if (code == 0) {
retval = errno;
goto cleanup;
}
pkey = (EVP_PKEY *) PEM_read_bio_PrivateKey(tmp, NULL, NULL, NULL);
if (pkey == NULL) {
retval = EIO;
pkiDebug("failed to read private key from %s\n", filename);
goto cleanup;
}
*retkey = pkey;
retval = 0;
cleanup:
if (tmp != NULL)
BIO_free(tmp);
return retval;
}
static void
pkinit_fini_pkinit_oids(pkinit_plg_crypto_context ctx)
{
if (ctx == NULL)
return;
ASN1_OBJECT_free(ctx->id_pkinit_san);
ASN1_OBJECT_free(ctx->id_pkinit_authData);
ASN1_OBJECT_free(ctx->id_pkinit_DHKeyData);
ASN1_OBJECT_free(ctx->id_pkinit_rkeyData);
ASN1_OBJECT_free(ctx->id_pkinit_KPClientAuth);
ASN1_OBJECT_free(ctx->id_pkinit_KPKdc);
ASN1_OBJECT_free(ctx->id_ms_kp_sc_logon);
ASN1_OBJECT_free(ctx->id_ms_san_upn);
ASN1_OBJECT_free(ctx->id_kp_serverAuth);
}
static DH *
make_dhprime(uint8_t *prime, size_t len)
{
DH *dh = NULL;
BIGNUM *p = NULL, *q = NULL, *g = NULL;
if ((p = BN_bin2bn(prime, len, NULL)) == NULL)
goto cleanup;
if ((q = BN_new()) == NULL)
goto cleanup;
if (!BN_rshift1(q, p))
goto cleanup;
if ((g = BN_new()) == NULL)
goto cleanup;
if (!BN_set_word(g, DH_GENERATOR_2))
goto cleanup;
dh = DH_new();
if (dh == NULL)
goto cleanup;
DH_set0_pqg(dh, p, q, g);
p = g = q = NULL;
cleanup:
BN_free(p);
BN_free(q);
BN_free(g);
return dh;
}
static krb5_error_code
pkinit_init_dh_params(pkinit_plg_crypto_context plgctx)
{
krb5_error_code retval = ENOMEM;
plgctx->dh_1024 = make_dhprime(pkinit_1024_dhprime,
sizeof(pkinit_1024_dhprime));
if (plgctx->dh_1024 == NULL)
goto cleanup;
plgctx->dh_2048 = make_dhprime(pkinit_2048_dhprime,
sizeof(pkinit_2048_dhprime));
if (plgctx->dh_2048 == NULL)
goto cleanup;
plgctx->dh_4096 = make_dhprime(pkinit_4096_dhprime,
sizeof(pkinit_4096_dhprime));
if (plgctx->dh_4096 == NULL)
goto cleanup;
retval = 0;
cleanup:
if (retval)
pkinit_fini_dh_params(plgctx);
return retval;
}
static void
pkinit_fini_dh_params(pkinit_plg_crypto_context plgctx)
{
if (plgctx->dh_1024 != NULL)
DH_free(plgctx->dh_1024);
if (plgctx->dh_2048 != NULL)
DH_free(plgctx->dh_2048);
if (plgctx->dh_4096 != NULL)
DH_free(plgctx->dh_4096);
plgctx->dh_1024 = plgctx->dh_2048 = plgctx->dh_4096 = NULL;
}
static krb5_error_code
pkinit_init_certs(pkinit_identity_crypto_context ctx)
{
/* Solaris Kerberos */
int i;
for (i = 0; i < MAX_CREDS_ALLOWED; i++)
ctx->creds[i] = NULL;
ctx->my_certs = NULL;
ctx->cert_index = 0;
ctx->my_key = NULL;
ctx->trustedCAs = NULL;
ctx->intermediateCAs = NULL;
ctx->revoked = NULL;
return 0;
}
static void
pkinit_fini_certs(pkinit_identity_crypto_context ctx)
{
if (ctx == NULL)
return;
if (ctx->my_certs != NULL)
sk_X509_pop_free(ctx->my_certs, X509_free);
if (ctx->my_key != NULL)
EVP_PKEY_free(ctx->my_key);
if (ctx->trustedCAs != NULL)
sk_X509_pop_free(ctx->trustedCAs, X509_free);
if (ctx->intermediateCAs != NULL)
sk_X509_pop_free(ctx->intermediateCAs, X509_free);
if (ctx->revoked != NULL)
sk_X509_CRL_pop_free(ctx->revoked, X509_CRL_free);
}
static krb5_error_code
pkinit_init_pkcs11(pkinit_identity_crypto_context ctx)
{
/* Solaris Kerberos */
#ifndef WITHOUT_PKCS11
ctx->p11_module_name = strdup(PKCS11_MODNAME);
if (ctx->p11_module_name == NULL)
return ENOMEM;
ctx->p11_module = NULL;
ctx->slotid = PK_NOSLOT;
ctx->token_label = NULL;
ctx->cert_label = NULL;
ctx->PIN = NULL;
ctx->session = CK_INVALID_HANDLE;
ctx->p11 = NULL;
ctx->p11flags = 0; /* Solaris Kerberos */
#endif
ctx->pkcs11_method = 0;
(void) memset(ctx->creds, 0, sizeof(ctx->creds));
return 0;
}
static void
pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx)
{
#ifndef WITHOUT_PKCS11
if (ctx == NULL)
return;
if (ctx->p11 != NULL) {
if (ctx->session != CK_INVALID_HANDLE) {
ctx->p11->C_CloseSession(ctx->session);
ctx->session = CK_INVALID_HANDLE;
}
/*
* Solaris Kerberos:
* Only call C_Finalize if the process was not already using pkcs11.
*/
if (ctx->finalize_pkcs11 == TRUE)
ctx->p11->C_Finalize(NULL_PTR);
ctx->p11 = NULL;
}
if (ctx->p11_module != NULL) {
pkinit_C_UnloadModule(ctx->p11_module);
ctx->p11_module = NULL;
}
if (ctx->p11_module_name != NULL)
free(ctx->p11_module_name);
if (ctx->token_label != NULL)
free(ctx->token_label);
if (ctx->cert_id != NULL)
free(ctx->cert_id);
if (ctx->cert_label != NULL)
free(ctx->cert_label);
if (ctx->PIN != NULL) {
(void) memset(ctx->PIN, 0, strlen(ctx->PIN));
free(ctx->PIN);
}
#endif
}
krb5_error_code
pkinit_identity_set_prompter(pkinit_identity_crypto_context id_cryptoctx,
krb5_prompter_fct prompter,
void *prompter_data)
{
id_cryptoctx->prompter = prompter;
id_cryptoctx->prompter_data = prompter_data;
return 0;
}
/* Create a CMS ContentInfo of type oid containing the octet string in data. */
static krb5_error_code
create_contentinfo(krb5_context context,
ASN1_OBJECT *oid,
unsigned char *data,
size_t data_len,
PKCS7 **p7_out)
{
PKCS7 *p7 = NULL;
ASN1_OCTET_STRING *ostr = NULL;
*p7_out = NULL;
ostr = ASN1_OCTET_STRING_new();
if (ostr == NULL)
goto oom;
if (!ASN1_OCTET_STRING_set(ostr, (unsigned char *)data, data_len))
goto oom;
p7 = PKCS7_new();
if (p7 == NULL)
goto oom;
p7->type = OBJ_dup(oid);
if (p7->type == NULL)
goto oom;
if (OBJ_obj2nid(oid) == NID_pkcs7_data) {
/* Draft 9 uses id-pkcs7-data for signed data. For this type OpenSSL
* expects an octet string in d.data. */
p7->d.data = ostr;
} else {
p7->d.other = ASN1_TYPE_new();
if (p7->d.other == NULL)
goto oom;
p7->d.other->type = V_ASN1_OCTET_STRING;
p7->d.other->value.octet_string = ostr;
}
*p7_out = p7;
return 0;
oom:
if (ostr != NULL)
ASN1_OCTET_STRING_free(ostr);
if (p7 != NULL)
PKCS7_free(p7);
return ENOMEM;
}
/* ARGSUSED */
krb5_error_code
cms_signeddata_create(krb5_context context,
pkinit_plg_crypto_context plg_cryptoctx,
pkinit_req_crypto_context req_cryptoctx,
pkinit_identity_crypto_context id_cryptoctx,
int cms_msg_type,
int include_certchain,
unsigned char *data,
unsigned int data_len,
unsigned char **signed_data,
unsigned int *signed_data_len)
{
/* Solaris Kerberos */
krb5_error_code retval = KRB5KRB_ERR_GENERIC;
PKCS7 *p7 = NULL, *inner_p7 = NULL;
PKCS7_SIGNED *p7s = NULL;
PKCS7_SIGNER_INFO *p7si = NULL;
unsigned char *p;
STACK_OF(X509) * cert_stack = NULL;
ASN1_OCTET_STRING *digest_attr = NULL;
EVP_MD_CTX *ctx = NULL, *ctx2 = NULL;
const EVP_MD *md_tmp = NULL;
unsigned char md_data[EVP_MAX_MD_SIZE], md_data2[EVP_MAX_MD_SIZE];
unsigned char *digestInfo_buf = NULL, *abuf = NULL;
unsigned int md_len, md_len2, alen, digestInfo_len;
STACK_OF(X509_ATTRIBUTE) * sk;
unsigned char *sig = NULL;
unsigned int sig_len = 0;
X509_ALGOR *alg = NULL;
ASN1_OCTET_STRING *digest = NULL;
unsigned int alg_len = 0, digest_len = 0;
unsigned char *y = NULL, *alg_buf = NULL, *digest_buf = NULL;
X509 *cert = NULL;
ASN1_OBJECT *oid = NULL, *oid_copy;
/* Solaris Kerberos */
if (signed_data == NULL)
return EINVAL;
if (signed_data_len == NULL)
return EINVAL;
/* start creating PKCS7 data */
if ((p7 = PKCS7_new()) == NULL)
goto cleanup;
p7->type = OBJ_nid2obj(NID_pkcs7_signed);
if ((p7s = PKCS7_SIGNED_new()) == NULL)
goto cleanup;
p7->d.sign = p7s;
if (!ASN1_INTEGER_set(p7s->version, 3))
goto cleanup;
/* create a cert chain that has at least the signer's certificate */
if ((cert_stack = sk_X509_new_null()) == NULL)
goto cleanup;
cert = sk_X509_value(id_cryptoctx->my_certs, id_cryptoctx->cert_index);
if (!include_certchain) {
pkiDebug("only including signer's certificate\n");
sk_X509_push(cert_stack, X509_dup(cert));
} else {
/* create a cert chain */
X509_STORE *certstore = NULL;
X509_STORE_CTX *certctx;
STACK_OF(X509) *certstack = NULL;
char buf[DN_BUF_LEN];
int i = 0, size = 0;
if ((certstore = X509_STORE_new()) == NULL)
goto cleanup;
if ((certctx = X509_STORE_CTX_new()) == NULL)
goto cleanup;
pkiDebug("building certificate chain\n");
X509_STORE_set_verify_cb(certstore, openssl_callback);
X509_STORE_CTX_init(certctx, certstore, cert,
id_cryptoctx->intermediateCAs);
X509_STORE_CTX_set0_trusted_stack(certctx, id_cryptoctx->trustedCAs);
/* Solaris Kerberos */
if (X509_verify_cert(certctx) <= 0) {
pkiDebug("failed to create a certificate chain: %s\n",
X509_verify_cert_error_string(X509_STORE_CTX_get_error(certctx)));
if (!sk_X509_num(id_cryptoctx->trustedCAs))
pkiDebug("No trusted CAs found. Check your X509_anchors\n");
goto cleanup;
}
certstack = X509_STORE_CTX_get1_chain(certctx);
size = sk_X509_num(certstack);
pkiDebug("size of certificate chain = %d\n", size);
for(i = 0; i < size - 1; i++) {
X509 *x = sk_X509_value(certstack, i);
X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));
pkiDebug("cert #%d: %s\n", i, buf);
sk_X509_push(cert_stack, X509_dup(x));
}
X509_STORE_CTX_free(certctx);
X509_STORE_free(certstore);
sk_X509_pop_free(certstack, X509_free);
}
p7s->cert = cert_stack;
/* fill-in PKCS7_SIGNER_INFO */
if ((p7si = PKCS7_SIGNER_INFO_new()) == NULL)
goto cleanup;
if (!ASN1_INTEGER_set(p7si->version, 1))
goto cleanup;
if (!X509_NAME_set(&p7si->issuer_and_serial->issuer,
X509_get_issuer_name(cert)))
goto cleanup;
/* because ASN1_INTEGER_set is used to set a 'long' we will do
* things the ugly way. */
ASN1_INTEGER_free(p7si->issuer_and_serial->serial);
if (!(p7si->issuer_and_serial->serial =
ASN1_INTEGER_dup(X509_get_serialNumber(cert))))
goto cleanup;
/* will not fill-out EVP_PKEY because it's on the smartcard */
/* Set digest algs */
p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha1);
if (p7si->digest_alg->parameter != NULL)
ASN1_TYPE_free(p7si->digest_alg->parameter);
if ((p7si->digest_alg->parameter = ASN1_TYPE_new()) == NULL)
goto cleanup;
p7si->digest_alg->parameter->type = V_ASN1_NULL;
/* Set sig algs */
if (p7si->digest_enc_alg->parameter != NULL)
ASN1_TYPE_free(p7si->digest_enc_alg->parameter);
p7si->digest_enc_alg->algorithm = OBJ_nid2obj(NID_sha1WithRSAEncryption);
if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new()))
goto cleanup;
p7si->digest_enc_alg->parameter->type = V_ASN1_NULL;
/* pick the correct oid for the eContentInfo */
oid = pkinit_pkcs7type2oid(plg_cryptoctx, cms_msg_type);
if (oid == NULL)
goto cleanup;
if (cms_msg_type == CMS_SIGN_DRAFT9) {
/* don't include signed attributes for pa-type 15 request */
abuf = data;
alen = data_len;
} else {
/* add signed attributes */
/* compute sha1 digest over the EncapsulatedContentInfo */
ctx = EVP_MD_CTX_new();
if (ctx == NULL)
goto cleanup2;
EVP_MD_CTX_init(ctx);
EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
EVP_DigestUpdate(ctx, data, data_len);
md_tmp = EVP_MD_CTX_md(ctx);
EVP_DigestFinal_ex(ctx, md_data, &md_len);
EVP_MD_CTX_free(ctx);
ctx = NULL;
/* create a message digest attr */
digest_attr = ASN1_OCTET_STRING_new();
ASN1_OCTET_STRING_set(digest_attr, md_data, (int)md_len);
PKCS7_add_signed_attribute(p7si, NID_pkcs9_messageDigest,
V_ASN1_OCTET_STRING, (char *) digest_attr);
/* create a content-type attr */
oid_copy = OBJ_dup(oid);
if (oid_copy == NULL)
goto cleanup2;
PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType,
V_ASN1_OBJECT, oid_copy);
/* create the signature over signed attributes. get DER encoded value */
/* This is the place where smartcard signature needs to be calculated */
sk = p7si->auth_attr;
alen = ASN1_item_i2d((ASN1_VALUE *) sk, &abuf,
ASN1_ITEM_rptr(PKCS7_ATTR_SIGN));
if (abuf == NULL)
goto cleanup2;
}
#ifndef WITHOUT_PKCS11
/* Some tokens can only do RSAEncryption without sha1 hash */
/* to compute sha1WithRSAEncryption, encode the algorithm ID for the hash
* function and the hash value into an ASN.1 value of type DigestInfo
* DigestInfo::=SEQUENCE {
* digestAlgorithm AlgorithmIdentifier,
* digest OCTET STRING }
*/
if (id_cryptoctx->pkcs11_method == 1 &&
id_cryptoctx->mech == CKM_RSA_PKCS) {
pkiDebug("mech = CKM_RSA_PKCS\n");
ctx2 = EVP_MD_CTX_new();
if (ctx2 == NULL)
goto cleanup2;
EVP_MD_CTX_init(ctx2);
/* if this is not draft9 request, include digest signed attribute */
if (cms_msg_type != CMS_SIGN_DRAFT9)
EVP_DigestInit_ex(ctx2, md_tmp, NULL);
else
EVP_DigestInit_ex(ctx2, EVP_sha1(), NULL);
EVP_DigestUpdate(ctx2, abuf, alen);
EVP_DigestFinal_ex(ctx2, md_data2, &md_len2);
EVP_MD_CTX_free(ctx2);
ctx2 = NULL;
alg = X509_ALGOR_new();
if (alg == NULL)
goto cleanup2;
alg->algorithm = OBJ_nid2obj(NID_sha1);
alg->parameter = NULL;
alg_len = i2d_X509_ALGOR(alg, NULL);
alg_buf = (unsigned char *)malloc(alg_len);
if (alg_buf == NULL)
goto cleanup2;
digest = ASN1_OCTET_STRING_new();
if (digest == NULL)
goto cleanup2;
ASN1_OCTET_STRING_set(digest, md_data2, (int)md_len2);
digest_len = i2d_ASN1_OCTET_STRING(digest, NULL);
digest_buf = (unsigned char *)malloc(digest_len);
if (digest_buf == NULL)
goto cleanup2;
digestInfo_len = ASN1_object_size(1, (int)(alg_len + digest_len),
V_ASN1_SEQUENCE);
y = digestInfo_buf = (unsigned char *)malloc(digestInfo_len);
if (digestInfo_buf == NULL)
goto cleanup2;
ASN1_put_object(&y, 1, (int)(alg_len + digest_len), V_ASN1_SEQUENCE,
V_ASN1_UNIVERSAL);
i2d_X509_ALGOR(alg, &y);
i2d_ASN1_OCTET_STRING(digest, &y);
#ifdef DEBUG_SIG
pkiDebug("signing buffer\n");
print_buffer(digestInfo_buf, digestInfo_len);
print_buffer_bin(digestInfo_buf, digestInfo_len, "/tmp/pkcs7_tosign");
#endif
retval = pkinit_sign_data(context, id_cryptoctx, digestInfo_buf,
digestInfo_len, &sig, &sig_len);
} else
#endif
{
pkiDebug("mech = %s\n",
id_cryptoctx->pkcs11_method == 1 ? "CKM_SHA1_RSA_PKCS" : "FS");
retval = pkinit_sign_data(context, id_cryptoctx, abuf, alen,
&sig, &sig_len);
}
#ifdef DEBUG_SIG
print_buffer(sig, sig_len);
#endif
if (cms_msg_type != CMS_SIGN_DRAFT9)
free(abuf);
if (retval)
goto cleanup2;
/* Add signature */
if (!ASN1_STRING_set(p7si->enc_digest, (unsigned char *) sig,
(int)sig_len)) {
unsigned long err = ERR_peek_error();
retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_set_error_message(context, retval, "%s\n",
ERR_error_string(err, NULL));
pkiDebug("failed to add a signed digest attribute\n");
goto cleanup2;
}
/* adder signer_info to pkcs7 signed */
if (!PKCS7_add_signer(p7, p7si))
goto cleanup2;
/* start on adding data to the pkcs7 signed */
retval = create_contentinfo(context, oid, data, data_len, &inner_p7);
if (p7s->contents != NULL)
PKCS7_free(p7s->contents);
p7s->contents = inner_p7;
*signed_data_len = i2d_PKCS7(p7, NULL);
if (!(*signed_data_len)) {
unsigned long err = ERR_peek_error();
retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_set_error_message(context, retval, "%s\n",
ERR_error_string(err, NULL));
pkiDebug("failed to der encode pkcs7\n");
goto cleanup2;
}
if ((p = *signed_data =
(unsigned char *) malloc((size_t)*signed_data_len)) == NULL)
goto cleanup2;
/* DER encode PKCS7 data */
retval = i2d_PKCS7(p7, &p);
if (!retval) {
unsigned long err = ERR_peek_error();
retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_set_error_message(context, retval, "%s\n",
ERR_error_string(err, NULL));
pkiDebug("failed to der encode pkcs7\n");
goto cleanup2;
}
retval = 0;
#ifdef DEBUG_ASN1
if (cms_msg_type == CMS_SIGN_CLIENT) {
print_buffer_bin(*signed_data, *signed_data_len,
"/tmp/client_pkcs7_signeddata");
} else {
if (cms_msg_type == CMS_SIGN_SERVER) {
print_buffer_bin(*signed_data, *signed_data_len,
"/tmp/kdc_pkcs7_signeddata");
} else {
print_buffer_bin(*signed_data, *signed_data_len,
"/tmp/draft9_pkcs7_signeddata");
}
}
#endif
cleanup2:
if (cms_msg_type != CMS_SIGN_DRAFT9)
if (ctx != NULL)
EVP_MD_CTX_free(ctx);
#ifndef WITHOUT_PKCS11
if (id_cryptoctx->pkcs11_method == 1 &&
id_cryptoctx->mech == CKM_RSA_PKCS) {
if (ctx2 != NULL)
EVP_MD_CTX_free(ctx2);
if (digest_buf != NULL)
free(digest_buf);
if (digestInfo_buf != NULL)
free(digestInfo_buf);
if (alg_buf != NULL)
free(alg_buf);
if (digest != NULL)
ASN1_OCTET_STRING_free(digest);
}
#endif
if (alg != NULL)
X509_ALGOR_free(alg);
cleanup:
if (p7 != NULL)
PKCS7_free(p7);
if (sig != NULL)
free(sig);
return retval;
}
krb5_error_code
cms_signeddata_verify(krb5_context context,
pkinit_plg_crypto_context plgctx,
pkinit_req_crypto_context reqctx,
pkinit_identity_crypto_context idctx,
int cms_msg_type,
int require_crl_checking,
unsigned char *signed_data,
unsigned int signed_data_len,
unsigned char **data,
unsigned int *data_len,
unsigned char **authz_data,
unsigned int *authz_data_len)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
PKCS7 *p7 = NULL;
BIO *out = NULL;
int flags = PKCS7_NOVERIFY, i = 0;
unsigned int vflags = 0, size = 0;
const unsigned char *p = signed_data;
STACK_OF(PKCS7_SIGNER_INFO) *si_sk = NULL;
PKCS7_SIGNER_INFO *si = NULL;
X509 *x = NULL;
X509_STORE *store = NULL;
X509_STORE_CTX *cert_ctx;
STACK_OF(X509) *intermediateCAs = NULL;
STACK_OF(X509_CRL) *revoked = NULL;
STACK_OF(X509) *verified_chain = NULL;
ASN1_OBJECT *oid = NULL;
krb5_external_principal_identifier **krb5_verified_chain = NULL;
krb5_data *authz = NULL;
char buf[DN_BUF_LEN];
#ifdef DEBUG_ASN1
print_buffer_bin(signed_data, signed_data_len,
"/tmp/client_received_pkcs7_signeddata");
#endif
oid = pkinit_pkcs7type2oid(plgctx, cms_msg_type);
if (oid == NULL)
goto cleanup;
/* decode received PKCS7 message */
if ((p7 = d2i_PKCS7(NULL, &p, (int)signed_data_len)) == NULL) {
unsigned long err = ERR_peek_error();
krb5_set_error_message(context, retval, "%s\n",
ERR_error_string(err, NULL));
pkiDebug("%s: failed to decode message: %s\n",
__FUNCTION__, ERR_error_string(err, NULL));
goto cleanup;
}
/* verify that the received message is PKCS7 SignedData message */
if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed) {
pkiDebug("Expected id-signedData PKCS7 msg (received type = %d)\n",
OBJ_obj2nid(p7->type));
krb5_set_error_message(context, retval, "wrong oid\n");
goto cleanup;
}
/* setup to verify X509 certificate used to sign PKCS7 message */
if (!(store = X509_STORE_new()))
goto cleanup;
/* check if we are inforcing CRL checking */
vflags = X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL;
if (require_crl_checking)
X509_STORE_set_verify_cb(store, openssl_callback);
else
X509_STORE_set_verify_cb(store, openssl_callback_ignore_crls);
X509_STORE_set_flags(store, vflags);
/* get the signer's information from the PKCS7 message */
if ((si_sk = PKCS7_get_signer_info(p7)) == NULL)
goto cleanup;
if ((si = sk_PKCS7_SIGNER_INFO_value(si_sk, 0)) == NULL)
goto cleanup;
if ((x = PKCS7_cert_from_signer_info(p7, si)) == NULL)
goto cleanup;
/* create available CRL information (get local CRLs and include CRLs
* received in the PKCS7 message
*/
if (idctx->revoked == NULL)
revoked = p7->d.sign->crl;
else if (p7->d.sign->crl == NULL)
revoked = idctx->revoked;
else {
size = sk_X509_CRL_num(idctx->revoked);
revoked = sk_X509_CRL_new_null();
for (i = 0; i < size; i++)
sk_X509_CRL_push(revoked, sk_X509_CRL_value(idctx->revoked, i));
size = sk_X509_CRL_num(p7->d.sign->crl);
for (i = 0; i < size; i++)
sk_X509_CRL_push(revoked, sk_X509_CRL_value(p7->d.sign->crl, i));
}
/* create available intermediate CAs chains (get local intermediateCAs and
* include the CA chain received in the PKCS7 message
*/
if (idctx->intermediateCAs == NULL)
intermediateCAs = p7->d.sign->cert;
else if (p7->d.sign->cert == NULL)
intermediateCAs = idctx->intermediateCAs;
else {
size = sk_X509_num(idctx->intermediateCAs);
intermediateCAs = sk_X509_new_null();
for (i = 0; i < size; i++) {
sk_X509_push(intermediateCAs,
sk_X509_value(idctx->intermediateCAs, i));
}
size = sk_X509_num(p7->d.sign->cert);
for (i = 0; i < size; i++) {
sk_X509_push(intermediateCAs, sk_X509_value(p7->d.sign->cert, i));
}
}
/* initialize x509 context with the received certificate and
* trusted and intermediate CA chains and CRLs
*/
if ((cert_ctx = X509_STORE_CTX_new()) == NULL)
goto cleanup;
if (!X509_STORE_CTX_init(cert_ctx, store, x, intermediateCAs))
goto cleanup;
X509_STORE_CTX_set0_crls(cert_ctx, revoked);
/* add trusted CAs certificates for cert verification */
if (idctx->trustedCAs != NULL)
X509_STORE_CTX_set0_trusted_stack(cert_ctx, idctx->trustedCAs);
else {
pkiDebug("unable to find any trusted CAs\n");
goto cleanup;
}
#ifdef DEBUG_CERTCHAIN
if (intermediateCAs != NULL) {
size = sk_X509_num(intermediateCAs);
pkiDebug("untrusted cert chain of size %d\n", size);
for (i = 0; i < size; i++) {
X509_NAME_oneline(X509_get_subject_name(
sk_X509_value(intermediateCAs, i)), buf, sizeof(buf));
pkiDebug("cert #%d: %s\n", i, buf);
}
}
if (idctx->trustedCAs != NULL) {
size = sk_X509_num(idctx->trustedCAs);
pkiDebug("trusted cert chain of size %d\n", size);
for (i = 0; i < size; i++) {
X509_NAME_oneline(X509_get_subject_name(
sk_X509_value(idctx->trustedCAs, i)), buf, sizeof(buf));
pkiDebug("cert #%d: %s\n", i, buf);
}
}
if (revoked != NULL) {
size = sk_X509_CRL_num(revoked);
pkiDebug("CRL chain of size %d\n", size);
for (i = 0; i < size; i++) {
X509_CRL *crl = sk_X509_CRL_value(revoked, i);
X509_NAME_oneline(X509_CRL_get_issuer(crl), buf, sizeof(buf));
pkiDebug("crls by CA #%d: %s\n", i , buf);
}
}
#endif
i = X509_verify_cert(cert_ctx);
if (i <= 0) {
int j = X509_STORE_CTX_get_error(cert_ctx);
reqctx->received_cert = X509_dup(
X509_STORE_CTX_get_current_cert(cert_ctx));
switch(j) {
case X509_V_ERR_CERT_REVOKED:
retval = KRB5KDC_ERR_REVOKED_CERTIFICATE;
break;
case X509_V_ERR_UNABLE_TO_GET_CRL:
retval = KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN;
break;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
retval = KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE;
break;
default:
retval = KRB5KDC_ERR_INVALID_CERTIFICATE;
}
X509_NAME_oneline(X509_get_subject_name(
reqctx->received_cert), buf, sizeof(buf));
pkiDebug("problem with cert DN = %s (error=%d) %s\n", buf, j,
X509_verify_cert_error_string(j));
krb5_set_error_message(context, retval, "%s\n",
X509_verify_cert_error_string(j));
#ifdef DEBUG_CERTCHAIN
size = sk_X509_num(p7->d.sign->cert);
pkiDebug("received cert chain of size %d\n", size);
for (j = 0; j < size; j++) {
X509 *tmp_cert = sk_X509_value(p7->d.sign->cert, j);
X509_NAME_oneline(X509_get_subject_name(tmp_cert), buf, sizeof(buf));
pkiDebug("cert #%d: %s\n", j, buf);
}
#endif
} else {
/* retrieve verified certificate chain */
if (cms_msg_type == CMS_SIGN_CLIENT || cms_msg_type == CMS_SIGN_DRAFT9)
verified_chain = X509_STORE_CTX_get1_chain(cert_ctx);
}
X509_STORE_CTX_free(cert_ctx);
if (i <= 0)
goto cleanup;
out = BIO_new(BIO_s_mem());
if (cms_msg_type == CMS_SIGN_DRAFT9)
flags |= PKCS7_NOATTR;
if (PKCS7_verify(p7, NULL, store, NULL, out, flags)) {
int valid_oid = 0;
if (!OBJ_cmp(p7->d.sign->contents->type, oid))
valid_oid = 1;
else if (cms_msg_type == CMS_SIGN_DRAFT9) {
/*
* Various implementations of the pa-type 15 request use
* different OIDS. We check that the returned object
* has any of the acceptable OIDs
*/
ASN1_OBJECT *client_oid = NULL, *server_oid = NULL, *rsa_oid = NULL;
client_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_CLIENT);
server_oid = pkinit_pkcs7type2oid(plgctx, CMS_SIGN_SERVER);
rsa_oid = pkinit_pkcs7type2oid(plgctx, CMS_ENVEL_SERVER);
if (!OBJ_cmp(p7->d.sign->contents->type, client_oid) ||
!OBJ_cmp(p7->d.sign->contents->type, server_oid) ||
!OBJ_cmp(p7->d.sign->contents->type, rsa_oid))
valid_oid = 1;
}
if (valid_oid)
pkiDebug("PKCS7 Verification successful\n");
else {
const ASN1_OBJECT *etype = p7->d.sign->contents->type;
pkiDebug("wrong oid in eContentType\n");
print_buffer((unsigned char *)OBJ_get0_data(etype),
OBJ_length(etype));
retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_set_error_message(context, retval, "wrong oid\n");
goto cleanup;
}
}
else {
unsigned long err = ERR_peek_error();
switch(ERR_GET_REASON(err)) {
case PKCS7_R_DIGEST_FAILURE:
retval = KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED;
break;
case PKCS7_R_SIGNATURE_FAILURE:
default:
retval = KRB5KDC_ERR_INVALID_SIG;
}
pkiDebug("PKCS7 Verification failure\n");
krb5_set_error_message(context, retval, "%s\n",
ERR_error_string(err, NULL));
goto cleanup;
}
/* transfer the data from PKCS7 message into return buffer */
for (size = 0;;) {
if ((*data = realloc(*data, size + 1024 * 10)) == NULL)
goto cleanup;
i = BIO_read(out, &((*data)[size]), 1024 * 10);
if (i <= 0)
break;
else
size += i;
}
*data_len = size;
reqctx->received_cert = X509_dup(x);
/* generate authorization data */
if (cms_msg_type == CMS_SIGN_CLIENT || cms_msg_type == CMS_SIGN_DRAFT9) {
if (authz_data == NULL || authz_data_len == NULL)
goto out;
*authz_data = NULL;
retval = create_identifiers_from_stack(verified_chain,
&krb5_verified_chain);
if (retval) {
pkiDebug("create_identifiers_from_stack failed\n");
goto cleanup;
}
retval = k5int_encode_krb5_td_trusted_certifiers((const krb5_external_principal_identifier **)krb5_verified_chain, &authz);
if (retval) {
pkiDebug("encode_krb5_td_trusted_certifiers failed\n");
goto cleanup;
}
#ifdef DEBUG_ASN1
print_buffer_bin((unsigned char *)authz->data, authz->length,
"/tmp/kdc_ad_initial_verified_cas");
#endif
*authz_data = (unsigned char *)malloc(authz->length);
if (*authz_data == NULL) {
retval = ENOMEM;
goto cleanup;
}
(void) memcpy(*authz_data, authz->data, authz->length);
*authz_data_len = authz->length;
}
out:
retval = 0;
cleanup:
if (out != NULL)
BIO_free(out);
if (store != NULL)
X509_STORE_free(store);
if (p7 != NULL) {
if (idctx->intermediateCAs != NULL && p7->d.sign->cert)
sk_X509_free(intermediateCAs);
if (idctx->revoked != NULL && p7->d.sign->crl)
sk_X509_CRL_free(revoked);
PKCS7_free(p7);
}
if (verified_chain != NULL)
sk_X509_pop_free(verified_chain, X509_free);
if (krb5_verified_chain != NULL)
free_krb5_external_principal_identifier(&krb5_verified_chain);
if (authz != NULL)
krb5_free_data(context, authz);
return retval;
}
krb5_error_code
cms_envelopeddata_create(krb5_context context,
pkinit_plg_crypto_context plgctx,
pkinit_req_crypto_context reqctx,
pkinit_identity_crypto_context idctx,
krb5_preauthtype pa_type,
int include_certchain,
unsigned char *key_pack,
unsigned int key_pack_len,
unsigned char **out,
unsigned int *out_len)
{
/* Solaris Kerberos */
krb5_error_code retval = KRB5KRB_ERR_GENERIC;
PKCS7 *p7 = NULL;
BIO *in = NULL;
unsigned char *p = NULL, *signed_data = NULL, *enc_data = NULL;
int signed_data_len = 0, enc_data_len = 0, flags = PKCS7_BINARY;
STACK_OF(X509) *encerts = NULL;
const EVP_CIPHER *cipher = NULL;
int cms_msg_type;
/* create the PKCS7 SignedData portion of the PKCS7 EnvelopedData */
switch ((int)pa_type) {
case KRB5_PADATA_PK_AS_REQ_OLD:
case KRB5_PADATA_PK_AS_REP_OLD:
cms_msg_type = CMS_SIGN_DRAFT9;
break;
case KRB5_PADATA_PK_AS_REQ:
cms_msg_type = CMS_ENVEL_SERVER;
break;
default:
/* Solaris Kerberos */
retval = EINVAL;
goto cleanup;
}
retval = cms_signeddata_create(context, plgctx, reqctx, idctx,
cms_msg_type, include_certchain, key_pack, key_pack_len,
&signed_data, (unsigned int *)&signed_data_len);
if (retval) {
pkiDebug("failed to create pkcs7 signed data\n");
goto cleanup;
}
/* check we have client's certificate */
if (reqctx->received_cert == NULL) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
encerts = sk_X509_new_null();
sk_X509_push(encerts, reqctx->received_cert);
cipher = EVP_des_ede3_cbc();
in = BIO_new(BIO_s_mem());
switch (pa_type) {
case KRB5_PADATA_PK_AS_REQ:
prepare_enc_data(signed_data, signed_data_len, &enc_data,
&enc_data_len);
retval = BIO_write(in, enc_data, enc_data_len);
if (retval != enc_data_len) {
pkiDebug("BIO_write only wrote %d\n", retval);
goto cleanup;
}
break;
case KRB5_PADATA_PK_AS_REP_OLD:
case KRB5_PADATA_PK_AS_REQ_OLD:
retval = BIO_write(in, signed_data, signed_data_len);
if (retval != signed_data_len) {
pkiDebug("BIO_write only wrote %d\n", retval);
/* Solaris Kerberos */
retval = KRB5KRB_ERR_GENERIC;
goto cleanup;
}
break;
default:
retval = -1;
goto cleanup;
}
p7 = PKCS7_encrypt(encerts, in, cipher, flags);
if (p7 == NULL) {
pkiDebug("failed to encrypt PKCS7 object\n");
retval = -1;
goto cleanup;
}
switch (pa_type) {
case KRB5_PADATA_PK_AS_REQ:
p7->d.enveloped->enc_data->content_type =
OBJ_nid2obj(NID_pkcs7_signed);
break;
case KRB5_PADATA_PK_AS_REP_OLD:
case KRB5_PADATA_PK_AS_REQ_OLD:
p7->d.enveloped->enc_data->content_type =
OBJ_nid2obj(NID_pkcs7_data);
break;
}
*out_len = i2d_PKCS7(p7, NULL);
if (!*out_len || (p = *out = (unsigned char *)malloc(*out_len)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
retval = i2d_PKCS7(p7, &p);
if (!retval) {
pkiDebug("unable to write pkcs7 object\n");
goto cleanup;
}
retval = 0;
#ifdef DEBUG_ASN1
print_buffer_bin(*out, *out_len, "/tmp/kdc_enveloped_data");
#endif
cleanup:
if (p7 != NULL)
PKCS7_free(p7);
if (in != NULL)
BIO_free(in);
if (signed_data != NULL)
free(signed_data);
if (enc_data != NULL)
free(enc_data);
if (encerts != NULL)
sk_X509_free(encerts);
return retval;
}
krb5_error_code
cms_envelopeddata_verify(krb5_context context,
pkinit_plg_crypto_context plg_cryptoctx,
pkinit_req_crypto_context req_cryptoctx,
pkinit_identity_crypto_context id_cryptoctx,
krb5_preauthtype pa_type,
int require_crl_checking,
unsigned char *enveloped_data,
unsigned int enveloped_data_len,
unsigned char **data,
unsigned int *data_len)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
PKCS7 *p7 = NULL;
BIO *out = NULL;
int i = 0;
unsigned int size = 0;
const unsigned char *p = enveloped_data;
unsigned int tmp_buf_len = 0, tmp_buf2_len = 0, vfy_buf_len = 0;
unsigned char *tmp_buf = NULL, *tmp_buf2 = NULL, *vfy_buf = NULL;
int msg_type = 0;
#ifdef DEBUG_ASN1
print_buffer_bin(enveloped_data, enveloped_data_len,
"/tmp/client_envelopeddata");
#endif
/* decode received PKCS7 message */
if ((p7 = d2i_PKCS7(NULL, &p, (int)enveloped_data_len)) == NULL) {
unsigned long err = ERR_peek_error();
pkiDebug("failed to decode pkcs7\n");
krb5_set_error_message(context, retval, "%s\n",
ERR_error_string(err, NULL));
goto cleanup;
}
/* verify that the received message is PKCS7 EnvelopedData message */
if (OBJ_obj2nid(p7->type) != NID_pkcs7_enveloped) {
pkiDebug("Expected id-enveloped PKCS7 msg (received type = %d)\n",
OBJ_obj2nid(p7->type));
krb5_set_error_message(context, retval, "wrong oid\n");
goto cleanup;
}
/* decrypt received PKCS7 message */
out = BIO_new(BIO_s_mem());
if (pkcs7_decrypt(context, id_cryptoctx, p7, out)) {
pkiDebug("PKCS7 decryption successful\n");
} else {
unsigned long err = ERR_peek_error();
if (err != 0)
krb5_set_error_message(context, retval, "%s\n",
ERR_error_string(err, NULL));
pkiDebug("PKCS7 decryption failed\n");
goto cleanup;
}
/* transfer the decoded PKCS7 SignedData message into a separate buffer */
for (;;) {
if ((tmp_buf = realloc(tmp_buf, size + 1024 * 10)) == NULL)
goto cleanup;
i = BIO_read(out, &(tmp_buf[size]), 1024 * 10);
if (i <= 0)
break;
else
size += i;
}
tmp_buf_len = size;
#ifdef DEBUG_ASN1
print_buffer_bin(tmp_buf, tmp_buf_len, "/tmp/client_enc_keypack");
#endif
/* verify PKCS7 SignedData message */
switch (pa_type) {
case KRB5_PADATA_PK_AS_REP:
msg_type = CMS_ENVEL_SERVER;
break;
case KRB5_PADATA_PK_AS_REP_OLD:
msg_type = CMS_SIGN_DRAFT9;
break;
default:
pkiDebug("%s: unrecognized pa_type = %d\n", __FUNCTION__, pa_type);
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
/*
* If this is the RFC style, wrap the signed data to make
* decoding easier in the verify routine.
* For draft9-compatible, we don't do anything because it
* is already wrapped.
*/
#ifdef LONGHORN_BETA_COMPAT
/*
* The Longhorn server returns the expected RFC-style data, but
* it is missing the sequence tag and length, so it requires
* special processing when wrapping.
* This will hopefully be fixed before the final release and
* this can all be removed.
*/
if (msg_type == CMS_ENVEL_SERVER || longhorn == 1) {
retval = wrap_signeddata(tmp_buf, tmp_buf_len,
&tmp_buf2, &tmp_buf2_len, longhorn);
if (retval) {
pkiDebug("failed to encode signeddata\n");
goto cleanup;
}
vfy_buf = tmp_buf2;
vfy_buf_len = tmp_buf2_len;
} else {
vfy_buf = tmp_buf;
vfy_buf_len = tmp_buf_len;
}
#else
if (msg_type == CMS_ENVEL_SERVER) {
retval = wrap_signeddata(tmp_buf, tmp_buf_len,
&tmp_buf2, &tmp_buf2_len);
if (retval) {
pkiDebug("failed to encode signeddata\n");
goto cleanup;
}
vfy_buf = tmp_buf2;
vfy_buf_len = tmp_buf2_len;
} else {
vfy_buf = tmp_buf;
vfy_buf_len = tmp_buf_len;
}
#endif
#ifdef DEBUG_ASN1
print_buffer_bin(vfy_buf, vfy_buf_len, "/tmp/client_enc_keypack2");
#endif
retval = cms_signeddata_verify(context, plg_cryptoctx, req_cryptoctx,
id_cryptoctx, msg_type,
require_crl_checking,
vfy_buf, vfy_buf_len,
data, data_len, NULL, NULL);
if (!retval)
pkiDebug("PKCS7 Verification Success\n");
else {
pkiDebug("PKCS7 Verification Failure\n");
goto cleanup;
}
retval = 0;
cleanup:
if (p7 != NULL)
PKCS7_free(p7);
if (out != NULL)
BIO_free(out);
if (tmp_buf != NULL)
free(tmp_buf);
if (tmp_buf2 != NULL)
free(tmp_buf2);
return retval;
}
/* ARGSUSED */
static krb5_error_code
crypto_retrieve_X509_sans(krb5_context context,
pkinit_plg_crypto_context plgctx,
pkinit_req_crypto_context reqctx,
X509 *cert,
krb5_principal **princs_ret,
krb5_principal **upn_ret,
unsigned char ***dns_ret)
{
krb5_error_code retval = EINVAL;
char buf[DN_BUF_LEN];
int p = 0, u = 0, d = 0;
krb5_principal *princs = NULL;
krb5_principal *upns = NULL;
unsigned char **dnss = NULL;
int i, num_found = 0;
if (princs_ret == NULL && upn_ret == NULL && dns_ret == NULL) {
pkiDebug("%s: nowhere to return any values!\n", __FUNCTION__);
return retval;
}
if (cert == NULL) {
pkiDebug("%s: no certificate!\n", __FUNCTION__);
return retval;
}
X509_NAME_oneline(X509_get_subject_name(cert),
buf, sizeof(buf));
pkiDebug("%s: looking for SANs in cert = %s\n", __FUNCTION__, buf);
if ((i = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1)) >= 0) {
X509_EXTENSION *ext = NULL;
GENERAL_NAMES *ialt = NULL;
GENERAL_NAME *gen = NULL;
int ret = 0;
unsigned int num_sans = 0;
if (!(ext = X509_get_ext(cert, i)) || !(ialt = X509V3_EXT_d2i(ext))) {
pkiDebug("%s: found no subject alt name extensions\n",
__FUNCTION__);
goto cleanup;
}
num_sans = sk_GENERAL_NAME_num(ialt);
pkiDebug("%s: found %d subject alt name extension(s)\n",
__FUNCTION__, num_sans);
/* OK, we're likely returning something. Allocate return values */
if (princs_ret != NULL) {
princs = calloc(num_sans + 1, sizeof(krb5_principal));
if (princs == NULL) {
retval = ENOMEM;
goto cleanup;
}
}
if (upn_ret != NULL) {
upns = calloc(num_sans + 1, sizeof(krb5_principal));
if (upns == NULL) {
retval = ENOMEM;
goto cleanup;
}
}
if (dns_ret != NULL) {
dnss = calloc(num_sans + 1, sizeof(*dnss));
if (dnss == NULL) {
retval = ENOMEM;
goto cleanup;
}
}
for (i = 0; i < num_sans; i++) {
krb5_data name = { 0, 0, NULL };
gen = sk_GENERAL_NAME_value(ialt, i);
switch (gen->type) {
case GEN_OTHERNAME:
name.length = gen->d.otherName->value->value.sequence->length;
name.data = (char *)gen->d.otherName->value->value.sequence->data;
if (princs != NULL
&& OBJ_cmp(plgctx->id_pkinit_san,
gen->d.otherName->type_id) == 0) {
#ifdef DEBUG_ASN1
print_buffer_bin((unsigned char *)name.data, name.length,
"/tmp/pkinit_san");
#endif
ret = k5int_decode_krb5_principal_name(&name, &princs[p]);
if (ret) {
pkiDebug("%s: failed decoding pkinit san value\n",
__FUNCTION__);
} else {
p++;
num_found++;
}
} else if (upns != NULL
&& OBJ_cmp(plgctx->id_ms_san_upn,
gen->d.otherName->type_id) == 0) {
ret = krb5_parse_name(context, name.data, &upns[u]);
if (ret) {
pkiDebug("%s: failed parsing ms-upn san value\n",
__FUNCTION__);
} else {
u++;
num_found++;
}
} else {
pkiDebug("%s: unrecognized othername oid in SAN\n",
__FUNCTION__);
continue;
}
break;
case GEN_DNS:
if (dnss != NULL) {
pkiDebug("%s: found dns name = %s\n",
__FUNCTION__, gen->d.dNSName->data);
dnss[d] = (unsigned char *)
strdup((char *)gen->d.dNSName->data);
if (dnss[d] == NULL) {
pkiDebug("%s: failed to duplicate dns name\n",
__FUNCTION__);
} else {
d++;
num_found++;
}
}
break;
default:
pkiDebug("%s: SAN type = %d expecting %d\n",
__FUNCTION__, gen->type, GEN_OTHERNAME);
}
}
sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free);
}
retval = 0;
if (princs)
*princs_ret = princs;
if (upns)
*upn_ret = upns;
if (dnss)
*dns_ret = dnss;
cleanup:
if (retval) {
if (princs != NULL) {
for (i = 0; princs[i] != NULL; i++)
krb5_free_principal(context, princs[i]);
free(princs);
}
if (upns != NULL) {
for (i = 0; upns[i] != NULL; i++)
krb5_free_principal(context, upns[i]);
free(upns);
}
if (dnss != NULL) {
for (i = 0; dnss[i] != NULL; i++)
free(dnss[i]);
free(dnss);
}
}
return retval;
}
/* ARGSUSED */
krb5_error_code
crypto_retrieve_cert_sans(krb5_context context,
pkinit_plg_crypto_context plgctx,
pkinit_req_crypto_context reqctx,
pkinit_identity_crypto_context idctx,
krb5_principal **princs_ret,
krb5_principal **upn_ret,
unsigned char ***dns_ret)
{
krb5_error_code retval = EINVAL;
if (reqctx->received_cert == NULL) {
pkiDebug("%s: No certificate!\n", __FUNCTION__);
return retval;
}
return crypto_retrieve_X509_sans(context, plgctx, reqctx,
reqctx->received_cert, princs_ret,
upn_ret, dns_ret);
}
/* ARGSUSED */
krb5_error_code
crypto_check_cert_eku(krb5_context context,
pkinit_plg_crypto_context plgctx,
pkinit_req_crypto_context reqctx,
pkinit_identity_crypto_context idctx,
int checking_kdc_cert,
int allow_secondary_usage,
int *valid_eku)
{
char buf[DN_BUF_LEN];
int found_eku = 0;
krb5_error_code retval = EINVAL;
int i;
/* Solaris Kerberos */
if (valid_eku == NULL)
return retval;
*valid_eku = 0;
if (reqctx->received_cert == NULL)
goto cleanup;
X509_NAME_oneline(X509_get_subject_name(reqctx->received_cert),
buf, sizeof(buf));
pkiDebug("%s: looking for EKUs in cert = %s\n", __FUNCTION__, buf);
if ((i = X509_get_ext_by_NID(reqctx->received_cert,
NID_ext_key_usage, -1)) >= 0) {
EXTENDED_KEY_USAGE *extusage;
extusage = X509_get_ext_d2i(reqctx->received_cert, NID_ext_key_usage,
NULL, NULL);
if (extusage) {
pkiDebug("%s: found eku info in the cert\n", __FUNCTION__);
for (i = 0; found_eku == 0 && i < sk_ASN1_OBJECT_num(extusage); i++) {
ASN1_OBJECT *tmp_oid;
tmp_oid = sk_ASN1_OBJECT_value(extusage, i);
pkiDebug("%s: checking eku %d of %d, allow_secondary = %d\n",
__FUNCTION__, i+1, sk_ASN1_OBJECT_num(extusage),
allow_secondary_usage);
if (checking_kdc_cert) {
if ((OBJ_cmp(tmp_oid, plgctx->id_pkinit_KPKdc) == 0)
|| (allow_secondary_usage
&& OBJ_cmp(tmp_oid, plgctx->id_kp_serverAuth) == 0))
found_eku = 1;
} else {
if ((OBJ_cmp(tmp_oid, plgctx->id_pkinit_KPClientAuth) == 0)
|| (allow_secondary_usage
&& OBJ_cmp(tmp_oid, plgctx->id_ms_kp_sc_logon) == 0))
found_eku = 1;
}
}
}
EXTENDED_KEY_USAGE_free(extusage);
if (found_eku) {
ASN1_BIT_STRING *usage = NULL;
pkiDebug("%s: found acceptable EKU, checking for digitalSignature\n", __FUNCTION__);
/* check that digitalSignature KeyUsage is present */
if ((usage = X509_get_ext_d2i(reqctx->received_cert,
NID_key_usage, NULL, NULL))) {
if (!ku_reject(reqctx->received_cert,
X509v3_KU_DIGITAL_SIGNATURE)) {
pkiDebug("%s: found digitalSignature KU\n",
__FUNCTION__);
*valid_eku = 1;
} else
pkiDebug("%s: didn't find digitalSignature KU\n",
__FUNCTION__);
}
ASN1_BIT_STRING_free(usage);
}
}
retval = 0;
cleanup:
pkiDebug("%s: returning retval %d, valid_eku %d\n",
__FUNCTION__, retval, *valid_eku);
return retval;
}
krb5_error_code
pkinit_octetstring2key(krb5_context context,
krb5_enctype etype,
unsigned char *key,
unsigned int dh_key_len,
krb5_keyblock * key_block)
{
krb5_error_code retval;
unsigned char *buf = NULL;
unsigned char md[SHA_DIGEST_LENGTH];
unsigned char counter;
size_t keybytes, keylength, offset;
krb5_data random_data;
if ((buf = (unsigned char *) malloc(dh_key_len)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
(void) memset(buf, 0, dh_key_len);
counter = 0;
offset = 0;
do {
SHA_CTX c;
SHA1_Init(&c);
SHA1_Update(&c, &counter, 1);
SHA1_Update(&c, key, dh_key_len);
SHA1_Final(md, &c);
if (dh_key_len - offset < sizeof(md))
(void) memcpy(buf + offset, md, dh_key_len - offset);
else
(void) memcpy(buf + offset, md, sizeof(md));
offset += sizeof(md);
counter++;
} while (offset < dh_key_len);
/* Solaris Kerberos */
key_block->magic = KV5M_KEYBLOCK;
key_block->enctype = etype;
retval = krb5_c_keylengths(context, etype, &keybytes, &keylength);
if (retval)
goto cleanup;
key_block->length = keylength;
key_block->contents = calloc(keylength, sizeof(unsigned char *));
if (key_block->contents == NULL) {
retval = ENOMEM;
goto cleanup;
}
random_data.length = keybytes;
random_data.data = (char *)buf;
retval = krb5_c_random_to_key(context, etype, &random_data, key_block);
cleanup:
if (buf != NULL)
free(buf);
if (retval && key_block->contents != NULL && key_block->length != 0) {
(void) memset(key_block->contents, 0, key_block->length);
key_block->length = 0;
}
return retval;
}
/* ARGSUSED */
krb5_error_code
client_create_dh(krb5_context context,
pkinit_plg_crypto_context plg_cryptoctx,
pkinit_req_crypto_context cryptoctx,
pkinit_identity_crypto_context id_cryptoctx,
int dh_size,
unsigned char **dh_params,
unsigned int *dh_params_len,
unsigned char **dh_pubkey,
unsigned int *dh_pubkey_len)
{
krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;
unsigned char *buf = NULL;
int dh_err = 0;
ASN1_INTEGER *asn_pub_key = NULL;
BIGNUM *p, *g, *q;
const BIGNUM *pub_key;
if (cryptoctx->dh == NULL) {
if ((cryptoctx->dh = DH_new()) == NULL)
goto cleanup;
if ((g = BN_new()) == NULL || (q = BN_new()) == NULL)
goto cleanup;
switch(dh_size) {
case 1024:
pkiDebug("client uses 1024 DH keys\n");
cryptoctx->dh = make_dhprime(pkinit_1024_dhprime,
sizeof(pkinit_1024_dhprime));
break;
case 2048:
pkiDebug("client uses 2048 DH keys\n");
cryptoctx->dh = make_dhprime(pkinit_2048_dhprime,
sizeof(pkinit_2048_dhprime));
break;
case 4096:
pkiDebug("client uses 4096 DH keys\n");
cryptoctx->dh = make_dhprime(pkinit_4096_dhprime,
sizeof(pkinit_4096_dhprime));
break;
}
if (cryptoctx->dh == NULL)
goto cleanup;
}
DH_generate_key(cryptoctx->dh);
DH_get0_key(cryptoctx->dh, &pub_key, NULL);
/* Solaris Kerberos */
#ifdef DEBUG
DH_check(cryptoctx->dh, &dh_err);
if (dh_err != 0) {
pkiDebug("Warning: dh_check failed with %d\n", dh_err);
if (dh_err & DH_CHECK_P_NOT_PRIME)
pkiDebug("p value is not prime\n");
if (dh_err & DH_CHECK_P_NOT_SAFE_PRIME)
pkiDebug("p value is not a safe prime\n");
if (dh_err & DH_UNABLE_TO_CHECK_GENERATOR)
pkiDebug("unable to check the generator value\n");
if (dh_err & DH_NOT_SUITABLE_GENERATOR)
pkiDebug("the g value is not a generator\n");
}
#endif
#ifdef DEBUG_DH
print_dh(cryptoctx->dh, "client's DH params\n");
print_pubkey(pub_key, "client's pub_key=");
#endif
DH_check_pub_key(cryptoctx->dh, pub_key, &dh_err);
if (dh_err != 0) {
pkiDebug("dh_check_pub_key failed with %d\n", dh_err);
goto cleanup;
}
/* pack DHparams */
/* aglo: usually we could just call i2d_DHparams to encode DH params
* however, PKINIT requires RFC3279 encoding and openssl does pkcs#3.
*/
DH_get0_pqg(cryptoctx->dh, (const BIGNUM **)&p, (const BIGNUM **)&q,
(const BIGNUM **)&g);
retval = pkinit_encode_dh_params(p, g, q, dh_params, dh_params_len);
if (retval)
goto cleanup;
/* pack DH public key */
/* Diffie-Hellman public key must be ASN1 encoded as an INTEGER; this
* encoding shall be used as the contents (the value) of the
* subjectPublicKey component (a BIT STRING) of the SubjectPublicKeyInfo
* data element
*/
if ((asn_pub_key = BN_to_ASN1_INTEGER(pub_key, NULL)) == NULL)
goto cleanup;
*dh_pubkey_len = i2d_ASN1_INTEGER(asn_pub_key, NULL);
if ((buf = *dh_pubkey = (unsigned char *)
malloc((size_t) *dh_pubkey_len)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
i2d_ASN1_INTEGER(asn_pub_key, &buf);
if (asn_pub_key != NULL)
ASN1_INTEGER_free(asn_pub_key);
retval = 0;
return retval;
cleanup:
if (cryptoctx->dh != NULL)
DH_free(cryptoctx->dh);
cryptoctx->dh = NULL;
if (*dh_params != NULL)
free(*dh_params);
*dh_params = NULL;
if (*dh_pubkey != NULL)
free(*dh_pubkey);
*dh_pubkey = NULL;
if (asn_pub_key != NULL)
ASN1_INTEGER_free(asn_pub_key);
return retval;
}
/* ARGSUSED */
krb5_error_code
client_process_dh(krb5_context context,
pkinit_plg_crypto_context plg_cryptoctx,
pkinit_req_crypto_context cryptoctx,
pkinit_identity_crypto_context id_cryptoctx,
unsigned char *subjectPublicKey_data,
unsigned int subjectPublicKey_length,
unsigned char **client_key,
unsigned int *client_key_len)
{
/* Solaris Kerberos */
krb5_error_code retval = KRB5_PREAUTH_FAILED;
BIGNUM *server_pub_key = NULL;
ASN1_INTEGER *pub_key = NULL;
const unsigned char *p = NULL;
unsigned char *data = NULL;
long data_len;
/* decode subjectPublicKey (retrieve INTEGER from OCTET_STRING) */
if (der_decode_data(subjectPublicKey_data, (long)subjectPublicKey_length,
&data, &data_len) != 0) {
pkiDebug("failed to decode subjectPublicKey\n");
/* Solaris Kerberos */
retval = KRB5_PREAUTH_FAILED;
goto cleanup;
}
*client_key_len = DH_size(cryptoctx->dh);
if ((*client_key = (unsigned char *)
malloc((size_t) *client_key_len)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
p = data;
if ((pub_key = d2i_ASN1_INTEGER(NULL, &p, data_len)) == NULL)
goto cleanup;
if ((server_pub_key = ASN1_INTEGER_to_BN(pub_key, NULL)) == NULL)
goto cleanup;
DH_compute_key(*client_key, server_pub_key, cryptoctx->dh);
#ifdef DEBUG_DH
print_pubkey(server_pub_key, "server's pub_key=");
pkiDebug("client secret key (%d)= ", *client_key_len);
print_buffer(*client_key, *client_key_len);
#endif
retval = 0;
if (server_pub_key != NULL)
BN_free(server_pub_key);
if (pub_key != NULL)
ASN1_INTEGER_free(pub_key);
if (data != NULL)
free (data);
return retval;
cleanup:
if (*client_key != NULL)
free(*client_key);
*client_key = NULL;
if (pub_key != NULL)
ASN1_INTEGER_free(pub_key);
if (data != NULL)
free (data);
return retval;
}
/* ARGSUSED */
krb5_error_code
server_check_dh(krb5_context context,
pkinit_plg_crypto_context cryptoctx,
pkinit_req_crypto_context req_cryptoctx,
pkinit_identity_crypto_context id_cryptoctx,
krb5_octet_data *dh_params,
int minbits)
{
DH *dh = NULL;
unsigned char *tmp = NULL;
int dh_prime_bits;
krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
const BIGNUM *p, *g, *q, *p2;
tmp = dh_params->data;
dh = DH_new();
dh = pkinit_decode_dh_params(&dh, &tmp, dh_params->length);
if (dh == NULL) {
pkiDebug("failed to decode dhparams\n");
goto cleanup;
}
DH_get0_pqg(dh, &p, &q, &g);
/* KDC SHOULD check to see if the key parameters satisfy its policy */
dh_prime_bits = BN_num_bits(p);
if (minbits && dh_prime_bits < minbits) {
pkiDebug("client sent dh params with %d bits, we require %d\n",
dh_prime_bits, minbits);
goto cleanup;
}
/* check dhparams is group 2 */
DH_get0_pqg(cryptoctx->dh_1024, &p2, NULL, NULL);
if (pkinit_check_dh_params(p2, p, g, q) == 0) {
retval = 0;
goto cleanup;
}
/* check dhparams is group 14 */
DH_get0_pqg(cryptoctx->dh_2048, &p2, NULL, NULL);
if (pkinit_check_dh_params(p2, p, g, q) == 0) {
retval = 0;
goto cleanup;
}
/* check dhparams is group 16 */
DH_get0_pqg(cryptoctx->dh_4096, &p2, NULL, NULL);
if (pkinit_check_dh_params(p2, p, g, q) == 0) {
retval = 0;
goto cleanup;
}
cleanup:
if (retval == 0)
req_cryptoctx->dh = dh;
else
DH_free(dh);
return retval;
}
/* kdc's dh function */
/* ARGSUSED */
krb5_error_code
server_process_dh(krb5_context context,
pkinit_plg_crypto_context plg_cryptoctx,
pkinit_req_crypto_context cryptoctx,
pkinit_identity_crypto_context id_cryptoctx,
unsigned char *data,
unsigned int data_len,
unsigned char **dh_pubkey,
unsigned int *dh_pubkey_len,
unsigned char **server_key,
unsigned int *server_key_len)
{
/* Solaris Kerberos */
krb5_error_code retval = KRB5KRB_ERR_GENERIC;
DH *dh = NULL, *dh_server = NULL;
const BIGNUM *p, *g, *q, *s_pub_key;
BIGNUM *pub_key;
unsigned char *s = NULL;
ASN1_INTEGER *asn_pub_key = NULL;
/* get client's received DH parameters that we saved in server_check_dh */
dh = cryptoctx->dh;
dh_server = DH_new();
if (dh_server == NULL)
goto cleanup;
DH_get0_pqg(dh, &p, &g, &q);
DH_set0_pqg(dh_server, BN