secport.c (27469B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * secport.c - portability interfaces for security libraries 7 * 8 * This file abstracts out libc functionality that libsec depends on 9 * 10 * NOTE - These are not public interfaces 11 */ 12 13 #include "seccomon.h" 14 #include "prmem.h" 15 #include "prerror.h" 16 #include "plarena.h" 17 #include "secerr.h" 18 #include "prmon.h" 19 #include "nssilock.h" 20 #include "secport.h" 21 #include "prenv.h" 22 #include "prinit.h" 23 24 #include <stdint.h> 25 26 #ifdef DEBUG 27 #define THREADMARK 28 #endif /* DEBUG */ 29 30 #ifdef THREADMARK 31 #include "prthread.h" 32 #endif /* THREADMARK */ 33 34 #if defined(XP_UNIX) 35 #include <stdlib.h> 36 #else 37 #include "wtypes.h" 38 #endif 39 40 #define SET_ERROR_CODE /* place holder for code to set PR error code. */ 41 42 #ifdef THREADMARK 43 typedef struct threadmark_mark_str { 44 struct threadmark_mark_str *next; 45 void *mark; 46 } threadmark_mark; 47 48 #endif /* THREADMARK */ 49 50 /* The value of this magic must change each time PORTArenaPool changes. */ 51 #define ARENAPOOL_MAGIC 0xB8AC9BDF 52 53 #define CHEAP_ARENAPOOL_MAGIC 0x3F16BB09 54 55 typedef struct PORTArenaPool_str { 56 PLArenaPool arena; 57 PRUint32 magic; 58 PRLock *lock; 59 #ifdef THREADMARK 60 PRThread *marking_thread; 61 threadmark_mark *first_mark; 62 #endif 63 } PORTArenaPool; 64 65 /* locations for registering Unicode conversion functions. 66 * XXX is this the appropriate location? or should they be 67 * moved to client/server specific locations? 68 */ 69 PORTCharConversionFunc ucs4Utf8ConvertFunc; 70 PORTCharConversionFunc ucs2Utf8ConvertFunc; 71 PORTCharConversionWSwapFunc ucs2AsciiConvertFunc; 72 73 /* NSPR memory allocation functions (PR_Malloc, PR_Calloc, and PR_Realloc) 74 * use the PRUint32 type for the size parameter. Before we pass a size_t or 75 * unsigned long size to these functions, we need to ensure it is <= half of 76 * the maximum PRUint32 value to avoid truncation and catch a negative size. 77 */ 78 #define MAX_SIZE (PR_UINT32_MAX >> 1) 79 80 void * 81 PORT_Alloc(size_t bytes) 82 { 83 void *rv = NULL; 84 85 if (bytes <= MAX_SIZE) { 86 /* Always allocate a non-zero amount of bytes */ 87 rv = PR_Malloc(bytes ? bytes : 1); 88 } 89 if (!rv) { 90 PORT_SetError(SEC_ERROR_NO_MEMORY); 91 } 92 return rv; 93 } 94 95 void * 96 PORT_Realloc(void *oldptr, size_t bytes) 97 { 98 void *rv = NULL; 99 100 if (bytes <= MAX_SIZE) { 101 rv = PR_Realloc(oldptr, bytes); 102 } 103 if (!rv) { 104 PORT_SetError(SEC_ERROR_NO_MEMORY); 105 } 106 return rv; 107 } 108 109 void * 110 PORT_ZAlloc(size_t bytes) 111 { 112 void *rv = NULL; 113 114 if (bytes <= MAX_SIZE) { 115 /* Always allocate a non-zero amount of bytes */ 116 rv = PR_Calloc(1, bytes ? bytes : 1); 117 } 118 if (!rv) { 119 PORT_SetError(SEC_ERROR_NO_MEMORY); 120 } 121 return rv; 122 } 123 124 /* aligned_alloc is C11. This is an alternative to get aligned memory. */ 125 void * 126 PORT_ZAllocAligned(size_t bytes, size_t alignment, void **mem) 127 { 128 size_t x = alignment - 1; 129 130 /* This only works if alignment is a power of 2. */ 131 if ((alignment == 0) || (alignment & (alignment - 1))) { 132 PORT_SetError(SEC_ERROR_INVALID_ARGS); 133 return NULL; 134 } 135 136 if (!mem) { 137 return NULL; 138 } 139 140 /* Always allocate a non-zero amount of bytes */ 141 *mem = PORT_ZAlloc((bytes ? bytes : 1) + x); 142 if (!*mem) { 143 PORT_SetError(SEC_ERROR_NO_MEMORY); 144 return NULL; 145 } 146 147 return (void *)(((uintptr_t)*mem + x) & ~(uintptr_t)x); 148 } 149 150 void * 151 PORT_ZAllocAlignedOffset(size_t size, size_t alignment, size_t offset) 152 { 153 PORT_Assert(offset < size); 154 if (offset > size) { 155 return NULL; 156 } 157 158 void *mem = NULL; 159 void *v = PORT_ZAllocAligned(size, alignment, &mem); 160 if (!v) { 161 return NULL; 162 } 163 164 PORT_Assert(mem); 165 *((void **)((uintptr_t)v + offset)) = mem; 166 return v; 167 } 168 169 void 170 PORT_Free(void *ptr) 171 { 172 if (ptr) { 173 PR_Free(ptr); 174 } 175 } 176 177 void 178 PORT_ZFree(void *ptr, size_t len) 179 { 180 if (ptr) { 181 memset(ptr, 0, len); 182 PR_Free(ptr); 183 } 184 } 185 186 char * 187 PORT_Strdup(const char *str) 188 { 189 size_t len = PORT_Strlen(str) + 1; 190 char *newstr; 191 192 newstr = (char *)PORT_Alloc(len); 193 if (newstr) { 194 PORT_Memcpy(newstr, str, len); 195 } 196 return newstr; 197 } 198 199 void 200 PORT_SetError(int value) 201 { 202 PR_SetError(value, 0); 203 return; 204 } 205 206 int 207 PORT_GetError(void) 208 { 209 return (PR_GetError()); 210 } 211 212 void 213 PORT_SafeZero(void *p, size_t n) 214 { 215 /* there are cases where the compiler optimizes away our attempt to clear 216 * out our stack variables. There are multiple solutions for this problem, 217 * but they aren't universally accepted on all platforms. This attempts 218 * to select the best solution available given our os, compilier, and 219 * libc */ 220 #ifdef __STDC_LIB_EXT1__ 221 /* if the os implements C11 annex K, use memset_s */ 222 memset_s(p, n, 0, n); 223 #else 224 /* _DEFAULT_SORUCE == BSD source in GCC based environments 225 * if other environmens support explicit_bzero, their defines 226 * should be added here */ 227 #if (defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE)) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) 228 explicit_bzero(p, n); 229 #else 230 #ifdef XP_WIN 231 /* windows has a secure zero funtion */ 232 SecureZeroMemory(p, n); 233 #else 234 /* if the os doesn't support one of the above, but does support 235 * memset_explicit, you can add the definition for memset with the 236 * appropriate define check here */ 237 /* define an explicitly implementated Safe zero if the OS 238 * doesn't provide one */ 239 if (p != NULL) { 240 volatile unsigned char *__vl = (unsigned char *)p; 241 size_t __nl = n; 242 while (__nl--) 243 *__vl++ = 0; 244 } 245 #endif /* no windows SecureZeroMemory */ 246 #endif /* no explicit_bzero */ 247 #endif /* no memset_s */ 248 } 249 250 /********************* Arena code follows ***************************** 251 * ArenaPools are like heaps. The memory in them consists of large blocks, 252 * called arenas, which are allocated from the/a system heap. Inside an 253 * ArenaPool, the arenas are organized as if they were in a stack. Newly 254 * allocated arenas are "pushed" on that stack. When you attempt to 255 * allocate memory from an ArenaPool, the code first looks to see if there 256 * is enough unused space in the top arena on the stack to satisfy your 257 * request, and if so, your request is satisfied from that arena. 258 * Otherwise, a new arena is allocated (or taken from NSPR's list of freed 259 * arenas) and pushed on to the stack. The new arena is always big enough 260 * to satisfy the request, and is also at least a minimum size that is 261 * established at the time that the ArenaPool is created. 262 * 263 * The ArenaMark function returns the address of a marker in the arena at 264 * the top of the arena stack. It is the address of the place in the arena 265 * on the top of the arena stack from which the next block of memory will 266 * be allocated. Each ArenaPool has its own separate stack, and hence 267 * marks are only relevant to the ArenaPool from which they are gotten. 268 * Marks may be nested. That is, a thread can get a mark, and then get 269 * another mark. 270 * 271 * It is intended that all the marks in an ArenaPool may only be owned by a 272 * single thread. In DEBUG builds, this is enforced. In non-DEBUG builds, 273 * it is not. In DEBUG builds, when a thread gets a mark from an 274 * ArenaPool, no other thread may acquire a mark in that ArenaPool while 275 * that mark exists, that is, until that mark is unmarked or released. 276 * Therefore, it is important that every mark be unmarked or released when 277 * the creating thread has no further need for exclusive ownership of the 278 * right to manage the ArenaPool. 279 * 280 * The ArenaUnmark function discards the ArenaMark at the address given, 281 * and all marks nested inside that mark (that is, acquired from that same 282 * ArenaPool while that mark existed). It is an error for a thread other 283 * than the mark's creator to try to unmark it. When a thread has unmarked 284 * all its marks from an ArenaPool, then another thread is able to set 285 * marks in that ArenaPool. ArenaUnmark does not deallocate (or "pop") any 286 * memory allocated from the ArenaPool since the mark was created. 287 * 288 * ArenaRelease "pops" the stack back to the mark, deallocating all the 289 * memory allocated from the arenas in the ArenaPool since that mark was 290 * created, and removing any arenas from the ArenaPool that have no 291 * remaining active allocations when that is done. It implicitly releases 292 * any marks nested inside the mark being explicitly released. It is the 293 * only operation, other than destroying the arenapool, that potentially 294 * reduces the number of arenas on the stack. Otherwise, the stack grows 295 * until the arenapool is destroyed, at which point all the arenas are 296 * freed or returned to a "free arena list", depending on their sizes. 297 */ 298 PLArenaPool * 299 PORT_NewArena(unsigned long chunksize) 300 { 301 PORTArenaPool *pool; 302 303 if (chunksize > MAX_SIZE) { 304 PORT_SetError(SEC_ERROR_NO_MEMORY); 305 return NULL; 306 } 307 pool = PORT_ZNew(PORTArenaPool); 308 if (!pool) { 309 return NULL; 310 } 311 pool->magic = ARENAPOOL_MAGIC; 312 pool->lock = PZ_NewLock(nssILockArena); 313 if (!pool->lock) { 314 PORT_Free(pool); 315 return NULL; 316 } 317 PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double)); 318 return (&pool->arena); 319 } 320 321 void 322 PORT_InitCheapArena(PORTCheapArenaPool *pool, unsigned long chunksize) 323 { 324 pool->magic = CHEAP_ARENAPOOL_MAGIC; 325 PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double)); 326 } 327 328 void * 329 PORT_ArenaAlloc(PLArenaPool *arena, size_t size) 330 { 331 void *p = NULL; 332 333 PORTArenaPool *pool = (PORTArenaPool *)arena; 334 335 if (size <= 0) { 336 size = 1; 337 } 338 339 if (size > MAX_SIZE) { 340 /* you lose. */ 341 } else 342 /* Is it one of ours? Assume so and check the magic */ 343 if (ARENAPOOL_MAGIC == pool->magic) { 344 PZ_Lock(pool->lock); 345 #ifdef THREADMARK 346 /* Most likely one of ours. Is there a thread id? */ 347 if (pool->marking_thread && 348 pool->marking_thread != PR_GetCurrentThread()) { 349 /* Another thread holds a mark in this arena */ 350 PZ_Unlock(pool->lock); 351 PORT_SetError(SEC_ERROR_NO_MEMORY); 352 PORT_Assert(0); 353 return NULL; 354 } /* tid != null */ 355 #endif /* THREADMARK */ 356 PL_ARENA_ALLOCATE(p, arena, size); 357 PZ_Unlock(pool->lock); 358 } else { 359 PL_ARENA_ALLOCATE(p, arena, size); 360 } 361 362 if (!p) { 363 PORT_SetError(SEC_ERROR_NO_MEMORY); 364 } 365 366 return (p); 367 } 368 369 void * 370 PORT_ArenaZAlloc(PLArenaPool *arena, size_t size) 371 { 372 void *p; 373 374 if (size <= 0) 375 size = 1; 376 377 p = PORT_ArenaAlloc(arena, size); 378 379 if (p) { 380 PORT_Memset(p, 0, size); 381 } 382 383 return (p); 384 } 385 386 static PRCallOnceType setupUseFreeListOnce; 387 static PRBool useFreeList; 388 389 static PRStatus 390 SetupUseFreeList(void) 391 { 392 useFreeList = (PR_GetEnvSecure("NSS_DISABLE_ARENA_FREE_LIST") == NULL); 393 return PR_SUCCESS; 394 } 395 396 /* 397 * If zero is true, zeroize the arena memory before freeing it. 398 */ 399 void 400 PORT_FreeArena(PLArenaPool *arena, PRBool zero) 401 { 402 PORTArenaPool *pool = (PORTArenaPool *)arena; 403 PRLock *lock = (PRLock *)0; 404 size_t len = sizeof *arena; 405 406 if (!pool) 407 return; 408 if (ARENAPOOL_MAGIC == pool->magic) { 409 len = sizeof *pool; 410 lock = pool->lock; 411 PZ_Lock(lock); 412 } 413 if (zero) { 414 PL_ClearArenaPool(arena, 0); 415 } 416 (void)PR_CallOnce(&setupUseFreeListOnce, &SetupUseFreeList); 417 if (useFreeList) { 418 PL_FreeArenaPool(arena); 419 } else { 420 PL_FinishArenaPool(arena); 421 } 422 PORT_ZFree(arena, len); 423 if (lock) { 424 PZ_Unlock(lock); 425 PZ_DestroyLock(lock); 426 } 427 } 428 429 void 430 PORT_DestroyCheapArena(PORTCheapArenaPool *pool) 431 { 432 (void)PR_CallOnce(&setupUseFreeListOnce, &SetupUseFreeList); 433 if (useFreeList) { 434 PL_FreeArenaPool(&pool->arena); 435 } else { 436 PL_FinishArenaPool(&pool->arena); 437 } 438 } 439 440 void * 441 PORT_ArenaGrow(PLArenaPool *arena, void *ptr, size_t oldsize, size_t newsize) 442 { 443 PORTArenaPool *pool = (PORTArenaPool *)arena; 444 PORT_Assert(newsize >= oldsize); 445 446 if (newsize > MAX_SIZE) { 447 PORT_SetError(SEC_ERROR_NO_MEMORY); 448 return NULL; 449 } 450 451 if (ARENAPOOL_MAGIC == pool->magic) { 452 PZ_Lock(pool->lock); 453 /* Do we do a THREADMARK check here? */ 454 PL_ARENA_GROW(ptr, arena, oldsize, (newsize - oldsize)); 455 PZ_Unlock(pool->lock); 456 } else { 457 PL_ARENA_GROW(ptr, arena, oldsize, (newsize - oldsize)); 458 } 459 460 return (ptr); 461 } 462 463 void * 464 PORT_ArenaMark(PLArenaPool *arena) 465 { 466 void *result; 467 468 PORTArenaPool *pool = (PORTArenaPool *)arena; 469 if (ARENAPOOL_MAGIC == pool->magic) { 470 PZ_Lock(pool->lock); 471 #ifdef THREADMARK 472 { 473 threadmark_mark *tm, **pw; 474 PRThread *currentThread = PR_GetCurrentThread(); 475 476 if (!pool->marking_thread) { 477 /* First mark */ 478 pool->marking_thread = currentThread; 479 } else if (currentThread != pool->marking_thread) { 480 PZ_Unlock(pool->lock); 481 PORT_SetError(SEC_ERROR_NO_MEMORY); 482 PORT_Assert(0); 483 return NULL; 484 } 485 486 result = PL_ARENA_MARK(arena); 487 PL_ARENA_ALLOCATE(tm, arena, sizeof(threadmark_mark)); 488 if (!tm) { 489 PZ_Unlock(pool->lock); 490 PORT_SetError(SEC_ERROR_NO_MEMORY); 491 return NULL; 492 } 493 494 tm->mark = result; 495 tm->next = (threadmark_mark *)NULL; 496 497 pw = &pool->first_mark; 498 while (*pw) { 499 pw = &(*pw)->next; 500 } 501 502 *pw = tm; 503 } 504 #else /* THREADMARK */ 505 result = PL_ARENA_MARK(arena); 506 #endif /* THREADMARK */ 507 PZ_Unlock(pool->lock); 508 } else { 509 /* a "pure" NSPR arena */ 510 result = PL_ARENA_MARK(arena); 511 } 512 return result; 513 } 514 515 /* 516 * This function accesses the internals of PLArena, which is why it needs 517 * to use the NSPR internal macro PL_MAKE_MEM_UNDEFINED before the memset 518 * calls. 519 * 520 * We should move this function to NSPR as PL_ClearArenaAfterMark or add 521 * a PL_ARENA_CLEAR_AND_RELEASE macro. 522 * 523 * TODO: remove the #ifdef PL_MAKE_MEM_UNDEFINED tests when NSPR 4.10+ is 524 * widely available. 525 */ 526 static void 527 port_ArenaZeroAfterMark(PLArenaPool *arena, void *mark) 528 { 529 PLArena *a = arena->current; 530 if (a->base <= (PRUword)mark && (PRUword)mark <= a->avail) { 531 /* fast path: mark falls in the current arena */ 532 #ifdef PL_MAKE_MEM_UNDEFINED 533 PL_MAKE_MEM_UNDEFINED(mark, a->avail - (PRUword)mark); 534 #endif 535 memset(mark, 0, a->avail - (PRUword)mark); 536 } else { 537 /* slow path: need to find the arena that mark falls in */ 538 for (a = arena->first.next; a; a = a->next) { 539 PR_ASSERT(a->base <= a->avail && a->avail <= a->limit); 540 if (a->base <= (PRUword)mark && (PRUword)mark <= a->avail) { 541 #ifdef PL_MAKE_MEM_UNDEFINED 542 PL_MAKE_MEM_UNDEFINED(mark, a->avail - (PRUword)mark); 543 #endif 544 memset(mark, 0, a->avail - (PRUword)mark); 545 a = a->next; 546 break; 547 } 548 } 549 for (; a; a = a->next) { 550 PR_ASSERT(a->base <= a->avail && a->avail <= a->limit); 551 #ifdef PL_MAKE_MEM_UNDEFINED 552 PL_MAKE_MEM_UNDEFINED((void *)a->base, a->avail - a->base); 553 #endif 554 memset((void *)a->base, 0, a->avail - a->base); 555 } 556 } 557 } 558 559 static void 560 port_ArenaRelease(PLArenaPool *arena, void *mark, PRBool zero) 561 { 562 PORTArenaPool *pool = (PORTArenaPool *)arena; 563 if (ARENAPOOL_MAGIC == pool->magic) { 564 PZ_Lock(pool->lock); 565 #ifdef THREADMARK 566 { 567 threadmark_mark **pw; 568 569 if (PR_GetCurrentThread() != pool->marking_thread) { 570 PZ_Unlock(pool->lock); 571 PORT_SetError(SEC_ERROR_NO_MEMORY); 572 PORT_Assert(0); 573 return /* no error indication available */; 574 } 575 576 pw = &pool->first_mark; 577 while (*pw && (mark != (*pw)->mark)) { 578 pw = &(*pw)->next; 579 } 580 581 if (!*pw) { 582 /* bad mark */ 583 PZ_Unlock(pool->lock); 584 PORT_SetError(SEC_ERROR_NO_MEMORY); 585 PORT_Assert(0); 586 return /* no error indication available */; 587 } 588 589 *pw = (threadmark_mark *)NULL; 590 591 if (zero) { 592 port_ArenaZeroAfterMark(arena, mark); 593 } 594 PL_ARENA_RELEASE(arena, mark); 595 596 if (!pool->first_mark) { 597 pool->marking_thread = (PRThread *)NULL; 598 } 599 } 600 #else /* THREADMARK */ 601 if (zero) { 602 port_ArenaZeroAfterMark(arena, mark); 603 } 604 PL_ARENA_RELEASE(arena, mark); 605 #endif /* THREADMARK */ 606 PZ_Unlock(pool->lock); 607 } else { 608 if (zero) { 609 port_ArenaZeroAfterMark(arena, mark); 610 } 611 PL_ARENA_RELEASE(arena, mark); 612 } 613 } 614 615 void 616 PORT_ArenaRelease(PLArenaPool *arena, void *mark) 617 { 618 port_ArenaRelease(arena, mark, PR_FALSE); 619 } 620 621 /* 622 * Zeroize the arena memory before releasing it. 623 */ 624 void 625 PORT_ArenaZRelease(PLArenaPool *arena, void *mark) 626 { 627 port_ArenaRelease(arena, mark, PR_TRUE); 628 } 629 630 void 631 PORT_ArenaUnmark(PLArenaPool *arena, void *mark) 632 { 633 #ifdef THREADMARK 634 PORTArenaPool *pool = (PORTArenaPool *)arena; 635 if (ARENAPOOL_MAGIC == pool->magic) { 636 threadmark_mark **pw; 637 638 PZ_Lock(pool->lock); 639 640 if (PR_GetCurrentThread() != pool->marking_thread) { 641 PZ_Unlock(pool->lock); 642 PORT_SetError(SEC_ERROR_NO_MEMORY); 643 PORT_Assert(0); 644 return /* no error indication available */; 645 } 646 647 pw = &pool->first_mark; 648 while (((threadmark_mark *)NULL != *pw) && (mark != (*pw)->mark)) { 649 pw = &(*pw)->next; 650 } 651 652 if ((threadmark_mark *)NULL == *pw) { 653 /* bad mark */ 654 PZ_Unlock(pool->lock); 655 PORT_SetError(SEC_ERROR_NO_MEMORY); 656 PORT_Assert(0); 657 return /* no error indication available */; 658 } 659 660 *pw = (threadmark_mark *)NULL; 661 662 if (!pool->first_mark) { 663 pool->marking_thread = (PRThread *)NULL; 664 } 665 666 PZ_Unlock(pool->lock); 667 } 668 #endif /* THREADMARK */ 669 } 670 671 char * 672 PORT_ArenaStrdup(PLArenaPool *arena, const char *str) 673 { 674 int len = PORT_Strlen(str) + 1; 675 char *newstr; 676 677 newstr = (char *)PORT_ArenaAlloc(arena, len); 678 if (newstr) { 679 PORT_Memcpy(newstr, str, len); 680 } 681 return newstr; 682 } 683 684 /********************** end of arena functions ***********************/ 685 686 /****************** unicode conversion functions ***********************/ 687 /* 688 * NOTE: These conversion functions all assume that the multibyte 689 * characters are going to be in NETWORK BYTE ORDER, not host byte 690 * order. This is because the only time we deal with UCS-2 and UCS-4 691 * are when the data was received from or is going to be sent out 692 * over the wire (in, e.g. certificates). 693 */ 694 695 void 696 PORT_SetUCS4_UTF8ConversionFunction(PORTCharConversionFunc convFunc) 697 { 698 ucs4Utf8ConvertFunc = convFunc; 699 } 700 701 void 702 PORT_SetUCS2_ASCIIConversionFunction(PORTCharConversionWSwapFunc convFunc) 703 { 704 ucs2AsciiConvertFunc = convFunc; 705 } 706 707 void 708 PORT_SetUCS2_UTF8ConversionFunction(PORTCharConversionFunc convFunc) 709 { 710 ucs2Utf8ConvertFunc = convFunc; 711 } 712 713 PRBool 714 PORT_UCS4_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf, 715 unsigned int inBufLen, unsigned char *outBuf, 716 unsigned int maxOutBufLen, unsigned int *outBufLen) 717 { 718 if (!ucs4Utf8ConvertFunc) { 719 return sec_port_ucs4_utf8_conversion_function(toUnicode, 720 inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen); 721 } 722 723 return (*ucs4Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, 724 maxOutBufLen, outBufLen); 725 } 726 727 PRBool 728 PORT_UCS2_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf, 729 unsigned int inBufLen, unsigned char *outBuf, 730 unsigned int maxOutBufLen, unsigned int *outBufLen) 731 { 732 if (!ucs2Utf8ConvertFunc) { 733 return sec_port_ucs2_utf8_conversion_function(toUnicode, 734 inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen); 735 } 736 737 return (*ucs2Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, 738 maxOutBufLen, outBufLen); 739 } 740 741 PRBool 742 PORT_ISO88591_UTF8Conversion(const unsigned char *inBuf, 743 unsigned int inBufLen, unsigned char *outBuf, 744 unsigned int maxOutBufLen, unsigned int *outBufLen) 745 { 746 return sec_port_iso88591_utf8_conversion_function(inBuf, inBufLen, 747 outBuf, maxOutBufLen, outBufLen); 748 } 749 750 PRBool 751 PORT_UCS2_ASCIIConversion(PRBool toUnicode, unsigned char *inBuf, 752 unsigned int inBufLen, unsigned char *outBuf, 753 unsigned int maxOutBufLen, unsigned int *outBufLen, 754 PRBool swapBytes) 755 { 756 if (!ucs2AsciiConvertFunc) { 757 return PR_FALSE; 758 } 759 760 return (*ucs2AsciiConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, 761 maxOutBufLen, outBufLen, swapBytes); 762 } 763 764 /* Portable putenv. Creates/replaces an environment variable of the form 765 * envVarName=envValue 766 */ 767 int 768 NSS_PutEnv(const char *envVarName, const char *envValue) 769 { 770 SECStatus result = SECSuccess; 771 #ifdef _WIN32 772 PRBool setOK; 773 774 setOK = SetEnvironmentVariable(envVarName, envValue); 775 if (!setOK) { 776 SET_ERROR_CODE 777 return SECFailure; 778 } 779 #elif defined(__GNUC__) && __GNUC__ >= 7 780 int setEnvFailed; 781 setEnvFailed = setenv(envVarName, envValue, 1); 782 if (setEnvFailed) { 783 SET_ERROR_CODE 784 return SECFailure; 785 } 786 #else 787 char *encoded = (char *)PORT_ZAlloc(strlen(envVarName) + 2 + strlen(envValue)); 788 if (!encoded) { 789 return SECFailure; 790 } 791 strcpy(encoded, envVarName); 792 strcat(encoded, "="); 793 strcat(encoded, envValue); 794 int putEnvFailed = putenv(encoded); /* adopt. */ 795 796 if (putEnvFailed) { 797 SET_ERROR_CODE 798 result = SECFailure; 799 PORT_Free(encoded); 800 } 801 #endif 802 return result; 803 } 804 805 /* 806 * Perform a constant-time compare of two memory regions. The return value is 807 * 0 if the memory regions are equal and non-zero otherwise. 808 */ 809 int 810 NSS_SecureMemcmp(const void *ia, const void *ib, size_t n) 811 { 812 const unsigned char *a = (const unsigned char *)ia; 813 const unsigned char *b = (const unsigned char *)ib; 814 int r = 0; 815 816 for (size_t i = 0; i < n; ++i) { 817 r |= a[i] ^ b[i]; 818 } 819 820 /* 0 <= r < 256, so -r has bit 8 set when r != 0 */ 821 return 1 & (-r >> 8); 822 } 823 824 /* 825 * Perform a constant-time check if a memory region is all 0. The return value 826 * is 0 if the memory region is all zero. 827 */ 828 unsigned int 829 NSS_SecureMemcmpZero(const void *mem, size_t n) 830 { 831 const unsigned char *a = (const unsigned char *)mem; 832 int r = 0; 833 834 for (size_t i = 0; i < n; ++i) { 835 r |= a[i]; 836 } 837 838 /* 0 <= r < 256, so -r has bit 8 set when r != 0 */ 839 return 1 & (-r >> 8); 840 } 841 842 /* 843 * A "value barrier" prevents the compiler from making optimizations based on 844 * the value that a variable takes. 845 * 846 * Standard C does not have value barriers, so C implementations of them are 847 * compiler-specific and are not guaranteed to be effective. Thus, the value 848 * barriers here are a best-effort, defense-in-depth, strategy. They are not a 849 * substitute for standard constant-time programming discipline. 850 * 851 * Some implementations have a performance penalty, so value barriers should 852 * be used sparingly. 853 */ 854 static inline int 855 value_barrier_int(int x) 856 { 857 #if defined(__GNUC__) || defined(__clang__) 858 /* This inline assembly trick from Chandler Carruth's CppCon 2015 talk 859 * generates no instructions. 860 * 861 * "+r"(x) means that x will be mapped to a register that is both an input 862 * and an output to the assembly routine (""). The compiler will not 863 * inspect the assembly routine itself, so it cannot assume anything about 864 * the value of x after this line. 865 */ 866 __asm__("" 867 : "+r"(x) 868 : /* no other inputs */); 869 return x; 870 #else 871 /* If the compiler does not support the inline assembly trick above, we can 872 * put x in `volatile` storage and read it out again. This will generate 873 * explict store and load instructions, and possibly more depending on the 874 * target. 875 */ 876 volatile int y = x; 877 return y; 878 #endif 879 } 880 881 /* 882 * A branch-free implementation of 883 * if (!b) { 884 * memmove(dest, src0, n); 885 * } else { 886 * memmove(dest, src1, n); 887 * } 888 * 889 * The memmove is performed with src0 if `b == 0` and with src1 890 * otherwise. 891 * 892 * As with memmove, the selected src can overlap dest. 893 * 894 * Each of dest, src0, and src1 must point to an allocated buffer 895 * of at least n bytes. 896 */ 897 void 898 NSS_SecureSelect(void *dest, const void *src0, const void *src1, size_t n, unsigned char b) 899 900 { 901 // This value barrier makes it safe for the compiler to inline 902 // NSS_SecureSelect into a routine where it could otherwise infer something 903 // about the value of b, e.g. that b is 0/1 valued. 904 int w = value_barrier_int(b); 905 906 // 0 <= b < 256, and int is at least 16 bits, so -w has bits 8-15 907 // set when w != 0. 908 unsigned char mask = 0xff & (-w >> 8); 909 910 for (size_t i = 0; i < n; ++i) { 911 unsigned char s0i = ((unsigned char *)src0)[i]; 912 unsigned char s1i = ((unsigned char *)src1)[i]; 913 // if mask == 0 this simplifies to s0 ^ 0 914 // if mask == -1 this simplifies to s0 ^ s0 ^ s1 915 ((unsigned char *)dest)[i] = s0i ^ (mask & (s0i ^ s1i)); 916 } 917 } 918 919 /* 920 * consolidate all the calls to get the system FIPS status in one spot. 921 * This function allows an environment variable to override what is returned. 922 */ 923 PRBool 924 NSS_GetSystemFIPSEnabled(void) 925 { 926 /* if FIPS is disabled in NSS, always return FALSE, even if the environment 927 * variable is set, or the system is in FIPS mode */ 928 #ifndef NSS_FIPS_DISABLED 929 const char *env; 930 931 /* The environment variable is active for all platforms */ 932 env = PR_GetEnvSecure("NSS_FIPS"); 933 /* we generally accept y, Y, 1, FIPS, TRUE, and ON as turning on FIPS 934 * mode. Anything else is considered 'off' */ 935 if (env && (*env == 'y' || *env == '1' || *env == 'Y' || 936 (PORT_Strcasecmp(env, "fips") == 0) || 937 (PORT_Strcasecmp(env, "true") == 0) || 938 (PORT_Strcasecmp(env, "on") == 0))) { 939 return PR_TRUE; 940 } 941 942 /* currently only Linux has a system FIPS indicator. Add others here 943 * as they become available/known */ 944 #ifdef LINUX 945 { 946 FILE *f; 947 char d; 948 size_t size; 949 f = fopen("/proc/sys/crypto/fips_enabled", "r"); 950 if (!f) 951 return PR_FALSE; 952 953 size = fread(&d, 1, 1, f); 954 fclose(f); 955 if (size != 1) 956 return PR_FALSE; 957 if (d == '1') 958 return PR_TRUE; 959 } 960 #endif /* LINUX */ 961 #endif /* NSS_FIPS_DISABLED == 0 */ 962 return PR_FALSE; 963 }