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