ksslrec.c revision 2ec7cc7fc084163eaed884efee9bbd322cc8951b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#define _SUN_TPI_VERSION 2
#include <sys/sysmacros.h>
#include <sys/isa_defs.h>
#include "ksslimpl.h"
#include "ksslapi.h"
#include "ksslproto.h"
static ssl3CipherSuiteDef cipher_suite_defs[] = {
/* 2 X 16 byte keys + 2 x 20 byte MAC secrets, no IVs */
/* 2 X 16 byte keys + 2 x 16 byte MAC secrets, no IVs */
/* 2 X 8 byte keys + 2 x 20 byte MAC secrets, 2 x 8 byte IVs */
/* 2 X 24 byte keys + 2 x 20 byte MAC secrets, 2 x 8 byte IVs */
/* 2 X 16 byte keys + 2 x 20 byte MAC secrets, 2 x 16 byte IVs */
/* 2 X 32 byte keys + 2 x 20 byte MAC secrets, 2 x 16 byte IVs */
};
static int cipher_suite_defs_nentries =
sizeof (cipher_suite_defs) / sizeof (cipher_suite_defs[0]);
/* macsz padsz HashInit HashUpdate HashFinal */
};
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36
};
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c
};
int kssl_cache_count;
kssl_callback_t, void *);
static int kssl_send_server_hello(ssl_t *);
static int kssl_send_certificate_and_server_hello_done(ssl_t *);
static int kssl_send_change_cipher_specs(ssl_t *);
static int kssl_send_finished(ssl_t *, int);
static void kssl_get_hello_random(uchar_t *);
kssl_entry_t *);
static int kssl_generate_tls_keyblock(ssl_t *);
static void kssl_generate_keyblock(ssl_t *);
int, uchar_t *, int);
static void kssl_cke_done(void *, int);
#define HMAC_INIT(m, k, c) \
#define HMAC_UPDATE(c, d, l) \
#define HMAC_FINAL(c, d, l) \
/*
* This hack can go away once we have SSL3 MAC support by KCF
* software providers (See 4873559).
*/
extern int kcf_md5_threshold;
int
int direction,
int len,
{
int rv = 0;
if (spec->mac_hashsz == 0) {
return (1);
}
p = temp;
*p++ = (seq_num) & 0xff;
*p++ = versionp[0];
*p++ = versionp[1];
}
*p++ = (len) & 0xff;
len >= kcf_md5_threshold)) {
/* init the array of iovecs for use in the uio struct */
/* init the uio struct for use in the crypto_data_t struct */
/*
* The calling context can tolerate a blocking call here.
* For outgoing traffic, we are in user context
* when called from strsock_kssl_output(). For incoming
* traffic past the SSL handshake, we are in user
* context when called from strsock_kssl_input(). During the
* SSL handshake, we are called for client_finished message
* handling from a squeue worker thread that gets scheduled
* by an squeue_fill() call. This thread is not in interrupt
* context and so can block.
*/
if (CRYPTO_ERR(rv)) {
if (!hash_use_ok) {
int, rv);
}
}
} else
if (hash_use_ok) {
sizeof (KSSL_HASHCTX));
sizeof (KSSL_HASHCTX));
}
return (rv);
}
/*
* Handles handshake messages.
* Messages to be replied are returned in handshake_sendbuf.
*/
int
{
}
sender_client) != 0) {
return (0);
}
}
}
case client_hello:
return (1);
}
return (0);
}
return (1);
case client_key_exchange:
return (1);
}
return (1);
case finished:
return (1);
}
return (1);
default:
return (1);
}
}
static void
{
}
static int
{
char *label;
/*
* Do not take another hash step here.
* Just complete the operation.
*/
if (sender == sender_client)
else
return (kssl_tls_PRF(ssl,
} else {
uchar_t s[4];
return (0);
}
}
/*
* Minimum message length for a client hello =
* 2-byte client_version +
* 32-byte random +
* 1-byte session_id length +
* 2-byte cipher_suites length +
* 1-byte compression_methods length +
* 1-byte CompressionMethod.null
*/
#define KSSL_SSL3_CH_MIN_MSGLEN (39)
static int
{
int err;
uint_t i, j;
int ch_msglen = KSSL_SSL3_CH_MIN_MSGLEN;
goto falert;
}
/* Support SSLv3 (version == 3.0) or TLS (version == 3.1) */
goto falert;
}
goto falert;
}
if (sidlen != SSL3_SESSIONID_BYTES) {
} else {
ssl->kssl_entry);
}
/*
* This check can't be a "!=" since there can be
* compression methods other than CompressionMethod.null.
* Also, there can be extra data (TLS extensions) after the
* compression methods field. We do not support any TLS
* extensions and hence ignore them.
*/
goto falert;
}
/* The length has to be even since a cipher suite is 2-byte long */
if (cslen & 0x1) {
goto falert;
}
for (j = 0; j < cslen; j += 2) {
break;
}
}
if (j < cslen) {
goto suite_found;
}
}
/* Check if this server is capable of the cipher suite */
for (j = 0; j < cslen; j += 2) {
break;
}
}
if (j < cslen) {
break;
}
}
return (SSL_MISS);
}
goto falert;
}
/*
* Check for the mandatory CompressionMethod.null. We do not
* support any other compression methods.
*/
goto falert;
}
while (cmlen >= 1) {
break;
cmlen--;
}
if (cmlen == 0) {
goto falert;
}
for (i = 0; i < cipher_suite_defs_nentries; i++) {
break;
}
}
if (err != 0) {
return (err);
}
else
if (err != 0) {
return (err);
}
if (err != 0)
return (err);
if (err != 0)
return (err);
return (0);
}
if (err != 0) {
return (err);
}
if (err != 0) {
return (err);
}
return (0);
return (EBADMSG);
}
int addr; \
\
}
/*
* Creates a cache entry. Sets the sid->cached flag
* and sid->time fields. So, the caller should not set them.
*/
static void
{
/* set the values before creating the cache entry */
}
/*
* Invalidates the cache entry, if any. Clears the sid->cached flag
* as a side effect.
*/
void
{
return;
}
}
static void
{
return;
}
return;
}
}
static uchar_t *
{
int i = 2;
return (NULL);
}
while (i < len) {
if (buf[i++] == 0) {
break;
}
}
if (i == len) {
return (NULL);
}
return (buf + i);
}
#define KSSL_SSL3_SH_RECLEN (74)
#define KSSL_SSL3_FIN_MSGLEN (36)
static int
{
return (ENOMEM);
}
/* 5 byte record header */
buf[0] = content_handshake;
buf += SSL3_HDR_LEN;
/* 6 byte message header */
0xff; /* message len byte 1 */
buf += 6;
buf[0] = SSL3_SESSIONID_BYTES;
return (0);
}
static void
{
gethrestime(&ts);
/* Should this be caching? */
}
static int
{
int rv = 0;
int bytes_left = datalen;
/*
* A(i) = HMAC_hash(secret, seed + A(i-1));
* A(0) = seed;
*
* Compute A(1):
* A(1) = HMAC_hash(secret, label + seed)
*
*/
/* Compute A(2) ... A(n) */
while (bytes_left > 0) {
/*
* The A(i) value is stored in "result".
* Save the results of the MAC so it can be input to next
* iteration.
*/
if (bytes_left > hashlen) {
/* Store the chunk result */
bytes_left -= hashlen;
/* Update A1 for next iteration */
} else {
data += bytes_left;
bytes_left = 0;
}
}
end:
if (CRYPTO_ERR(rv)) {
}
return (rv);
}
/* ARGSUSED */
static int
{
/*
* RFC 2246:
* PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
* P_SHA1(S2, label + seed);
* S1 = 1st half of secret.
* S1 = 2nd half of secret.
*
*/
int rv, i;
/* length of secret keys is ceil(length/2) */
if (prfresult_len > MAX_KEYBLOCK_LENGTH) {
return (CRYPTO_ARGUMENTS_BAD);
}
if (CRYPTO_ERR(rv))
goto end;
if (CRYPTO_ERR(rv))
goto end;
for (i = 0; i < prfresult_len; i++)
end:
if (CRYPTO_ERR(rv))
return (rv);
}
}
static int
{
/*
* Computing the master secret:
* ----------------------------
* master_secret = PRF (pms, "master secret",
* ClientHello.random + ServerHello.random);
*/
/* if pms is bad fake it to thwart Bleichenbacher attack */
}
return (kssl_tls_PRF(ssl,
}
static void
{
int hlen = MD5_HASH_LEN;
/* if pms is bad fake it to thwart Bleichenbacher attack */
}
0);
}
static int
{
}
static void
{
int hlen = MD5_HASH_LEN;
int i;
for (i = 1; i <= steps; i++) {
}
}
"FFFFFF", "GGGGGGG", "HHHHHHHH", "IIIIIIIII"};
static void
int step,
int sr_first)
{
sizeof (ssl3_key_derive_seeds) /
sizeof (ssl3_key_derive_seeds[0]));
step--;
step + 1);
if (sr_first) {
} else {
}
}
static int
{
int cur_reclen;
int mss;
int cert_len;
return (ENOENT);
}
/* Assume MSS is at least 80 bytes */
/* new record always starts in a new mblk for simplicity */
for (;;) {
cur_reclen += copylen;
if (len == 0) {
break;
}
if (cur_reclen == SSL3_MAX_RECORD_LENGTH) {
cur_reclen = 0;
}
return (ENOMEM);
}
if (cur_reclen == 0) {
cur_reclen = 0;
}
}
/* adjust the record length field for the first record */
return (0);
}
static int
{
/* We're most likely to hit the fast path for resumed sessions */
} else {
return (ENOMEM); /* need to do better job! */
} else {
}
}
/* 5 byte record header */
buf[3] = 0;
buf += SSL3_HDR_LEN;
buf[0] = 1;
}
int
{
int ret = 0;
spec->mac_hashsz);
} else {
}
/* Pre-compute these here. will save cycles on each record later */
spec->mac_hashsz);
spec->mac_hashsz);
}
spec->cipher_ctx = 0;
}
/*
* Initialize HMAC keys for TLS and SSL3 HMAC keys
* for SSL 3.0.
*/
}
} else {
crypto_mech2id("CKM_SSL3_MD5_MAC");
crypto_mech2id("CKM_SSL3_SHA1_MAC");
}
}
/* We're done if this is the nil cipher */
if (spec->cipher_keysz == 0) {
return (0);
}
/* Initialize the key and the active context */
/* client_write_IV */
}
/* client_write_key */
if (CRYPTO_ERR(ret)) {
int, ret);
}
} else {
/* server_write_IV */
}
/* server_write_key */
spec->cipher_keysz]);
if (CRYPTO_ERR(ret))
int, ret);
}
return (ret);
}
static int
{
int ret;
else
/* 5 byte record header */
buf[0] = content_handshake;
buf[3] = 0;
buf += SSL3_HDR_LEN;
/* 4 byte message header */
buf += 4;
sizeof (ssl3hashes.md5));
sizeof (ssl3hashes.sha1));
}
/* Compute hashes for the SENDER side */
if (ret != 0)
return (ret);
} else {
}
if (update_hsh) {
}
return (ret);
}
int
{
int mac_sz;
int ret = 0;
int pad_sz;
int i;
if (mac_sz != 0) {
if (ret == CRYPTO_SUCCESS) {
} else {
return (ret);
}
}
for (i = 0; i < pad_sz; i++) {
}
}
if (spec->cipher_ctx == 0)
return (ret);
/* One record at a time. Otherwise, gotta allocate the crypt_data_t */
if (CRYPTO_ERR(ret)) {
int, ret);
}
return (ret);
}
void
{
if (level == alert_fatal) {
}
} else
return;
}
/* 5 byte record header */
buf[0] = content_alert;
buf[3] = 0;
buf += SSL3_HDR_LEN;
/* alert contents */
}
/* Assumes RSA encryption */
static int
{
char *buf;
int allocated;
return (ENOENT);
}
/*
* TLS adds an extra 2 byte length field before the data.
*/
}
/*
* Allocate all we need in one shot. about 300 bytes total, for
* 1024 bit RSA modulus.
* The buffer layout will be: pms_data, wrapped_pms_data, the
* value of the wrapped pms from the client, then room for the
* resulting decrypted premaster secret.
*/
return (ENOMEM);
}
/* Proceed synchronously if out of interrupt and configured to do so */
if ((kssl_synchronous) && (!servicing_interrupt())) {
} else {
/* The callback routine will release this one */
}
if (ep->ke_is_nxkey) {
s = ep->ke_sessinfo;
if (!s->is_valid_handle) {
/* Reauthenticate to the provider */
if (s->do_reauth) {
if (err == CRYPTO_SUCCESS) {
s->is_valid_handle = B_TRUE;
}
} else
err = CRYPTO_FAILED;
}
if (err == CRYPTO_SUCCESS) {
ASSERT(s->is_valid_handle);
}
/*
* Deal with session specific errors. We translate to
* the closest errno.
*/
switch (err) {
s->is_valid_handle = B_FALSE;
break;
case CRYPTO_PIN_EXPIRED:
case CRYPTO_PIN_LOCKED:
break;
case CRYPTO_UNKNOWN_PROVIDER:
break;
}
} else {
}
switch (err) {
case CRYPTO_SUCCESS:
break;
case CRYPTO_QUEUED:
/*
* Finish the master secret then the rest of key material
* derivation later.
*/
return (0);
default:
return (rverr);
}
/* generate master key and save it in the ssl sid structure */
if (!CRYPTO_ERR(err))
} else {
}
if (err == CRYPTO_SUCCESS)
return (0);
}
static int
{
int err;
int hashcompare;
else
if (msglen != finish_len) {
return (EBADMSG);
}
} else {
}
/* The handshake hashes should be computed by now */
if (hashcompare != 0) {
return (EBADMSG);
}
return (0);
}
if (err != 0) {
return (err);
}
if (err != 0) {
return (err);
}
return (0);
}
#define KSSL2_CH_MIN_RECSZ (9)
/*
* This method is needed to handle clients which send the
* We are not really doing SSLv2 here, just handling the header
* and then switching to SSLv3.
*/
int
{
int err;
uint_t i, j;
int ch_recsz = KSSL2_CH_MIN_RECSZ;
goto falert;
}
goto falert;
}
if (cslen % 3 != 0) {
goto falert;
}
if (randlen < SSL_MIN_CHALLENGE_BYTES ||
goto falert;
}
goto falert;
}
if (randlen < SSL3_RANDOM_LENGTH) {
}
randlen);
for (j = 0; j < cslen; j += 3) {
if (suitesp[j] != 0) {
continue;
}
break;
}
}
if (j < cslen) {
break;
}
}
return (SSL_MISS);
}
for (i = 0; i < cipher_suite_defs_nentries; i++) {
break;
}
}
if (err != 0) {
return (err);
}
if (err != 0) {
return (err);
}
return (0);
return (EBADMSG);
}
/*
* Call back routine for asynchronously submitted RSA decryption jobs.
* This routine retrieves the pre-master secret, and proceeds to generate
* the remaining key materials.
*/
static void
{
int ret = 0;
void *cbarg;
if (status != CRYPTO_SUCCESS) {
goto out;
}
/* generate master key and save it in the ssl sid structure */
if (!CRYPTO_ERR(ret))
} else {
}
if (ret == CRYPTO_SUCCESS)
out:
/* If we're the only ones left, then we won't callback */
return;
}
/* Now call the callback routine */
}
/*
* Returns the first complete contiguous record out of rec_ass_head
* The record is returned in a separate contiguous mblk, rec_ass_head is
* left pointing to the next record in the queue.
*
* The output looks as follows:
*
* |--------|---------- .... -----|<---------->|<----------->|--- ... ---|
* ^ ^ ^ mac_size pad_size ^
* | |___ b_rptr b_wptr __| |
* | |
* |___ db_base db_lim ___|
*/
mblk_t *
{
int rhsz = SSL3_HDR_LEN;
int mpsz, total_size;
return (NULL);
/* Fast path: when mp has at least a complete record */
/* Not even a complete header in there yet */
return (NULL);
}
return (NULL);
}
}
if (content_type == content_handshake_v2) {
rhsz = 2;
} else {
}
/*
* same tests as above. Only rare very fragmented cases will
* incur the cost of msgdsize() and msgpullup(). Well formed
* packets will fall in the most frequent fast path.
*/
/*
* Missing: defensive against record fabricated with longer than
* MAX record length.
*/
/* Not a complete record yet. Keep accumulating */
return (NULL);
}
return (NULL);
}
}
if (mpsz > total_size) {
/* gotta allocate a new block */
return (NULL);
}
} else {
}
/* Adjust the tail */
}
}
return (retmp);
}
static void
{
}
}
}
}
}
static void
{
spec->cipher_ctx = 0;
}
spec->cipher_ctx = 0;
}
}
/*
* Frees the ssl structure (aka the context of an SSL session).
* Any pending crypto jobs are cancelled.
* Any initiated crypto contexts are freed as well.
*/
void
{
/* we're coming from an external API entry point */
}
}
}