/* libp11, a simple layer on to of PKCS#11 API
* Copyright (C) 2010-2012 Free Software Foundation, Inc.
* Copyright (C) 2014 Red Hat
* Copyright (C) 2018 MichaĆ Trojnara <Michal.Trojnara@stunnel.org>
*
* Author: Nikos Mavrogiannopoulos
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
#include "libp11-int.h"
#ifndef _WIN32
#ifndef __STDC_VERSION__
/* older than C90 */
#define inline
#endif /* __STDC_VERSION__ */
#ifdef HAVE___REGISTER_ATFORK
#ifdef __sun
#pragma fini(lib_deinit)
#pragma init(lib_init)
#define _CONSTRUCTOR
#define _DESTRUCTOR
#else /* __sun */
#define _CONSTRUCTOR __attribute__((constructor))
#define _DESTRUCTOR __attribute__((destructor))
#endif /* __sun */
static unsigned int P11_forkid = 0;
inline static unsigned int _P11_get_forkid(void)
{
return P11_forkid;
}
inline static int _P11_detect_fork(unsigned int forkid)
{
if (forkid == P11_forkid)
return 0;
return 1;
}
static void fork_handler(void)
{
P11_forkid++;
}
extern int __register_atfork(void (*)(void), void(*)(void), void (*)(void), void *);
extern void *__dso_handle;
_CONSTRUCTOR
int _P11_register_fork_handler(void)
{
if (__register_atfork(0, 0, fork_handler, __dso_handle) != 0)
return -1;
return 0;
}
#else /* HAVE___REGISTER_ATFORK */
inline static unsigned int _P11_get_forkid(void)
{
return getpid();
}
inline static int _P11_detect_fork(unsigned int forkid)
{
if (getpid() == forkid)
return 0;
return 1;
}
#endif /* HAVE___REGISTER_ATFORK */
#else /* !_WIN32 */
#define _P11_get_forkid() 0
#define _P11_detect_fork(x) 0
#endif /* !_WIN32 */
unsigned int get_forkid()
{
return _P11_get_forkid();
}
/*
* PKCS#11 reinitialization after fork
* It wipes out the internal state of the PKCS#11 library
* Any libp11 references to this state are no longer valid
*/
static int check_fork_int(PKCS11_CTX *ctx)
{
PKCS11_CTX_private *cpriv = PRIVCTX(ctx);
if (_P11_detect_fork(cpriv->forkid)) {
if (pkcs11_CTX_reload(ctx) < 0)
return -1;
cpriv->forkid = _P11_get_forkid();
}
return 0;
}
/*
* PKCS#11 reinitialization after fork
* Also relogins and reopens the session if needed
*/
static int check_slot_fork_int(PKCS11_SLOT *slot)
{
PKCS11_SLOT_private *spriv = PRIVSLOT(slot);
PKCS11_CTX *ctx = SLOT2CTX(slot);
PKCS11_CTX_private *cpriv = PRIVCTX(ctx);
if (check_fork_int(SLOT2CTX(slot)) < 0)
return -1;
if (spriv->forkid != cpriv->forkid) {
if (spriv->loggedIn) {
int saved = spriv->haveSession;
spriv->haveSession = 0;
spriv->loggedIn = 0;
if (pkcs11_relogin(slot) < 0)
return -1;
spriv->haveSession = saved;
}
if (spriv->haveSession) {
spriv->haveSession = 0;
if (pkcs11_reopen_session(slot) < 0)
return -1;
}
spriv->forkid = cpriv->forkid;
}
return 0;
}
/*
* PKCS#11 reinitialization after fork
* Also reloads the key
*/
static int check_key_fork_int(PKCS11_KEY *key)
{
PKCS11_SLOT *slot = KEY2SLOT(key);
PKCS11_KEY_private *kpriv = PRIVKEY(key);
PKCS11_SLOT_private *spriv = PRIVSLOT(slot);
if (check_slot_fork_int(slot) < 0)
return -1;
if (spriv->forkid != kpriv->forkid) {
pkcs11_reload_key(key);
kpriv->forkid = spriv->forkid;
}
return 0;
}
/*
* Locking interface to check_fork_int()
*/
int check_fork(PKCS11_CTX *ctx)
{
PKCS11_CTX_private *cpriv;
int rv;
if (ctx == NULL)
return -1;
cpriv = PRIVCTX(ctx);
CRYPTO_THREAD_write_lock(cpriv->rwlock);
rv = check_fork_int(ctx);
CRYPTO_THREAD_unlock(cpriv->rwlock);
return rv;
}
/*
* Locking interface to check_slot_fork_int()
*/
int check_slot_fork(PKCS11_SLOT *slot)
{
PKCS11_CTX_private *cpriv;
int rv;
if (slot == NULL)
return -1;
cpriv = PRIVCTX(SLOT2CTX(slot));
CRYPTO_THREAD_write_lock(cpriv->rwlock);
rv = check_slot_fork_int(slot);
CRYPTO_THREAD_unlock(cpriv->rwlock);
return rv;
}
/*
* Reinitialize token (just its slot)
*/
int check_token_fork(PKCS11_TOKEN *token)
{
if (token == NULL)
return -1;
return check_slot_fork(TOKEN2SLOT(token));
}
/*
* Locking interface to check_key_fork_int()
*/
int check_key_fork(PKCS11_KEY *key)
{
PKCS11_CTX_private *cpriv;
int rv;
if (key == NULL)
return -1;
cpriv = PRIVCTX(KEY2CTX(key));
CRYPTO_THREAD_write_lock(cpriv->rwlock);
rv = check_key_fork_int(key);
CRYPTO_THREAD_unlock(cpriv->rwlock);
return rv;
}
/*
* Reinitialize cert (just its token)
*/
int check_cert_fork(PKCS11_CERT *cert)
{
if (cert == NULL)
return -1;
return check_token_fork(CERT2TOKEN(cert));
}
/* vim: set noexpandtab: */