1/* 2 * The contents of this file are subject to the Mozilla Public 3 * License Version 1.1 (the "License"); you may not use this file 4 * except in compliance with the License. You may obtain a copy of 5 * the License at http://www.mozilla.org/MPL/ 6 * 7 * Software distributed under the License is distributed on an "AS 8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 9 * implied. See the License for the specific language governing 10 * rights and limitations under the License. 11 * 12 * The Original Code is the Netscape security libraries. 13 * 14 * The Initial Developer of the Original Code is Netscape 15 * Communications Corporation. Portions created by Netscape are 16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All 17 * Rights Reserved. 18 * 19 * Contributor(s): 20 * 21 * Alternatively, the contents of this file may be used under the 22 * terms of the GNU General Public License Version 2 or later (the 23 * "GPL"), in which case the provisions of the GPL are applicable 24 * instead of those above. If you wish to allow use of your 25 * version of this file only under the terms of the GPL and not to 26 * allow others to use your version of this file under the MPL, 27 * indicate your decision by deleting the provisions above and 28 * replace them with the notice and other provisions required by 29 * the GPL. If you do not delete the provisions above, a recipient 30 * may use your version of this file under either the MPL or the 31 * GPL. 32 */ 33 34/* 35 * secport.c - portability interfaces for security libraries 36 * 37 * This file abstracts out libc functionality that libsec depends on 38 * 39 * NOTE - These are not public interfaces 40 * 41 * $Id: secport.c,v 1.5 2004/10/27 20:36:36 dmitch Exp $ 42 */ 43 44#include "seccomon.h" 45#include "prmem.h" 46#include "prerror.h" 47#include "plarena.h" 48#include "secerr.h" 49#include "prmon.h" 50#include "nsslocks.h" 51#include "secport.h" 52#include "prvrsion.h" 53#include "prenv.h" 54 55#ifdef DEBUG 56//#define THREADMARK 57#endif /* DEBUG */ 58 59#ifdef THREADMARK 60#include "prthread.h" 61#endif /* THREADMARK */ 62 63#if defined(XP_UNIX) || defined(XP_MAC) || defined(XP_OS2) || defined(XP_BEOS) 64#include <stdlib.h> 65#else 66#include "wtypes.h" 67#endif 68 69#define SET_ERROR_CODE /* place holder for code to set PR error code. */ 70 71#ifdef THREADMARK 72typedef struct threadmark_mark_str { 73 struct threadmark_mark_str *next; 74 void *mark; 75} threadmark_mark; 76 77#endif /* THREADMARK */ 78 79/* The value of this magic must change each time PORTArenaPool changes. */ 80#define ARENAPOOL_MAGIC 0xB8AC9BDF 81 82/* enable/disable mutex in PORTArenaPool */ 83#define ARENA_POOL_LOCK 0 84 85typedef struct PORTArenaPool_str { 86 PLArenaPool arena; 87 PRUint32 magic; 88 #if ARENA_POOL_LOCK 89 PRLock * lock; 90 #endif 91#ifdef THREADMARK 92 PRThread *marking_thread; 93 threadmark_mark *first_mark; 94#endif 95} PORTArenaPool; 96 97 98/* count of allocation failures. */ 99unsigned long port_allocFailures; 100 101#ifndef __APPLE__ 102/* locations for registering Unicode conversion functions. 103 * XXX is this the appropriate location? or should they be 104 * moved to client/server specific locations? 105 */ 106PORTCharConversionFunc ucs4Utf8ConvertFunc; 107PORTCharConversionFunc ucs2Utf8ConvertFunc; 108PORTCharConversionWSwapFunc ucs2AsciiConvertFunc; 109#endif /* __APPLE__ */ 110 111void * 112PORT_Alloc(size_t bytes) 113{ 114 void *rv; 115 116 /* Always allocate a non-zero amount of bytes */ 117 rv = (void *)PR_Malloc(bytes ? bytes : 1); 118 if (!rv) { 119 ++port_allocFailures; 120 PORT_SetError(SEC_ERROR_NO_MEMORY); 121 } 122 return rv; 123} 124 125void * 126PORT_Realloc(void *oldptr, size_t bytes) 127{ 128 void *rv; 129 130 rv = (void *)PR_Realloc(oldptr, bytes); 131 if (!rv) { 132 ++port_allocFailures; 133 PORT_SetError(SEC_ERROR_NO_MEMORY); 134 } 135 return rv; 136} 137 138void * 139PORT_ZAlloc(size_t bytes) 140{ 141 void *rv; 142 143 /* Always allocate a non-zero amount of bytes */ 144 rv = (void *)PR_Calloc(1, bytes ? bytes : 1); 145 if (!rv) { 146 ++port_allocFailures; 147 PORT_SetError(SEC_ERROR_NO_MEMORY); 148 } 149 return rv; 150} 151 152void 153PORT_Free(void *ptr) 154{ 155 if (ptr) { 156 PR_Free(ptr); 157 } 158} 159 160void 161PORT_ZFree(void *ptr, size_t len) 162{ 163 if (ptr) { 164 memset(ptr, 0, len); 165 PR_Free(ptr); 166 } 167} 168 169char * 170PORT_Strdup(const char *str) 171{ 172 size_t len = PORT_Strlen(str)+1; 173 char *newstr; 174 175 newstr = (char *)PORT_Alloc(len); 176 if (newstr) { 177 PORT_Memcpy(newstr, str, len); 178 } 179 return newstr; 180} 181 182void 183PORT_SetError(int value) 184{ 185 PR_SetError(value, 0); 186 return; 187} 188 189int 190PORT_GetError(void) 191{ 192 return(PR_GetError()); 193} 194 195/********************* Arena code follows *****************************/ 196 197PLArenaPool * 198PORT_NewArena(unsigned long chunksize) 199{ 200 PORTArenaPool *pool; 201 202 /* 64 bits cast: Safe. We only use chunksize 1024. */ 203 PORT_Assert(chunksize<=PR_UINT32_MAX); 204 205 pool = PORT_ZNew(PORTArenaPool); 206 if (!pool) { 207 return NULL; 208 } 209 pool->magic = ARENAPOOL_MAGIC; 210 #if ARENA_POOL_LOCK 211 pool->lock = PZ_NewLock(nssILockArena); 212 if (!pool->lock) { 213 ++port_allocFailures; 214 PORT_Free(pool); 215 return NULL; 216 } 217 #endif 218 PL_InitArenaPool(&pool->arena, "security", (PRUint32) chunksize, (PRUint32)sizeof(double)); 219 return(&pool->arena); 220} 221 222void * 223PORT_ArenaAlloc(PLArenaPool *arena, size_t size) 224{ 225 void *p; 226 227 PORTArenaPool *pool = (PORTArenaPool *)arena; 228 229 PORT_Assert(size<=PR_UINT32_MAX); 230 231 /* Is it one of ours? Assume so and check the magic */ 232 if (ARENAPOOL_MAGIC == pool->magic ) { 233 #if ARENA_POOL_LOCK 234 PZ_Lock(pool->lock); 235 #ifdef THREADMARK 236 /* Most likely one of ours. Is there a thread id? */ 237 if (pool->marking_thread && 238 pool->marking_thread != PR_GetCurrentThread() ) { 239 /* Another thread holds a mark in this arena */ 240 PZ_Unlock(pool->lock); 241 PORT_SetError(SEC_ERROR_NO_MEMORY); 242 PORT_Assert(0); 243 return NULL; 244 } /* tid != null */ 245 #endif /* THREADMARK */ 246 #endif /* ARENA_POOL_LOCK */ 247 PL_ARENA_ALLOCATE(p, arena, (PRUint32)size); 248 #if ARENA_POOL_LOCK 249 PZ_Unlock(pool->lock); 250 #endif 251 } else { 252 PL_ARENA_ALLOCATE(p, arena, (PRUint32)size); 253 } 254 255 if (!p) { 256 ++port_allocFailures; 257 PORT_SetError(SEC_ERROR_NO_MEMORY); 258 } 259 260 return(p); 261} 262 263void * 264PORT_ArenaZAlloc(PLArenaPool *arena, size_t size) 265{ 266 void *p = PORT_ArenaAlloc(arena, size); 267 268 if (p) { 269 PORT_Memset(p, 0, size); 270 } 271 272 return(p); 273} 274 275/* XXX - need to zeroize!! - jsw */ 276void 277PORT_FreeArena(PLArenaPool *arena, PRBool zero) 278{ 279 PORTArenaPool *pool = (PORTArenaPool *)arena; 280 #if ARENA_POOL_LOCK 281 PRLock * lock = (PRLock *)0; 282 #endif 283 size_t len = sizeof *arena; 284 extern const PRVersionDescription * libVersionPoint(void); 285 #ifndef __APPLE__ 286 static const PRVersionDescription * pvd; 287 #endif 288 static PRBool doFreeArenaPool = PR_FALSE; 289 290 if (ARENAPOOL_MAGIC == pool->magic ) { 291 len = sizeof *pool; 292 #if ARENA_POOL_LOCK 293 lock = pool->lock; 294 PZ_Lock(lock); 295 #endif 296 } 297 #ifndef __APPLE__ 298 /* dmitch - not needed */ 299 if (!pvd) { 300 /* Each of NSPR's DLLs has a function libVersionPoint(). 301 ** We could do a lot of extra work to be sure we're calling the 302 ** one in the DLL that holds PR_FreeArenaPool, but instead we 303 ** rely on the fact that ALL NSPR DLLs in the same directory 304 ** must be from the same release, and we call which ever one we get. 305 */ 306 /* no need for thread protection here */ 307 pvd = libVersionPoint(); 308 if ((pvd->vMajor > 4) || 309 (pvd->vMajor == 4 && pvd->vMinor > 1) || 310 (pvd->vMajor == 4 && pvd->vMinor == 1 && pvd->vPatch >= 1)) { 311 const char *ev = PR_GetEnv("NSS_DISABLE_ARENA_FREE_LIST"); 312 if (!ev) doFreeArenaPool = PR_TRUE; 313 } 314 } 315 #endif 316 if (doFreeArenaPool) { 317 PL_FreeArenaPool(arena); 318 } else { 319 PL_FinishArenaPool(arena); 320 } 321 #if ARENA_POOL_LOCK 322 if (lock) { 323 PZ_Unlock(lock); 324 PZ_DestroyLock(lock); 325 } 326 #endif 327 PORT_ZFree(arena, len); 328} 329 330void * 331PORT_ArenaGrow(PLArenaPool *arena, void *ptr, size_t oldsize, size_t newsize) 332{ 333 PORTArenaPool *pool = (PORTArenaPool *)arena; 334 PORT_Assert(newsize >= oldsize); 335 PORT_Assert(oldsize <= PR_UINT32_MAX); 336 PORT_Assert(newsize <= PR_UINT32_MAX); 337 338 if (ARENAPOOL_MAGIC == pool->magic ) { 339 #if ARENA_POOL_LOCK 340 PZ_Lock(pool->lock); 341 #endif 342 /* Do we do a THREADMARK check here? */ 343 PL_ARENA_GROW(ptr, arena, (PRUint32)oldsize, (PRUint32)( newsize - oldsize ) ); 344 #if ARENA_POOL_LOCK 345 PZ_Unlock(pool->lock); 346 #endif 347 } else { 348 PL_ARENA_GROW(ptr, arena, (PRUint32)oldsize, (PRUint32)( newsize - oldsize ) ); 349 } 350 351 return(ptr); 352} 353 354void * 355PORT_ArenaMark(PLArenaPool *arena) 356{ 357#if ARENA_MARK_ENABLE 358 void * result; 359 360 PORTArenaPool *pool = (PORTArenaPool *)arena; 361 if (ARENAPOOL_MAGIC == pool->magic ) { 362 PZ_Lock(pool->lock); 363#ifdef THREADMARK 364 { 365 threadmark_mark *tm, **pw; 366 PRThread * currentThread = PR_GetCurrentThread(); 367 368 if (! pool->marking_thread ) { 369 /* First mark */ 370 pool->marking_thread = currentThread; 371 } else if (currentThread != pool->marking_thread ) { 372 PZ_Unlock(pool->lock); 373 PORT_SetError(SEC_ERROR_NO_MEMORY); 374 PORT_Assert(0); 375 return NULL; 376 } 377 378 result = PL_ARENA_MARK(arena); 379 PL_ARENA_ALLOCATE(tm, arena, sizeof(threadmark_mark)); 380 if (!tm) { 381 PZ_Unlock(pool->lock); 382 PORT_SetError(SEC_ERROR_NO_MEMORY); 383 return NULL; 384 } 385 386 tm->mark = result; 387 tm->next = (threadmark_mark *)NULL; 388 389 pw = &pool->first_mark; 390 while( *pw ) { 391 pw = &(*pw)->next; 392 } 393 394 *pw = tm; 395 } 396#else /* THREADMARK */ 397 result = PL_ARENA_MARK(arena); 398#endif /* THREADMARK */ 399 PZ_Unlock(pool->lock); 400 } else { 401 /* a "pure" NSPR arena */ 402 result = PL_ARENA_MARK(arena); 403 } 404 return result; 405#else 406 /* Some code in libsecurity_smime really checks for a nonzero 407 * return here, so... */ 408 return (void *)-1; 409#endif 410} 411 412void 413PORT_ArenaRelease(PLArenaPool *arena, void *mark) 414{ 415#if ARENA_MARK_ENABLE 416 PORTArenaPool *pool = (PORTArenaPool *)arena; 417 if (ARENAPOOL_MAGIC == pool->magic ) { 418 PZ_Lock(pool->lock); 419#ifdef THREADMARK 420 { 421 threadmark_mark **pw, *tm; 422 423 if (PR_GetCurrentThread() != pool->marking_thread ) { 424 PZ_Unlock(pool->lock); 425 PORT_SetError(SEC_ERROR_NO_MEMORY); 426 PORT_Assert(0); 427 return /* no error indication available */ ; 428 } 429 430 pw = &pool->first_mark; 431 while( *pw && (mark != (*pw)->mark) ) { 432 pw = &(*pw)->next; 433 } 434 435 if (! *pw ) { 436 /* bad mark */ 437 PZ_Unlock(pool->lock); 438 PORT_SetError(SEC_ERROR_NO_MEMORY); 439 PORT_Assert(0); 440 return /* no error indication available */ ; 441 } 442 443 tm = *pw; 444 *pw = (threadmark_mark *)NULL; 445 446 PL_ARENA_RELEASE(arena, mark); 447 448 if (! pool->first_mark ) { 449 pool->marking_thread = (PRThread *)NULL; 450 } 451 } 452#else /* THREADMARK */ 453 PL_ARENA_RELEASE(arena, mark); 454#endif /* THREADMARK */ 455 PZ_Unlock(pool->lock); 456 } else { 457 PL_ARENA_RELEASE(arena, mark); 458 } 459#endif /* ARENA_MARK_ENABLE */ 460} 461 462void 463PORT_ArenaUnmark(PLArenaPool *arena, void *mark) 464{ 465#if ARENA_MARK_ENABLE 466#ifdef THREADMARK 467 PORTArenaPool *pool = (PORTArenaPool *)arena; 468 if (ARENAPOOL_MAGIC == pool->magic ) { 469 threadmark_mark **pw, *tm; 470 471 PZ_Lock(pool->lock); 472 473 if (PR_GetCurrentThread() != pool->marking_thread ) { 474 PZ_Unlock(pool->lock); 475 PORT_SetError(SEC_ERROR_NO_MEMORY); 476 PORT_Assert(0); 477 return /* no error indication available */ ; 478 } 479 480 pw = &pool->first_mark; 481 while( ((threadmark_mark *)NULL != *pw) && (mark != (*pw)->mark) ) { 482 pw = &(*pw)->next; 483 } 484 485 if ((threadmark_mark *)NULL == *pw ) { 486 /* bad mark */ 487 PZ_Unlock(pool->lock); 488 PORT_SetError(SEC_ERROR_NO_MEMORY); 489 PORT_Assert(0); 490 return /* no error indication available */ ; 491 } 492 493 tm = *pw; 494 *pw = (threadmark_mark *)NULL; 495 496 if (! pool->first_mark ) { 497 pool->marking_thread = (PRThread *)NULL; 498 } 499 500 PZ_Unlock(pool->lock); 501 } 502#endif /* THREADMARK */ 503#endif /* ARENA_MARK_ENABLE */ 504} 505 506char * 507PORT_ArenaStrdup(PLArenaPool *arena, const char *str) { 508 size_t len = PORT_Strlen(str)+1; 509 char *newstr; 510 511 newstr = (char*)PORT_ArenaAlloc(arena,len); 512 if (newstr) { 513 PORT_Memcpy(newstr,str,len); 514 } 515 return newstr; 516} 517 518/********************** end of arena functions ***********************/ 519 520#ifndef __APPLE__ 521 522/****************** unicode conversion functions ***********************/ 523/* 524 * NOTE: These conversion functions all assume that the multibyte 525 * characters are going to be in NETWORK BYTE ORDER, not host byte 526 * order. This is because the only time we deal with UCS-2 and UCS-4 527 * are when the data was received from or is going to be sent out 528 * over the wire (in, e.g. certificates). 529 */ 530 531void 532PORT_SetUCS4_UTF8ConversionFunction(PORTCharConversionFunc convFunc) 533{ 534 ucs4Utf8ConvertFunc = convFunc; 535} 536 537void 538PORT_SetUCS2_ASCIIConversionFunction(PORTCharConversionWSwapFunc convFunc) 539{ 540 ucs2AsciiConvertFunc = convFunc; 541} 542 543void 544PORT_SetUCS2_UTF8ConversionFunction(PORTCharConversionFunc convFunc) 545{ 546 ucs2Utf8ConvertFunc = convFunc; 547} 548 549//#ifndef __APPLE__ 550/* dmitch - not needed */ 551PRBool 552PORT_UCS4_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf, 553 unsigned int inBufLen, unsigned char *outBuf, 554 unsigned int maxOutBufLen, unsigned int *outBufLen) 555{ 556 if(!ucs4Utf8ConvertFunc) { 557 return sec_port_ucs4_utf8_conversion_function(toUnicode, 558 inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen); 559 } 560 561 return (*ucs4Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, 562 maxOutBufLen, outBufLen); 563} 564 565PRBool 566PORT_UCS2_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf, 567 unsigned int inBufLen, unsigned char *outBuf, 568 unsigned int maxOutBufLen, unsigned int *outBufLen) 569{ 570 if(!ucs2Utf8ConvertFunc) { 571 return sec_port_ucs2_utf8_conversion_function(toUnicode, 572 inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen); 573 } 574 575 return (*ucs2Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, 576 maxOutBufLen, outBufLen); 577} 578//#endif /* __APPLE__ */ 579 580PRBool 581PORT_UCS2_ASCIIConversion(PRBool toUnicode, unsigned char *inBuf, 582 unsigned int inBufLen, unsigned char *outBuf, 583 unsigned int maxOutBufLen, unsigned int *outBufLen, 584 PRBool swapBytes) 585{ 586 if(!ucs2AsciiConvertFunc) { 587 return PR_FALSE; 588 } 589 590 return (*ucs2AsciiConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, 591 maxOutBufLen, outBufLen, swapBytes); 592} 593 594 595/* Portable putenv. Creates/replaces an environment variable of the form 596 * envVarName=envValue 597 */ 598int 599NSS_PutEnv(const char * envVarName, const char * envValue) 600{ 601#if defined(XP_MAC) || defined(_WIN32_WCE) 602 return SECFailure; 603#else 604 SECStatus result = SECSuccess; 605 char * encoded; 606 int putEnvFailed; 607#ifdef _WIN32 608 PRBool setOK; 609 610 setOK = SetEnvironmentVariable(envVarName, envValue); 611 if (!setOK) { 612 SET_ERROR_CODE 613 return SECFailure; 614 } 615#endif 616 617 encoded = (char *)PORT_ZAlloc(strlen(envVarName) + 2 + strlen(envValue)); 618 strcpy(encoded, envVarName); 619 strcat(encoded, "="); 620 strcat(encoded, envValue); 621 622 putEnvFailed = putenv(encoded); /* adopt. */ 623 if (putEnvFailed) { 624 SET_ERROR_CODE 625 result = SECFailure; 626 PORT_Free(encoded); 627 } 628 return result; 629#endif 630} 631#endif /* __APPLE__ */ 632 633