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