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