/*
* 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 "ksslimpl.h"
#include "ksslproto.h"
#include "ksslapi.h"
/*
* The socket bind request is intercepted and re-routed here
* to see is there is SSL relevant job to do, based on the kssl config
* in the kssl_entry_tab.
* Looks up the kernel SSL proxy table, to find an entry that matches the
* same serveraddr, and has one of the following two criteria:
* 1. in_port is an ssl_port. This endpoint can be used later as a fallback
* to complete connections that cannot be handled by the SSL kernel proxy
* (typically non supported ciphersuite). The cookie for the calling client
* is saved with the kssl_entry to be retrieved for the fallback.
* The function returns KSSL_HAS_PROXY.
*
* 2. in_port is a proxy port for another ssl port. The ssl port is then
* substituted to the in_port in the bind_req TPI structure, so that
* the bind falls through to the SSL port. At the end of this operation,
* all the packets arriving to the SSL port will be delivered to an
* accepted endpoint child of this bound socket.
* The kssl_entry_t is returned in *ksslent, for later use by the
* lower modules' SSL hooks that handle the Handshake messages.
* The function returns KSSL_IS_PROXY.
*
* The function returns KSSL_NO_PROXY otherwise.
*/
{
int i;
if (kssl_entry_tab_nentries == 0) {
return (KSSL_NO_PROXY);
}
ret = KSSL_NO_PROXY;
switch (len) {
case sizeof (sin_t):
v6addr = &mapped_v4addr;
break;
case sizeof (sin6_t):
break;
default:
return (ret);
}
for (i = 0; i < kssl_entry_tab_size; i++) {
continue;
/* This is an SSL port to fallback to */
/*
* Let's see first if there's at least
* an endpoint for a proxy server.
* If there's none, then we return as we have
* no proxy, so that the bind() to the
* transport layer goes through.
* The calling module will ask for this
* cookie if it wants to fall back to it,
* so add this one to the list of fallback
* clients.
*/
if (!kssl_enqueue((kssl_chain_t **)
break;
}
break;
}
/* This is a proxy port. */
/* Add the caller's cookie to proxies list */
if (!kssl_enqueue((kssl_chain_t **)
break;
}
/*
* Make this look like the SSL port to the
* transport below
*/
ret = KSSL_IS_PROXY;
break;
}
}
}
return (ret);
}
/*
* Retrieved an endpoint "bound" to the SSL entry.
* Such endpoint has previously called kssl_check_proxy(), got itself
* linked to the kssl_entry's ke_fallback_head list.
* This routine returns the cookie from that SSL entry ke_fallback_head list.
*/
void *
{
return (NULL);
}
/*
* matches "item"
* The chain is simple-linked and NULL ended.
*/
/*
* This routine returns TRUE if the item was either successfully added to
* the chain, or is already there. It returns FALSE otherwise.
*/
static boolean_t
{
/* Lookup the existing entries to avoid duplicates */
return (B_TRUE);
}
}
return (B_FALSE);
}
return (B_TRUE);
}
static void
{
else
return;
}
}
}
/*
* Holds the kssl_entry
*/
void
{
}
/*
* Releases the kssl_entry
* If the caller passes a cookie, then it should be removed from both
* proxies and fallbacks chains.
*/
void
{
if (endpt_type == KSSL_IS_PROXY) {
cookie);
}
if (endpt_type == KSSL_HAS_PROXY) {
cookie);
}
}
}
/*
* Releases the kssl_context
*/
void
{
}
/*
* Done with asynchronous processing
*/
void
{
if (--ssl->async_ops_pending == 0)
}
/*
* Packets are accumulated here, if there are packets already queued,
* or if the context is active.
* The context is active when an incoming record processing function
* is already executing on a different thread.
* Queued packets are handled either when an mblk arrived and completes
* a record, or, when the active context processor finishes the task at
* hand.
* The caller has to keep calling this routine in a loop until it returns
* B_FALSE in *more. The reason for this is SSL3: The protocol
* allows the client to send its first application_data message right
* after it had sent its Finished message, and not wait for the server
* ChangeCipherSpec and Finished. This overlap means we can't batch up
* a returned Handshake message to be sent on the wire
* with a decrypted application_data to be delivered to the application.
*/
{
int mplen;
}
goto sendnewalert;
}
/* Whomever is currently processing this connection will get to this */
if (ssl->activeinput) {
}
return (KSSL_CMD_NONE);
}
/*
* Fast path for complete incoming application_data records on an empty
* queue.
* This is by far the most frequently encountered case
*/
if ((content_type == content_application_data) &&
return (KSSL_CMD_DELIVER_PROXY);
}
}
}
/* Accumulate at least one record */
}
goto sendalert;
}
/* Not even a complete header yet. wait for the rest */
return (KSSL_CMD_NONE);
}
do {
switch (content_type) {
case content_application_data:
/*
* application_data records are decrypted and
* MAC-verified by the stream head, and in the context
* a read()'ing thread. This avoids unfairly charging
* the cost of handling this record on the whole system,
* and prevents doing it while in the shared IP
* perimeter.
*/
goto sendnewalert;
}
break;
case content_alert:
case content_handshake:
case content_handshake_v2:
/*
* If we're past the initial handshake, start letting
* the stream head process all records, in particular
* the close_notify.
* This is needed to avoid processing them out of
* sequence when previous application data packets are
*/
} else {
}
break;
default:
goto sendnewalert;
}
/* Priority to Alert messages */
goto sendalert;
}
/* Then handshake messages */
if (ssl->handshake_sendbuf) {
} else {
}
(!ssl->activeinput));
return (kssl_cmd);
}
(!ssl->activeinput));
}
/*
* Don't process any packet after an application_data.
* We could well receive the close_notify which should
* be handled separately.
*/
return (kssl_cmd);
}
/*
* The current record isn't done yet. Don't start the next one
*/
if (ssl->activeinput) {
return (kssl_cmd);
}
return (kssl_cmd);
}
return (KSSL_CMD_SEND);
}
/*
* Decrypt and verify the MAC of an incoming chain of application_data record.
* Each block has exactly one SSL record.
*/
{
int mac_sz;
more:
/*
* Fortunately copyb() preserves the offset,
* tail space and alignment so the copy is
* ready to be made an SSL record.
*/
return (NULL);
}
}
switch (content_type) {
case content_application_data:
break;
case content_alert:
case content_handshake:
case content_handshake_v2:
/* Remove this message */
/*
* If we had processed blocks that need to
* be delivered, then remember that error code
*/
if (kssl_cmd == KSSL_CMD_DELIVER_PROXY)
}
/* NOTE: This routine could free mp. */
goto sendalert;
}
if (deliverit) {
}
continue; /* to the while loop */
default:
desc = decode_error;
goto makealert;
}
/*
* Check the assumption that each mblk contains exactly
* one complete SSL record. We bail out if the check fails.
*/
desc = decode_error;
goto makealert;
}
if (spec->cipher_ctx != 0) {
/*
* The record length must be a multiple of the
* block size for block ciphers.
* The cipher_bsize is always a power of 2.
*/
int, spec->cipher_bsize);
goto makealert;
}
cipher_data.cd_offset = 0;
if (CRYPTO_ERR(error)) {
int, error);
goto makealert;
}
}
pad_sz++;
goto makealert;
}
}
if (mac_sz != 0) {
goto makealert;
}
if (ret != CRYPTO_SUCCESS ||
goto makealert;
}
}
goto makealert;
}
}
return (kssl_cmd);
/* internal memory allocation failure. just return. */
if (mp) {
goto more;
}
return (KSSL_CMD_NONE);
}
} else {
}
if (mp) {
goto more;
}
return (kssl_cmd);
}
/*
* This is the routine that handles incoming SSL records.
* When called the first time, with a NULL context, this routine expects
* a ClientHello SSL Handshake packet and shall allocate a context
* of a new SSL connection.
* During the rest of the handshake packets, the routine adjusts the
* state of the context according to the record received.
* After the ChangeCipherSpec message is received, the routine first
* decrypts/authenticated the packet using the key materials in the
* connection's context.
* The return code tells the caller what to do with the returned packet.
*/
static kssl_cmd_t
{
int sz;
int mac_sz;
if (content_type == content_handshake_v2) {
/* V2 compatible ClientHello */
} else {
/* We don't support "pure" SSLv2 */
goto sendalert;
}
}
rhsz = 2;
} else {
}
/*
* Check the assumption that each mblk contains exactly
* one complete SSL record. We bail out if the check fails.
*/
desc = decode_error;
goto sendalert;
}
if (spec->cipher_ctx != 0) {
/*
* The record length must be a multiple of the
* block size for block ciphers.
*/
goto sendalert;
}
if (CRYPTO_ERR(error)) {
int, error);
goto sendalert;
}
}
pad_sz++;
goto sendalert;
}
}
if (mac_sz != 0) {
int, mac_sz);
goto sendalert;
}
if (ret != CRYPTO_SUCCESS ||
goto sendalert;
}
}
switch (content_type) {
case content_handshake:
do {
if (error != 0 ||
/* ignore client renegotiation for now */
}
if (error != 0) {
goto error;
}
return (KSSL_CMD_QUEUED);
}
}
int msglenb =
msglenb < 3) {
msglenb++;
}
if (msglenb == 3) {
}
}
return (KSSL_CMD_NONE);
}
}
continue;
}
continue;
}
goto error;
}
}
return (KSSL_CMD_NONE);
} else {
return (KSSL_CMD_NONE);
}
arg));
return (KSSL_CMD_NOT_SUPPORTED);
}
return (KSSL_CMD_QUEUED);
} else {
return (KSSL_CMD_NONE);
}
case content_alert:
if (rec_sz != 2) {
goto sendalert;
} else {
ssl->kssl_entry);
}
goto error;
} else {
return (KSSL_CMD_NONE);
}
}
} else {
int, error);
goto error;
}
return (KSSL_CMD_NONE);
}
goto sendalert;
case content_application_data:
goto sendalert;
}
return (KSSL_CMD_DELIVER_PROXY);
case content_handshake_v2:
return (KSSL_CMD_NOT_SUPPORTED);
} else if (error != 0) {
goto error;
}
return (KSSL_CMD_SEND);
default:
break;
}
return (KSSL_CMD_NONE);
}
/*
* Initialize the context of an SSL connection, coming to the specified
* address. The ssl structure is returned held.
*/
{
return (KSSL_STS_ERR);
}
} else {
/* struct assignment */
}
return (KSSL_STS_OK);
}
void
{
}
/*
* Builds SSL records out of the chain of mblks, and returns it.
* Takes a copy of the message before encrypting it if it has another
* reference.
* In case of failure, NULL is returned, and the message will be
* freed by the caller.
* A NULL mp means a close_notify is requested.
*/
mblk_t *
{
/*
* Produce new close_notify message. This is necessary to perform
* proper cleanup w.r.t. SSL protocol spec by sending close_notify SSL
* alert record if running with KSSL proxy.
* This should be done prior to sending the FIN so the client side can
* attempt to do graceful cleanup. Ideally, we should wait for client's
* close_notify but not all clients send it which would hang the
* connection. This way of closing the SSL session (Incomplete Close)
* prevents truncation attacks for protocols without end-of-data
* markers (as opposed to the Premature Close).
* Checking the close_notify_srvr flag will prevent from sending the
* close_notify message twice in case of duplicate shutdown() calls.
*/
return (NULL);
}
do {
/*
* Fortunately copyb() preserves the offset,
* tail space and alignment so the copy is
* ready to be made an SSL record.
*/
return (NULL);
} else {
}
}
return (NULL);
return (retmp);
}
/*
* Builds a single SSL record by prepending SSL header (optional) and performing
* encryption and MAC. The encryption of the record is done in-line.
* Expects an mblk with associated dblk's base to have space for the SSL header
* or an mblk which already has the header present. In both cases it presumes
* that the mblk's dblk limit has space for the MAC + padding.
* If the close_notify_srvr flag is set it is presumed that the mblk already
* contains SSL header in which case only the record length field will be
*/
static kssl_status_t
{
int len;
int reclen;
int mac_sz;
int pad_sz;
/* The dblk must always have space for the padding and MAC suffix. */
/* kssl_send_alert() constructs the SSL header by itself. */
if (!ssl->close_notify_srvr)
else
if (!ssl->close_notify_srvr) {
/* The dblk must have space for the SSL header prefix. */
}
}
/* Do we need an internal_error Alert here? */
return (KSSL_STS_ERR);
}
/* Alert messages are accounted in kssl_send_alert(). */
if (recstart[0] == content_application_data)
return (KSSL_STS_OK);
}