sctp_auth.c revision 202449
1178676Ssam/*-
2198429Srpaulo * Copyright (c) 2001-2008, by Cisco Systems, Inc. All rights reserved.
3178676Ssam *
4178676Ssam * Redistribution and use in source and binary forms, with or without
5178676Ssam * modification, are permitted provided that the following conditions are met:
6178676Ssam *
7178676Ssam * a) Redistributions of source code must retain the above copyright notice,
8178676Ssam *   this list of conditions and the following disclaimer.
9178676Ssam *
10178676Ssam * b) Redistributions in binary form must reproduce the above copyright
11178676Ssam *    notice, this list of conditions and the following disclaimer in
12178676Ssam *   the documentation and/or other materials provided with the distribution.
13178676Ssam *
14178676Ssam * c) Neither the name of Cisco Systems, Inc. nor the names of its
15178676Ssam *    contributors may be used to endorse or promote products derived
16178676Ssam *    from this software without specific prior written permission.
17178676Ssam *
18178676Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19178676Ssam * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20178676Ssam * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21178676Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22201209Srpaulo * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23201209Srpaulo * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24178676Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25178676Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26178676Ssam * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27178676Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28178676Ssam * THE POSSIBILITY OF SUCH DAMAGE.
29178676Ssam */
30178676Ssam
31178676Ssam#include <sys/cdefs.h>
32178676Ssam__FBSDID("$FreeBSD: head/sys/netinet/sctp_auth.c 202449 2010-01-16 20:04:17Z tuexen $");
33178676Ssam
34178676Ssam#include <netinet/sctp_os.h>
35178676Ssam#include <netinet/sctp.h>
36178676Ssam#include <netinet/sctp_header.h>
37178676Ssam#include <netinet/sctp_pcb.h>
38178676Ssam#include <netinet/sctp_var.h>
39178676Ssam#include <netinet/sctp_sysctl.h>
40178676Ssam#include <netinet/sctputil.h>
41178676Ssam#include <netinet/sctp_indata.h>
42178676Ssam#include <netinet/sctp_output.h>
43178676Ssam#include <netinet/sctp_auth.h>
44178676Ssam
45178676Ssam#ifdef SCTP_DEBUG
46178676Ssam#define SCTP_AUTH_DEBUG		(SCTP_BASE_SYSCTL(sctp_debug_on) & SCTP_DEBUG_AUTH1)
47178676Ssam#define SCTP_AUTH_DEBUG2	(SCTP_BASE_SYSCTL(sctp_debug_on) & SCTP_DEBUG_AUTH2)
48178676Ssam#endif				/* SCTP_DEBUG */
49178676Ssam
50178676Ssam
51178676Ssamvoid
52178676Ssamsctp_clear_chunklist(sctp_auth_chklist_t * chklist)
53178676Ssam{
54178676Ssam	bzero(chklist, sizeof(*chklist));
55178676Ssam	/* chklist->num_chunks = 0; */
56178676Ssam}
57178676Ssam
58178676Ssamsctp_auth_chklist_t *
59178676Ssamsctp_alloc_chunklist(void)
60178676Ssam{
61178676Ssam	sctp_auth_chklist_t *chklist;
62178676Ssam
63178676Ssam	SCTP_MALLOC(chklist, sctp_auth_chklist_t *, sizeof(*chklist),
64178676Ssam	    SCTP_M_AUTH_CL);
65178676Ssam	if (chklist == NULL) {
66178676Ssam		SCTPDBG(SCTP_DEBUG_AUTH1, "sctp_alloc_chunklist: failed to get memory!\n");
67178676Ssam	} else {
68178676Ssam		sctp_clear_chunklist(chklist);
69178676Ssam	}
70206358Srpaulo	return (chklist);
71178676Ssam}
72178676Ssam
73178676Ssamvoid
74178676Ssamsctp_free_chunklist(sctp_auth_chklist_t * list)
75220723Sbschmidt{
76220723Sbschmidt	if (list != NULL)
77220723Sbschmidt		SCTP_FREE(list, SCTP_M_AUTH_CL);
78220723Sbschmidt}
79220723Sbschmidt
80220723Sbschmidtsctp_auth_chklist_t *
81220895Sbschmidtsctp_copy_chunklist(sctp_auth_chklist_t * list)
82220895Sbschmidt{
83220895Sbschmidt	sctp_auth_chklist_t *new_list;
84220895Sbschmidt
85220895Sbschmidt	if (list == NULL)
86220895Sbschmidt		return (NULL);
87220895Sbschmidt
88220895Sbschmidt	/* get a new list */
89220895Sbschmidt	new_list = sctp_alloc_chunklist();
90220895Sbschmidt	if (new_list == NULL)
91220895Sbschmidt		return (NULL);
92220895Sbschmidt	/* copy it */
93220895Sbschmidt	bcopy(list, new_list, sizeof(*new_list));
94220895Sbschmidt
95221634Sbschmidt	return (new_list);
96220895Sbschmidt}
97220895Sbschmidt
98221634Sbschmidt
99220895Sbschmidt/*
100220895Sbschmidt * add a chunk to the required chunks list
101220895Sbschmidt */
102220895Sbschmidtint
103220895Sbschmidtsctp_auth_add_chunk(uint8_t chunk, sctp_auth_chklist_t * list)
104220895Sbschmidt{
105220895Sbschmidt	if (list == NULL)
106220895Sbschmidt		return (-1);
107220895Sbschmidt
108220723Sbschmidt	/* is chunk restricted? */
109220723Sbschmidt	if ((chunk == SCTP_INITIATION) ||
110220723Sbschmidt	    (chunk == SCTP_INITIATION_ACK) ||
111178676Ssam	    (chunk == SCTP_SHUTDOWN_COMPLETE) ||
112178676Ssam	    (chunk == SCTP_AUTHENTICATION)) {
113220728Sbschmidt		return (-1);
114220728Sbschmidt	}
115206477Sbschmidt	if (list->chunks[chunk] == 0) {
116220723Sbschmidt		list->chunks[chunk] = 1;
117178676Ssam		list->num_chunks++;
118178676Ssam		SCTPDBG(SCTP_DEBUG_AUTH1,
119178676Ssam		    "SCTP: added chunk %u (0x%02x) to Auth list\n",
120178676Ssam		    chunk, chunk);
121178676Ssam	}
122206474Sbschmidt	return (0);
123220723Sbschmidt}
124220723Sbschmidt
125220723Sbschmidt/*
126206477Sbschmidt * delete a chunk from the required chunks list
127206477Sbschmidt */
128206477Sbschmidtint
129206477Sbschmidtsctp_auth_delete_chunk(uint8_t chunk, sctp_auth_chklist_t * list)
130206474Sbschmidt{
131178676Ssam	if (list == NULL)
132220691Sbschmidt		return (-1);
133178676Ssam
134206477Sbschmidt	/* is chunk restricted? */
135206477Sbschmidt	if ((chunk == SCTP_ASCONF) ||
136206477Sbschmidt	    (chunk == SCTP_ASCONF_ACK)) {
137206477Sbschmidt		return (-1);
138206477Sbschmidt	}
139206477Sbschmidt	if (list->chunks[chunk] == 1) {
140206477Sbschmidt		list->chunks[chunk] = 0;
141206477Sbschmidt		list->num_chunks--;
142206477Sbschmidt		SCTPDBG(SCTP_DEBUG_AUTH1,
143206477Sbschmidt		    "SCTP: deleted chunk %u (0x%02x) from Auth list\n",
144206477Sbschmidt		    chunk, chunk);
145206477Sbschmidt	}
146178676Ssam	return (0);
147206477Sbschmidt}
148206477Sbschmidt
149206477Sbschmidtsize_t
150206477Sbschmidtsctp_auth_get_chklist_size(const sctp_auth_chklist_t * list)
151198429Srpaulo{
152206477Sbschmidt	if (list == NULL)
153206477Sbschmidt		return (0);
154206477Sbschmidt	else
155206474Sbschmidt		return (list->num_chunks);
156206474Sbschmidt}
157206474Sbschmidt
158220726Sbschmidt/*
159220723Sbschmidt * set the default list of chunks requiring AUTH
160220723Sbschmidt */
161220723Sbschmidtvoid
162220723Sbschmidtsctp_auth_set_default_chunks(sctp_auth_chklist_t * list)
163220726Sbschmidt{
164206477Sbschmidt	(void)sctp_auth_add_chunk(SCTP_ASCONF, list);
165206477Sbschmidt	(void)sctp_auth_add_chunk(SCTP_ASCONF_ACK, list);
166198429Srpaulo}
167220715Sbschmidt
168206477Sbschmidt/*
169206477Sbschmidt * return the current number and list of required chunks caller must
170220667Sbschmidt * guarantee ptr has space for up to 256 bytes
171206477Sbschmidt */
172198429Srpauloint
173206477Sbschmidtsctp_serialize_auth_chunks(const sctp_auth_chklist_t * list, uint8_t * ptr)
174178676Ssam{
175206477Sbschmidt	int i, count = 0;
176201209Srpaulo
177220674Sbschmidt	if (list == NULL)
178220674Sbschmidt		return (0);
179206477Sbschmidt
180198429Srpaulo	for (i = 0; i < 256; i++) {
181206477Sbschmidt		if (list->chunks[i] != 0) {
182198429Srpaulo			*ptr++ = i;
183206477Sbschmidt			count++;
184198429Srpaulo		}
185206477Sbschmidt	}
186198429Srpaulo	return (count);
187221651Sbschmidt}
188206477Sbschmidt
189206477Sbschmidtint
190206477Sbschmidtsctp_pack_auth_chunks(const sctp_auth_chklist_t * list, uint8_t * ptr)
191206477Sbschmidt{
192206477Sbschmidt	int i, size = 0;
193206477Sbschmidt
194206477Sbschmidt	if (list == NULL)
195198429Srpaulo		return (0);
196206477Sbschmidt
197198429Srpaulo	if (list->num_chunks <= 32) {
198206475Sbschmidt		/* just list them, one byte each */
199206477Sbschmidt		for (i = 0; i < 256; i++) {
200206475Sbschmidt			if (list->chunks[i] != 0) {
201206477Sbschmidt				*ptr++ = i;
202220720Sbschmidt				size++;
203220720Sbschmidt			}
204220720Sbschmidt		}
205220720Sbschmidt	} else {
206198429Srpaulo		int index, offset;
207198429Srpaulo
208206477Sbschmidt		/* pack into a 32 byte bitfield */
209206477Sbschmidt		for (i = 0; i < 256; i++) {
210220667Sbschmidt			if (list->chunks[i] != 0) {
211206477Sbschmidt				index = i / 8;
212206477Sbschmidt				offset = i % 8;
213206477Sbschmidt				ptr[index] |= (1 << offset);
214198429Srpaulo			}
215206477Sbschmidt		}
216198429Srpaulo		size = 32;
217220715Sbschmidt	}
218220715Sbschmidt	return (size);
219206477Sbschmidt}
220220721Sbschmidt
221201209Srpauloint
222206477Sbschmidtsctp_unpack_auth_chunks(const uint8_t * ptr, uint8_t num_chunks,
223206477Sbschmidt    sctp_auth_chklist_t * list)
224206477Sbschmidt{
225206477Sbschmidt	int i;
226206477Sbschmidt	int size;
227201882Skeramida
228206477Sbschmidt	if (list == NULL)
229201882Skeramida		return (0);
230206477Sbschmidt
231206477Sbschmidt	if (num_chunks <= 32) {
232206477Sbschmidt		/* just pull them, one byte each */
233206477Sbschmidt		for (i = 0; i < num_chunks; i++) {
234206477Sbschmidt			(void)sctp_auth_add_chunk(*ptr++, list);
235206477Sbschmidt		}
236206477Sbschmidt		size = num_chunks;
237178676Ssam	} else {
238206477Sbschmidt		int index, offset;
239206477Sbschmidt
240206477Sbschmidt		/* unpack from a 32 byte bitfield */
241206477Sbschmidt		for (index = 0; index < 32; index++) {
242206477Sbschmidt			for (offset = 0; offset < 8; offset++) {
243178676Ssam				if (ptr[index] & (1 << offset)) {
244206477Sbschmidt					(void)sctp_auth_add_chunk((index * 8) + offset, list);
245206477Sbschmidt				}
246220662Sbschmidt			}
247220891Sbschmidt		}
248206477Sbschmidt		size = 32;
249220634Sbschmidt	}
250206477Sbschmidt	return (size);
251206477Sbschmidt}
252206477Sbschmidt
253221650Sbschmidt
254221650Sbschmidt/*
255221650Sbschmidt * allocate structure space for a key of length keylen
256221650Sbschmidt */
257221651Sbschmidtsctp_key_t *
258221651Sbschmidtsctp_alloc_key(uint32_t keylen)
259221651Sbschmidt{
260221651Sbschmidt	sctp_key_t *new_key;
261206474Sbschmidt
262206474Sbschmidt	SCTP_MALLOC(new_key, sctp_key_t *, sizeof(*new_key) + keylen,
263221651Sbschmidt	    SCTP_M_AUTH_KY);
264221651Sbschmidt	if (new_key == NULL) {
265206474Sbschmidt		/* out of memory */
266221651Sbschmidt		return (NULL);
267221651Sbschmidt	}
268220726Sbschmidt	new_key->keylen = keylen;
269206474Sbschmidt	return (new_key);
270221651Sbschmidt}
271221651Sbschmidt
272220726Sbschmidtvoid
273220674Sbschmidtsctp_free_key(sctp_key_t * key)
274220674Sbschmidt{
275206477Sbschmidt	if (key != NULL)
276220677Sbschmidt		SCTP_FREE(key, SCTP_M_AUTH_KY);
277220676Sbschmidt}
278206477Sbschmidt
279206477Sbschmidtvoid
280206477Sbschmidtsctp_print_key(sctp_key_t * key, const char *str)
281198429Srpaulo{
282206477Sbschmidt	uint32_t i;
283206477Sbschmidt
284198429Srpaulo	if (key == NULL) {
285206477Sbschmidt		printf("%s: [Null key]\n", str);
286210111Sbschmidt		return;
287210111Sbschmidt	}
288210111Sbschmidt	printf("%s: len %u, ", str, key->keylen);
289210111Sbschmidt	if (key->keylen) {
290206477Sbschmidt		for (i = 0; i < key->keylen; i++)
291206477Sbschmidt			printf("%02x", key->key[i]);
292206477Sbschmidt		printf("\n");
293206477Sbschmidt	} else {
294206477Sbschmidt		printf("[Null key]\n");
295206477Sbschmidt	}
296206477Sbschmidt}
297206477Sbschmidt
298206477Sbschmidtvoid
299206477Sbschmidtsctp_show_key(sctp_key_t * key, const char *str)
300220723Sbschmidt{
301220723Sbschmidt	uint32_t i;
302206477Sbschmidt
303206477Sbschmidt	if (key == NULL) {
304206477Sbschmidt		printf("%s: [Null key]\n", str);
305206477Sbschmidt		return;
306220726Sbschmidt	}
307220726Sbschmidt	printf("%s: len %u, ", str, key->keylen);
308220726Sbschmidt	if (key->keylen) {
309220726Sbschmidt		for (i = 0; i < key->keylen; i++)
310220726Sbschmidt			printf("%02x", key->key[i]);
311198429Srpaulo		printf("\n");
312178676Ssam	} else {
313178676Ssam		printf("[Null key]\n");
314178676Ssam	}
315178676Ssam}
316178676Ssam
317178676Ssamstatic uint32_t
318178676Ssamsctp_get_keylen(sctp_key_t * key)
319178676Ssam{
320178676Ssam	if (key != NULL)
321178676Ssam		return (key->keylen);
322178676Ssam	else
323178676Ssam		return (0);
324178676Ssam}
325178676Ssam
326178676Ssam/*
327178676Ssam * generate a new random key of length 'keylen'
328178676Ssam */
329178676Ssamsctp_key_t *
330178676Ssamsctp_generate_random_key(uint32_t keylen)
331178676Ssam{
332178676Ssam	sctp_key_t *new_key;
333178676Ssam
334178676Ssam	/* validate keylen */
335178676Ssam	if (keylen > SCTP_AUTH_RANDOM_SIZE_MAX)
336178676Ssam		keylen = SCTP_AUTH_RANDOM_SIZE_MAX;
337178676Ssam
338220723Sbschmidt	new_key = sctp_alloc_key(keylen);
339220723Sbschmidt	if (new_key == NULL) {
340220723Sbschmidt		/* out of memory */
341220723Sbschmidt		return (NULL);
342220723Sbschmidt	}
343220723Sbschmidt	SCTP_READ_RANDOM(new_key->key, keylen);
344220723Sbschmidt	new_key->keylen = keylen;
345220723Sbschmidt	return (new_key);
346220723Sbschmidt}
347220723Sbschmidt
348220723Sbschmidtsctp_key_t *
349220723Sbschmidtsctp_set_key(uint8_t * key, uint32_t keylen)
350220723Sbschmidt{
351220723Sbschmidt	sctp_key_t *new_key;
352220723Sbschmidt
353220723Sbschmidt	new_key = sctp_alloc_key(keylen);
354220723Sbschmidt	if (new_key == NULL) {
355220723Sbschmidt		/* out of memory */
356220723Sbschmidt		return (NULL);
357220723Sbschmidt	}
358220723Sbschmidt	bcopy(key, new_key->key, keylen);
359220723Sbschmidt	return (new_key);
360220723Sbschmidt}
361220723Sbschmidt
362220723Sbschmidt/*-
363220723Sbschmidt * given two keys of variable size, compute which key is "larger/smaller"
364220723Sbschmidt * returns:  1 if key1 > key2
365220723Sbschmidt *          -1 if key1 < key2
366220723Sbschmidt *           0 if key1 = key2
367220723Sbschmidt */
368220723Sbschmidtstatic int
369220723Sbschmidtsctp_compare_key(sctp_key_t * key1, sctp_key_t * key2)
370220723Sbschmidt{
371220723Sbschmidt	uint32_t maxlen;
372220723Sbschmidt	uint32_t i;
373220723Sbschmidt	uint32_t key1len, key2len;
374220723Sbschmidt	uint8_t *key_1, *key_2;
375220723Sbschmidt	uint8_t temp[SCTP_AUTH_RANDOM_SIZE_MAX];
376220723Sbschmidt
377220723Sbschmidt	/* sanity/length check */
378220723Sbschmidt	key1len = sctp_get_keylen(key1);
379220723Sbschmidt	key2len = sctp_get_keylen(key2);
380178676Ssam	if ((key1len == 0) && (key2len == 0))
381178676Ssam		return (0);
382178676Ssam	else if (key1len == 0)
383178676Ssam		return (-1);
384220723Sbschmidt	else if (key2len == 0)
385220723Sbschmidt		return (1);
386220723Sbschmidt
387220723Sbschmidt	if (key1len != key2len) {
388220723Sbschmidt		if (key1len >= key2len)
389220723Sbschmidt			maxlen = key1len;
390220723Sbschmidt		else
391220723Sbschmidt			maxlen = key2len;
392220723Sbschmidt		bzero(temp, maxlen);
393178676Ssam		if (key1len < maxlen) {
394178676Ssam			/* prepend zeroes to key1 */
395220723Sbschmidt			bcopy(key1->key, temp + (maxlen - key1len), key1len);
396220723Sbschmidt			key_1 = temp;
397220723Sbschmidt			key_2 = key2->key;
398220726Sbschmidt		} else {
399178676Ssam			/* prepend zeroes to key2 */
400220723Sbschmidt			bcopy(key2->key, temp + (maxlen - key2len), key2len);
401178676Ssam			key_1 = key1->key;
402220723Sbschmidt			key_2 = temp;
403220726Sbschmidt		}
404220723Sbschmidt	} else {
405220723Sbschmidt		maxlen = key1len;
406220723Sbschmidt		key_1 = key1->key;
407220723Sbschmidt		key_2 = key2->key;
408178676Ssam	}
409178676Ssam
410178676Ssam	for (i = 0; i < maxlen; i++) {
411198429Srpaulo		if (*key_1 > *key_2)
412178676Ssam			return (1);
413198429Srpaulo		else if (*key_1 < *key_2)
414198429Srpaulo			return (-1);
415198429Srpaulo		key_1++;
416198429Srpaulo		key_2++;
417198429Srpaulo	}
418198429Srpaulo
419198429Srpaulo	/* keys are equal value, so check lengths */
420198429Srpaulo	if (key1len == key2len)
421178676Ssam		return (0);
422178676Ssam	else if (key1len < key2len)
423178676Ssam		return (-1);
424178676Ssam	else
425178676Ssam		return (1);
426178676Ssam}
427178676Ssam
428178676Ssam/*
429220721Sbschmidt * generate the concatenated keying material based on the two keys and the
430184233Smav * shared key (if available). draft-ietf-tsvwg-auth specifies the specific
431190526Ssam * order for concatenation
432178676Ssam */
433178676Ssamsctp_key_t *
434178676Ssamsctp_compute_hashkey(sctp_key_t * key1, sctp_key_t * key2, sctp_key_t * shared)
435198429Srpaulo{
436198429Srpaulo	uint32_t keylen;
437198429Srpaulo	sctp_key_t *new_key;
438198429Srpaulo	uint8_t *key_ptr;
439219902Sjhb
440198429Srpaulo	keylen = sctp_get_keylen(key1) + sctp_get_keylen(key2) +
441198429Srpaulo	    sctp_get_keylen(shared);
442198429Srpaulo
443178676Ssam	if (keylen > 0) {
444178676Ssam		/* get space for the new key */
445198429Srpaulo		new_key = sctp_alloc_key(keylen);
446178676Ssam		if (new_key == NULL) {
447178676Ssam			/* out of memory */
448198429Srpaulo			return (NULL);
449220721Sbschmidt		}
450220721Sbschmidt		new_key->keylen = keylen;
451198429Srpaulo		key_ptr = new_key->key;
452198429Srpaulo	} else {
453220721Sbschmidt		/* all keys empty/null?! */
454220721Sbschmidt		return (NULL);
455198429Srpaulo	}
456198429Srpaulo
457198429Srpaulo	/* concatenate the keys */
458178676Ssam	if (sctp_compare_key(key1, key2) <= 0) {
459178676Ssam		/* key is shared + key1 + key2 */
460198429Srpaulo		if (sctp_get_keylen(shared)) {
461178676Ssam			bcopy(shared->key, key_ptr, shared->keylen);
462198429Srpaulo			key_ptr += shared->keylen;
463220726Sbschmidt		}
464220724Sbschmidt		if (sctp_get_keylen(key1)) {
465198429Srpaulo			bcopy(key1->key, key_ptr, key1->keylen);
466178676Ssam			key_ptr += key1->keylen;
467178676Ssam		}
468178676Ssam		if (sctp_get_keylen(key2)) {
469178676Ssam			bcopy(key2->key, key_ptr, key2->keylen);
470220726Sbschmidt			key_ptr += key2->keylen;
471178676Ssam		}
472184233Smav	} else {
473184233Smav		/* key is shared + key2 + key1 */
474184233Smav		if (sctp_get_keylen(shared)) {
475220725Sbschmidt			bcopy(shared->key, key_ptr, shared->keylen);
476178676Ssam			key_ptr += shared->keylen;
477198429Srpaulo		}
478178676Ssam		if (sctp_get_keylen(key2)) {
479220724Sbschmidt			bcopy(key2->key, key_ptr, key2->keylen);
480178676Ssam			key_ptr += key2->keylen;
481198429Srpaulo		}
482178676Ssam		if (sctp_get_keylen(key1)) {
483178676Ssam			bcopy(key1->key, key_ptr, key1->keylen);
484178676Ssam			key_ptr += key1->keylen;
485178676Ssam		}
486220728Sbschmidt	}
487220728Sbschmidt	return (new_key);
488220728Sbschmidt}
489220728Sbschmidt
490220728Sbschmidt
491220728Sbschmidtsctp_sharedkey_t *
492220728Sbschmidtsctp_alloc_sharedkey(void)
493220728Sbschmidt{
494220728Sbschmidt	sctp_sharedkey_t *new_key;
495198429Srpaulo
496198429Srpaulo	SCTP_MALLOC(new_key, sctp_sharedkey_t *, sizeof(*new_key),
497198429Srpaulo	    SCTP_M_AUTH_KY);
498220726Sbschmidt	if (new_key == NULL) {
499198429Srpaulo		/* out of memory */
500178676Ssam		return (NULL);
501178676Ssam	}
502178676Ssam	new_key->keyid = 0;
503198429Srpaulo	new_key->key = NULL;
504220726Sbschmidt	new_key->refcount = 1;
505178676Ssam	new_key->deactivated = 0;
506198429Srpaulo	return (new_key);
507198429Srpaulo}
508178676Ssam
509178676Ssamvoid
510178676Ssamsctp_free_sharedkey(sctp_sharedkey_t * skey)
511198429Srpaulo{
512220726Sbschmidt	if (skey == NULL)
513178676Ssam		return;
514220724Sbschmidt
515178676Ssam	if (SCTP_DECREMENT_AND_CHECK_REFCOUNT(&skey->refcount)) {
516178676Ssam		if (skey->key != NULL)
517178676Ssam			sctp_free_key(skey->key);
518201209Srpaulo		SCTP_FREE(skey, SCTP_M_AUTH_KY);
519201209Srpaulo	}
520201209Srpaulo}
521220724Sbschmidt
522220724Sbschmidtsctp_sharedkey_t *
523201209Srpaulosctp_find_sharedkey(struct sctp_keyhead *shared_keys, uint16_t key_id)
524201209Srpaulo{
525201209Srpaulo	sctp_sharedkey_t *skey;
526198429Srpaulo
527220726Sbschmidt	LIST_FOREACH(skey, shared_keys, next) {
528178676Ssam		if (skey->keyid == key_id)
529220726Sbschmidt			return (skey);
530178676Ssam	}
531178676Ssam	return (NULL);
532178676Ssam}
533220725Sbschmidt
534220728Sbschmidtint
535220726Sbschmidtsctp_insert_sharedkey(struct sctp_keyhead *shared_keys,
536178676Ssam    sctp_sharedkey_t * new_skey)
537220724Sbschmidt{
538220724Sbschmidt	sctp_sharedkey_t *skey;
539178676Ssam
540178676Ssam	if ((shared_keys == NULL) || (new_skey == NULL))
541178676Ssam		return (EINVAL);
542178676Ssam
543198429Srpaulo	/* insert into an empty list? */
544220726Sbschmidt	if (LIST_EMPTY(shared_keys)) {
545220724Sbschmidt		LIST_INSERT_HEAD(shared_keys, new_skey, next);
546220724Sbschmidt		return (0);
547178676Ssam	}
548178676Ssam	/* insert into the existing list, ordered by key id */
549178676Ssam	LIST_FOREACH(skey, shared_keys, next) {
550198429Srpaulo		if (new_skey->keyid < skey->keyid) {
551198429Srpaulo			/* insert it before here */
552198429Srpaulo			LIST_INSERT_BEFORE(skey, new_skey, next);
553178676Ssam			return (0);
554178676Ssam		} else if (new_skey->keyid == skey->keyid) {
555178676Ssam			/* replace the existing key */
556178676Ssam			/* verify this key *can* be replaced */
557178676Ssam			if ((skey->deactivated) && (skey->refcount > 1)) {
558220726Sbschmidt				SCTPDBG(SCTP_DEBUG_AUTH1,
559178676Ssam				    "can't replace shared key id %u\n",
560198429Srpaulo				    new_skey->keyid);
561178676Ssam				return (EBUSY);
562178676Ssam			}
563178676Ssam			SCTPDBG(SCTP_DEBUG_AUTH1,
564198429Srpaulo			    "replacing shared key id %u\n",
565178676Ssam			    new_skey->keyid);
566178957Ssam			LIST_INSERT_BEFORE(skey, new_skey, next);
567178957Ssam			LIST_REMOVE(skey, next);
568178676Ssam			sctp_free_sharedkey(skey);
569178676Ssam			return (0);
570178676Ssam		}
571178676Ssam		if (LIST_NEXT(skey, next) == NULL) {
572178676Ssam			/* belongs at the end of the list */
573178676Ssam			LIST_INSERT_AFTER(skey, new_skey, next);
574178676Ssam			return (0);
575178676Ssam		}
576178676Ssam	}
577221640Sbschmidt	/* shouldn't reach here */
578221640Sbschmidt	return (0);
579221640Sbschmidt}
580221642Sbschmidt
581221642Sbschmidtvoid
582221642Sbschmidtsctp_auth_key_acquire(struct sctp_tcb *stcb, uint16_t key_id)
583221642Sbschmidt{
584221642Sbschmidt	sctp_sharedkey_t *skey;
585221642Sbschmidt
586221642Sbschmidt	/* find the shared key */
587221642Sbschmidt	skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, key_id);
588221642Sbschmidt
589221642Sbschmidt	/* bump the ref count */
590221642Sbschmidt	if (skey) {
591221642Sbschmidt		atomic_add_int(&skey->refcount, 1);
592221642Sbschmidt		SCTPDBG(SCTP_DEBUG_AUTH2,
593221642Sbschmidt		    "%s: stcb %p key %u refcount acquire to %d\n",
594221642Sbschmidt		    __FUNCTION__, stcb, key_id, skey->refcount);
595221642Sbschmidt	}
596221642Sbschmidt}
597221642Sbschmidt
598221642Sbschmidtvoid
599221642Sbschmidtsctp_auth_key_release(struct sctp_tcb *stcb, uint16_t key_id)
600221642Sbschmidt{
601221642Sbschmidt	sctp_sharedkey_t *skey;
602221657Sbschmidt
603221657Sbschmidt	/* find the shared key */
604221657Sbschmidt	skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, key_id);
605221657Sbschmidt
606221657Sbschmidt	/* decrement the ref count */
607221657Sbschmidt	if (skey) {
608221657Sbschmidt		sctp_free_sharedkey(skey);
609221657Sbschmidt		SCTPDBG(SCTP_DEBUG_AUTH2,
610221657Sbschmidt		    "%s: stcb %p key %u refcount release to %d\n",
611221657Sbschmidt		    __FUNCTION__, stcb, key_id, skey->refcount);
612201209Srpaulo
613221657Sbschmidt		/* see if a notification should be generated */
614221657Sbschmidt		if ((skey->refcount <= 1) && (skey->deactivated)) {
615221657Sbschmidt			/* notify ULP that key is no longer used */
616178678Ssam			sctp_ulp_notify(SCTP_NOTIFY_AUTH_FREE_KEY, stcb,
617201209Srpaulo			    key_id, 0, SCTP_SO_NOT_LOCKED);
618221657Sbschmidt			SCTPDBG(SCTP_DEBUG_AUTH2,
619221657Sbschmidt			    "%s: stcb %p key %u no longer used, %d\n",
620221657Sbschmidt			    __FUNCTION__, stcb, key_id, skey->refcount);
621221657Sbschmidt		}
622221657Sbschmidt	}
623201209Srpaulo}
624221657Sbschmidt
625221657Sbschmidtstatic sctp_sharedkey_t *
626201209Srpaulosctp_copy_sharedkey(const sctp_sharedkey_t * skey)
627178676Ssam{
628178676Ssam	sctp_sharedkey_t *new_skey;
629178676Ssam
630178676Ssam	if (skey == NULL)
631178676Ssam		return (NULL);
632178676Ssam	new_skey = sctp_alloc_sharedkey();
633207554Ssobomax	if (new_skey == NULL)
634207554Ssobomax		return (NULL);
635178676Ssam	if (skey->key != NULL)
636178676Ssam		new_skey->key = sctp_set_key(skey->key->key, skey->key->keylen);
637190526Ssam	else
638178676Ssam		new_skey->key = NULL;
639178676Ssam	new_skey->keyid = skey->keyid;
640178676Ssam	return (new_skey);
641178676Ssam}
642221650Sbschmidt
643220723Sbschmidtint
644221650Sbschmidtsctp_copy_skeylist(const struct sctp_keyhead *src, struct sctp_keyhead *dest)
645220723Sbschmidt{
646221651Sbschmidt	sctp_sharedkey_t *skey, *new_skey;
647221651Sbschmidt	int count = 0;
648221651Sbschmidt
649221651Sbschmidt	if ((src == NULL) || (dest == NULL))
650221651Sbschmidt		return (0);
651221651Sbschmidt	LIST_FOREACH(skey, src, next) {
652220715Sbschmidt		new_skey = sctp_copy_sharedkey(skey);
653220721Sbschmidt		if (new_skey != NULL) {
654201209Srpaulo			(void)sctp_insert_sharedkey(dest, new_skey);
655198429Srpaulo			count++;
656198429Srpaulo		}
657198429Srpaulo	}
658198429Srpaulo	return (count);
659198429Srpaulo}
660201209Srpaulo
661178676Ssam
662198429Srpaulosctp_hmaclist_t *
663220667Sbschmidtsctp_alloc_hmaclist(uint8_t num_hmacs)
664220667Sbschmidt{
665220667Sbschmidt	sctp_hmaclist_t *new_list;
666220726Sbschmidt	int alloc_size;
667220726Sbschmidt
668220726Sbschmidt	alloc_size = sizeof(*new_list) + num_hmacs * sizeof(new_list->hmac[0]);
669220667Sbschmidt	SCTP_MALLOC(new_list, sctp_hmaclist_t *, alloc_size,
670178676Ssam	    SCTP_M_AUTH_HL);
671178676Ssam	if (new_list == NULL) {
672198429Srpaulo		/* out of memory */
673198429Srpaulo		return (NULL);
674198429Srpaulo	}
675198429Srpaulo	new_list->max_algo = num_hmacs;
676178676Ssam	new_list->num_algo = 0;
677198429Srpaulo	return (new_list);
678220724Sbschmidt}
679198429Srpaulo
680198429Srpaulovoid
681198429Srpaulosctp_free_hmaclist(sctp_hmaclist_t * list)
682178676Ssam{
683220724Sbschmidt	if (list != NULL) {
684220724Sbschmidt		SCTP_FREE(list, SCTP_M_AUTH_HL);
685178676Ssam		list = NULL;
686178676Ssam	}
687220635Sbschmidt}
688178676Ssam
689178676Ssamint
690178676Ssamsctp_auth_add_hmacid(sctp_hmaclist_t * list, uint16_t hmac_id)
691220728Sbschmidt{
692220728Sbschmidt	int i;
693178676Ssam
694220728Sbschmidt	if (list == NULL)
695198429Srpaulo		return (-1);
696220728Sbschmidt	if (list->num_algo == list->max_algo) {
697220728Sbschmidt		SCTPDBG(SCTP_DEBUG_AUTH1,
698220728Sbschmidt		    "SCTP: HMAC id list full, ignoring add %u\n", hmac_id);
699220728Sbschmidt		return (-1);
700220728Sbschmidt	}
701220728Sbschmidt	if ((hmac_id != SCTP_AUTH_HMAC_ID_SHA1) &&
702220728Sbschmidt#ifdef HAVE_SHA224
703220728Sbschmidt	    (hmac_id != SCTP_AUTH_HMAC_ID_SHA224) &&
704220728Sbschmidt#endif
705220728Sbschmidt#ifdef HAVE_SHA2
706220728Sbschmidt	    (hmac_id != SCTP_AUTH_HMAC_ID_SHA256) &&
707220728Sbschmidt	    (hmac_id != SCTP_AUTH_HMAC_ID_SHA384) &&
708220728Sbschmidt	    (hmac_id != SCTP_AUTH_HMAC_ID_SHA512) &&
709220728Sbschmidt#endif
710220728Sbschmidt	    1) {
711221651Sbschmidt		return (-1);
712220728Sbschmidt	}
713220728Sbschmidt	/* Now is it already in the list */
714220728Sbschmidt	for (i = 0; i < list->num_algo; i++) {
715220728Sbschmidt		if (list->hmac[i] == hmac_id) {
716220728Sbschmidt			/* already in list */
717220728Sbschmidt			return (-1);
718220728Sbschmidt		}
719220728Sbschmidt	}
720220728Sbschmidt	SCTPDBG(SCTP_DEBUG_AUTH1, "SCTP: add HMAC id %u to list\n", hmac_id);
721220728Sbschmidt	list->hmac[list->num_algo++] = hmac_id;
722220728Sbschmidt	return (0);
723220728Sbschmidt}
724220728Sbschmidt
725220728Sbschmidtsctp_hmaclist_t *
726220728Sbschmidtsctp_copy_hmaclist(sctp_hmaclist_t * list)
727220728Sbschmidt{
728220728Sbschmidt	sctp_hmaclist_t *new_list;
729220728Sbschmidt	int i;
730220728Sbschmidt
731220728Sbschmidt	if (list == NULL)
732220728Sbschmidt		return (NULL);
733220728Sbschmidt	/* get a new list */
734220728Sbschmidt	new_list = sctp_alloc_hmaclist(list->max_algo);
735220728Sbschmidt	if (new_list == NULL)
736220728Sbschmidt		return (NULL);
737220728Sbschmidt	/* copy it */
738220728Sbschmidt	new_list->max_algo = list->max_algo;
739220728Sbschmidt	new_list->num_algo = list->num_algo;
740220728Sbschmidt	for (i = 0; i < list->num_algo; i++)
741220728Sbschmidt		new_list->hmac[i] = list->hmac[i];
742220728Sbschmidt	return (new_list);
743220728Sbschmidt}
744220728Sbschmidt
745220728Sbschmidtsctp_hmaclist_t *
746220728Sbschmidtsctp_default_supported_hmaclist(void)
747220728Sbschmidt{
748220728Sbschmidt	sctp_hmaclist_t *new_list;
749221651Sbschmidt
750220728Sbschmidt	new_list = sctp_alloc_hmaclist(2);
751220728Sbschmidt	if (new_list == NULL)
752220728Sbschmidt		return (NULL);
753220728Sbschmidt	(void)sctp_auth_add_hmacid(new_list, SCTP_AUTH_HMAC_ID_SHA1);
754220728Sbschmidt	(void)sctp_auth_add_hmacid(new_list, SCTP_AUTH_HMAC_ID_SHA256);
755220728Sbschmidt	return (new_list);
756220728Sbschmidt}
757220728Sbschmidt
758220866Sbschmidt/*-
759220866Sbschmidt * HMAC algos are listed in priority/preference order
760220728Sbschmidt * find the best HMAC id to use for the peer based on local support
761198429Srpaulo */
762198429Srpaulouint16_t
763201209Srpaulosctp_negotiate_hmacid(sctp_hmaclist_t * peer, sctp_hmaclist_t * local)
764198439Srpaulo{
765220727Sbschmidt	int i, j;
766201209Srpaulo
767201209Srpaulo	if ((local == NULL) || (peer == NULL))
768198429Srpaulo		return (SCTP_AUTH_HMAC_ID_RSVD);
769198429Srpaulo
770201209Srpaulo	for (i = 0; i < peer->num_algo; i++) {
771198439Srpaulo		for (j = 0; j < local->num_algo; j++) {
772198429Srpaulo			if (peer->hmac[i] == local->hmac[j]) {
773198429Srpaulo				/* found the "best" one */
774198429Srpaulo				SCTPDBG(SCTP_DEBUG_AUTH1,
775201209Srpaulo				    "SCTP: negotiated peer HMAC id %u\n",
776198439Srpaulo				    peer->hmac[i]);
777198429Srpaulo				return (peer->hmac[i]);
778198429Srpaulo			}
779206444Sbschmidt		}
780198439Srpaulo	}
781198429Srpaulo	/* didn't find one! */
782198429Srpaulo	return (SCTP_AUTH_HMAC_ID_RSVD);
783201209Srpaulo}
784198439Srpaulo
785220728Sbschmidt/*-
786201209Srpaulo * serialize the HMAC algo list and return space used
787220727Sbschmidt * caller must guarantee ptr has appropriate space
788201209Srpaulo */
789201209Srpauloint
790201209Srpaulosctp_serialize_hmaclist(sctp_hmaclist_t * list, uint8_t * ptr)
791198429Srpaulo{
792198429Srpaulo	int i;
793201209Srpaulo	uint16_t hmac_id;
794210109Sbschmidt
795220867Sbschmidt	if (list == NULL)
796220867Sbschmidt		return (0);
797220867Sbschmidt
798198429Srpaulo	for (i = 0; i < list->num_algo; i++) {
799210109Sbschmidt		hmac_id = htons(list->hmac[i]);
800210109Sbschmidt		bcopy(&hmac_id, ptr, sizeof(hmac_id));
801220894Sbschmidt		ptr += sizeof(hmac_id);
802220894Sbschmidt	}
803220891Sbschmidt	return (list->num_algo * sizeof(hmac_id));
804220894Sbschmidt}
805220894Sbschmidt
806210109Sbschmidtint
807198429Srpaulosctp_verify_hmac_param(struct sctp_auth_hmac_algo *hmacs, uint32_t num_hmacs)
808198429Srpaulo{
809198429Srpaulo	uint32_t i;
810220728Sbschmidt	uint16_t hmac_id;
811198429Srpaulo	uint32_t sha1_supported = 0;
812220728Sbschmidt
813178676Ssam	for (i = 0; i < num_hmacs; i++) {
814178676Ssam		hmac_id = ntohs(hmacs->hmac_ids[i]);
815178676Ssam		if (hmac_id == SCTP_AUTH_HMAC_ID_SHA1)
816198429Srpaulo			sha1_supported = 1;
817178676Ssam	}
818206477Sbschmidt	/* all HMAC id's are supported */
819198429Srpaulo	if (sha1_supported == 0)
820178676Ssam		return (-1);
821178676Ssam	else
822178676Ssam		return (0);
823178676Ssam}
824198429Srpaulo
825198429Srpaulosctp_authinfo_t *
826198429Srpaulosctp_alloc_authinfo(void)
827198429Srpaulo{
828198429Srpaulo	sctp_authinfo_t *new_authinfo;
829178676Ssam
830178676Ssam	SCTP_MALLOC(new_authinfo, sctp_authinfo_t *, sizeof(*new_authinfo),
831220723Sbschmidt	    SCTP_M_AUTH_IF);
832220723Sbschmidt
833220723Sbschmidt	if (new_authinfo == NULL) {
834220723Sbschmidt		/* out of memory */
835220723Sbschmidt		return (NULL);
836220723Sbschmidt	}
837220723Sbschmidt	bzero(new_authinfo, sizeof(*new_authinfo));
838220723Sbschmidt	return (new_authinfo);
839220723Sbschmidt}
840220723Sbschmidt
841220723Sbschmidtvoid
842220723Sbschmidtsctp_free_authinfo(sctp_authinfo_t * authinfo)
843220723Sbschmidt{
844178676Ssam	if (authinfo == NULL)
845178676Ssam		return;
846220726Sbschmidt
847220726Sbschmidt	if (authinfo->random != NULL)
848220726Sbschmidt		sctp_free_key(authinfo->random);
849178676Ssam	if (authinfo->peer_random != NULL)
850178676Ssam		sctp_free_key(authinfo->peer_random);
851178676Ssam	if (authinfo->assoc_key != NULL)
852178676Ssam		sctp_free_key(authinfo->assoc_key);
853178676Ssam	if (authinfo->recv_key != NULL)
854178676Ssam		sctp_free_key(authinfo->recv_key);
855178676Ssam
856178676Ssam	/* We are NOT dynamically allocating authinfo's right now... */
857178676Ssam	/* SCTP_FREE(authinfo, SCTP_M_AUTH_??); */
858178676Ssam}
859178676Ssam
860178676Ssam
861178676Ssamuint32_t
862198429Srpaulosctp_get_auth_chunk_len(uint16_t hmac_algo)
863178676Ssam{
864178676Ssam	int size;
865178676Ssam
866206358Srpaulo	size = sizeof(struct sctp_auth_chunk) + sctp_get_hmac_digest_len(hmac_algo);
867198429Srpaulo	return (SCTP_SIZE32(size));
868206476Sbschmidt}
869178676Ssam
870178676Ssamuint32_t
871178676Ssamsctp_get_hmac_digest_len(uint16_t hmac_algo)
872178676Ssam{
873178676Ssam	switch (hmac_algo) {
874178676Ssam	case SCTP_AUTH_HMAC_ID_SHA1:
875178676Ssam		return (SCTP_AUTH_DIGEST_LEN_SHA1);
876178676Ssam#ifdef HAVE_SHA224
877178676Ssam	case SCTP_AUTH_HMAC_ID_SHA224:
878206358Srpaulo		return (SCTP_AUTH_DIGEST_LEN_SHA224);
879178676Ssam#endif
880178676Ssam#ifdef HAVE_SHA2
881178676Ssam	case SCTP_AUTH_HMAC_ID_SHA256:
882178676Ssam		return (SCTP_AUTH_DIGEST_LEN_SHA256);
883206477Sbschmidt	case SCTP_AUTH_HMAC_ID_SHA384:
884220635Sbschmidt		return (SCTP_AUTH_DIGEST_LEN_SHA384);
885178676Ssam	case SCTP_AUTH_HMAC_ID_SHA512:
886178676Ssam		return (SCTP_AUTH_DIGEST_LEN_SHA512);
887198429Srpaulo#endif
888198429Srpaulo	default:
889220721Sbschmidt		/* unknown HMAC algorithm: can't do anything */
890178676Ssam		return (0);
891198429Srpaulo	}			/* end switch */
892198429Srpaulo}
893198429Srpaulo
894198429Srpaulostatic inline int
895198429Srpaulosctp_get_hmac_block_len(uint16_t hmac_algo)
896198429Srpaulo{
897198429Srpaulo	switch (hmac_algo) {
898198429Srpaulo		case SCTP_AUTH_HMAC_ID_SHA1:
899220667Sbschmidt#ifdef HAVE_SHA224
900220667Sbschmidt		case SCTP_AUTH_HMAC_ID_SHA224:
901198429Srpaulo#endif
902198429Srpaulo		return (64);
903198429Srpaulo#ifdef HAVE_SHA2
904220725Sbschmidt	case SCTP_AUTH_HMAC_ID_SHA256:
905220723Sbschmidt		return (64);
906220723Sbschmidt	case SCTP_AUTH_HMAC_ID_SHA384:
907220723Sbschmidt	case SCTP_AUTH_HMAC_ID_SHA512:
908220723Sbschmidt		return (128);
909220723Sbschmidt#endif
910220723Sbschmidt	case SCTP_AUTH_HMAC_ID_RSVD:
911220723Sbschmidt	default:
912201209Srpaulo		/* unknown HMAC algorithm: can't do anything */
913198429Srpaulo		return (0);
914220728Sbschmidt	}			/* end switch */
915220728Sbschmidt}
916198429Srpaulo
917198429Srpaulostatic void
918201209Srpaulosctp_hmac_init(uint16_t hmac_algo, sctp_hash_context_t * ctx)
919201209Srpaulo{
920198429Srpaulo	switch (hmac_algo) {
921198429Srpaulo		case SCTP_AUTH_HMAC_ID_SHA1:
922198429Srpaulo		SHA1_Init(&ctx->sha1);
923198429Srpaulo		break;
924198429Srpaulo#ifdef HAVE_SHA224
925198429Srpaulo	case SCTP_AUTH_HMAC_ID_SHA224:
926198429Srpaulo		break;
927198429Srpaulo#endif
928198429Srpaulo#ifdef HAVE_SHA2
929178676Ssam	case SCTP_AUTH_HMAC_ID_SHA256:
930178676Ssam		SHA256_Init(&ctx->sha256);
931178676Ssam		break;
932178676Ssam	case SCTP_AUTH_HMAC_ID_SHA384:
933220723Sbschmidt		SHA384_Init(&ctx->sha384);
934220723Sbschmidt		break;
935220723Sbschmidt	case SCTP_AUTH_HMAC_ID_SHA512:
936220723Sbschmidt		SHA512_Init(&ctx->sha512);
937220723Sbschmidt		break;
938220723Sbschmidt#endif
939220723Sbschmidt	case SCTP_AUTH_HMAC_ID_RSVD:
940220723Sbschmidt	default:
941220723Sbschmidt		/* unknown HMAC algorithm: can't do anything */
942220723Sbschmidt		return;
943220723Sbschmidt	}			/* end switch */
944220723Sbschmidt}
945220723Sbschmidt
946220723Sbschmidtstatic void
947220723Sbschmidtsctp_hmac_update(uint16_t hmac_algo, sctp_hash_context_t * ctx,
948220723Sbschmidt    uint8_t * text, uint32_t textlen)
949220723Sbschmidt{
950220723Sbschmidt	switch (hmac_algo) {
951220723Sbschmidt		case SCTP_AUTH_HMAC_ID_SHA1:
952220723Sbschmidt		SHA1_Update(&ctx->sha1, text, textlen);
953220723Sbschmidt		break;
954220723Sbschmidt#ifdef HAVE_SHA224
955220723Sbschmidt	case SCTP_AUTH_HMAC_ID_SHA224:
956220723Sbschmidt		break;
957220723Sbschmidt#endif
958220723Sbschmidt#ifdef HAVE_SHA2
959220723Sbschmidt	case SCTP_AUTH_HMAC_ID_SHA256:
960220723Sbschmidt		SHA256_Update(&ctx->sha256, text, textlen);
961220723Sbschmidt		break;
962220723Sbschmidt	case SCTP_AUTH_HMAC_ID_SHA384:
963220723Sbschmidt		SHA384_Update(&ctx->sha384, text, textlen);
964220723Sbschmidt		break;
965220723Sbschmidt	case SCTP_AUTH_HMAC_ID_SHA512:
966220723Sbschmidt		SHA512_Update(&ctx->sha512, text, textlen);
967220723Sbschmidt		break;
968220723Sbschmidt#endif
969220723Sbschmidt	case SCTP_AUTH_HMAC_ID_RSVD:
970220723Sbschmidt	default:
971220723Sbschmidt		/* unknown HMAC algorithm: can't do anything */
972220723Sbschmidt		return;
973220723Sbschmidt	}			/* end switch */
974220723Sbschmidt}
975220723Sbschmidt
976220723Sbschmidtstatic void
977198429Srpaulosctp_hmac_final(uint16_t hmac_algo, sctp_hash_context_t * ctx,
978178676Ssam    uint8_t * digest)
979198429Srpaulo{
980178676Ssam	switch (hmac_algo) {
981198429Srpaulo		case SCTP_AUTH_HMAC_ID_SHA1:
982198429Srpaulo		SHA1_Final(digest, &ctx->sha1);
983178676Ssam		break;
984198429Srpaulo#ifdef HAVE_SHA224
985198429Srpaulo	case SCTP_AUTH_HMAC_ID_SHA224:
986198429Srpaulo		break;
987220726Sbschmidt#endif
988198429Srpaulo#ifdef HAVE_SHA2
989198429Srpaulo	case SCTP_AUTH_HMAC_ID_SHA256:
990198429Srpaulo		SHA256_Final(digest, &ctx->sha256);
991198429Srpaulo		break;
992198429Srpaulo	case SCTP_AUTH_HMAC_ID_SHA384:
993198429Srpaulo		/* SHA384 is truncated SHA512 */
994198429Srpaulo		SHA384_Final(digest, &ctx->sha384);
995198429Srpaulo		break;
996198429Srpaulo	case SCTP_AUTH_HMAC_ID_SHA512:
997198429Srpaulo		SHA512_Final(digest, &ctx->sha512);
998198429Srpaulo		break;
999198429Srpaulo#endif
1000198429Srpaulo	case SCTP_AUTH_HMAC_ID_RSVD:
1001198429Srpaulo	default:
1002198429Srpaulo		/* unknown HMAC algorithm: can't do anything */
1003198429Srpaulo		return;
1004198429Srpaulo	}			/* end switch */
1005201209Srpaulo}
1006198429Srpaulo
1007198429Srpaulo/*-
1008198429Srpaulo * Keyed-Hashing for Message Authentication: FIPS 198 (RFC 2104)
1009198429Srpaulo *
1010198429Srpaulo * Compute the HMAC digest using the desired hash key, text, and HMAC
1011198429Srpaulo * algorithm.  Resulting digest is placed in 'digest' and digest length
1012198429Srpaulo * is returned, if the HMAC was performed.
1013201209Srpaulo *
1014198429Srpaulo * WARNING: it is up to the caller to supply sufficient space to hold the
1015198429Srpaulo * resultant digest.
1016198429Srpaulo */
1017198429Srpaulouint32_t
1018198429Srpaulosctp_hmac(uint16_t hmac_algo, uint8_t * key, uint32_t keylen,
1019198429Srpaulo    uint8_t * text, uint32_t textlen, uint8_t * digest)
1020198429Srpaulo{
1021198429Srpaulo	uint32_t digestlen;
1022198429Srpaulo	uint32_t blocklen;
1023198429Srpaulo	sctp_hash_context_t ctx;
1024198429Srpaulo	uint8_t ipad[128], opad[128];	/* keyed hash inner/outer pads */
1025198429Srpaulo	uint8_t temp[SCTP_AUTH_DIGEST_LEN_MAX];
1026198429Srpaulo	uint32_t i;
1027198429Srpaulo
1028198429Srpaulo	/* sanity check the material and length */
1029198429Srpaulo	if ((key == NULL) || (keylen == 0) || (text == NULL) ||
1030198429Srpaulo	    (textlen == 0) || (digest == NULL)) {
1031198429Srpaulo		/* can't do HMAC with empty key or text or digest store */
1032198429Srpaulo		return (0);
1033198429Srpaulo	}
1034198429Srpaulo	/* validate the hmac algo and get the digest length */
1035198429Srpaulo	digestlen = sctp_get_hmac_digest_len(hmac_algo);
1036198429Srpaulo	if (digestlen == 0)
1037198429Srpaulo		return (0);
1038198429Srpaulo
1039198429Srpaulo	/* hash the key if it is longer than the hash block size */
1040198429Srpaulo	blocklen = sctp_get_hmac_block_len(hmac_algo);
1041201209Srpaulo	if (keylen > blocklen) {
1042198429Srpaulo		sctp_hmac_init(hmac_algo, &ctx);
1043198429Srpaulo		sctp_hmac_update(hmac_algo, &ctx, key, keylen);
1044198429Srpaulo		sctp_hmac_final(hmac_algo, &ctx, temp);
1045198429Srpaulo		/* set the hashed key as the key */
1046198429Srpaulo		keylen = digestlen;
1047198429Srpaulo		key = temp;
1048198429Srpaulo	}
1049201209Srpaulo	/* initialize the inner/outer pads with the key and "append" zeroes */
1050198429Srpaulo	bzero(ipad, blocklen);
1051198429Srpaulo	bzero(opad, blocklen);
1052198429Srpaulo	bcopy(key, ipad, keylen);
1053198429Srpaulo	bcopy(key, opad, keylen);
1054198429Srpaulo
1055198429Srpaulo	/* XOR the key with ipad and opad values */
1056198429Srpaulo	for (i = 0; i < blocklen; i++) {
1057198429Srpaulo		ipad[i] ^= 0x36;
1058198429Srpaulo		opad[i] ^= 0x5c;
1059198429Srpaulo	}
1060198429Srpaulo
1061198429Srpaulo	/* perform inner hash */
1062198429Srpaulo	sctp_hmac_init(hmac_algo, &ctx);
1063198429Srpaulo	sctp_hmac_update(hmac_algo, &ctx, ipad, blocklen);
1064198429Srpaulo	sctp_hmac_update(hmac_algo, &ctx, text, textlen);
1065198429Srpaulo	sctp_hmac_final(hmac_algo, &ctx, temp);
1066198429Srpaulo
1067198429Srpaulo	/* perform outer hash */
1068198429Srpaulo	sctp_hmac_init(hmac_algo, &ctx);
1069198429Srpaulo	sctp_hmac_update(hmac_algo, &ctx, opad, blocklen);
1070198429Srpaulo	sctp_hmac_update(hmac_algo, &ctx, temp, digestlen);
1071198429Srpaulo	sctp_hmac_final(hmac_algo, &ctx, digest);
1072198429Srpaulo
1073198429Srpaulo	return (digestlen);
1074198429Srpaulo}
1075198429Srpaulo
1076198429Srpaulo/* mbuf version */
1077198429Srpaulouint32_t
1078198429Srpaulosctp_hmac_m(uint16_t hmac_algo, uint8_t * key, uint32_t keylen,
1079198429Srpaulo    struct mbuf *m, uint32_t m_offset, uint8_t * digest, uint32_t trailer)
1080198429Srpaulo{
1081198429Srpaulo	uint32_t digestlen;
1082206477Sbschmidt	uint32_t blocklen;
1083198429Srpaulo	sctp_hash_context_t ctx;
1084198429Srpaulo	uint8_t ipad[128], opad[128];	/* keyed hash inner/outer pads */
1085198429Srpaulo	uint8_t temp[SCTP_AUTH_DIGEST_LEN_MAX];
1086198429Srpaulo	uint32_t i;
1087198429Srpaulo	struct mbuf *m_tmp;
1088198429Srpaulo
1089198429Srpaulo	/* sanity check the material and length */
1090198429Srpaulo	if ((key == NULL) || (keylen == 0) || (m == NULL) || (digest == NULL)) {
1091198429Srpaulo		/* can't do HMAC with empty key or text or digest store */
1092198429Srpaulo		return (0);
1093198429Srpaulo	}
1094198429Srpaulo	/* validate the hmac algo and get the digest length */
1095198429Srpaulo	digestlen = sctp_get_hmac_digest_len(hmac_algo);
1096198429Srpaulo	if (digestlen == 0)
1097198429Srpaulo		return (0);
1098198429Srpaulo
1099198429Srpaulo	/* hash the key if it is longer than the hash block size */
1100198429Srpaulo	blocklen = sctp_get_hmac_block_len(hmac_algo);
1101198429Srpaulo	if (keylen > blocklen) {
1102198429Srpaulo		sctp_hmac_init(hmac_algo, &ctx);
1103198429Srpaulo		sctp_hmac_update(hmac_algo, &ctx, key, keylen);
1104198429Srpaulo		sctp_hmac_final(hmac_algo, &ctx, temp);
1105198429Srpaulo		/* set the hashed key as the key */
1106198429Srpaulo		keylen = digestlen;
1107198429Srpaulo		key = temp;
1108198429Srpaulo	}
1109198429Srpaulo	/* initialize the inner/outer pads with the key and "append" zeroes */
1110198429Srpaulo	bzero(ipad, blocklen);
1111198429Srpaulo	bzero(opad, blocklen);
1112198429Srpaulo	bcopy(key, ipad, keylen);
1113206477Sbschmidt	bcopy(key, opad, keylen);
1114198429Srpaulo
1115198429Srpaulo	/* XOR the key with ipad and opad values */
1116203934Sbschmidt	for (i = 0; i < blocklen; i++) {
1117201209Srpaulo		ipad[i] ^= 0x36;
1118198429Srpaulo		opad[i] ^= 0x5c;
1119201209Srpaulo	}
1120220726Sbschmidt
1121198429Srpaulo	/* perform inner hash */
1122198429Srpaulo	sctp_hmac_init(hmac_algo, &ctx);
1123220726Sbschmidt	sctp_hmac_update(hmac_algo, &ctx, ipad, blocklen);
1124198429Srpaulo	/* find the correct starting mbuf and offset (get start of text) */
1125198429Srpaulo	m_tmp = m;
1126198429Srpaulo	while ((m_tmp != NULL) && (m_offset >= (uint32_t) SCTP_BUF_LEN(m_tmp))) {
1127198429Srpaulo		m_offset -= SCTP_BUF_LEN(m_tmp);
1128198429Srpaulo		m_tmp = SCTP_BUF_NEXT(m_tmp);
1129198429Srpaulo	}
1130201209Srpaulo	/* now use the rest of the mbuf chain for the text */
1131201209Srpaulo	while (m_tmp != NULL) {
1132201209Srpaulo		if ((SCTP_BUF_NEXT(m_tmp) == NULL) && trailer) {
1133201209Srpaulo			sctp_hmac_update(hmac_algo, &ctx, mtod(m_tmp, uint8_t *) + m_offset,
1134201209Srpaulo			    SCTP_BUF_LEN(m_tmp) - (trailer + m_offset));
1135198429Srpaulo		} else {
1136198429Srpaulo			sctp_hmac_update(hmac_algo, &ctx, mtod(m_tmp, uint8_t *) + m_offset,
1137198429Srpaulo			    SCTP_BUF_LEN(m_tmp) - m_offset);
1138198429Srpaulo		}
1139198429Srpaulo
1140201209Srpaulo		/* clear the offset since it's only for the first mbuf */
1141203934Sbschmidt		m_offset = 0;
1142203934Sbschmidt		m_tmp = SCTP_BUF_NEXT(m_tmp);
1143201209Srpaulo	}
1144201209Srpaulo	sctp_hmac_final(hmac_algo, &ctx, temp);
1145201209Srpaulo
1146201209Srpaulo	/* perform outer hash */
1147203934Sbschmidt	sctp_hmac_init(hmac_algo, &ctx);
1148201209Srpaulo	sctp_hmac_update(hmac_algo, &ctx, opad, blocklen);
1149201209Srpaulo	sctp_hmac_update(hmac_algo, &ctx, temp, digestlen);
1150201209Srpaulo	sctp_hmac_final(hmac_algo, &ctx, digest);
1151201209Srpaulo
1152201209Srpaulo	return (digestlen);
1153201209Srpaulo}
1154203934Sbschmidt
1155201209Srpaulo/*-
1156201209Srpaulo * verify the HMAC digest using the desired hash key, text, and HMAC
1157203934Sbschmidt * algorithm.
1158201209Srpaulo * Returns -1 on error, 0 on success.
1159201209Srpaulo */
1160203934Sbschmidtint
1161201209Srpaulosctp_verify_hmac(uint16_t hmac_algo, uint8_t * key, uint32_t keylen,
1162178676Ssam    uint8_t * text, uint32_t textlen,
1163178676Ssam    uint8_t * digest, uint32_t digestlen)
1164178676Ssam{
1165206477Sbschmidt	uint32_t len;
1166198429Srpaulo	uint8_t temp[SCTP_AUTH_DIGEST_LEN_MAX];
1167198429Srpaulo
1168220723Sbschmidt	/* sanity check the material and length */
1169198429Srpaulo	if ((key == NULL) || (keylen == 0) ||
1170198429Srpaulo	    (text == NULL) || (textlen == 0) || (digest == NULL)) {
1171198429Srpaulo		/* can't do HMAC with empty key or text or digest */
1172201209Srpaulo		return (-1);
1173198429Srpaulo	}
1174198429Srpaulo	len = sctp_get_hmac_digest_len(hmac_algo);
1175201209Srpaulo	if ((len == 0) || (digestlen != len))
1176198429Srpaulo		return (-1);
1177198429Srpaulo
1178198429Srpaulo	/* compute the expected hash */
1179198429Srpaulo	if (sctp_hmac(hmac_algo, key, keylen, text, textlen, temp) != len)
1180198429Srpaulo		return (-1);
1181201209Srpaulo
1182198429Srpaulo	if (memcmp(digest, temp, digestlen) != 0)
1183198429Srpaulo		return (-1);
1184198429Srpaulo	else
1185198429Srpaulo		return (0);
1186198429Srpaulo}
1187198429Srpaulo
1188198429Srpaulo
1189198429Srpaulo/*
1190198429Srpaulo * computes the requested HMAC using a key struct (which may be modified if
1191198429Srpaulo * the keylen exceeds the HMAC block len).
1192198429Srpaulo */
1193198429Srpaulouint32_t
1194198429Srpaulosctp_compute_hmac(uint16_t hmac_algo, sctp_key_t * key, uint8_t * text,
1195198429Srpaulo    uint32_t textlen, uint8_t * digest)
1196198429Srpaulo{
1197198429Srpaulo	uint32_t digestlen;
1198198429Srpaulo	uint32_t blocklen;
1199198429Srpaulo	sctp_hash_context_t ctx;
1200198429Srpaulo	uint8_t temp[SCTP_AUTH_DIGEST_LEN_MAX];
1201198429Srpaulo
1202198429Srpaulo	/* sanity check */
1203198429Srpaulo	if ((key == NULL) || (text == NULL) || (textlen == 0) ||
1204198429Srpaulo	    (digest == NULL)) {
1205198429Srpaulo		/* can't do HMAC with empty key or text or digest store */
1206198429Srpaulo		return (0);
1207178676Ssam	}
1208178676Ssam	/* validate the hmac algo and get the digest length */
1209178676Ssam	digestlen = sctp_get_hmac_digest_len(hmac_algo);
1210198429Srpaulo	if (digestlen == 0)
1211198429Srpaulo		return (0);
1212198429Srpaulo
1213198429Srpaulo	/* hash the key if it is longer than the hash block size */
1214178676Ssam	blocklen = sctp_get_hmac_block_len(hmac_algo);
1215178676Ssam	if (key->keylen > blocklen) {
1216198429Srpaulo		sctp_hmac_init(hmac_algo, &ctx);
1217178676Ssam		sctp_hmac_update(hmac_algo, &ctx, key->key, key->keylen);
1218220691Sbschmidt		sctp_hmac_final(hmac_algo, &ctx, temp);
1219178676Ssam		/* save the hashed key as the new key */
1220198429Srpaulo		key->keylen = digestlen;
1221178676Ssam		bcopy(temp, key->key, key->keylen);
1222220723Sbschmidt	}
1223178676Ssam	return (sctp_hmac(hmac_algo, key->key, key->keylen, text, textlen,
1224178676Ssam	    digest));
1225198429Srpaulo}
1226178676Ssam
1227220691Sbschmidt/* mbuf version */
1228220711Sbschmidtuint32_t
1229178676Ssamsctp_compute_hmac_m(uint16_t hmac_algo, sctp_key_t * key, struct mbuf *m,
1230220711Sbschmidt    uint32_t m_offset, uint8_t * digest)
1231178676Ssam{
1232220691Sbschmidt	uint32_t digestlen;
1233220711Sbschmidt	uint32_t blocklen;
1234178676Ssam	sctp_hash_context_t ctx;
1235220711Sbschmidt	uint8_t temp[SCTP_AUTH_DIGEST_LEN_MAX];
1236220691Sbschmidt
1237220691Sbschmidt	/* sanity check */
1238220711Sbschmidt	if ((key == NULL) || (m == NULL) || (digest == NULL)) {
1239178676Ssam		/* can't do HMAC with empty key or text or digest store */
1240178676Ssam		return (0);
1241220704Sbschmidt	}
1242220704Sbschmidt	/* validate the hmac algo and get the digest length */
1243178676Ssam	digestlen = sctp_get_hmac_digest_len(hmac_algo);
1244178676Ssam	if (digestlen == 0)
1245220726Sbschmidt		return (0);
1246178676Ssam
1247220726Sbschmidt	/* hash the key if it is longer than the hash block size */
1248220726Sbschmidt	blocklen = sctp_get_hmac_block_len(hmac_algo);
1249178676Ssam	if (key->keylen > blocklen) {
1250178676Ssam		sctp_hmac_init(hmac_algo, &ctx);
1251178676Ssam		sctp_hmac_update(hmac_algo, &ctx, key->key, key->keylen);
1252206477Sbschmidt		sctp_hmac_final(hmac_algo, &ctx, temp);
1253178676Ssam		/* save the hashed key as the new key */
1254178676Ssam		key->keylen = digestlen;
1255220701Sbschmidt		bcopy(temp, key->key, key->keylen);
1256220701Sbschmidt	}
1257220701Sbschmidt	return (sctp_hmac_m(hmac_algo, key->key, key->keylen, m, m_offset, digest, 0));
1258220701Sbschmidt}
1259220701Sbschmidt
1260178676Ssamint
1261220701Sbschmidtsctp_auth_is_supported_hmac(sctp_hmaclist_t * list, uint16_t id)
1262178676Ssam{
1263220701Sbschmidt	int i;
1264220701Sbschmidt
1265220701Sbschmidt	if ((list == NULL) || (id == SCTP_AUTH_HMAC_ID_RSVD))
1266220701Sbschmidt		return (0);
1267178676Ssam
1268220701Sbschmidt	for (i = 0; i < list->num_algo; i++)
1269178676Ssam		if (list->hmac[i] == id)
1270178676Ssam			return (1);
1271178676Ssam
1272206477Sbschmidt	/* not in the list */
1273198429Srpaulo	return (0);
1274178676Ssam}
1275198429Srpaulo
1276220691Sbschmidt
1277220728Sbschmidt/*-
1278178676Ssam * clear any cached key(s) if they match the given key id on an association.
1279178676Ssam * the cached key(s) will be recomputed and re-cached at next use.
1280206477Sbschmidt * ASSUMES TCB_LOCK is already held
1281198429Srpaulo */
1282178676Ssamvoid
1283198429Srpaulosctp_clear_cachedkeys(struct sctp_tcb *stcb, uint16_t keyid)
1284178676Ssam{
1285178676Ssam	if (stcb == NULL)
1286206477Sbschmidt		return;
1287178676Ssam
1288178676Ssam	if (keyid == stcb->asoc.authinfo.assoc_keyid) {
1289198429Srpaulo		sctp_free_key(stcb->asoc.authinfo.assoc_key);
1290220691Sbschmidt		stcb->asoc.authinfo.assoc_key = NULL;
1291178676Ssam	}
1292178676Ssam	if (keyid == stcb->asoc.authinfo.recv_keyid) {
1293206477Sbschmidt		sctp_free_key(stcb->asoc.authinfo.recv_key);
1294178676Ssam		stcb->asoc.authinfo.recv_key = NULL;
1295178676Ssam	}
1296178676Ssam}
1297178676Ssam
1298178676Ssam/*-
1299206477Sbschmidt * clear any cached key(s) if they match the given key id for all assocs on
1300201209Srpaulo * an endpoint.
1301201209Srpaulo * ASSUMES INP_WLOCK is already held
1302201209Srpaulo */
1303220691Sbschmidtvoid
1304220691Sbschmidtsctp_clear_cachedkeys_ep(struct sctp_inpcb *inp, uint16_t keyid)
1305201209Srpaulo{
1306201209Srpaulo	struct sctp_tcb *stcb;
1307206477Sbschmidt
1308201209Srpaulo	if (inp == NULL)
1309201209Srpaulo		return;
1310201209Srpaulo
1311201209Srpaulo	/* clear the cached keys on all assocs on this instance */
1312201209Srpaulo	LIST_FOREACH(stcb, &inp->sctp_asoc_list, sctp_tcblist) {
1313206477Sbschmidt		SCTP_TCB_LOCK(stcb);
1314178676Ssam		sctp_clear_cachedkeys(stcb, keyid);
1315178676Ssam		SCTP_TCB_UNLOCK(stcb);
1316198429Srpaulo	}
1317220728Sbschmidt}
1318178676Ssam
1319178676Ssam/*-
1320206477Sbschmidt * delete a shared key from an association
1321178676Ssam * ASSUMES TCB_LOCK is already held
1322178676Ssam */
1323178676Ssamint
1324178676Ssamsctp_delete_sharedkey(struct sctp_tcb *stcb, uint16_t keyid)
1325178676Ssam{
1326206477Sbschmidt	sctp_sharedkey_t *skey;
1327178676Ssam
1328178676Ssam	if (stcb == NULL)
1329198429Srpaulo		return (-1);
1330178676Ssam
1331178676Ssam	/* is the keyid the assoc active sending key */
1332178676Ssam	if (keyid == stcb->asoc.authinfo.active_keyid)
1333178676Ssam		return (-1);
1334198429Srpaulo
1335198429Srpaulo	/* does the key exist? */
1336220691Sbschmidt	skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, keyid);
1337220691Sbschmidt	if (skey == NULL)
1338178676Ssam		return (-1);
1339178676Ssam
1340220711Sbschmidt	/* are there other refcount holders on the key? */
1341178676Ssam	if (skey->refcount > 1)
1342178676Ssam		return (-1);
1343178676Ssam
1344178676Ssam	/* remove it */
1345220702Sbschmidt	LIST_REMOVE(skey, next);
1346220702Sbschmidt	sctp_free_sharedkey(skey);	/* frees skey->key as well */
1347220702Sbschmidt
1348198429Srpaulo	/* clear any cached keys */
1349198429Srpaulo	sctp_clear_cachedkeys(stcb, keyid);
1350220711Sbschmidt	return (0);
1351178676Ssam}
1352198429Srpaulo
1353198429Srpaulo/*-
1354178676Ssam * deletes a shared key from the endpoint
1355220702Sbschmidt * ASSUMES INP_WLOCK is already held
1356220702Sbschmidt */
1357220702Sbschmidtint
1358220702Sbschmidtsctp_delete_sharedkey_ep(struct sctp_inpcb *inp, uint16_t keyid)
1359220702Sbschmidt{
1360198429Srpaulo	sctp_sharedkey_t *skey;
1361198429Srpaulo
1362220711Sbschmidt	if (inp == NULL)
1363198429Srpaulo		return (-1);
1364198429Srpaulo
1365198429Srpaulo	/* is the keyid the active sending key on the endpoint */
1366198429Srpaulo	if (keyid == inp->sctp_ep.default_keyid)
1367178676Ssam		return (-1);
1368198429Srpaulo
1369178676Ssam	/* does the key exist? */
1370178676Ssam	skey = sctp_find_sharedkey(&inp->sctp_ep.shared_keys, keyid);
1371178676Ssam	if (skey == NULL)
1372178676Ssam		return (-1);
1373178676Ssam
1374201209Srpaulo	/* endpoint keys are not refcounted */
1375178676Ssam
1376178676Ssam	/* remove it */
1377220711Sbschmidt	LIST_REMOVE(skey, next);
1378178676Ssam	sctp_free_sharedkey(skey);	/* frees skey->key as well */
1379178676Ssam
1380178676Ssam	/* clear any cached keys */
1381198429Srpaulo	sctp_clear_cachedkeys_ep(inp, keyid);
1382220692Sbschmidt	return (0);
1383220692Sbschmidt}
1384198439Srpaulo
1385178676Ssam/*-
1386220711Sbschmidt * set the active key on an association
1387220710Sbschmidt * ASSUMES TCB_LOCK is already held
1388178676Ssam */
1389178676Ssamint
1390198429Srpaulosctp_auth_setactivekey(struct sctp_tcb *stcb, uint16_t keyid)
1391201209Srpaulo{
1392220692Sbschmidt	sctp_sharedkey_t *skey = NULL;
1393220692Sbschmidt
1394178676Ssam	/* find the key on the assoc */
1395178676Ssam	skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, keyid);
1396220711Sbschmidt	if (skey == NULL) {
1397220711Sbschmidt		/* that key doesn't exist */
1398178676Ssam		return (-1);
1399178676Ssam	}
1400178676Ssam	if ((skey->deactivated) && (skey->refcount > 1)) {
1401198429Srpaulo		/* can't reactivate a deactivated key with other refcounts */
1402178676Ssam		return (-1);
1403178676Ssam	}
1404220726Sbschmidt	/* set the (new) active key */
1405178676Ssam	stcb->asoc.authinfo.active_keyid = keyid;
1406178676Ssam	/* reset the deactivated flag */
1407220726Sbschmidt	skey->deactivated = 0;
1408178676Ssam
1409220726Sbschmidt	return (0);
1410220726Sbschmidt}
1411178676Ssam
1412178676Ssam/*-
1413178676Ssam * set the active key on an endpoint
1414206477Sbschmidt * ASSUMES INP_WLOCK is already held
1415178676Ssam */
1416178676Ssamint
1417178676Ssamsctp_auth_setactivekey_ep(struct sctp_inpcb *inp, uint16_t keyid)
1418178676Ssam{
1419198429Srpaulo	sctp_sharedkey_t *skey;
1420198429Srpaulo
1421198429Srpaulo	/* find the key */
1422198429Srpaulo	skey = sctp_find_sharedkey(&inp->sctp_ep.shared_keys, keyid);
1423198429Srpaulo	if (skey == NULL) {
1424198429Srpaulo		/* that key doesn't exist */
1425198429Srpaulo		return (-1);
1426198429Srpaulo	}
1427198429Srpaulo	inp->sctp_ep.default_keyid = keyid;
1428198429Srpaulo	return (0);
1429178676Ssam}
1430198429Srpaulo
1431178676Ssam/*-
1432178676Ssam * deactivates a shared key from the association
1433206477Sbschmidt * ASSUMES INP_WLOCK is already held
1434178676Ssam */
1435178676Ssamint
1436178676Ssamsctp_deact_sharedkey(struct sctp_tcb *stcb, uint16_t keyid)
1437178676Ssam{
1438178676Ssam	sctp_sharedkey_t *skey;
1439198429Srpaulo
1440178676Ssam	if (stcb == NULL)
1441198429Srpaulo		return (-1);
1442198429Srpaulo
1443198429Srpaulo	/* is the keyid the assoc active sending key */
1444198429Srpaulo	if (keyid == stcb->asoc.authinfo.active_keyid)
1445201209Srpaulo		return (-1);
1446198439Srpaulo
1447201209Srpaulo	/* does the key exist? */
1448198429Srpaulo	skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, keyid);
1449220710Sbschmidt	if (skey == NULL)
1450198429Srpaulo		return (-1);
1451201209Srpaulo
1452201209Srpaulo	/* are there other refcount holders on the key? */
1453198429Srpaulo	if (skey->refcount == 1) {
1454220701Sbschmidt		/* no other users, send a notification for this key */
1455220701Sbschmidt		sctp_ulp_notify(SCTP_NOTIFY_AUTH_FREE_KEY, stcb, keyid, 0,
1456220701Sbschmidt		    SCTP_SO_LOCKED);
1457220701Sbschmidt	}
1458178676Ssam	/* mark the key as deactivated */
1459178676Ssam	skey->deactivated = 1;
1460206477Sbschmidt
1461178676Ssam	return (0);
1462178676Ssam}
1463220723Sbschmidt
1464178676Ssam/*-
1465178676Ssam * deactivates a shared key from the endpoint
1466178676Ssam * ASSUMES INP_WLOCK is already held
1467178676Ssam */
1468178676Ssamint
1469178676Ssamsctp_deact_sharedkey_ep(struct sctp_inpcb *inp, uint16_t keyid)
1470178676Ssam{
1471220725Sbschmidt	sctp_sharedkey_t *skey;
1472220726Sbschmidt
1473220691Sbschmidt	if (inp == NULL)
1474220691Sbschmidt		return (-1);
1475178676Ssam
1476178676Ssam	/* is the keyid the active sending key on the endpoint */
1477198429Srpaulo	if (keyid == inp->sctp_ep.default_keyid)
1478178676Ssam		return (-1);
1479178676Ssam
1480178676Ssam	/* does the key exist? */
1481198429Srpaulo	skey = sctp_find_sharedkey(&inp->sctp_ep.shared_keys, keyid);
1482220726Sbschmidt	if (skey == NULL)
1483220691Sbschmidt		return (-1);
1484220691Sbschmidt
1485178676Ssam	/* endpoint keys are not refcounted */
1486178676Ssam
1487198429Srpaulo	/* remove it */
1488178676Ssam	LIST_REMOVE(skey, next);
1489178676Ssam	sctp_free_sharedkey(skey);	/* frees skey->key as well */
1490178676Ssam
1491178676Ssam	return (0);
1492198429Srpaulo}
1493220726Sbschmidt
1494220726Sbschmidt/*
1495220726Sbschmidt * get local authentication parameters from cookie (from INIT-ACK)
1496198429Srpaulo */
1497198429Srpaulovoid
1498220711Sbschmidtsctp_auth_get_cookie_params(struct sctp_tcb *stcb, struct mbuf *m,
1499178676Ssam    uint32_t offset, uint32_t length)
1500198429Srpaulo{
1501198429Srpaulo	struct sctp_paramhdr *phdr, tmp_param;
1502178676Ssam	uint16_t plen, ptype;
1503198429Srpaulo	uint8_t random_store[SCTP_PARAM_BUFFER_SIZE];
1504178676Ssam	struct sctp_auth_random *p_random = NULL;
1505178676Ssam	uint16_t random_len = 0;
1506178676Ssam	uint8_t hmacs_store[SCTP_PARAM_BUFFER_SIZE];
1507198429Srpaulo	struct sctp_auth_hmac_algo *hmacs = NULL;
1508198429Srpaulo	uint16_t hmacs_len = 0;
1509198429Srpaulo	uint8_t chunks_store[SCTP_PARAM_BUFFER_SIZE];
1510198429Srpaulo	struct sctp_auth_chunk_list *chunks = NULL;
1511201209Srpaulo	uint16_t num_chunks = 0;
1512178676Ssam	sctp_key_t *new_key;
1513178676Ssam	uint32_t keylen;
1514220711Sbschmidt
1515178676Ssam	/* convert to upper bound */
1516178676Ssam	length += offset;
1517178676Ssam
1518178676Ssam	phdr = (struct sctp_paramhdr *)sctp_m_getptr(m, offset,
1519178676Ssam	    sizeof(struct sctp_paramhdr), (uint8_t *) & tmp_param);
1520220726Sbschmidt	while (phdr != NULL) {
1521220726Sbschmidt		ptype = ntohs(phdr->param_type);
1522178676Ssam		plen = ntohs(phdr->param_length);
1523178676Ssam
1524178676Ssam		if ((plen == 0) || (offset + plen > length))
1525206477Sbschmidt			break;
1526178676Ssam
1527178676Ssam		if (ptype == SCTP_RANDOM) {
1528198429Srpaulo			if (plen > sizeof(random_store))
1529178676Ssam				break;
1530178676Ssam			phdr = sctp_get_next_param(m, offset,
1531178676Ssam			    (struct sctp_paramhdr *)random_store, min(plen, sizeof(random_store)));
1532178676Ssam			if (phdr == NULL)
1533178676Ssam				return;
1534220704Sbschmidt			/* save the random and length for the key */
1535220704Sbschmidt			p_random = (struct sctp_auth_random *)phdr;
1536201209Srpaulo			random_len = plen - sizeof(*p_random);
1537178676Ssam		} else if (ptype == SCTP_HMAC_LIST) {
1538178676Ssam			int num_hmacs;
1539178676Ssam			int i;
1540178676Ssam
1541198429Srpaulo			if (plen > sizeof(hmacs_store))
1542198429Srpaulo				break;
1543198439Srpaulo			phdr = sctp_get_next_param(m, offset,
1544198439Srpaulo			    (struct sctp_paramhdr *)hmacs_store, min(plen, sizeof(hmacs_store)));
1545198429Srpaulo			if (phdr == NULL)
1546178676Ssam				return;
1547178676Ssam			/* save the hmacs list and num for the key */
1548178676Ssam			hmacs = (struct sctp_auth_hmac_algo *)phdr;
1549178676Ssam			hmacs_len = plen - sizeof(*hmacs);
1550206477Sbschmidt			num_hmacs = hmacs_len / sizeof(hmacs->hmac_ids[0]);
1551178676Ssam			if (stcb->asoc.local_hmacs != NULL)
1552178676Ssam				sctp_free_hmaclist(stcb->asoc.local_hmacs);
1553178676Ssam			stcb->asoc.local_hmacs = sctp_alloc_hmaclist(num_hmacs);
1554178676Ssam			if (stcb->asoc.local_hmacs != NULL) {
1555178676Ssam				for (i = 0; i < num_hmacs; i++) {
1556178676Ssam					(void)sctp_auth_add_hmacid(stcb->asoc.local_hmacs,
1557178676Ssam					    ntohs(hmacs->hmac_ids[i]));
1558201209Srpaulo				}
1559201209Srpaulo			}
1560178676Ssam		} else if (ptype == SCTP_CHUNK_LIST) {
1561201209Srpaulo			int i;
1562201209Srpaulo
1563201209Srpaulo			if (plen > sizeof(chunks_store))
1564201209Srpaulo				break;
1565201209Srpaulo			phdr = sctp_get_next_param(m, offset,
1566178676Ssam			    (struct sctp_paramhdr *)chunks_store, min(plen, sizeof(chunks_store)));
1567201209Srpaulo			if (phdr == NULL)
1568201209Srpaulo				return;
1569178676Ssam			chunks = (struct sctp_auth_chunk_list *)phdr;
1570220701Sbschmidt			num_chunks = plen - sizeof(*chunks);
1571220701Sbschmidt			/* save chunks list and num for the key */
1572220701Sbschmidt			if (stcb->asoc.local_auth_chunks != NULL)
1573220701Sbschmidt				sctp_clear_chunklist(stcb->asoc.local_auth_chunks);
1574178676Ssam			else
1575178676Ssam				stcb->asoc.local_auth_chunks = sctp_alloc_chunklist();
1576206477Sbschmidt			for (i = 0; i < num_chunks; i++) {
1577201209Srpaulo				(void)sctp_auth_add_chunk(chunks->chunk_types[i],
1578201209Srpaulo				    stcb->asoc.local_auth_chunks);
1579201209Srpaulo			}
1580201209Srpaulo		}
1581201209Srpaulo		/* get next parameter */
1582201209Srpaulo		offset += SCTP_SIZE32(plen);
1583201209Srpaulo		if (offset + sizeof(struct sctp_paramhdr) > length)
1584201209Srpaulo			break;
1585201209Srpaulo		phdr = (struct sctp_paramhdr *)sctp_m_getptr(m, offset, sizeof(struct sctp_paramhdr),
1586220725Sbschmidt		    (uint8_t *) & tmp_param);
1587201209Srpaulo	}
1588201209Srpaulo	/* concatenate the full random key */
1589201209Srpaulo	keylen = sizeof(*p_random) + random_len + sizeof(*hmacs) + hmacs_len;
1590201209Srpaulo	if (chunks != NULL) {
1591201209Srpaulo		keylen += sizeof(*chunks) + num_chunks;
1592201209Srpaulo	}
1593201209Srpaulo	new_key = sctp_alloc_key(keylen);
1594201209Srpaulo	if (new_key != NULL) {
1595201209Srpaulo		/* copy in the RANDOM */
1596201209Srpaulo		if (p_random != NULL) {
1597201209Srpaulo			keylen = sizeof(*p_random) + random_len;
1598201209Srpaulo			bcopy(p_random, new_key->key, keylen);
1599201209Srpaulo		}
1600201209Srpaulo		/* append in the AUTH chunks */
1601206477Sbschmidt		if (chunks != NULL) {
1602198429Srpaulo			bcopy(chunks, new_key->key + keylen,
1603178676Ssam			    sizeof(*chunks) + num_chunks);
1604220728Sbschmidt			keylen += sizeof(*chunks) + num_chunks;
1605220723Sbschmidt		}
1606198429Srpaulo		/* append in the HMACs */
1607178676Ssam		if (hmacs != NULL) {
1608198429Srpaulo			bcopy(hmacs, new_key->key + keylen,
1609198429Srpaulo			    sizeof(*hmacs) + hmacs_len);
1610198429Srpaulo		}
1611198429Srpaulo	}
1612198429Srpaulo	if (stcb->asoc.authinfo.random != NULL)
1613198429Srpaulo		sctp_free_key(stcb->asoc.authinfo.random);
1614178676Ssam	stcb->asoc.authinfo.random = new_key;
1615201209Srpaulo	stcb->asoc.authinfo.random_len = random_len;
1616220726Sbschmidt	sctp_clear_cachedkeys(stcb, stcb->asoc.authinfo.assoc_keyid);
1617201209Srpaulo	sctp_clear_cachedkeys(stcb, stcb->asoc.authinfo.recv_keyid);
1618220726Sbschmidt
1619220726Sbschmidt	/* negotiate what HMAC to use for the peer */
1620201209Srpaulo	stcb->asoc.peer_hmac_id = sctp_negotiate_hmacid(stcb->asoc.peer_hmacs,
1621201209Srpaulo	    stcb->asoc.local_hmacs);
1622201209Srpaulo
1623198429Srpaulo	/* copy defaults from the endpoint */
1624198429Srpaulo	/* FIX ME: put in cookie? */
1625198429Srpaulo	stcb->asoc.authinfo.active_keyid = stcb->sctp_ep->sctp_ep.default_keyid;
1626198429Srpaulo	/* copy out the shared key list (by reference) from the endpoint */
1627220726Sbschmidt	(void)sctp_copy_skeylist(&stcb->sctp_ep->sctp_ep.shared_keys,
1628220726Sbschmidt	    &stcb->asoc.shared_keys);
1629198429Srpaulo}
1630198429Srpaulo
1631198429Srpaulo/*
1632201209Srpaulo * compute and fill in the HMAC digest for a packet
1633220726Sbschmidt */
1634201209Srpaulovoid
1635201209Srpaulosctp_fill_hmac_digest_m(struct mbuf *m, uint32_t auth_offset,
1636201209Srpaulo    struct sctp_auth_chunk *auth, struct sctp_tcb *stcb, uint16_t keyid)
1637201209Srpaulo{
1638201209Srpaulo	uint32_t digestlen;
1639198429Srpaulo	sctp_sharedkey_t *skey;
1640178676Ssam	sctp_key_t *key;
1641220729Sbschmidt
1642220729Sbschmidt	if ((stcb == NULL) || (auth == NULL))
1643220729Sbschmidt		return;
1644220729Sbschmidt
1645220729Sbschmidt	/* zero the digest + chunk padding */
1646220729Sbschmidt	digestlen = sctp_get_hmac_digest_len(stcb->asoc.peer_hmac_id);
1647198429Srpaulo	bzero(auth->hmac, SCTP_SIZE32(digestlen));
1648198429Srpaulo
1649198429Srpaulo	/* is the desired key cached? */
1650220727Sbschmidt	if ((keyid != stcb->asoc.authinfo.assoc_keyid) ||
1651220727Sbschmidt	    (stcb->asoc.authinfo.assoc_key == NULL)) {
1652220727Sbschmidt		if (stcb->asoc.authinfo.assoc_key != NULL) {
1653220727Sbschmidt			/* free the old cached key */
1654220727Sbschmidt			sctp_free_key(stcb->asoc.authinfo.assoc_key);
1655178676Ssam		}
1656198429Srpaulo		skey = sctp_find_sharedkey(&stcb->asoc.shared_keys, keyid);
1657198429Srpaulo		/* the only way skey is NULL is if null key id 0 is used */
1658178676Ssam		if (skey != NULL)
1659198429Srpaulo			key = skey->key;
1660220728Sbschmidt		else
1661178676Ssam			key = NULL;
1662201209Srpaulo		/* compute a new assoc key and cache it */
1663201209Srpaulo		stcb->asoc.authinfo.assoc_key =
1664198429Srpaulo		    sctp_compute_hashkey(stcb->asoc.authinfo.random,
1665198429Srpaulo		    stcb->asoc.authinfo.peer_random, key);
1666178676Ssam		stcb->asoc.authinfo.assoc_keyid = keyid;
1667178676Ssam		SCTPDBG(SCTP_DEBUG_AUTH1, "caching key id %u\n",
1668206477Sbschmidt		    stcb->asoc.authinfo.assoc_keyid);
1669198429Srpaulo#ifdef SCTP_DEBUG
1670178676Ssam		if (SCTP_AUTH_DEBUG)
1671201209Srpaulo			sctp_print_key(stcb->asoc.authinfo.assoc_key,
1672220723Sbschmidt			    "Assoc Key");
1673198429Srpaulo#endif
1674178676Ssam	}
1675220725Sbschmidt	/* set in the active key id */
1676198429Srpaulo	auth->shared_key_id = htons(keyid);
1677178676Ssam
1678220725Sbschmidt	/* compute and fill in the digest */
1679221636Sbschmidt	(void)sctp_compute_hmac_m(stcb->asoc.peer_hmac_id, stcb->asoc.authinfo.assoc_key,
1680201209Srpaulo	    m, auth_offset, auth->hmac);
1681201209Srpaulo}
1682201209Srpaulo
1683198429Srpaulo
1684198429Srpaulostatic void
1685198429Srpaulosctp_bzero_m(struct mbuf *m, uint32_t m_offset, uint32_t size)
1686198429Srpaulo{
1687198429Srpaulo	struct mbuf *m_tmp;
1688198429Srpaulo	uint8_t *data;
1689198429Srpaulo
1690198429Srpaulo	/* sanity check */
1691198429Srpaulo	if (m == NULL)
1692198429Srpaulo		return;
1693198429Srpaulo
1694198429Srpaulo	/* find the correct starting mbuf and offset (get start position) */
1695198429Srpaulo	m_tmp = m;
1696198429Srpaulo	while ((m_tmp != NULL) && (m_offset >= (uint32_t) SCTP_BUF_LEN(m_tmp))) {
1697198429Srpaulo		m_offset -= SCTP_BUF_LEN(m_tmp);
1698198429Srpaulo		m_tmp = SCTP_BUF_NEXT(m_tmp);
1699198429Srpaulo	}
1700198429Srpaulo	/* now use the rest of the mbuf chain */
1701198429Srpaulo	while ((m_tmp != NULL) && (size > 0)) {
1702198429Srpaulo		data = mtod(m_tmp, uint8_t *) + m_offset;
1703198429Srpaulo		if (size > (uint32_t) SCTP_BUF_LEN(m_tmp)) {
1704198429Srpaulo			bzero(data, SCTP_BUF_LEN(m_tmp));
1705198429Srpaulo			size -= SCTP_BUF_LEN(m_tmp);
1706198429Srpaulo		} else {
1707198429Srpaulo			bzero(data, size);
1708201209Srpaulo			size = 0;
1709198429Srpaulo		}
1710198429Srpaulo		/* clear the offset since it's only for the first mbuf */
1711178676Ssam		m_offset = 0;
1712198429Srpaulo		m_tmp = SCTP_BUF_NEXT(m_tmp);
1713178676Ssam	}
1714178676Ssam}
1715198429Srpaulo
1716206477Sbschmidt/*-
1717198429Srpaulo * process the incoming Authentication chunk
1718178676Ssam * return codes:
1719198429Srpaulo *   -1 on any authentication error
1720198429Srpaulo *    0 on authentication verification
1721198429Srpaulo */
1722178676Ssamint
1723198429Srpaulosctp_handle_auth(struct sctp_tcb *stcb, struct sctp_auth_chunk *auth,
1724198429Srpaulo    struct mbuf *m, uint32_t offset)
1725198429Srpaulo{
1726198429Srpaulo	uint16_t chunklen;
1727198429Srpaulo	uint16_t shared_key_id;
1728198429Srpaulo	uint16_t hmac_id;
1729198429Srpaulo	sctp_sharedkey_t *skey;
1730198429Srpaulo	uint32_t digestlen;
1731198429Srpaulo	uint8_t digest[SCTP_AUTH_DIGEST_LEN_MAX];
1732198429Srpaulo	uint8_t computed_digest[SCTP_AUTH_DIGEST_LEN_MAX];
1733198429Srpaulo
1734198429Srpaulo	/* auth is checked for NULL by caller */
1735198429Srpaulo	chunklen = ntohs(auth->ch.chunk_length);
1736198429Srpaulo	if (chunklen < sizeof(*auth)) {
1737198429Srpaulo		SCTP_STAT_INCR(sctps_recvauthfailed);
1738198429Srpaulo		return (-1);
1739198429Srpaulo	}
1740198429Srpaulo	SCTP_STAT_INCR(sctps_recvauth);
1741198429Srpaulo
1742198429Srpaulo	/* get the auth params */
1743198429Srpaulo	shared_key_id = ntohs(auth->shared_key_id);
1744198429Srpaulo	hmac_id = ntohs(auth->hmac_id);
1745198429Srpaulo	SCTPDBG(SCTP_DEBUG_AUTH1,
1746198429Srpaulo	    "SCTP AUTH Chunk: shared key %u, HMAC id %u\n",
1747178676Ssam	    shared_key_id, hmac_id);
1748198429Srpaulo
1749178676Ssam	/* is the indicated HMAC supported? */
1750206477Sbschmidt	if (!sctp_auth_is_supported_hmac(stcb->asoc.local_hmacs, hmac_id)) {
1751198429Srpaulo		struct mbuf *m_err;
1752178676Ssam		struct sctp_auth_invalid_hmac *err;
1753206444Sbschmidt
1754220674Sbschmidt		SCTP_STAT_INCR(sctps_recvivalhmacid);
1755220723Sbschmidt		SCTPDBG(SCTP_DEBUG_AUTH1,
1756220723Sbschmidt		    "SCTP Auth: unsupported HMAC id %u\n",
1757198429Srpaulo		    hmac_id);
1758178676Ssam		/*
1759220725Sbschmidt		 * report this in an Error Chunk: Unsupported HMAC
1760198429Srpaulo		 * Identifier
1761198429Srpaulo		 */
1762198429Srpaulo		m_err = sctp_get_mbuf_for_msg(sizeof(*err), 0, M_DONTWAIT,
1763198429Srpaulo		    1, MT_HEADER);
1764178676Ssam		if (m_err != NULL) {
1765220725Sbschmidt			/* pre-reserve some space */
1766221636Sbschmidt			SCTP_BUF_RESV_UF(m_err, sizeof(struct sctp_chunkhdr));
1767221635Sbschmidt			/* fill in the error */
1768221635Sbschmidt			err = mtod(m_err, struct sctp_auth_invalid_hmac *);
1769221635Sbschmidt			bzero(err, sizeof(*err));
1770221635Sbschmidt			err->ph.param_type = htons(SCTP_CAUSE_UNSUPPORTED_HMACID);
1771201209Srpaulo			err->ph.param_length = htons(sizeof(*err));
1772198429Srpaulo			err->hmac_id = ntohs(hmac_id);
1773178676Ssam			SCTP_BUF_LEN(m_err) = sizeof(*err);
1774201209Srpaulo			/* queue it */
1775201209Srpaulo			sctp_queue_op_err(stcb, m_err);
1776201209Srpaulo		}
1777201209Srpaulo		return (-1);
1778198429Srpaulo	}
1779198429Srpaulo	/* get the indicated shared key, if available */
1780206444Sbschmidt	if ((stcb->asoc.authinfo.recv_key == NULL) ||
1781206444Sbschmidt	    (stcb->asoc.authinfo.recv_keyid != shared_key_id)) {
1782220726Sbschmidt		/* find the shared key on the assoc first */
1783220726Sbschmidt		skey = sctp_find_sharedkey(&stcb->asoc.shared_keys,
1784210108Sbschmidt		    shared_key_id);
1785206444Sbschmidt		/* if the shared key isn't found, discard the chunk */
1786198429Srpaulo		if (skey == NULL) {
1787201209Srpaulo			SCTP_STAT_INCR(sctps_recvivalkeyid);
1788198429Srpaulo			SCTPDBG(SCTP_DEBUG_AUTH1,
1789220674Sbschmidt			    "SCTP Auth: unknown key id %u\n",
1790198429Srpaulo			    shared_key_id);
1791198429Srpaulo			return (-1);
1792220674Sbschmidt		}
1793201209Srpaulo		/* generate a notification if this is a new key id */
1794220674Sbschmidt		if (stcb->asoc.authinfo.recv_keyid != shared_key_id)
1795220674Sbschmidt			/*
1796220674Sbschmidt			 * sctp_ulp_notify(SCTP_NOTIFY_AUTH_NEW_KEY, stcb,
1797220674Sbschmidt			 * shared_key_id, (void
1798220674Sbschmidt			 * *)stcb->asoc.authinfo.recv_keyid);
1799220674Sbschmidt			 */
1800220674Sbschmidt			sctp_notify_authentication(stcb, SCTP_AUTH_NEWKEY,
1801178676Ssam			    shared_key_id, stcb->asoc.authinfo.recv_keyid,
1802178676Ssam			    SCTP_SO_NOT_LOCKED);
1803178676Ssam		/* compute a new recv assoc key and cache it */
1804201209Srpaulo		if (stcb->asoc.authinfo.recv_key != NULL)
1805201209Srpaulo			sctp_free_key(stcb->asoc.authinfo.recv_key);
1806201209Srpaulo		stcb->asoc.authinfo.recv_key =
1807201209Srpaulo		    sctp_compute_hashkey(stcb->asoc.authinfo.random,
1808201209Srpaulo		    stcb->asoc.authinfo.peer_random, skey->key);
1809201209Srpaulo		stcb->asoc.authinfo.recv_keyid = shared_key_id;
1810201209Srpaulo#ifdef SCTP_DEBUG
1811201209Srpaulo		if (SCTP_AUTH_DEBUG)
1812201209Srpaulo			sctp_print_key(stcb->asoc.authinfo.recv_key, "Recv Key");
1813201209Srpaulo#endif
1814201209Srpaulo	}
1815201209Srpaulo	/* validate the digest length */
1816201209Srpaulo	digestlen = sctp_get_hmac_digest_len(hmac_id);
1817201209Srpaulo	if (chunklen < (sizeof(*auth) + digestlen)) {
1818201209Srpaulo		/* invalid digest length */
1819201209Srpaulo		SCTP_STAT_INCR(sctps_recvauthfailed);
1820201209Srpaulo		SCTPDBG(SCTP_DEBUG_AUTH1,
1821201209Srpaulo		    "SCTP Auth: chunk too short for HMAC\n");
1822201209Srpaulo		return (-1);
1823201209Srpaulo	}
1824201209Srpaulo	/* save a copy of the digest, zero the pseudo header, and validate */
1825201209Srpaulo	bcopy(auth->hmac, digest, digestlen);
1826198429Srpaulo	sctp_bzero_m(m, offset + sizeof(*auth), SCTP_SIZE32(digestlen));
1827201209Srpaulo	(void)sctp_compute_hmac_m(hmac_id, stcb->asoc.authinfo.recv_key,
1828178676Ssam	    m, offset, computed_digest);
1829198429Srpaulo
1830198429Srpaulo	/* compare the computed digest with the one in the AUTH chunk */
1831201209Srpaulo	if (memcmp(digest, computed_digest, digestlen) != 0) {
1832201209Srpaulo		SCTP_STAT_INCR(sctps_recvauthfailed);
1833198429Srpaulo		SCTPDBG(SCTP_DEBUG_AUTH1,
1834220687Sbschmidt		    "SCTP Auth: HMAC digest check failed\n");
1835220687Sbschmidt		return (-1);
1836178676Ssam	}
1837198429Srpaulo	return (0);
1838198429Srpaulo}
1839198429Srpaulo
1840198429Srpaulo/*
1841198429Srpaulo * Generate NOTIFICATION
1842198429Srpaulo */
1843198429Srpaulovoid
1844198429Srpaulosctp_notify_authentication(struct sctp_tcb *stcb, uint32_t indication,
1845198429Srpaulo    uint16_t keyid, uint16_t alt_keyid, int so_locked
1846201209Srpaulo#if !defined(__APPLE__) && !defined(SCTP_SO_LOCK_TESTING)
1847178676Ssam    SCTP_UNUSED
1848198429Srpaulo#endif
1849198429Srpaulo)
1850198429Srpaulo{
1851198429Srpaulo	struct mbuf *m_notify;
1852206445Sbschmidt	struct sctp_authkey_event *auth;
1853201209Srpaulo	struct sctp_queued_to_read *control;
1854220726Sbschmidt
1855198429Srpaulo	if ((stcb == NULL) ||
1856198429Srpaulo	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
1857198429Srpaulo	    (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE) ||
1858198429Srpaulo	    (stcb->asoc.state & SCTP_STATE_CLOSED_SOCKET)
1859198429Srpaulo	    ) {
1860198429Srpaulo		/* If the socket is gone we are out of here */
1861220726Sbschmidt		return;
1862198429Srpaulo	}
1863178676Ssam	if (sctp_is_feature_off(stcb->sctp_ep, SCTP_PCB_FLAGS_AUTHEVNT))
1864220723Sbschmidt		/* event not enabled */
1865220723Sbschmidt		return;
1866220723Sbschmidt
1867220723Sbschmidt	m_notify = sctp_get_mbuf_for_msg(sizeof(struct sctp_authkey_event),
1868220723Sbschmidt	    0, M_DONTWAIT, 1, MT_HEADER);
1869220726Sbschmidt	if (m_notify == NULL)
1870220726Sbschmidt		/* no space left */
1871220723Sbschmidt		return;
1872221636Sbschmidt
1873221636Sbschmidt	SCTP_BUF_LEN(m_notify) = 0;
1874221636Sbschmidt	auth = mtod(m_notify, struct sctp_authkey_event *);
1875221636Sbschmidt	auth->auth_type = SCTP_AUTHENTICATION_EVENT;
1876221636Sbschmidt	auth->auth_flags = 0;
1877221636Sbschmidt	auth->auth_length = sizeof(*auth);
1878178676Ssam	auth->auth_keynumber = keyid;
1879178676Ssam	auth->auth_altkeynumber = alt_keyid;
1880178676Ssam	auth->auth_indication = indication;
1881198429Srpaulo	auth->auth_assoc_id = sctp_get_associd(stcb);
1882201209Srpaulo
1883178676Ssam	SCTP_BUF_LEN(m_notify) = sizeof(*auth);
1884198429Srpaulo	SCTP_BUF_NEXT(m_notify) = NULL;
1885198429Srpaulo
1886201209Srpaulo	/* append to socket */
1887201209Srpaulo	control = sctp_build_readq_entry(stcb, stcb->asoc.primary_destination,
1888198429Srpaulo	    0, 0, 0, 0, 0, 0, m_notify);
1889221636Sbschmidt	if (control == NULL) {
1890221636Sbschmidt		/* no memory */
1891178676Ssam		sctp_m_freem(m_notify);
1892221636Sbschmidt		return;
1893221636Sbschmidt	}
1894221636Sbschmidt	control->spec_flags = M_NOTIFICATION;
1895198429Srpaulo	control->length = SCTP_BUF_LEN(m_notify);
1896221636Sbschmidt	/* not that we need this */
1897198429Srpaulo	control->tail_mbuf = m_notify;
1898198429Srpaulo	sctp_add_to_readq(stcb->sctp_ep, stcb, control,
1899198429Srpaulo	    &stcb->sctp_socket->so_rcv, 1, SCTP_READ_LOCK_NOT_HELD, so_locked);
1900198429Srpaulo}
1901198429Srpaulo
1902198429Srpaulo
1903221636Sbschmidt/*-
1904221636Sbschmidt * validates the AUTHentication related parameters in an INIT/INIT-ACK
1905221636Sbschmidt * Note: currently only used for INIT as INIT-ACK is handled inline
1906198429Srpaulo * with sctp_load_addresses_from_init()
1907198429Srpaulo */
1908198429Srpauloint
1909198429Srpaulosctp_validate_init_auth_params(struct mbuf *m, int offset, int limit)
1910221636Sbschmidt{
1911221636Sbschmidt	struct sctp_paramhdr *phdr, parm_buf;
1912198429Srpaulo	uint16_t ptype, plen;
1913198429Srpaulo	int peer_supports_asconf = 0;
1914221636Sbschmidt	int peer_supports_auth = 0;
1915198429Srpaulo	int got_random = 0, got_hmacs = 0, got_chklist = 0;
1916198429Srpaulo	uint8_t saw_asconf = 0;
1917198429Srpaulo	uint8_t saw_asconf_ack = 0;
1918221636Sbschmidt
1919198429Srpaulo	/* go through each of the params. */
1920198429Srpaulo	phdr = sctp_get_next_param(m, offset, &parm_buf, sizeof(parm_buf));
1921221636Sbschmidt	while (phdr) {
1922221636Sbschmidt		ptype = ntohs(phdr->param_type);
1923198429Srpaulo		plen = ntohs(phdr->param_length);
1924198429Srpaulo
1925178676Ssam		if (offset + plen > limit) {
1926198429Srpaulo			break;
1927198429Srpaulo		}
1928221636Sbschmidt		if (plen < sizeof(struct sctp_paramhdr)) {
1929178676Ssam			break;
1930198429Srpaulo		}
1931198429Srpaulo		if (ptype == SCTP_SUPPORTED_CHUNK_EXT) {
1932198429Srpaulo			/* A supported extension chunk */
1933198429Srpaulo			struct sctp_supported_chunk_types_param *pr_supported;
1934221636Sbschmidt			uint8_t local_store[SCTP_PARAM_BUFFER_SIZE];
1935198429Srpaulo			int num_ent, i;
1936198429Srpaulo
1937198429Srpaulo			phdr = sctp_get_next_param(m, offset,
1938198429Srpaulo			    (struct sctp_paramhdr *)&local_store, min(plen, sizeof(local_store)));
1939221636Sbschmidt			if (phdr == NULL) {
1940178676Ssam				return (-1);
1941198429Srpaulo			}
1942178676Ssam			pr_supported = (struct sctp_supported_chunk_types_param *)phdr;
1943198429Srpaulo			num_ent = plen - sizeof(struct sctp_paramhdr);
1944201209Srpaulo			for (i = 0; i < num_ent; i++) {
1945198429Srpaulo				switch (pr_supported->chunk_types[i]) {
1946198429Srpaulo				case SCTP_ASCONF:
1947198429Srpaulo				case SCTP_ASCONF_ACK:
1948178676Ssam					peer_supports_asconf = 1;
1949201209Srpaulo					break;
1950201209Srpaulo				case SCTP_AUTHENTICATION:
1951201209Srpaulo					peer_supports_auth = 1;
1952198429Srpaulo					break;
1953201209Srpaulo				default:
1954198429Srpaulo					/* one we don't care about */
1955201209Srpaulo					break;
1956198429Srpaulo				}
1957178676Ssam			}
1958178676Ssam		} else if (ptype == SCTP_RANDOM) {
1959220723Sbschmidt			got_random = 1;
1960220723Sbschmidt			/* enforce the random length */
1961220723Sbschmidt			if (plen != (sizeof(struct sctp_auth_random) +
1962221636Sbschmidt			    SCTP_AUTH_RANDOM_SIZE_REQUIRED)) {
1963220723Sbschmidt				SCTPDBG(SCTP_DEBUG_AUTH1,
1964221636Sbschmidt				    "SCTP: invalid RANDOM len\n");
1965221636Sbschmidt				return (-1);
1966221636Sbschmidt			}
1967221636Sbschmidt		} else if (ptype == SCTP_HMAC_LIST) {
1968221636Sbschmidt			uint8_t store[SCTP_PARAM_BUFFER_SIZE];
1969221636Sbschmidt			struct sctp_auth_hmac_algo *hmacs;
1970221636Sbschmidt			int num_hmacs;
1971221636Sbschmidt
1972221636Sbschmidt			if (plen > sizeof(store))
1973220723Sbschmidt				break;
1974221636Sbschmidt			phdr = sctp_get_next_param(m, offset,
1975221636Sbschmidt			    (struct sctp_paramhdr *)store, min(plen, sizeof(store)));
1976221636Sbschmidt			if (phdr == NULL)
1977221636Sbschmidt				return (-1);
1978221636Sbschmidt			hmacs = (struct sctp_auth_hmac_algo *)phdr;
1979221636Sbschmidt			num_hmacs = (plen - sizeof(*hmacs)) /
1980221636Sbschmidt			    sizeof(hmacs->hmac_ids[0]);
1981220723Sbschmidt			/* validate the hmac list */
1982220723Sbschmidt			if (sctp_verify_hmac_param(hmacs, num_hmacs)) {
1983220723Sbschmidt				SCTPDBG(SCTP_DEBUG_AUTH1,
1984220723Sbschmidt				    "SCTP: invalid HMAC param\n");
1985220723Sbschmidt				return (-1);
1986220723Sbschmidt			}
1987220723Sbschmidt			got_hmacs = 1;
1988220723Sbschmidt		} else if (ptype == SCTP_CHUNK_LIST) {
1989220723Sbschmidt			int i, num_chunks;
1990220723Sbschmidt			uint8_t chunks_store[SCTP_SMALL_CHUNK_STORE];
1991220723Sbschmidt
1992220723Sbschmidt			/* did the peer send a non-empty chunk list? */
1993220723Sbschmidt			struct sctp_auth_chunk_list *chunks = NULL;
1994220723Sbschmidt
1995220723Sbschmidt			phdr = sctp_get_next_param(m, offset,
1996220723Sbschmidt			    (struct sctp_paramhdr *)chunks_store,
1997220723Sbschmidt			    min(plen, sizeof(chunks_store)));
1998220723Sbschmidt			if (phdr == NULL)
1999220723Sbschmidt				return (-1);
2000220723Sbschmidt
2001220723Sbschmidt			/*-
2002220723Sbschmidt			 * Flip through the list and mark that the
2003220723Sbschmidt			 * peer supports asconf/asconf_ack.
2004220723Sbschmidt			 */
2005220723Sbschmidt			chunks = (struct sctp_auth_chunk_list *)phdr;
2006220723Sbschmidt			num_chunks = plen - sizeof(*chunks);
2007220723Sbschmidt			for (i = 0; i < num_chunks; i++) {
2008220723Sbschmidt				/* record asconf/asconf-ack if listed */
2009220723Sbschmidt				if (chunks->chunk_types[i] == SCTP_ASCONF)
2010220723Sbschmidt					saw_asconf = 1;
2011220723Sbschmidt				if (chunks->chunk_types[i] == SCTP_ASCONF_ACK)
2012201209Srpaulo					saw_asconf_ack = 1;
2013201209Srpaulo
2014206477Sbschmidt			}
2015201209Srpaulo			if (num_chunks)
2016201209Srpaulo				got_chklist = 1;
2017201209Srpaulo		}
2018221637Sbschmidt		offset += SCTP_SIZE32(plen);
2019221637Sbschmidt		if (offset >= limit) {
2020221637Sbschmidt			break;
2021201209Srpaulo		}
2022201209Srpaulo		phdr = sctp_get_next_param(m, offset, &parm_buf,
2023221637Sbschmidt		    sizeof(parm_buf));
2024221637Sbschmidt	}
2025201209Srpaulo	/* validate authentication required parameters */
2026201209Srpaulo	if (got_random && got_hmacs) {
2027201209Srpaulo		peer_supports_auth = 1;
2028201209Srpaulo	} else {
2029201209Srpaulo		peer_supports_auth = 0;
2030201209Srpaulo	}
2031201209Srpaulo	if (!peer_supports_auth && got_chklist) {
2032221637Sbschmidt		SCTPDBG(SCTP_DEBUG_AUTH1,
2033221637Sbschmidt		    "SCTP: peer sent chunk list w/o AUTH\n");
2034201209Srpaulo		return (-1);
2035201209Srpaulo	}
2036201209Srpaulo	if (!SCTP_BASE_SYSCTL(sctp_asconf_auth_nochk) && peer_supports_asconf &&
2037201209Srpaulo	    !peer_supports_auth) {
2038201209Srpaulo		SCTPDBG(SCTP_DEBUG_AUTH1,
2039201209Srpaulo		    "SCTP: peer supports ASCONF but not AUTH\n");
2040201209Srpaulo		return (-1);
2041201209Srpaulo	} else if ((peer_supports_asconf) && (peer_supports_auth) &&
2042201209Srpaulo	    ((saw_asconf == 0) || (saw_asconf_ack == 0))) {
2043201209Srpaulo		return (-2);
2044201209Srpaulo	}
2045201209Srpaulo	return (0);
2046201209Srpaulo}
2047201209Srpaulo
2048221637Sbschmidtvoid
2049221637Sbschmidtsctp_initialize_auth_params(struct sctp_inpcb *inp, struct sctp_tcb *stcb)
2050221637Sbschmidt{
2051221637Sbschmidt	uint16_t chunks_len = 0;
2052221637Sbschmidt	uint16_t hmacs_len = 0;
2053221637Sbschmidt	uint16_t random_len = SCTP_AUTH_RANDOM_SIZE_DEFAULT;
2054221637Sbschmidt	sctp_key_t *new_key;
2055221637Sbschmidt	uint16_t keylen;
2056221637Sbschmidt
2057221637Sbschmidt	/* initialize hmac list from endpoint */
2058221637Sbschmidt	stcb->asoc.local_hmacs = sctp_copy_hmaclist(inp->sctp_ep.local_hmacs);
2059221637Sbschmidt	if (stcb->asoc.local_hmacs != NULL) {
2060221637Sbschmidt		hmacs_len = stcb->asoc.local_hmacs->num_algo *
2061221637Sbschmidt		    sizeof(stcb->asoc.local_hmacs->hmac[0]);
2062221637Sbschmidt	}
2063221637Sbschmidt	/* initialize auth chunks list from endpoint */
2064221637Sbschmidt	stcb->asoc.local_auth_chunks =
2065221637Sbschmidt	    sctp_copy_chunklist(inp->sctp_ep.local_auth_chunks);
2066221637Sbschmidt	if (stcb->asoc.local_auth_chunks != NULL) {
2067221637Sbschmidt		int i;
2068221637Sbschmidt
2069221637Sbschmidt		for (i = 0; i < 256; i++) {
2070221637Sbschmidt			if (stcb->asoc.local_auth_chunks->chunks[i])
2071221637Sbschmidt				chunks_len++;
2072221637Sbschmidt		}
2073221637Sbschmidt	}
2074221637Sbschmidt	/* copy defaults from the endpoint */
2075201209Srpaulo	stcb->asoc.authinfo.active_keyid = inp->sctp_ep.default_keyid;
2076201209Srpaulo
2077201209Srpaulo	/* copy out the shared key list (by reference) from the endpoint */
2078206477Sbschmidt	(void)sctp_copy_skeylist(&inp->sctp_ep.shared_keys,
2079198429Srpaulo	    &stcb->asoc.shared_keys);
2080178676Ssam
2081198429Srpaulo	/* now set the concatenated key (random + chunks + hmacs) */
2082198429Srpaulo	/* key includes parameter headers */
2083178676Ssam	keylen = (3 * sizeof(struct sctp_paramhdr)) + random_len + chunks_len +
2084221648Sbschmidt	    hmacs_len;
2085221648Sbschmidt	new_key = sctp_alloc_key(keylen);
2086221648Sbschmidt	if (new_key != NULL) {
2087221648Sbschmidt		struct sctp_paramhdr *ph;
2088221648Sbschmidt		int plen;
2089221648Sbschmidt
2090221648Sbschmidt		/* generate and copy in the RANDOM */
2091221648Sbschmidt		ph = (struct sctp_paramhdr *)new_key->key;
2092221648Sbschmidt		ph->param_type = htons(SCTP_RANDOM);
2093221648Sbschmidt		plen = sizeof(*ph) + random_len;
2094221648Sbschmidt		ph->param_length = htons(plen);
2095221648Sbschmidt		SCTP_READ_RANDOM(new_key->key + sizeof(*ph), random_len);
2096221648Sbschmidt		keylen = plen;
2097221648Sbschmidt
2098221648Sbschmidt		/* append in the AUTH chunks */
2099221648Sbschmidt		/* NOTE: currently we always have chunks to list */
2100221648Sbschmidt		ph = (struct sctp_paramhdr *)(new_key->key + keylen);
2101221648Sbschmidt		ph->param_type = htons(SCTP_CHUNK_LIST);
2102221648Sbschmidt		plen = sizeof(*ph) + chunks_len;
2103221648Sbschmidt		ph->param_length = htons(plen);
2104220715Sbschmidt		keylen += sizeof(*ph);
2105220715Sbschmidt		if (stcb->asoc.local_auth_chunks) {
2106220715Sbschmidt			int i;
2107221648Sbschmidt
2108221648Sbschmidt			for (i = 0; i < 256; i++) {
2109220715Sbschmidt				if (stcb->asoc.local_auth_chunks->chunks[i])
2110221649Sbschmidt					new_key->key[keylen++] = i;
2111221648Sbschmidt			}
2112220715Sbschmidt		}
2113221648Sbschmidt		/* append in the HMACs */
2114221649Sbschmidt		ph = (struct sctp_paramhdr *)(new_key->key + keylen);
2115221649Sbschmidt		ph->param_type = htons(SCTP_HMAC_LIST);
2116221648Sbschmidt		plen = sizeof(*ph) + hmacs_len;
2117221649Sbschmidt		ph->param_length = htons(plen);
2118221649Sbschmidt		keylen += sizeof(*ph);
2119221649Sbschmidt		(void)sctp_serialize_hmaclist(stcb->asoc.local_hmacs,
2120221649Sbschmidt		    new_key->key + keylen);
2121221649Sbschmidt	}
2122221649Sbschmidt	if (stcb->asoc.authinfo.random != NULL)
2123221649Sbschmidt		sctp_free_key(stcb->asoc.authinfo.random);
2124221649Sbschmidt	stcb->asoc.authinfo.random = new_key;
2125221649Sbschmidt	stcb->asoc.authinfo.random_len = random_len;
2126221649Sbschmidt}
2127221649Sbschmidt