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#include "seccomon.h"
6/* This ifdef should match the one in sslsnce.c */
7#if defined(XP_UNIX) || defined(XP_WIN32) || defined (XP_OS2) || defined(XP_BEOS)
8
9#include "sslmutex.h"
10#include "prerr.h"
11
12static SECStatus single_process_sslMutex_Init(sslMutex* pMutex)
13{
14    PR_ASSERT(pMutex != 0 && pMutex->u.sslLock == 0 );
15
16    pMutex->u.sslLock = PR_NewLock();
17    if (!pMutex->u.sslLock) {
18        return SECFailure;
19    }
20    return SECSuccess;
21}
22
23static SECStatus single_process_sslMutex_Destroy(sslMutex* pMutex)
24{
25    PR_ASSERT(pMutex != 0);
26    PR_ASSERT(pMutex->u.sslLock!= 0);
27    if (!pMutex->u.sslLock) {
28        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
29        return SECFailure;
30    }
31    PR_DestroyLock(pMutex->u.sslLock);
32    return SECSuccess;
33}
34
35static SECStatus single_process_sslMutex_Unlock(sslMutex* pMutex)
36{
37    PR_ASSERT(pMutex != 0 );
38    PR_ASSERT(pMutex->u.sslLock !=0);
39    if (!pMutex->u.sslLock) {
40        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
41        return SECFailure;
42    }
43    PR_Unlock(pMutex->u.sslLock);
44    return SECSuccess;
45}
46
47static SECStatus single_process_sslMutex_Lock(sslMutex* pMutex)
48{
49    PR_ASSERT(pMutex != 0);
50    PR_ASSERT(pMutex->u.sslLock != 0 );
51    if (!pMutex->u.sslLock) {
52        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
53        return SECFailure;
54    }
55    PR_Lock(pMutex->u.sslLock);
56    return SECSuccess;
57}
58
59#if defined(LINUX) || defined(AIX) || defined(BEOS) || defined(BSDI) || (defined(NETBSD) && __NetBSD_Version__ < 500000000) || defined(OPENBSD)
60
61#include <unistd.h>
62#include <fcntl.h>
63#include <string.h>
64#include <errno.h>
65#include "unix_err.h"
66#include "pratom.h"
67
68#define SSL_MUTEX_MAGIC 0xfeedfd
69#define NONBLOCKING_POSTS 1	/* maybe this is faster */
70
71#if NONBLOCKING_POSTS
72
73#ifndef FNONBLOCK
74#define FNONBLOCK O_NONBLOCK
75#endif
76
77static int
78setNonBlocking(int fd, int nonBlocking)
79{
80    int flags;
81    int err;
82
83    flags = fcntl(fd, F_GETFL, 0);
84    if (0 > flags)
85	return flags;
86    if (nonBlocking)
87	flags |= FNONBLOCK;
88    else
89	flags &= ~FNONBLOCK;
90    err = fcntl(fd, F_SETFL, flags);
91    return err;
92}
93#endif
94
95SECStatus
96sslMutex_Init(sslMutex *pMutex, int shared)
97{
98    int  err;
99    PR_ASSERT(pMutex);
100    pMutex->isMultiProcess = (PRBool)(shared != 0);
101    if (!shared) {
102        return single_process_sslMutex_Init(pMutex);
103    }
104    pMutex->u.pipeStr.mPipes[0] = -1;
105    pMutex->u.pipeStr.mPipes[1] = -1;
106    pMutex->u.pipeStr.mPipes[2] = -1;
107    pMutex->u.pipeStr.nWaiters  =  0;
108
109    err = pipe(pMutex->u.pipeStr.mPipes);
110    if (err) {
111	nss_MD_unix_map_default_error(errno);
112	return err;
113    }
114#if NONBLOCKING_POSTS
115    err = setNonBlocking(pMutex->u.pipeStr.mPipes[1], 1);
116    if (err)
117	goto loser;
118#endif
119
120    pMutex->u.pipeStr.mPipes[2] = SSL_MUTEX_MAGIC;
121
122#if defined(LINUX) && defined(i386)
123    /* Pipe starts out empty */
124    return SECSuccess;
125#else
126    /* Pipe starts with one byte. */
127    return sslMutex_Unlock(pMutex);
128#endif
129
130loser:
131    nss_MD_unix_map_default_error(errno);
132    close(pMutex->u.pipeStr.mPipes[0]);
133    close(pMutex->u.pipeStr.mPipes[1]);
134    return SECFailure;
135}
136
137SECStatus
138sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal)
139{
140    if (PR_FALSE == pMutex->isMultiProcess) {
141        return single_process_sslMutex_Destroy(pMutex);
142    }
143    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
144	PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
145	return SECFailure;
146    }
147    close(pMutex->u.pipeStr.mPipes[0]);
148    close(pMutex->u.pipeStr.mPipes[1]);
149
150    if (processLocal) {
151	return SECSuccess;
152    }
153
154    pMutex->u.pipeStr.mPipes[0] = -1;
155    pMutex->u.pipeStr.mPipes[1] = -1;
156    pMutex->u.pipeStr.mPipes[2] = -1;
157    pMutex->u.pipeStr.nWaiters  =  0;
158
159    return SECSuccess;
160}
161
162#if defined(LINUX) && defined(i386)
163/* No memory barrier needed for this platform */
164
165/* nWaiters includes the holder of the lock (if any) and the number
166** threads waiting for it.  After incrementing nWaiters, if the count
167** is exactly 1, then you have the lock and may proceed.  If the
168** count is greater than 1, then you must wait on the pipe.
169*/
170
171
172SECStatus
173sslMutex_Unlock(sslMutex *pMutex)
174{
175    PRInt32 newValue;
176    if (PR_FALSE == pMutex->isMultiProcess) {
177        return single_process_sslMutex_Unlock(pMutex);
178    }
179
180    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
181	PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
182	return SECFailure;
183    }
184    /* Do Memory Barrier here. */
185    newValue = PR_ATOMIC_DECREMENT(&pMutex->u.pipeStr.nWaiters);
186    if (newValue > 0) {
187	int  cc;
188	char c  = 1;
189	do {
190	    cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
191	} while (cc < 0 && (errno == EINTR || errno == EAGAIN));
192	if (cc != 1) {
193	    if (cc < 0)
194		nss_MD_unix_map_default_error(errno);
195	    else
196		PORT_SetError(PR_UNKNOWN_ERROR);
197	    return SECFailure;
198	}
199    }
200    return SECSuccess;
201}
202
203SECStatus
204sslMutex_Lock(sslMutex *pMutex)
205{
206    PRInt32 newValue;
207    if (PR_FALSE == pMutex->isMultiProcess) {
208        return single_process_sslMutex_Lock(pMutex);
209    }
210
211    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
212	PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
213	return SECFailure;
214    }
215    newValue = PR_ATOMIC_INCREMENT(&pMutex->u.pipeStr.nWaiters);
216    /* Do Memory Barrier here. */
217    if (newValue > 1) {
218	int   cc;
219	char  c;
220	do {
221	    cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
222	} while (cc < 0 && errno == EINTR);
223	if (cc != 1) {
224	    if (cc < 0)
225		nss_MD_unix_map_default_error(errno);
226	    else
227		PORT_SetError(PR_UNKNOWN_ERROR);
228	    return SECFailure;
229	}
230    }
231    return SECSuccess;
232}
233
234#else
235
236/* Using Atomic operations requires the use of a memory barrier instruction
237** on PowerPC, Sparc, and Alpha.  NSPR's PR_Atomic functions do not perform
238** them, and NSPR does not provide a function that does them (e.g. PR_Barrier).
239** So, we don't use them on those platforms.
240*/
241
242SECStatus
243sslMutex_Unlock(sslMutex *pMutex)
244{
245    int  cc;
246    char c  = 1;
247
248    if (PR_FALSE == pMutex->isMultiProcess) {
249        return single_process_sslMutex_Unlock(pMutex);
250    }
251
252    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
253	PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
254	return SECFailure;
255    }
256    do {
257	cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
258    } while (cc < 0 && (errno == EINTR || errno == EAGAIN));
259    if (cc != 1) {
260	if (cc < 0)
261	    nss_MD_unix_map_default_error(errno);
262	else
263	    PORT_SetError(PR_UNKNOWN_ERROR);
264	return SECFailure;
265    }
266
267    return SECSuccess;
268}
269
270SECStatus
271sslMutex_Lock(sslMutex *pMutex)
272{
273    int   cc;
274    char  c;
275
276    if (PR_FALSE == pMutex->isMultiProcess) {
277        return single_process_sslMutex_Lock(pMutex);
278    }
279
280    if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
281	PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
282	return SECFailure;
283    }
284
285    do {
286	cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
287    } while (cc < 0 && errno == EINTR);
288    if (cc != 1) {
289	if (cc < 0)
290	    nss_MD_unix_map_default_error(errno);
291	else
292	    PORT_SetError(PR_UNKNOWN_ERROR);
293	return SECFailure;
294    }
295
296    return SECSuccess;
297}
298
299#endif
300
301#elif defined(WIN32)
302
303#include "win32err.h"
304
305/* on Windows, we need to find the optimal type of locking mechanism to use
306 for the sslMutex.
307
308 There are 3 cases :
309 1) single-process, use a PRLock, as for all other platforms
310 2) Win95 multi-process, use a Win32 mutex
311 3) on WINNT multi-process, use a PRLock + a Win32 mutex
312
313*/
314
315#ifdef WINNT
316
317SECStatus sslMutex_2LevelInit(sslMutex *sem)
318{
319    /*  the following adds a PRLock to sslMutex . This is done in each
320        process of a multi-process server and is only needed on WINNT, if
321        using fibers. We can't tell if native threads or fibers are used, so
322        we always do it on WINNT
323    */
324    PR_ASSERT(sem);
325    if (sem) {
326        /* we need to reset the sslLock in the children or the single_process init
327           function below will assert */
328        sem->u.sslLock = NULL;
329    }
330    return single_process_sslMutex_Init(sem);
331}
332
333static SECStatus sslMutex_2LevelDestroy(sslMutex *sem)
334{
335    return single_process_sslMutex_Destroy(sem);
336}
337
338#endif
339
340SECStatus
341sslMutex_Init(sslMutex *pMutex, int shared)
342{
343#ifdef WINNT
344    SECStatus retvalue;
345#endif
346    HANDLE hMutex;
347    SECURITY_ATTRIBUTES attributes =
348                                { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
349
350    PR_ASSERT(pMutex != 0 && (pMutex->u.sslMutx == 0 ||
351              pMutex->u.sslMutx == INVALID_HANDLE_VALUE) );
352
353    pMutex->isMultiProcess = (PRBool)(shared != 0);
354
355    if (PR_FALSE == pMutex->isMultiProcess) {
356        return single_process_sslMutex_Init(pMutex);
357    }
358
359#ifdef WINNT
360    /*  we need a lock on WINNT for fibers in the parent process */
361    retvalue = sslMutex_2LevelInit(pMutex);
362    if (SECSuccess != retvalue)
363        return SECFailure;
364#endif
365
366    if (!pMutex || ((hMutex = pMutex->u.sslMutx) != 0 &&
367        hMutex != INVALID_HANDLE_VALUE)) {
368        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
369        return SECFailure;
370    }
371    attributes.bInheritHandle = (shared ? TRUE : FALSE);
372    hMutex = CreateMutex(&attributes, FALSE, NULL);
373    if (hMutex == NULL) {
374        hMutex = INVALID_HANDLE_VALUE;
375        nss_MD_win32_map_default_error(GetLastError());
376        return SECFailure;
377    }
378    pMutex->u.sslMutx = hMutex;
379    return SECSuccess;
380}
381
382SECStatus
383sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal)
384{
385    HANDLE hMutex;
386    int    rv;
387    int retvalue = SECSuccess;
388
389    PR_ASSERT(pMutex != 0);
390    if (PR_FALSE == pMutex->isMultiProcess) {
391        return single_process_sslMutex_Destroy(pMutex);
392    }
393
394    /*  multi-process mode */
395#ifdef WINNT
396    /* on NT, get rid of the PRLock used for fibers within a process */
397    retvalue = sslMutex_2LevelDestroy(pMutex);
398#endif
399
400    PR_ASSERT( pMutex->u.sslMutx != 0 &&
401               pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
402    if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0
403        || hMutex == INVALID_HANDLE_VALUE) {
404        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
405        return SECFailure;
406    }
407
408    rv = CloseHandle(hMutex); /* ignore error */
409    if (!processLocal && rv) {
410        pMutex->u.sslMutx = hMutex = INVALID_HANDLE_VALUE;
411    }
412    if (!rv) {
413        nss_MD_win32_map_default_error(GetLastError());
414        retvalue = SECFailure;
415    }
416    return retvalue;
417}
418
419int
420sslMutex_Unlock(sslMutex *pMutex)
421{
422    BOOL   success = FALSE;
423    HANDLE hMutex;
424
425    PR_ASSERT(pMutex != 0 );
426    if (PR_FALSE == pMutex->isMultiProcess) {
427        return single_process_sslMutex_Unlock(pMutex);
428    }
429
430    PR_ASSERT(pMutex->u.sslMutx != 0 &&
431              pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
432    if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0 ||
433        hMutex == INVALID_HANDLE_VALUE) {
434        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
435        return SECFailure;
436    }
437    success = ReleaseMutex(hMutex);
438    if (!success) {
439        nss_MD_win32_map_default_error(GetLastError());
440        return SECFailure;
441    }
442#ifdef WINNT
443    return single_process_sslMutex_Unlock(pMutex);
444    /* release PRLock for other fibers in the process */
445#else
446    return SECSuccess;
447#endif
448}
449
450int
451sslMutex_Lock(sslMutex *pMutex)
452{
453    HANDLE    hMutex;
454    DWORD     event;
455    DWORD     lastError;
456    SECStatus rv;
457    SECStatus retvalue = SECSuccess;
458    PR_ASSERT(pMutex != 0);
459
460    if (PR_FALSE == pMutex->isMultiProcess) {
461        return single_process_sslMutex_Lock(pMutex);
462    }
463#ifdef WINNT
464    /* lock first to preserve from other threads/fibers
465       in the same process */
466    retvalue = single_process_sslMutex_Lock(pMutex);
467#endif
468    PR_ASSERT(pMutex->u.sslMutx != 0 &&
469              pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
470    if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0 ||
471        hMutex == INVALID_HANDLE_VALUE) {
472        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
473        return SECFailure;      /* what else ? */
474    }
475    /* acquire the mutex to be the only owner accross all other processes */
476    event = WaitForSingleObject(hMutex, INFINITE);
477    switch (event) {
478    case WAIT_OBJECT_0:
479    case WAIT_ABANDONED:
480        rv = SECSuccess;
481        break;
482
483    case WAIT_TIMEOUT:
484#if defined(WAIT_IO_COMPLETION)
485    case WAIT_IO_COMPLETION:
486#endif
487    default:            /* should never happen. nothing we can do. */
488        PR_ASSERT(!("WaitForSingleObject returned invalid value."));
489	PORT_SetError(PR_UNKNOWN_ERROR);
490	rv = SECFailure;
491	break;
492
493    case WAIT_FAILED:           /* failure returns this */
494        rv = SECFailure;
495        lastError = GetLastError();     /* for debugging */
496        nss_MD_win32_map_default_error(lastError);
497        break;
498    }
499
500    if (! (SECSuccess == retvalue && SECSuccess == rv)) {
501        return SECFailure;
502    }
503
504    return SECSuccess;
505}
506
507#elif defined(XP_UNIX)
508
509#include <errno.h>
510#include "unix_err.h"
511
512SECStatus
513sslMutex_Init(sslMutex *pMutex, int shared)
514{
515    int rv;
516    PR_ASSERT(pMutex);
517    pMutex->isMultiProcess = (PRBool)(shared != 0);
518    if (!shared) {
519        return single_process_sslMutex_Init(pMutex);
520    }
521    do {
522        rv = sem_init(&pMutex->u.sem, shared, 1);
523    } while (rv < 0 && errno == EINTR);
524    if (rv < 0) {
525        nss_MD_unix_map_default_error(errno);
526        return SECFailure;
527    }
528    return SECSuccess;
529}
530
531SECStatus
532sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal)
533{
534    int rv;
535    if (PR_FALSE == pMutex->isMultiProcess) {
536        return single_process_sslMutex_Destroy(pMutex);
537    }
538
539    /* semaphores are global resources. See SEM_DESTROY(3) man page */
540    if (processLocal) {
541	return SECSuccess;
542    }
543    do {
544	rv = sem_destroy(&pMutex->u.sem);
545    } while (rv < 0 && errno == EINTR);
546    if (rv < 0) {
547	nss_MD_unix_map_default_error(errno);
548	return SECFailure;
549    }
550    return SECSuccess;
551}
552
553SECStatus
554sslMutex_Unlock(sslMutex *pMutex)
555{
556    int rv;
557    if (PR_FALSE == pMutex->isMultiProcess) {
558        return single_process_sslMutex_Unlock(pMutex);
559    }
560    do {
561	rv = sem_post(&pMutex->u.sem);
562    } while (rv < 0 && errno == EINTR);
563    if (rv < 0) {
564	nss_MD_unix_map_default_error(errno);
565	return SECFailure;
566    }
567    return SECSuccess;
568}
569
570SECStatus
571sslMutex_Lock(sslMutex *pMutex)
572{
573    int rv;
574    if (PR_FALSE == pMutex->isMultiProcess) {
575        return single_process_sslMutex_Lock(pMutex);
576    }
577    do {
578	rv = sem_wait(&pMutex->u.sem);
579    } while (rv < 0 && errno == EINTR);
580    if (rv < 0) {
581	nss_MD_unix_map_default_error(errno);
582	return SECFailure;
583    }
584    return SECSuccess;
585}
586
587#else
588
589SECStatus
590sslMutex_Init(sslMutex *pMutex, int shared)
591{
592    PR_ASSERT(pMutex);
593    pMutex->isMultiProcess = (PRBool)(shared != 0);
594    if (!shared) {
595        return single_process_sslMutex_Init(pMutex);
596    }
597    PORT_Assert(!("sslMutex_Init not implemented for multi-process applications !"));
598    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
599    return SECFailure;
600}
601
602SECStatus
603sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal)
604{
605    PR_ASSERT(pMutex);
606    if (PR_FALSE == pMutex->isMultiProcess) {
607        return single_process_sslMutex_Destroy(pMutex);
608    }
609    PORT_Assert(!("sslMutex_Destroy not implemented for multi-process applications !"));
610    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
611    return SECFailure;
612}
613
614SECStatus
615sslMutex_Unlock(sslMutex *pMutex)
616{
617    PR_ASSERT(pMutex);
618    if (PR_FALSE == pMutex->isMultiProcess) {
619        return single_process_sslMutex_Unlock(pMutex);
620    }
621    PORT_Assert(!("sslMutex_Unlock not implemented for multi-process applications !"));
622    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
623    return SECFailure;
624}
625
626SECStatus
627sslMutex_Lock(sslMutex *pMutex)
628{
629    PR_ASSERT(pMutex);
630    if (PR_FALSE == pMutex->isMultiProcess) {
631        return single_process_sslMutex_Lock(pMutex);
632    }
633    PORT_Assert(!("sslMutex_Lock not implemented for multi-process applications !"));
634    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
635    return SECFailure;
636}
637
638#endif
639
640#endif
641