Home | History | Annotate | Download | only in ssl
      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 {
     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 
     23 static 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 
     35 static 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 
     47 static 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 
     77 static int
     78 setNonBlocking(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 
     95 SECStatus
     96 sslMutex_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 
    130 loser:
    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 
    137 SECStatus
    138 sslMutex_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 
    172 SECStatus
    173 sslMutex_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 
    203 SECStatus
    204 sslMutex_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 
    242 SECStatus
    243 sslMutex_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 
    270 SECStatus
    271 sslMutex_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 
    317 SECStatus 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 
    333 static SECStatus sslMutex_2LevelDestroy(sslMutex *sem)
    334 {
    335     return single_process_sslMutex_Destroy(sem);
    336 }
    337 
    338 #endif
    339 
    340 SECStatus
    341 sslMutex_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 
    382 SECStatus
    383 sslMutex_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 
    419 int
    420 sslMutex_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 
    450 int
    451 sslMutex_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 
    512 SECStatus
    513 sslMutex_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 
    531 SECStatus
    532 sslMutex_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 
    553 SECStatus
    554 sslMutex_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 
    570 SECStatus
    571 sslMutex_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 
    589 SECStatus
    590 sslMutex_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 
    602 SECStatus
    603 sslMutex_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 
    614 SECStatus
    615 sslMutex_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 
    626 SECStatus
    627 sslMutex_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