xref: /onnv-gate/usr/src/uts/common/inet/kssl/ksslapi.c (revision 12696:3dfea19a9dc1)
1898Skais /*
2898Skais  * CDDL HEADER START
3898Skais  *
4898Skais  * The contents of this file are subject to the terms of the
51139Skais  * Common Development and Distribution License (the "License").
61139Skais  * You may not use this file except in compliance with the License.
7898Skais  *
8898Skais  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9898Skais  * or http://www.opensolaris.org/os/licensing.
10898Skais  * See the License for the specific language governing permissions
11898Skais  * and limitations under the License.
12898Skais  *
13898Skais  * When distributing Covered Code, include this CDDL HEADER in each
14898Skais  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15898Skais  * If applicable, add the following below this CDDL HEADER, with the
16898Skais  * fields enclosed by brackets "[]" replaced with your own identifying
17898Skais  * information: Portions Copyright [yyyy] [name of copyright owner]
18898Skais  *
19898Skais  * CDDL HEADER END
20898Skais  */
21898Skais /*
2212381SVladimir.Kotal@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23898Skais  */
24898Skais 
25898Skais #include <sys/types.h>
26898Skais #include <sys/stream.h>
27898Skais #include <sys/strsun.h>
28898Skais #include <sys/kmem.h>
29898Skais #include <sys/cpuvar.h>
30898Skais #include <sys/atomic.h>
31898Skais #include <sys/sysmacros.h>
32898Skais 
33898Skais #include <inet/common.h>
34898Skais #include <inet/ip.h>
35898Skais 
36898Skais #include <sys/systm.h>
37898Skais #include <sys/param.h>
38898Skais #include <sys/tihdr.h>
39898Skais 
40898Skais #include "ksslimpl.h"
41898Skais #include "ksslproto.h"
42898Skais #include "ksslapi.h"
43898Skais 
44898Skais static kssl_cmd_t kssl_handle_any_record(kssl_ctx_t ctx, mblk_t *mp,
45898Skais     mblk_t **decrmp, kssl_callback_t cbfn, void *arg);
46898Skais static boolean_t kssl_enqueue(kssl_chain_t **head, void *item);
47898Skais static void kssl_dequeue(kssl_chain_t **head, void *item);
48898Skais static kssl_status_t kssl_build_single_record(ssl_t *ssl, mblk_t *mp);
49898Skais 
50898Skais /*
5112644SAnders.Persson@Sun.COM  * The socket bind request is intercepted and re-routed here
52898Skais  * to see is there is SSL relevant job to do, based on the kssl config
53898Skais  * in the kssl_entry_tab.
54898Skais  * Looks up the kernel SSL proxy table, to find an entry that matches the
55898Skais  * same serveraddr, and has one of the following two criteria:
56898Skais  * 1. in_port is an ssl_port. This endpoint can be used later as a fallback
57898Skais  *    to complete connections that cannot be handled by the SSL kernel proxy
58898Skais  *    (typically non supported ciphersuite). The cookie for the calling client
59898Skais  *    is saved with the kssl_entry to be retrieved for the fallback.
60898Skais  *    The function returns KSSL_HAS_PROXY.
61898Skais  *
62898Skais  * 2. in_port is a proxy port for another ssl port. The ssl port is then
63898Skais  *    substituted to the in_port in the bind_req TPI structure, so that
64898Skais  *    the bind falls through to the SSL port. At the end of this operation,
65898Skais  *    all the packets arriving to the SSL port will be delivered to an
66898Skais  *    accepted endpoint child of this bound socket.
67898Skais  *    The  kssl_entry_t is returned in *ksslent, for later use by the
68898Skais  *    lower modules' SSL hooks that handle the Handshake messages.
69898Skais  *    The function returns KSSL_IS_PROXY.
70898Skais  *
7110520SBhargava.Yenduri@Sun.COM  * The function returns KSSL_NO_PROXY otherwise.
72898Skais  */
73898Skais 
74898Skais kssl_endpt_type_t
kssl_check_proxy(struct sockaddr * addr,socklen_t len,void * cookie,kssl_ent_t * ksslent)7512644SAnders.Persson@Sun.COM kssl_check_proxy(struct sockaddr *addr, socklen_t len, void *cookie,
7612644SAnders.Persson@Sun.COM     kssl_ent_t *ksslent)
77898Skais {
78898Skais 	int i;
79898Skais 	kssl_endpt_type_t ret;
80898Skais 	kssl_entry_t *ep;
81898Skais 	sin_t *sin;
829624SBhargava.Yenduri@Sun.COM 	sin6_t *sin6;
8310520SBhargava.Yenduri@Sun.COM 	in6_addr_t mapped_v4addr;
8410520SBhargava.Yenduri@Sun.COM 	in6_addr_t *v6addr;
85898Skais 	in_port_t in_port;
86898Skais 
8710520SBhargava.Yenduri@Sun.COM 	if (kssl_entry_tab_nentries == 0) {
88898Skais 		return (KSSL_NO_PROXY);
89898Skais 	}
90898Skais 
91898Skais 	ret = KSSL_NO_PROXY;
9212644SAnders.Persson@Sun.COM 	sin = (struct sockaddr_in *)addr;
93898Skais 
9412644SAnders.Persson@Sun.COM 	switch (len) {
95898Skais 	case sizeof (sin_t):
96898Skais 		in_port = ntohs(sin->sin_port);
9710520SBhargava.Yenduri@Sun.COM 		IN6_IPADDR_TO_V4MAPPED(sin->sin_addr.s_addr, &mapped_v4addr);
9810520SBhargava.Yenduri@Sun.COM 		v6addr = &mapped_v4addr;
99898Skais 		break;
100898Skais 
101898Skais 	case sizeof (sin6_t):
1029624SBhargava.Yenduri@Sun.COM 		sin6 = (sin6_t *)sin;
10310520SBhargava.Yenduri@Sun.COM 		in_port = ntohs(sin6->sin6_port);
10410520SBhargava.Yenduri@Sun.COM 		v6addr = &sin6->sin6_addr;
10510520SBhargava.Yenduri@Sun.COM 		break;
1069624SBhargava.Yenduri@Sun.COM 
107898Skais 	default:
108898Skais 		return (ret);
109898Skais 	}
110898Skais 
111898Skais 	mutex_enter(&kssl_tab_mutex);
112898Skais 
113898Skais 	for (i = 0; i < kssl_entry_tab_size; i++) {
114898Skais 		if ((ep = kssl_entry_tab[i]) == NULL)
115898Skais 			continue;
116898Skais 
11710520SBhargava.Yenduri@Sun.COM 		if (IN6_ARE_ADDR_EQUAL(&ep->ke_laddr, v6addr) ||
11810520SBhargava.Yenduri@Sun.COM 		    IN6_IS_ADDR_UNSPECIFIED(&ep->ke_laddr)) {
119898Skais 
120898Skais 			/* This is an SSL port to fallback to */
121898Skais 			if (ep->ke_ssl_port == in_port) {
122898Skais 
123898Skais 				/*
124898Skais 				 * Let's see first if there's at least
125898Skais 				 * an endpoint for a proxy server.
126898Skais 				 * If there's none, then we return as we have
127898Skais 				 * no proxy, so that the bind() to the
128898Skais 				 * transport layer goes through.
129898Skais 				 * The calling module will ask for this
130898Skais 				 * cookie if it wants to fall back to it,
131898Skais 				 * so add this one to the list of fallback
132898Skais 				 * clients.
133898Skais 				 */
134898Skais 				if (!kssl_enqueue((kssl_chain_t **)
135898Skais 				    &(ep->ke_fallback_head), cookie)) {
136898Skais 					break;
137898Skais 				}
138898Skais 
139898Skais 				KSSL_ENTRY_REFHOLD(ep);
140898Skais 				*ksslent = (kssl_ent_t)ep;
141898Skais 
142898Skais 				ret = KSSL_HAS_PROXY;
143898Skais 				break;
144898Skais 			}
145898Skais 
146898Skais 			/* This is a proxy port. */
147898Skais 			if (ep->ke_proxy_port == in_port) {
148898Skais 				/* Add the caller's cookie to proxies list */
149898Skais 
150898Skais 				if (!kssl_enqueue((kssl_chain_t **)
151898Skais 				    &(ep->ke_proxy_head), cookie)) {
152898Skais 					break;
153898Skais 				}
154898Skais 
155898Skais 				/*
156898Skais 				 * Make this look  like the SSL port to the
157898Skais 				 * transport below
158898Skais 				 */
159898Skais 				sin->sin_port = htons(ep->ke_ssl_port);
160898Skais 
161898Skais 				KSSL_ENTRY_REFHOLD(ep);
162898Skais 				*ksslent = (kssl_ent_t)ep;
163898Skais 
164898Skais 				ret = KSSL_IS_PROXY;
165898Skais 				break;
166898Skais 			}
167898Skais 		}
168898Skais 	}
169898Skais 
170898Skais 	mutex_exit(&kssl_tab_mutex);
171898Skais 	return (ret);
172898Skais }
173898Skais 
174898Skais /*
175898Skais  * Retrieved an endpoint "bound" to the SSL entry.
176898Skais  * Such endpoint has previously called kssl_check_proxy(), got itself
177898Skais  * linked to the kssl_entry's ke_fallback_head list.
178898Skais  * This routine returns the cookie from that SSL entry ke_fallback_head list.
179898Skais  */
180898Skais void *
kssl_find_fallback(kssl_ent_t ksslent)181898Skais kssl_find_fallback(kssl_ent_t ksslent)
182898Skais {
183898Skais 	kssl_entry_t *kssl_entry = (kssl_entry_t *)ksslent;
184898Skais 
185898Skais 	if (kssl_entry->ke_fallback_head != NULL)
186898Skais 		return (kssl_entry->ke_fallback_head->fallback_bound);
187898Skais 
188898Skais 	KSSL_COUNTER(proxy_fallback_failed, 1);
189898Skais 
190898Skais 	return (NULL);
191898Skais }
192898Skais 
193898Skais /*
194898Skais  * Re-usable code for adding and removing an element to/from a chain that
195898Skais  * matches "item"
196898Skais  * The chain is simple-linked and NULL ended.
197898Skais  */
198898Skais 
199898Skais /*
200898Skais  * This routine returns TRUE if the item was either successfully added to
201898Skais  * the chain, or is already there. It returns FALSE otherwise.
202898Skais  */
203898Skais static boolean_t
kssl_enqueue(kssl_chain_t ** head,void * item)204898Skais kssl_enqueue(kssl_chain_t **head, void *item)
205898Skais {
206898Skais 	kssl_chain_t *newchain, *cur;
207898Skais 
208898Skais 	/* Lookup the existing entries to avoid duplicates */
209898Skais 	cur = *head;
210898Skais 	while (cur != NULL) {
211898Skais 		if (cur->item == item) {
212898Skais 			return (B_TRUE);
213898Skais 		}
214898Skais 		cur = cur->next;
215898Skais 	}
216898Skais 
217898Skais 	newchain = kmem_alloc(sizeof (kssl_chain_t), KM_NOSLEEP);
218898Skais 	if (newchain == NULL) {
219898Skais 		return (B_FALSE);
220898Skais 	}
221898Skais 
222898Skais 	newchain->item = item;
223898Skais 	newchain->next = *head;
224898Skais 	*head = newchain;
225898Skais 	return (B_TRUE);
226898Skais }
227898Skais 
228898Skais static void
kssl_dequeue(kssl_chain_t ** head,void * item)229898Skais kssl_dequeue(kssl_chain_t **head, void *item)
230898Skais {
231898Skais 	kssl_chain_t *prev, *cur;
232898Skais 
233898Skais 	prev = cur = *head;
234898Skais 	while (cur != NULL) {
235898Skais 		if (cur->item == item) {
236898Skais 			if (cur == *head)
237898Skais 				*head = (*head)->next;
238898Skais 			else
239898Skais 				prev->next = cur->next;
240898Skais 			kmem_free(cur, sizeof (kssl_chain_t));
241898Skais 			return;
242898Skais 		}
243898Skais 		prev = cur;
244898Skais 		cur = cur->next;
245898Skais 	}
246898Skais }
247898Skais 
248898Skais /*
249898Skais  * Holds the kssl_entry
250898Skais  */
251898Skais void
kssl_hold_ent(kssl_ent_t ksslent)252898Skais kssl_hold_ent(kssl_ent_t ksslent)
253898Skais {
254898Skais 	KSSL_ENTRY_REFHOLD((kssl_entry_t *)ksslent);
255898Skais }
256898Skais 
257898Skais /*
258898Skais  * Releases the kssl_entry
259898Skais  * If the caller passes a cookie, then it should be removed from both
260898Skais  * proxies and fallbacks chains.
261898Skais  */
262898Skais void
kssl_release_ent(kssl_ent_t ksslent,void * cookie,kssl_endpt_type_t endpt_type)263898Skais kssl_release_ent(kssl_ent_t ksslent, void *cookie, kssl_endpt_type_t endpt_type)
264898Skais {
265898Skais 	kssl_entry_t *kssl_entry = (kssl_entry_t *)ksslent;
266898Skais 
267898Skais 	if (cookie != NULL) {
26810520SBhargava.Yenduri@Sun.COM 		if (endpt_type == KSSL_IS_PROXY) {
269898Skais 			ASSERT(kssl_entry->ke_proxy_head != NULL);
270898Skais 			kssl_dequeue(
271898Skais 			    (kssl_chain_t **)&kssl_entry->ke_proxy_head,
272898Skais 			    cookie);
27310520SBhargava.Yenduri@Sun.COM 		}
27410520SBhargava.Yenduri@Sun.COM 		if (endpt_type == KSSL_HAS_PROXY) {
275898Skais 			ASSERT(kssl_entry->ke_fallback_head != NULL);
276898Skais 			kssl_dequeue(
277898Skais 			    (kssl_chain_t **)&kssl_entry->ke_fallback_head,
278898Skais 			    cookie);
27910520SBhargava.Yenduri@Sun.COM 		}
280898Skais 	}
281898Skais 	KSSL_ENTRY_REFRELE(kssl_entry);
282898Skais }
283898Skais 
284898Skais /*
285898Skais  * Releases the kssl_context
286898Skais  */
287898Skais void
kssl_release_ctx(kssl_ctx_t ksslctx)288898Skais kssl_release_ctx(kssl_ctx_t ksslctx)
289898Skais {
29012644SAnders.Persson@Sun.COM 	kssl_free_context((ssl_t *)ksslctx);
29112644SAnders.Persson@Sun.COM }
29212644SAnders.Persson@Sun.COM 
29312644SAnders.Persson@Sun.COM /*
29412644SAnders.Persson@Sun.COM  * Done with asynchronous processing
29512644SAnders.Persson@Sun.COM  */
29612644SAnders.Persson@Sun.COM void
kssl_async_done(kssl_ctx_t ksslctx)29712644SAnders.Persson@Sun.COM kssl_async_done(kssl_ctx_t ksslctx)
29812644SAnders.Persson@Sun.COM {
29912644SAnders.Persson@Sun.COM 	ssl_t *ssl = (ssl_t *)ksslctx;
30012644SAnders.Persson@Sun.COM 
30112644SAnders.Persson@Sun.COM 	mutex_enter(&ssl->kssl_lock);
30212644SAnders.Persson@Sun.COM 	if (--ssl->async_ops_pending == 0)
30312644SAnders.Persson@Sun.COM 		cv_signal(&ssl->async_cv);
30412644SAnders.Persson@Sun.COM 	mutex_exit(&ssl->kssl_lock);
305898Skais }
306898Skais 
307898Skais /*
308898Skais  * Packets are accumulated here, if there are packets already queued,
309898Skais  * or if the context is active.
310898Skais  * The context is active when an incoming record processing function
311898Skais  * is already executing on a different thread.
312898Skais  * Queued packets are handled either when an mblk arrived and completes
313898Skais  * a record, or, when the active context processor finishes the task at
314898Skais  * hand.
315898Skais  * The caller has to keep calling this routine in a loop until it returns
316898Skais  * B_FALSE in *more. The reason for this is SSL3: The protocol
317898Skais  * allows the client to send its first application_data message right
318898Skais  * after it had sent its Finished message, and not wait for the server
319898Skais  * ChangeCipherSpec and Finished. This overlap means we can't batch up
320898Skais  * a returned Handshake message to be sent on the wire
321898Skais  * with a decrypted application_data to be delivered to the application.
322898Skais  */
323898Skais kssl_cmd_t
kssl_input(kssl_ctx_t ctx,mblk_t * mp,mblk_t ** decrmp,boolean_t * more,kssl_callback_t cbfn,void * arg)324898Skais kssl_input(kssl_ctx_t ctx, mblk_t *mp, mblk_t **decrmp, boolean_t *more,
325898Skais     kssl_callback_t cbfn, void *arg)
326898Skais {
327898Skais 	mblk_t *recmp, *outmp = NULL;
328898Skais 	kssl_cmd_t kssl_cmd;
329898Skais 	ssl_t *ssl;
330898Skais 	uint8_t *rec_sz_p;
331898Skais 	int mplen;
332898Skais 	SSL3ContentType content_type;
333898Skais 	uint16_t rec_sz;
334898Skais 
335898Skais 	ASSERT(ctx != NULL);
336898Skais 
337898Skais 	if (mp != NULL) {
338898Skais 		ASSERT(mp->b_prev == NULL && mp->b_next == NULL);
339898Skais 	}
340898Skais 
341898Skais 	ssl = (ssl_t *)(ctx);
342898Skais 
343898Skais 	*decrmp = NULL;
344898Skais 	*more = B_FALSE;
345898Skais 
346898Skais 	mutex_enter(&ssl->kssl_lock);
347898Skais 
348*12696SVladimir.Kotal@Sun.COM 	if (ssl->close_notify_clnt == B_TRUE) {
3495850Svk199839 		DTRACE_PROBE(kssl_err__close_notify);
350898Skais 		goto sendnewalert;
351898Skais 	}
352898Skais 
353898Skais 	/* Whomever is currently processing this connection will get to this */
354898Skais 	if (ssl->activeinput) {
355898Skais 		if (mp != NULL) {
356898Skais 			KSSL_ENQUEUE_MP(ssl, mp);
357898Skais 		}
358898Skais 		mutex_exit(&ssl->kssl_lock);
359898Skais 		return (KSSL_CMD_NONE);
360898Skais 	}
361898Skais 
362898Skais 	/*
363898Skais 	 * Fast path for complete incoming application_data records on an empty
364898Skais 	 * queue.
365898Skais 	 * This is by far the most frequently encountered case
366898Skais 	 */
367898Skais 
368898Skais 	if ((!ssl->activeinput) && (ssl->rec_ass_head == NULL) &&
369898Skais 	    ((mp != NULL) && (mplen = MBLKL(mp)) > SSL3_HDR_LEN)) {
370898Skais 
3715850Svk199839 		DTRACE_PROBE1(kssl_mblk__fast_path, mblk_t *, mp);
372898Skais 		content_type = (SSL3ContentType)mp->b_rptr[0];
373898Skais 
374898Skais 		if ((content_type == content_application_data) &&
375898Skais 		    (ssl->hs_waitstate == idle_handshake)) {
376898Skais 			rec_sz_p = SSL3_REC_SIZE(mp);
377898Skais 			rec_sz = BE16_TO_U16(rec_sz_p);
378898Skais 
379898Skais 			if ((mp->b_cont == NULL) && (mplen == rec_sz)) {
380898Skais 
381898Skais 				*decrmp = mp;
382898Skais 				mutex_exit(&ssl->kssl_lock);
383898Skais 				return (KSSL_CMD_DELIVER_PROXY);
384898Skais 			}
385898Skais 		}
386898Skais 	}
387898Skais 
388898Skais 	ssl->activeinput = B_TRUE;
389898Skais 	/* Accumulate at least one record */
390898Skais 	if (mp != NULL) {
391898Skais 		KSSL_ENQUEUE_MP(ssl, mp);
392898Skais 		mp = NULL;
393898Skais 	}
394898Skais 	recmp = kssl_get_next_record(ssl);
395898Skais 
396898Skais 	if (recmp == NULL) {
397898Skais 		ssl->activeinput = B_FALSE;
398898Skais 		if (ssl->alert_sendbuf != NULL) {
3995850Svk199839 			DTRACE_PROBE(kssl_err__alert_to_send);
400898Skais 			goto sendalert;
401898Skais 		}
402898Skais 		/* Not even a complete header yet. wait for the rest */
403898Skais 		mutex_exit(&ssl->kssl_lock);
404898Skais 		return (KSSL_CMD_NONE);
405898Skais 	}
406898Skais 
407898Skais 	do {
4085850Svk199839 		DTRACE_PROBE1(kssl_mblk__kssl_input_cycle, mblk_t *, recmp);
4094556Svk199839 		content_type = (SSL3ContentType)recmp->b_rptr[0];
4104556Svk199839 
4114556Svk199839 		switch (content_type) {
4124556Svk199839 		case content_application_data:
413898Skais 			/*
414898Skais 			 * application_data records are decrypted and
415898Skais 			 * MAC-verified by the stream head, and in the context
416898Skais 			 * a read()'ing thread. This avoids unfairly charging
417898Skais 			 * the cost of handling this record on the whole system,
418898Skais 			 * and prevents doing it while in the shared IP
419898Skais 			 * perimeter.
420898Skais 			 */
421898Skais 			ssl->activeinput = B_FALSE;
422898Skais 			if (ssl->hs_waitstate != idle_handshake) {
4235850Svk199839 				DTRACE_PROBE(kssl_err__waitstate_not_idle);
424898Skais 				goto sendnewalert;
425898Skais 			}
426898Skais 			outmp = recmp;
427898Skais 			kssl_cmd = KSSL_CMD_DELIVER_PROXY;
4284556Svk199839 			break;
4294556Svk199839 		case content_change_cipher_spec:
4304556Svk199839 		case content_alert:
4314556Svk199839 		case content_handshake:
4324556Svk199839 		case content_handshake_v2:
433898Skais 			/*
434898Skais 			 * If we're past the initial handshake, start letting
435898Skais 			 * the stream head process all records, in particular
436898Skais 			 * the close_notify.
437898Skais 			 * This is needed to avoid processing them out of
438898Skais 			 * sequence when previous application data packets are
439898Skais 			 * waiting to be decrypted/MAC'ed and delivered.
440898Skais 			 */
441898Skais 			if (ssl->hs_waitstate == idle_handshake) {
442898Skais 				ssl->activeinput = B_FALSE;
443898Skais 				outmp = recmp;
444898Skais 				kssl_cmd = KSSL_CMD_DELIVER_PROXY;
445898Skais 			} else {
446898Skais 				kssl_cmd = kssl_handle_any_record(ssl, recmp,
447898Skais 				    &outmp, cbfn, arg);
448898Skais 			}
4494556Svk199839 			break;
4504556Svk199839 		default:
4514556Svk199839 			ssl->activeinput = B_FALSE;
4525850Svk199839 			DTRACE_PROBE(kssl_err__invalid_content_type);
4534556Svk199839 			goto sendnewalert;
454898Skais 		}
455898Skais 
456898Skais 		/* Priority to Alert messages */
457898Skais 		if (ssl->alert_sendbuf != NULL) {
4585850Svk199839 			DTRACE_PROBE(kssl_err__alert_to_send_cycle);
459898Skais 			goto sendalert;
460898Skais 		}
461898Skais 
462898Skais 		/* Then handshake messages */
463898Skais 		if (ssl->handshake_sendbuf) {
464898Skais 			if (*decrmp != NULL) {
465898Skais 				linkb(*decrmp, ssl->handshake_sendbuf);
466898Skais 			} else {
467898Skais 				*decrmp = ssl->handshake_sendbuf;
468898Skais 			}
469898Skais 			ssl->handshake_sendbuf = NULL;
470898Skais 
471898Skais 			*more = ((ssl->rec_ass_head != NULL) &&
472898Skais 			    (!ssl->activeinput));
473898Skais 			mutex_exit(&ssl->kssl_lock);
474898Skais 			return (kssl_cmd);
475898Skais 		}
476898Skais 
477898Skais 		if (ssl->hs_waitstate == idle_handshake) {
478898Skais 			*more = ((ssl->rec_ass_head != NULL) &&
479898Skais 			    (!ssl->activeinput));
480898Skais 		}
481898Skais 
482898Skais 		if (outmp != NULL) {
483898Skais 			*decrmp = outmp;
484898Skais 			/*
485898Skais 			 * Don't process any packet after an application_data.
486898Skais 			 * We could well receive the close_notify which should
487898Skais 			 * be handled separately.
488898Skais 			 */
489898Skais 			mutex_exit(&ssl->kssl_lock);
490898Skais 			return (kssl_cmd);
491898Skais 		}
492898Skais 		/*
493898Skais 		 * The current record isn't done yet. Don't start the next one
494898Skais 		 */
495898Skais 		if (ssl->activeinput) {
496898Skais 			mutex_exit(&ssl->kssl_lock);
497898Skais 			return (kssl_cmd);
498898Skais 		}
499898Skais 	} while ((recmp = kssl_get_next_record(ssl)) != NULL);
500898Skais 
501898Skais 	mutex_exit(&ssl->kssl_lock);
502898Skais 	return (kssl_cmd);
503898Skais 
504898Skais sendnewalert:
505898Skais 	kssl_send_alert(ssl, alert_fatal, unexpected_message);
506898Skais 	if (mp != NULL) {
507898Skais 		freeb(mp);
508898Skais 	}
509898Skais 
510898Skais sendalert:
511898Skais 	*decrmp = ssl->alert_sendbuf;
512898Skais 	ssl->alert_sendbuf = NULL;
513898Skais 	mutex_exit(&ssl->kssl_lock);
514898Skais 	return (KSSL_CMD_SEND);
515898Skais }
516898Skais 
517898Skais /*
51812644SAnders.Persson@Sun.COM  * Decrypt and verify the MAC of an incoming chain of application_data record.
51912644SAnders.Persson@Sun.COM  * Each block has exactly one SSL record.
520898Skais  */
521898Skais kssl_cmd_t
kssl_handle_mblk(kssl_ctx_t ctx,mblk_t ** mpp,mblk_t ** outmp)5225850Svk199839 kssl_handle_mblk(kssl_ctx_t ctx, mblk_t **mpp, mblk_t **outmp)
523898Skais {
524898Skais 	uchar_t *recend, *rec_sz_p;
525898Skais 	uchar_t *real_recend;
5261139Skais 	mblk_t *prevmp = NULL, *nextmp, *firstmp, *mp, *copybp;
527898Skais 	int mac_sz;
528898Skais 	uchar_t version[2];
529898Skais 	uint16_t rec_sz;
530898Skais 	SSL3AlertDescription desc;
531898Skais 	SSL3ContentType content_type;
532898Skais 	ssl_t *ssl;
533898Skais 	KSSLCipherSpec *spec;
53410520SBhargava.Yenduri@Sun.COM 	int error, ret;
535898Skais 	kssl_cmd_t kssl_cmd = KSSL_CMD_DELIVER_PROXY;
536898Skais 	boolean_t deliverit = B_FALSE;
537898Skais 	crypto_data_t cipher_data;
538898Skais 
539898Skais 	ASSERT(ctx != NULL);
540898Skais 
541898Skais 	ssl = (ssl_t *)(ctx);
542898Skais 
5431139Skais 	mp = firstmp = *mpp;
544898Skais 	*outmp = NULL;
545898Skais more:
546898Skais 
547898Skais 	while (mp != NULL) {
5485850Svk199839 		ASSERT(DB_TYPE(mp) == M_DATA);
549898Skais 
550898Skais 		if (DB_REF(mp) > 1) {
551898Skais 			/*
552898Skais 			 * Fortunately copyb() preserves the offset,
55310520SBhargava.Yenduri@Sun.COM 			 * tail space and alignment so the copy is
554898Skais 			 * ready to be made an SSL record.
555898Skais 			 */
556898Skais 			if ((copybp = copyb(mp)) == NULL)
557898Skais 				return (NULL);
558898Skais 
559898Skais 			copybp->b_cont = mp->b_cont;
5601139Skais 			if (mp == firstmp) {
561898Skais 				*mpp = copybp;
5624556Svk199839 			} else if (prevmp != NULL) {
563898Skais 				prevmp->b_cont = copybp;
564898Skais 			}
565898Skais 			freeb(mp);
566898Skais 			mp = copybp;
567898Skais 		}
568898Skais 
5695850Svk199839 		DTRACE_PROBE1(kssl_mblk__handle_record_cycle, mblk_t *, mp);
570898Skais 		content_type = (SSL3ContentType)mp->b_rptr[0];
571898Skais 
5724556Svk199839 		switch (content_type) {
5734556Svk199839 		case content_application_data:
5744556Svk199839 			break;
5754556Svk199839 		case content_change_cipher_spec:
5764556Svk199839 		case content_alert:
5774556Svk199839 		case content_handshake:
5784556Svk199839 		case content_handshake_v2:
579898Skais 			nextmp = mp->b_cont;
580898Skais 
581898Skais 			/* Remove this message */
582898Skais 			if (prevmp != NULL) {
583898Skais 				prevmp->b_cont = nextmp;
584898Skais 
585898Skais 				/*
586898Skais 				 * If we had processed blocks that need to
587898Skais 				 * be delivered, then remember that error code
588898Skais 				 */
589898Skais 				if (kssl_cmd == KSSL_CMD_DELIVER_PROXY)
590898Skais 					deliverit = B_TRUE;
591898Skais 			}
592898Skais 
593898Skais 			mutex_enter(&ssl->kssl_lock);
5944556Svk199839 			/* NOTE: This routine could free mp. */
595898Skais 			kssl_cmd = kssl_handle_any_record(ssl, mp, outmp,
596898Skais 			    NULL, NULL);
597898Skais 
598898Skais 			if (ssl->alert_sendbuf != NULL) {
5994556Svk199839 				mp = nextmp;
6005850Svk199839 				DTRACE_PROBE(kssl_err__alert_after_handle_any);
601898Skais 				goto sendalert;
602898Skais 			}
603898Skais 			mutex_exit(&ssl->kssl_lock);
604898Skais 
605898Skais 			if (deliverit) {
606898Skais 				kssl_cmd = KSSL_CMD_DELIVER_PROXY;
607898Skais 			}
608898Skais 
609898Skais 			mp = nextmp;
6104556Svk199839 			continue;	/* to the while loop */
6114556Svk199839 		default:
6124556Svk199839 			desc = decode_error;
6134556Svk199839 			KSSL_COUNTER(internal_errors, 1);
6145850Svk199839 			DTRACE_PROBE(kssl_err__decode_error);
6154556Svk199839 			goto makealert;
616898Skais 		}
617898Skais 
618898Skais 		version[0] = mp->b_rptr[1];
619898Skais 		version[1] = mp->b_rptr[2];
620898Skais 		rec_sz_p = SSL3_REC_SIZE(mp);
621898Skais 		rec_sz = BE16_TO_U16(rec_sz_p);
622898Skais 
623898Skais 		mp->b_rptr += SSL3_HDR_LEN;
624898Skais 		recend = mp->b_rptr + rec_sz;
625898Skais 		real_recend = recend;
626898Skais 
6274556Svk199839 		/*
6284556Svk199839 		 * Check the assumption that each mblk contains exactly
6294556Svk199839 		 * one complete SSL record. We bail out if the check fails.
6304556Svk199839 		 */
6314556Svk199839 		ASSERT(recend == mp->b_wptr);
6324556Svk199839 		if (recend != mp->b_wptr) {
6334556Svk199839 			desc = decode_error;
6344556Svk199839 			KSSL_COUNTER(internal_errors, 1);
6355850Svk199839 			DTRACE_PROBE(kssl_err__not_complete_record);
6364556Svk199839 			goto makealert;
6374556Svk199839 		}
6384556Svk199839 
639898Skais 		spec = &ssl->spec[KSSL_READ];
640898Skais 		mac_sz = spec->mac_hashsz;
641898Skais 		if (spec->cipher_ctx != 0) {
6421139Skais 
6431139Skais 			/*
6441139Skais 			 * The record length must be a multiple of the
6451139Skais 			 * block size for block ciphers.
6461139Skais 			 * The cipher_bsize is always a power of 2.
6471139Skais 			 */
6481139Skais 			if ((spec->cipher_type == type_block) &&
6491139Skais 			    ((rec_sz & (spec->cipher_bsize - 1)) != 0)) {
6505850Svk199839 				DTRACE_PROBE2(kssl_err__bad_record_size,
6515850Svk199839 				    uint16_t, rec_sz,
6525850Svk199839 				    int, spec->cipher_bsize);
6531139Skais 				KSSL_COUNTER(record_decrypt_failure, 1);
6541139Skais 				mp->b_rptr = recend;
6551139Skais 				desc = decrypt_error;
6561139Skais 				goto makealert;
6571139Skais 			}
6581139Skais 
659898Skais 			cipher_data.cd_format = CRYPTO_DATA_RAW;
660898Skais 			cipher_data.cd_offset = 0;
661898Skais 			cipher_data.cd_length = rec_sz;
662898Skais 			cipher_data.cd_miscdata = NULL;
663898Skais 			cipher_data.cd_raw.iov_base = (char *)mp->b_rptr;
664898Skais 			cipher_data.cd_raw.iov_len = rec_sz;
665898Skais 			error = crypto_decrypt_update(spec->cipher_ctx,
666898Skais 			    &cipher_data, NULL, NULL);
667898Skais 			if (CRYPTO_ERR(error)) {
6685850Svk199839 				DTRACE_PROBE1(
6695850Svk199839 				    kssl_err__crypto_decrypt_update_failed,
6705850Svk199839 				    int, error);
671898Skais 				KSSL_COUNTER(record_decrypt_failure, 1);
672898Skais 				mp->b_rptr = recend;
673898Skais 				desc = decrypt_error;
674898Skais 				goto makealert;
675898Skais 			}
676898Skais 		}
677898Skais 		if (spec->cipher_type == type_block) {
678898Skais 			uint_t pad_sz = recend[-1];
679898Skais 			pad_sz++;
680898Skais 			if (pad_sz + mac_sz > rec_sz) {
6815850Svk199839 				DTRACE_PROBE(kssl_err__pad_mac_bigger);
682898Skais 				mp->b_rptr = recend;
683898Skais 				desc = bad_record_mac;
684898Skais 				goto makealert;
685898Skais 			}
686898Skais 			rec_sz -= pad_sz;
687898Skais 			recend -= pad_sz;
688898Skais 		}
689898Skais 		if (mac_sz != 0) {
690898Skais 			uchar_t hash[MAX_HASH_LEN];
691898Skais 			if (rec_sz < mac_sz) {
6925850Svk199839 				DTRACE_PROBE(kssl_err__pad_smaller_mac);
693898Skais 				mp->b_rptr = real_recend;
694898Skais 				desc = bad_record_mac;
695898Skais 				goto makealert;
696898Skais 			}
697898Skais 			rec_sz -= mac_sz;
698898Skais 			recend -= mac_sz;
699898Skais 			ret = kssl_compute_record_mac(ssl, KSSL_READ,
7005850Svk199839 			    ssl->seq_num[KSSL_READ], content_type,
7015850Svk199839 			    version, mp->b_rptr, rec_sz, hash);
702898Skais 			if (ret != CRYPTO_SUCCESS ||
703898Skais 			    bcmp(hash, recend, mac_sz)) {
7045850Svk199839 				DTRACE_PROBE1(kssl_mblk__MACmismatch_handlerec,
7055850Svk199839 				    mblk_t *, mp);
706898Skais 				mp->b_rptr = real_recend;
707898Skais 				desc = bad_record_mac;
7085850Svk199839 				DTRACE_PROBE(kssl_err__msg_MAC_mismatch);
709898Skais 				KSSL_COUNTER(verify_mac_failure, 1);
710898Skais 				goto makealert;
711898Skais 			}
712898Skais 			ssl->seq_num[KSSL_READ]++;
713898Skais 		}
714898Skais 
715898Skais 		if (ssl->hs_waitstate != idle_handshake) {
7165850Svk199839 			DTRACE_PROBE1(kssl_err__unexpected_msg,
7175850Svk199839 			    SSL3WaitState, ssl->hs_waitstate);
718898Skais 			mp->b_rptr = real_recend;
719898Skais 			desc = unexpected_message;
720898Skais 			goto makealert;
721898Skais 		}
722898Skais 		mp->b_wptr = recend;
723898Skais 
7245850Svk199839 		DTRACE_PROBE1(kssl_mblk__dblk_cooked, mblk_t *, mp);
7254556Svk199839 		KSSL_COUNTER(appdata_record_ins, 1);
7264556Svk199839 
727898Skais 		prevmp = mp;
728898Skais 		mp = mp->b_cont;
729898Skais 	}
730898Skais 
731898Skais 	return (kssl_cmd);
732898Skais 
733898Skais makealert:
734898Skais 	nextmp = mp->b_cont;
735898Skais 	freeb(mp);
736898Skais 	mp = nextmp;
737898Skais 	mutex_enter(&ssl->kssl_lock);
738898Skais 	kssl_send_alert(ssl, alert_fatal, desc);
739898Skais 
740898Skais 	if (ssl->alert_sendbuf == NULL) {
741898Skais 		/* internal memory allocation failure. just return. */
7425850Svk199839 		DTRACE_PROBE(kssl_err__alert_msg_alloc_failed);
743898Skais 		mutex_exit(&ssl->kssl_lock);
744898Skais 
745898Skais 		if (mp) {
746898Skais 			prevmp = NULL;
747898Skais 			goto more;
748898Skais 		}
749898Skais 
750898Skais 		return (KSSL_CMD_NONE);
751898Skais 	}
752898Skais 	kssl_cmd = KSSL_CMD_SEND;
753898Skais sendalert:
754898Skais 	if (*outmp == NULL) {
755898Skais 		*outmp = ssl->alert_sendbuf;
756898Skais 	} else {
757898Skais 		linkb(*outmp, ssl->alert_sendbuf);
758898Skais 	}
759898Skais 	ssl->alert_sendbuf = NULL;
760898Skais 	mutex_exit(&ssl->kssl_lock);
761898Skais 
762898Skais 	if (mp) {
763898Skais 		prevmp = NULL;
764898Skais 		goto more;
765898Skais 	}
766898Skais 
767898Skais 	return (kssl_cmd);
768898Skais }
769898Skais /*
770898Skais  * This is the routine that handles incoming SSL records.
771898Skais  * When called the first time, with a NULL context, this routine expects
772898Skais  * a ClientHello SSL Handshake packet and shall allocate a context
773898Skais  * of a new SSL connection.
774898Skais  * During the rest of the handshake packets, the routine adjusts the
775898Skais  * state of the context according to the record received.
776898Skais  * After the ChangeCipherSpec message is received, the routine first
777898Skais  * decrypts/authenticated the packet using the key materials in the
778898Skais  * connection's context.
779898Skais  * The return code tells the caller what to do with the returned packet.
780898Skais  */
781898Skais static kssl_cmd_t
kssl_handle_any_record(kssl_ctx_t ctx,mblk_t * mp,mblk_t ** decrmp,kssl_callback_t cbfn,void * arg)782898Skais kssl_handle_any_record(kssl_ctx_t ctx, mblk_t *mp, mblk_t **decrmp,
783898Skais     kssl_callback_t cbfn, void *arg)
784898Skais {
785898Skais 	uchar_t *recend, *rec_sz_p;
786898Skais 	uchar_t version[2];
787898Skais 	uchar_t *real_recend, *save_rptr, *save_wptr;
788898Skais 	int rhsz = SSL3_HDR_LEN;
789898Skais 	uint16_t rec_sz;
790898Skais 	int sz;
791898Skais 	int mac_sz;
792898Skais 	SSL3AlertDescription desc;
793898Skais 	SSL3AlertLevel level;
794898Skais 	SSL3ContentType content_type;
795898Skais 	ssl_t *ssl;
796898Skais 	KSSLCipherSpec *spec;
797898Skais 	int error = 0, ret;
798898Skais 
799898Skais 	ASSERT(ctx != NULL);
800898Skais 
801898Skais 	ssl = (ssl_t *)(ctx);
802898Skais 
803898Skais 	*decrmp = NULL;
804898Skais 
805898Skais 	save_rptr = mp->b_rptr;
806898Skais 	save_wptr = mp->b_wptr;
807898Skais 
808898Skais 	ASSERT(MUTEX_HELD(&ssl->kssl_lock));
809898Skais 
810898Skais 	content_type = (SSL3ContentType)mp->b_rptr[0];
811898Skais 	if (content_type == content_handshake_v2) {
812898Skais 		if (ssl->hs_waitstate == wait_client_hello) {
813898Skais 			/* V2 compatible ClientHello */
814898Skais 			if (mp->b_rptr[3] == 0x03 &&
815898Skais 			    (mp->b_rptr[4] == 0x01 ||
8165850Svk199839 			    mp->b_rptr[4] == 0x00)) {
817898Skais 				ssl->major_version = version[0] = mp->b_rptr[3];
818898Skais 				ssl->minor_version = version[1] = mp->b_rptr[4];
819898Skais 			} else {
820898Skais 			/* We don't support "pure" SSLv2 */
8215850Svk199839 				DTRACE_PROBE(kssl_err__no_SSLv2);
82212623SVladimir.Kotal@Sun.COM 				ssl->major_version = mp->b_rptr[3];
82312623SVladimir.Kotal@Sun.COM 				ssl->minor_version = mp->b_rptr[4];
824898Skais 				desc = protocol_version;
825898Skais 				goto sendalert;
826898Skais 			}
827898Skais 		}
828898Skais 		rec_sz = (uint16_t)mp->b_rptr[1];
829898Skais 		rhsz = 2;
830898Skais 	} else {
831898Skais 		ssl->major_version = version[0] = mp->b_rptr[1];
832898Skais 		ssl->minor_version = version[1] = mp->b_rptr[2];
833898Skais 		rec_sz_p = SSL3_REC_SIZE(mp);
834898Skais 		rec_sz = BE16_TO_U16(rec_sz_p);
835898Skais 	}
836898Skais 
837898Skais 	mp->b_rptr += rhsz;
838898Skais 	recend = mp->b_rptr + rec_sz;
839898Skais 	real_recend = recend;
840898Skais 
8414556Svk199839 	/*
8424556Svk199839 	 * Check the assumption that each mblk contains exactly
8434556Svk199839 	 * one complete SSL record. We bail out if the check fails.
8444556Svk199839 	 */
8454556Svk199839 	ASSERT(recend == mp->b_wptr);
8464556Svk199839 	if (recend != mp->b_wptr) {
8475850Svk199839 		DTRACE_PROBE3(kssl_mblk__handle_any_record_recszerr,
8485850Svk199839 		    mblk_t *, mp, int, rhsz, int, rec_sz);
8495850Svk199839 		DTRACE_PROBE(kssl_err__record_size);
8504556Svk199839 		desc = decode_error;
8514556Svk199839 		KSSL_COUNTER(internal_errors, 1);
8524556Svk199839 		goto sendalert;
8534556Svk199839 	}
8544556Svk199839 
855898Skais 	spec = &ssl->spec[KSSL_READ];
856898Skais 	mac_sz = spec->mac_hashsz;
857898Skais 	if (spec->cipher_ctx != 0) {
8581139Skais 		/*
8591139Skais 		 * The record length must be a multiple of the
8601139Skais 		 * block size for block ciphers.
8611139Skais 		 */
8621139Skais 		if ((spec->cipher_type == type_block) &&
8631139Skais 		    ((rec_sz & (spec->cipher_bsize - 1)) != 0)) {
8645850Svk199839 			DTRACE_PROBE2(kssl_err__bad_record_size,
8655850Svk199839 			    uint16_t, rec_sz, int, spec->cipher_bsize);
8661139Skais 			KSSL_COUNTER(record_decrypt_failure, 1);
8671139Skais 			mp->b_rptr = recend;
8681139Skais 			desc = decrypt_error;
8691139Skais 			goto sendalert;
8701139Skais 		}
8711139Skais 
872898Skais 		spec->cipher_data.cd_length = rec_sz;
873898Skais 		spec->cipher_data.cd_raw.iov_base = (char *)mp->b_rptr;
874898Skais 		spec->cipher_data.cd_raw.iov_len = rec_sz;
875898Skais 		error = crypto_decrypt_update(spec->cipher_ctx,
876898Skais 		    &spec->cipher_data, NULL, NULL);
877898Skais 		if (CRYPTO_ERR(error)) {
8785850Svk199839 			DTRACE_PROBE1(kssl_err__crypto_decrypt_update_failed,
8795850Svk199839 			    int, error);
880898Skais 			KSSL_COUNTER(record_decrypt_failure, 1);
881898Skais 			mp->b_rptr = recend;
882898Skais 			desc = decrypt_error;
883898Skais 			goto sendalert;
884898Skais 		}
885898Skais 	}
886898Skais 	if (spec->cipher_type == type_block) {
887898Skais 		uint_t pad_sz = recend[-1];
888898Skais 		pad_sz++;
889898Skais 		if (pad_sz + mac_sz > rec_sz) {
8905850Svk199839 			DTRACE_PROBE2(kssl_err__pad_mac_mismatch,
8915850Svk199839 			    int, pad_sz, int, mac_sz);
892898Skais 			mp->b_rptr = recend;
893898Skais 			desc = bad_record_mac;
894898Skais 			goto sendalert;
895898Skais 		}
896898Skais 		rec_sz -= pad_sz;
897898Skais 		recend -= pad_sz;
898898Skais 	}
899898Skais 	if (mac_sz != 0) {
900898Skais 		uchar_t hash[MAX_HASH_LEN];
901898Skais 		if (rec_sz < mac_sz) {
9025850Svk199839 			DTRACE_PROBE1(kssl_err__mac_size_too_big,
9035850Svk199839 			    int, mac_sz);
904898Skais 			mp->b_rptr = real_recend;
905898Skais 			desc = bad_record_mac;
906898Skais 			goto sendalert;
907898Skais 		}
908898Skais 		rec_sz -= mac_sz;
909898Skais 		recend -= mac_sz;
910898Skais 		ret = kssl_compute_record_mac(ssl, KSSL_READ,
9115850Svk199839 		    ssl->seq_num[KSSL_READ], content_type,
9125850Svk199839 		    version, mp->b_rptr, rec_sz, hash);
913898Skais 		if (ret != CRYPTO_SUCCESS ||
914898Skais 		    bcmp(hash, recend, mac_sz)) {
9155850Svk199839 			DTRACE_PROBE1(kssl_mblk__MACmismatch_anyrecord,
9165850Svk199839 			    mblk_t *, mp);
917898Skais 			mp->b_rptr = real_recend;
918898Skais 			desc = bad_record_mac;
9195850Svk199839 			DTRACE_PROBE(kssl_err__msg_MAC_mismatch);
920898Skais 			KSSL_COUNTER(verify_mac_failure, 1);
921898Skais 			goto sendalert;
922898Skais 		}
923898Skais 		ssl->seq_num[KSSL_READ]++;
9245850Svk199839 		DTRACE_PROBE1(kssl_mblk__after_compute_MAC,
9255850Svk199839 		    mblk_t *, mp);
926898Skais 	}
927898Skais 
928898Skais 	switch (content_type) {
929898Skais 	case content_handshake:
930898Skais 		do {
9315850Svk199839 			DTRACE_PROBE1(kssl_mblk__content_handshake_cycle,
9325850Svk199839 			    mblk_t *, mp);
933898Skais 			if (error != 0 ||
934898Skais 			    /* ignore client renegotiation for now */
935898Skais 			    ssl->hs_waitstate == idle_handshake) {
936898Skais 				mp->b_rptr = recend;
93712381SVladimir.Kotal@Sun.COM 				DTRACE_PROBE(kssl_renegotiation_request);
938898Skais 			}
939898Skais 			if (mp->b_rptr == recend) {
940898Skais 				mp->b_rptr = real_recend;
941898Skais 				if (error != 0) {
942898Skais 					goto error;
943898Skais 				}
944898Skais 				freeb(mp);
945898Skais 
946898Skais 				if (ssl->hs_waitstate == wait_client_key_done)
947898Skais 					return (KSSL_CMD_QUEUED);
948898Skais 
949898Skais 				return ((ssl->handshake_sendbuf != NULL) ?
950898Skais 				    KSSL_CMD_SEND : KSSL_CMD_NONE);
951898Skais 			}
952898Skais 			if (ssl->msg.state < MSG_BODY) {
953898Skais 				if (ssl->msg.state == MSG_INIT) {
954898Skais 					ssl->msg.type =
955898Skais 					    (SSL3HandshakeType)*mp->b_rptr++;
956898Skais 					ssl->msg.state = MSG_INIT_LEN;
957898Skais 				}
958898Skais 				if (ssl->msg.state == MSG_INIT_LEN) {
959898Skais 					int msglenb =
960898Skais 					    ssl->msg.msglen_bytes;
961898Skais 					int msglen = ssl->msg.msglen;
962898Skais 					while (mp->b_rptr < recend &&
963898Skais 					    msglenb < 3) {
964898Skais 						msglen = (msglen << 8) +
965898Skais 						    (uint_t)(*mp->b_rptr++);
966898Skais 						msglenb++;
967898Skais 					}
968898Skais 					ssl->msg.msglen_bytes = msglenb;
969898Skais 					ssl->msg.msglen = msglen;
970898Skais 					if (msglenb == 3) {
971898Skais 						ssl->msg.state = MSG_BODY;
972898Skais 					}
973898Skais 				}
974898Skais 				if (mp->b_rptr == recend) {
975898Skais 					mp->b_rptr = real_recend;
976898Skais 					freeb(mp);
977898Skais 					return (KSSL_CMD_NONE);
978898Skais 				}
979898Skais 			}
980898Skais 			ASSERT(ssl->msg.state == MSG_BODY);
981898Skais 
982898Skais 			sz = recend - mp->b_rptr;
983898Skais 
984898Skais 			if (ssl->msg.head == NULL &&
985898Skais 			    ssl->msg.msglen <= sz) {
986898Skais 				continue;
987898Skais 			}
988898Skais 			if (ssl->msg.head != NULL) {
989898Skais 				sz += msgdsize(ssl->msg.head);
990898Skais 				if (ssl->msg.msglen <= sz) {
991898Skais 					ssl->msg.tail->b_cont = mp;
992898Skais 					mp = ssl->msg.head;
993898Skais 					ssl->sslcnt = 100;
994898Skais 					ssl->msg.head = NULL;
995898Skais 					ssl->msg.tail = NULL;
996898Skais 					if (pullupmsg(mp, -1)) {
997898Skais 						recend = mp->b_rptr + sz;
998898Skais 						ASSERT(recend <= mp->b_wptr);
999898Skais 						continue;
1000898Skais 					}
1001898Skais 					mp->b_rptr = real_recend;
1002898Skais 					error = ENOMEM;
1003898Skais 					KSSL_COUNTER(alloc_fails, 1);
1004898Skais 					goto error;
1005898Skais 				}
1006898Skais 			}
1007898Skais 
1008898Skais 			mp->b_wptr = recend;
1009898Skais 
1010898Skais 			if (ssl->msg.head == NULL) {
1011898Skais 				ssl->msg.head = mp;
1012898Skais 				ssl->msg.tail = mp;
1013898Skais 				return (KSSL_CMD_NONE);
1014898Skais 			} else {
1015898Skais 				ssl->msg.tail->b_cont = mp;
1016898Skais 				ssl->msg.tail = mp;
1017898Skais 				return (KSSL_CMD_NONE);
1018898Skais 			}
1019898Skais 		} while (kssl_handle_handshake_message(ssl, mp, &error, cbfn,
1020898Skais 		    arg));
1021898Skais 		if (error == SSL_MISS) {
1022898Skais 			mp->b_rptr = save_rptr;
1023898Skais 			mp->b_wptr = save_wptr;
1024898Skais 			KSSL_COUNTER(fallback_connections, 1);
1025898Skais 			return (KSSL_CMD_NOT_SUPPORTED);
1026898Skais 		}
1027898Skais 		if (ssl->hs_waitstate == wait_client_key_done) {
1028898Skais 			return (KSSL_CMD_QUEUED);
1029898Skais 		} else {
1030898Skais 			return (KSSL_CMD_NONE);
1031898Skais 		}
1032898Skais 	case content_alert:
10335850Svk199839 		DTRACE_PROBE1(kssl_mblk__content_alert, mblk_t *, mp);
1034898Skais 		if (rec_sz != 2) {
10355850Svk199839 			DTRACE_PROBE(kssl_err__illegal_param);
1036898Skais 			mp->b_rptr = real_recend;
1037898Skais 			desc = illegal_parameter;
1038898Skais 			goto sendalert;
1039898Skais 		} else {
1040898Skais 			level = *mp->b_rptr++;
1041898Skais 			desc = *mp->b_rptr++;
1042898Skais 			mp->b_rptr = real_recend;
1043898Skais 			if (level != alert_warning || desc != close_notify) {
1044898Skais 				if (ssl->sid.cached == B_TRUE) {
1045898Skais 					kssl_uncache_sid(&ssl->sid,
1046898Skais 					    ssl->kssl_entry);
1047898Skais 				}
10485850Svk199839 				DTRACE_PROBE2(kssl_err__bad_content_alert,
10495850Svk199839 				    SSL3AlertLevel, level,
10505850Svk199839 				    SSL3AlertDescription, desc);
1051898Skais 				ssl->fatal_alert = B_TRUE;
1052898Skais 				error = EBADMSG;
1053898Skais 				goto error;
1054898Skais 			} else {
1055*12696SVladimir.Kotal@Sun.COM 				ssl->close_notify_clnt = B_TRUE;
1056898Skais 				ssl->activeinput = B_FALSE;
1057898Skais 				freeb(mp);
1058898Skais 				return (KSSL_CMD_NONE);
1059898Skais 			}
1060898Skais 		}
1061898Skais 	case content_change_cipher_spec:
10625850Svk199839 		DTRACE_PROBE1(kssl_mblk__change_cipher_spec,
10635850Svk199839 		    mblk_t *, mp);
1064898Skais 		if (ssl->hs_waitstate != wait_change_cipher) {
1065898Skais 			desc = unexpected_message;
1066898Skais 		} else if (rec_sz != 1 || *mp->b_rptr != 1) {
1067898Skais 			desc = illegal_parameter;
1068898Skais 		} else {
1069898Skais 			mp->b_rptr = real_recend;
1070898Skais 			ssl->hs_waitstate = wait_finished;
1071898Skais 			ssl->seq_num[KSSL_READ] = 0;
1072898Skais 			if ((error = kssl_spec_init(ssl, KSSL_READ)) != 0) {
10735850Svk199839 				DTRACE_PROBE1(kssl_err__kssl_spec_init_error,
10745850Svk199839 				    int, error);
1075898Skais 				goto error;
1076898Skais 			}
1077898Skais 			ssl->activeinput = B_FALSE;
1078898Skais 			freeb(mp);
1079898Skais 			return (KSSL_CMD_NONE);
1080898Skais 		}
1081898Skais 		mp->b_rptr = real_recend;
10825850Svk199839 		DTRACE_PROBE(kssl_err__change_cipher_spec);
1083898Skais 		goto sendalert;
1084898Skais 
1085898Skais 	case content_application_data:
10865850Svk199839 		DTRACE_PROBE1(kssl_mblk__content_app_data,
10875850Svk199839 		    mblk_t *, mp);
1088898Skais 		if (ssl->hs_waitstate != idle_handshake) {
10895850Svk199839 			DTRACE_PROBE(kssl_err__content_app_data);
1090898Skais 			mp->b_rptr = real_recend;
1091898Skais 			desc = unexpected_message;
1092898Skais 			goto sendalert;
1093898Skais 		}
1094898Skais 		mp->b_wptr = recend;
1095898Skais 		*decrmp = mp;
1096898Skais 		ssl->activeinput = B_FALSE;
1097898Skais 		return (KSSL_CMD_DELIVER_PROXY);
1098898Skais 
1099898Skais 	case content_handshake_v2:
11005850Svk199839 		DTRACE_PROBE1(kssl_mblk__content_handshake_v2,
11015850Svk199839 		    mblk_t *, mp);
1102898Skais 		error = kssl_handle_v2client_hello(ssl, mp, rec_sz);
1103898Skais 		if (error == SSL_MISS) {
1104898Skais 			mp->b_rptr = save_rptr;
1105898Skais 			mp->b_wptr = save_wptr;
1106898Skais 			KSSL_COUNTER(fallback_connections, 1);
1107898Skais 			return (KSSL_CMD_NOT_SUPPORTED);
1108898Skais 		} else if (error != 0) {
11095850Svk199839 			DTRACE_PROBE(kssl_err__v2client_hello_failed);
1110898Skais 			goto error;
1111898Skais 		}
1112898Skais 		freeb(mp);
1113898Skais 		return (KSSL_CMD_SEND);
1114898Skais 	default:
11155850Svk199839 		DTRACE_PROBE1(kssl_mblk__unexpected_msg,
11165850Svk199839 		    mblk_t *, mp);
1117898Skais 		mp->b_rptr = real_recend;
1118898Skais 		desc = unexpected_message;
1119898Skais 		break;
1120898Skais 	}
1121898Skais 
1122898Skais sendalert:
1123898Skais 	kssl_send_alert(ssl, alert_fatal, desc);
1124898Skais 	*decrmp = ssl->alert_sendbuf;
1125898Skais 	ssl->alert_sendbuf = NULL;
1126898Skais 	freeb(mp);
1127898Skais 	return ((*decrmp != NULL) ? KSSL_CMD_SEND : KSSL_CMD_NONE);
1128898Skais error:
1129898Skais 	freeb(mp);
1130898Skais 	return (KSSL_CMD_NONE);
1131898Skais }
1132898Skais 
1133898Skais /*
1134898Skais  * Initialize the context of an SSL connection, coming to the specified
113510520SBhargava.Yenduri@Sun.COM  * address. The ssl structure is returned held.
1136898Skais  */
1137898Skais kssl_status_t
kssl_init_context(kssl_ent_t kssl_ent,struct sockaddr * addr,int mss,kssl_ctx_t * kssl_ctxp)113812644SAnders.Persson@Sun.COM kssl_init_context(kssl_ent_t kssl_ent, struct sockaddr *addr, int mss,
113912644SAnders.Persson@Sun.COM     kssl_ctx_t *kssl_ctxp)
1140898Skais {
1141898Skais 	ssl_t *ssl = kmem_cache_alloc(kssl_cache, KM_NOSLEEP);
114212644SAnders.Persson@Sun.COM 	struct sockaddr_in *sin = (struct sockaddr_in *)addr;
1143898Skais 
1144898Skais 	if (ssl == NULL) {
1145898Skais 		return (KSSL_STS_ERR);
1146898Skais 	}
1147898Skais 
1148898Skais 	bzero(ssl, sizeof (ssl_t));
1149898Skais 
1150898Skais 	ssl->kssl_entry = (kssl_entry_t *)kssl_ent;
1151898Skais 	KSSL_ENTRY_REFHOLD(ssl->kssl_entry);
1152898Skais 
115312644SAnders.Persson@Sun.COM 	if (sin->sin_family == AF_INET) {
115412644SAnders.Persson@Sun.COM 		IN6_IPADDR_TO_V4MAPPED(sin->sin_addr.s_addr, &ssl->faddr);
115510520SBhargava.Yenduri@Sun.COM 	} else {
115612644SAnders.Persson@Sun.COM 		/* struct assignment */
115712644SAnders.Persson@Sun.COM 		ssl->faddr = ((struct sockaddr_in6 *)addr)->sin6_addr;
115810520SBhargava.Yenduri@Sun.COM 	}
1159898Skais 	ssl->tcp_mss = mss;
1160898Skais 	ssl->sendalert_level = alert_warning;
1161898Skais 	ssl->sendalert_desc = close_notify;
1162898Skais 	ssl->sid.cached = B_FALSE;
1163898Skais 
1164898Skais 	*kssl_ctxp = (kssl_ctx_t)ssl;
1165898Skais 	return (KSSL_STS_OK);
1166898Skais }
1167898Skais 
116812644SAnders.Persson@Sun.COM void
kssl_set_mss(kssl_ctx_t ctx,uint32_t mss)116912644SAnders.Persson@Sun.COM kssl_set_mss(kssl_ctx_t ctx, uint32_t mss)
117012644SAnders.Persson@Sun.COM {
117112644SAnders.Persson@Sun.COM 	ssl_t *ssl = (ssl_t *)ctx;
117212644SAnders.Persson@Sun.COM 	ssl->tcp_mss = mss;
117312644SAnders.Persson@Sun.COM }
117412644SAnders.Persson@Sun.COM 
1175898Skais /*
1176898Skais  * Builds SSL records out of the chain of mblks, and returns it.
117710520SBhargava.Yenduri@Sun.COM  * Takes a copy of the message before encrypting it if it has another
1178898Skais  * reference.
1179898Skais  * In case of failure, NULL is returned, and the message will be
1180898Skais  * freed by the caller.
1181898Skais  * A NULL mp means a close_notify is requested.
1182898Skais  */
1183898Skais mblk_t *
kssl_build_record(kssl_ctx_t ctx,mblk_t * mp)1184898Skais kssl_build_record(kssl_ctx_t ctx, mblk_t *mp)
1185898Skais {
1186898Skais 	ssl_t *ssl = (ssl_t *)ctx;
1187898Skais 	mblk_t *retmp = mp, *bp = mp, *prevbp = mp, *copybp;
1188898Skais 
1189898Skais 	ASSERT(ssl != NULL);
1190*12696SVladimir.Kotal@Sun.COM 
1191*12696SVladimir.Kotal@Sun.COM 	/*
1192*12696SVladimir.Kotal@Sun.COM 	 * Produce new close_notify message. This is necessary to perform
1193*12696SVladimir.Kotal@Sun.COM 	 * proper cleanup w.r.t. SSL protocol spec by sending close_notify SSL
1194*12696SVladimir.Kotal@Sun.COM 	 * alert record if running with KSSL proxy.
1195*12696SVladimir.Kotal@Sun.COM 	 * This should be done prior to sending the FIN so the client side can
1196*12696SVladimir.Kotal@Sun.COM 	 * attempt to do graceful cleanup. Ideally, we should wait for client's
1197*12696SVladimir.Kotal@Sun.COM 	 * close_notify but not all clients send it which would hang the
1198*12696SVladimir.Kotal@Sun.COM 	 * connection. This way of closing the SSL session (Incomplete Close)
1199*12696SVladimir.Kotal@Sun.COM 	 * prevents truncation attacks for protocols without end-of-data
1200*12696SVladimir.Kotal@Sun.COM 	 * markers (as opposed to the Premature Close).
1201*12696SVladimir.Kotal@Sun.COM 	 * Checking the close_notify_srvr flag will prevent from sending the
1202*12696SVladimir.Kotal@Sun.COM 	 * close_notify message twice in case of duplicate shutdown() calls.
1203*12696SVladimir.Kotal@Sun.COM 	 */
1204*12696SVladimir.Kotal@Sun.COM 	if (mp == NULL && !ssl->close_notify_srvr) {
1205*12696SVladimir.Kotal@Sun.COM 		kssl_send_alert(ssl, alert_warning, close_notify);
1206*12696SVladimir.Kotal@Sun.COM 		if (ssl->alert_sendbuf == NULL)
1207*12696SVladimir.Kotal@Sun.COM 			return (NULL);
1208*12696SVladimir.Kotal@Sun.COM 		mp = bp = retmp = prevbp = ssl->alert_sendbuf;
1209*12696SVladimir.Kotal@Sun.COM 		ssl->alert_sendbuf = NULL;
1210*12696SVladimir.Kotal@Sun.COM 		ssl->close_notify_srvr = B_TRUE;
1211*12696SVladimir.Kotal@Sun.COM 	}
1212*12696SVladimir.Kotal@Sun.COM 
1213898Skais 	ASSERT(mp != NULL);
1214*12696SVladimir.Kotal@Sun.COM 	ASSERT(bp != NULL);
1215898Skais 
1216898Skais 	do {
1217898Skais 		if (DB_REF(bp) > 1) {
1218898Skais 			/*
1219898Skais 			 * Fortunately copyb() preserves the offset,
122010520SBhargava.Yenduri@Sun.COM 			 * tail space and alignment so the copy is
1221898Skais 			 * ready to be made an SSL record.
1222898Skais 			 */
1223898Skais 			if ((copybp = copyb(bp)) == NULL)
1224898Skais 				return (NULL);
1225898Skais 
1226898Skais 			copybp->b_cont = bp->b_cont;
1227898Skais 			if (bp == mp) {
1228898Skais 				retmp = copybp;
1229898Skais 			} else {
1230898Skais 				prevbp->b_cont = copybp;
1231898Skais 			}
1232898Skais 			freeb(bp);
1233898Skais 			bp = copybp;
1234898Skais 		}
1235898Skais 
1236898Skais 		if (kssl_build_single_record(ssl, bp) != KSSL_STS_OK)
1237898Skais 			return (NULL);
1238898Skais 
1239898Skais 		prevbp = bp;
1240898Skais 		bp = bp->b_cont;
1241898Skais 	} while (bp != NULL);
1242898Skais 
1243898Skais 	return (retmp);
1244898Skais }
1245898Skais 
1246898Skais /*
1247*12696SVladimir.Kotal@Sun.COM  * Builds a single SSL record by prepending SSL header (optional) and performing
1248*12696SVladimir.Kotal@Sun.COM  * encryption and MAC. The encryption of the record is done in-line.
1249*12696SVladimir.Kotal@Sun.COM  * Expects an mblk with associated dblk's base to have space for the SSL header
1250*12696SVladimir.Kotal@Sun.COM  * or an mblk which already has the header present. In both cases it presumes
1251*12696SVladimir.Kotal@Sun.COM  * that the mblk's dblk limit has space for the MAC + padding.
1252*12696SVladimir.Kotal@Sun.COM  * If the close_notify_srvr flag is set it is presumed that the mblk already
1253*12696SVladimir.Kotal@Sun.COM  * contains SSL header in which case only the record length field will be
1254*12696SVladimir.Kotal@Sun.COM  * adjusted with the MAC/padding size.
1255898Skais  */
1256898Skais static kssl_status_t
kssl_build_single_record(ssl_t * ssl,mblk_t * mp)1257898Skais kssl_build_single_record(ssl_t *ssl, mblk_t *mp)
1258898Skais {
1259898Skais 	int len;
126010520SBhargava.Yenduri@Sun.COM 	int reclen;
1261898Skais 	uchar_t *recstart, *versionp;
1262898Skais 	KSSLCipherSpec *spec;
1263898Skais 	int mac_sz;
126410520SBhargava.Yenduri@Sun.COM 	int pad_sz;
1265898Skais 
1266898Skais 	spec = &ssl->spec[KSSL_WRITE];
1267898Skais 	mac_sz = spec->mac_hashsz;
1268898Skais 
1269898Skais 	ASSERT(DB_REF(mp) == 1);
1270*12696SVladimir.Kotal@Sun.COM 	/* The dblk must always have space for the padding and MAC suffix. */
1271*12696SVladimir.Kotal@Sun.COM 	ASSERT(mp->b_datap->db_lim - mp->b_wptr >= mac_sz + spec->cipher_bsize);
1272898Skais 
1273*12696SVladimir.Kotal@Sun.COM 	/* kssl_send_alert() constructs the SSL header by itself. */
1274*12696SVladimir.Kotal@Sun.COM 	if (!ssl->close_notify_srvr)
1275*12696SVladimir.Kotal@Sun.COM 		len = MBLKL(mp) - SSL3_HDR_LEN;
1276*12696SVladimir.Kotal@Sun.COM 	else
1277*12696SVladimir.Kotal@Sun.COM 		len = MBLKL(mp);
1278898Skais 
1279898Skais 	ASSERT(len > 0);
1280898Skais 
1281898Skais 	mutex_enter(&ssl->kssl_lock);
1282898Skais 
1283*12696SVladimir.Kotal@Sun.COM 	recstart = mp->b_rptr;
1284*12696SVladimir.Kotal@Sun.COM 	if (!ssl->close_notify_srvr) {
1285*12696SVladimir.Kotal@Sun.COM 		/* The dblk must have space for the SSL header prefix. */
1286*12696SVladimir.Kotal@Sun.COM 		ASSERT(mp->b_rptr - mp->b_datap->db_base >= SSL3_HDR_LEN);
1287*12696SVladimir.Kotal@Sun.COM 		recstart = mp->b_rptr = mp->b_rptr - SSL3_HDR_LEN;
1288*12696SVladimir.Kotal@Sun.COM 		recstart[0] = content_application_data;
1289*12696SVladimir.Kotal@Sun.COM 		recstart[1] = ssl->major_version;
1290*12696SVladimir.Kotal@Sun.COM 		recstart[2] = ssl->minor_version;
1291*12696SVladimir.Kotal@Sun.COM 	}
1292898Skais 	versionp = &recstart[1];
1293898Skais 
1294898Skais 	reclen = len + mac_sz;
1295898Skais 	if (spec->cipher_type == type_block) {
1296898Skais 		pad_sz = spec->cipher_bsize -
1297898Skais 		    (reclen & (spec->cipher_bsize - 1));
1298898Skais 		ASSERT(reclen + pad_sz <=
1299898Skais 		    SSL3_MAX_RECORD_LENGTH);
1300898Skais 		reclen += pad_sz;
1301898Skais 	}
1302898Skais 	recstart[3] = (reclen >> 8) & 0xff;
1303898Skais 	recstart[4] = reclen & 0xff;
1304898Skais 
1305*12696SVladimir.Kotal@Sun.COM 	if (kssl_mac_encrypt_record(ssl, recstart[0], versionp,
1306898Skais 	    recstart, mp) != 0) {
1307898Skais 		/* Do we need an internal_error Alert here? */
1308898Skais 		mutex_exit(&ssl->kssl_lock);
1309898Skais 		return (KSSL_STS_ERR);
1310898Skais 	}
1311898Skais 
1312*12696SVladimir.Kotal@Sun.COM 	/* Alert messages are accounted in kssl_send_alert(). */
1313*12696SVladimir.Kotal@Sun.COM 	if (recstart[0] == content_application_data)
1314*12696SVladimir.Kotal@Sun.COM 		KSSL_COUNTER(appdata_record_outs, 1);
1315898Skais 	mutex_exit(&ssl->kssl_lock);
1316898Skais 	return (KSSL_STS_OK);
1317898Skais }
1318