| /* |
| * 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
|