/*
* 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
*/
/*
*/
#include <sys/sysmacros.h>
#include <sys/isa_defs.h>
#include "ksslimpl.h"
#include "ksslapi.h"
#include "ksslproto.h"
/* 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
};
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 kssl_data_out_cb(). For incoming traffic past the
* SSL handshake, we are in user context when called from
* kssl_data_in_proc_cb(). During the SSL handshake, we are
* called for client_finished message handling from a taskq
* thread.
*/
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
*/
/*
* or SSL_MISS if no cipher suite of the server matches the list received
* in the message.
*/
static int
{
int err;
uint_t i, j;
goto falert;
}
/* Support SSLv3 (version == 3.0) or TLS (version == 3.1) */
goto falert;
}
/* read client random field */
/* read session ID length */
goto falert;
}
if (sidlen != SSL3_SESSIONID_BYTES) {
} else {
ssl->kssl_entry);
}
/* read cipher suite length */
/*
* 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.
*/
goto falert;
}
/* The length has to be even since a cipher suite is 2-byte long. */
if (cslen & 0x1) {
goto falert;
}
/* session resumption checks */
for (j = 0; j < cslen; j += 2) {
/* Check for regular (true) cipher suite. */
}
/* Check for SCSV. */
}
/*
* If we got cipher suite match and SCSV we can
* terminate the cycle now.
*/
break;
}
if (suite_found)
goto suite_found;
}
/* Check if this server is capable of the cipher suite. */
for (j = 0; j < cslen; j += 2) {
/* Check for regular (true) cipher suite. */
}
/* Check for SCSV. */
}
/*
* If we got cipher suite match and SCSV or went
* through the whole list of client cipher suites
* (hence we know if SCSV was present or not) we
* can terminate the cycle now.
*/
if (suite_found &&
(ssl->secure_renegotiation || (i > 0)))
break;
}
if (suite_found)
break;
}
if (!suite_found) {
/*
* If there is no fallback point terminate the
* handshake with SSL alert otherwise return with
* SSL_MISS.
*/
goto falert;
} else {
return (SSL_MISS);
}
}
goto falert;
}
/*
* Check for the mandatory CompressionMethod.null. We do not
* support any other compression methods.
*/
goto falert;
}
/*
* Search for null compression method (encoded as 0 byte) in the
* compression methods field.
*/
while (cmlen >= 1) {
break;
cmlen--;
}
if (cmlen == 0) {
goto falert;
}
/* Find the suite in the internal cipher suite table. */
for (i = 0; i < cipher_suite_defs_nentries; i++) {
break;
}
}
/* Get past the remaining compression methods (minus null method). */
/* Parse TLS extensions (if any). */
/* Get the length of the extensions. */
/*
* Consider zero extensions length as invalid extension
* encoding.
*/
if (ext_total_len == 0) {
goto falert;
}
ch_msglen += 2;
goto falert;
}
/*
* Go through the TLS extensions. This is only done to check
* for the presence of renegotiation_info extension. We do not
* support any other TLS extensions and hence ignore them.
*/
/*
* Check that the extension has at least type and
* length (2 + 2 bytes).
*/
goto falert;
}
/* Get extension type and length */
ch_msglen += 4;
/*
* Make sure the contents of the extension are
* accessible.
*/
goto falert;
}
switch (ext_type) {
/*
* Search for empty "renegotiation_info"
* extension (encoded as ff 01 00 01 00).
*/
if ((ext_len != 1) ||
goto falert;
}
break;
default:
/* FALLTHRU */
break;
}
/* jump to the next extension */
}
}
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);
}
/*
* Send ServerHello record to the client.
*/
static int
{
return (ENOMEM);
}
if (ssl->secure_renegotiation)
/* 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;
buf += 3;
/*
* Add "renegotiation_info" extension if the ClientHello message
* contained either SCSV value in cipher suite list or
* "renegotiation_info" extension. This is per RFC 5746, section 3.6.
*/
if (ssl->secure_renegotiation) {
/* Extensions length */
buf[0] = 0x00;
/* empty renegotiation_info extension encoding (section 3.2) */
}
return (0);
}
static void
{
gethrestime(&ts);
/* Should this be caching? */
}
static int
{
int rv = 0;
/*
* 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
{
/* if pms is bad fake it to thwart Bleichenbacher attack */
}
0);
}
static int
{
}
static void
{
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);
}
if (ssl->secure_renegotiation)
/* 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;
if (ssl->secure_renegotiation)
/*
* It should be either a message with Server Hello record or just plain
* SSL header (data packet).
*/
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);
}
/*
* it is only done to tear down the SSL connection so it has fixed encoding.
*/
void
{
if (level == alert_fatal) {
}
} else
} else {
/* KSSL generates 5 byte SSLv2 alert messages only. */
len = 5;
}
return;
}
/* 5 byte record header */
buf[0] = content_alert;
buf[3] = 0;
buf += SSL3_HDR_LEN;
/* alert contents */
buf += SSL3_ALERT_LEN;
} else {
/* SSLv2 has different encoding. */
/* 2-byte encoding of the length */
buf[0] = 0x80;
buf += 2;
/* Protocol Message Code = Error */
buf[0] = 0;
/* Error Message Code = Undefined Error */
buf[1] = 0;
buf[2] = 0;
buf += 3;
}
}
/* 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 {
}
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);
}
/*
* 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;
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;
}
/* Check for regular (true) cipher suite. */
}
/* Check for SCSV. */
}
/*
* If we got cipher suite match and SCSV or went
* through the whole list of client cipher suites
* (hence we know if SCSV was present or not) we
* can terminate the cycle now.
*/
if (suite_found &&
(ssl->secure_renegotiation || (i > 0)))
break;
}
if (suite_found)
break;
}
if (!suite_found) {
/*
* If there is no fallback point terminate the handshake with
* SSL alert otherwise return with SSL_MISS.
*/
goto falert;
} else {
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:
/* dropped by callback when it has completed */
ssl->async_ops_pending++;
/* 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 *
{
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 */
}
/*
* Cancel any active crypto request and wait for pending async
* operations to complete. We loop here because the async thread
* might submit a new cryto request.
*/
do {
/*
* Drop the lock before canceling the request;
* otherwise we might deadlock if the completion
* callback is running.
*/
/* completion callback might have done the cleanup */
}
}
while (ssl->async_ops_pending > 0)
}