зеркало из https://github.com/mozilla/gecko-dev.git
move handling of certificate reference counting into Stan. NSS 3.4 needs to maintain persistent references of both temp and perm certs in order to replicate the old temp database.
This commit is contained in:
Родитель
3c7d0ffdc4
Коммит
82b1f2de39
|
@ -34,7 +34,7 @@
|
|||
/*
|
||||
* Certificate handling code
|
||||
*
|
||||
* $Id: certdb.c,v 1.19 2001/11/21 18:00:08 relyea%netscape.com Exp $
|
||||
* $Id: certdb.c,v 1.20 2002/01/03 20:09:27 ian.mcgreer%sun.com Exp $
|
||||
*/
|
||||
|
||||
#include "nssilock.h"
|
||||
|
@ -63,7 +63,7 @@
|
|||
#ifndef NSS_3_4_CODE
|
||||
#define NSS_3_4_CODE
|
||||
#endif /* NSS_3_4_CODE */
|
||||
#include "pkit.h"
|
||||
#include "pki.h"
|
||||
|
||||
/*
|
||||
* Certificate database handling code
|
||||
|
@ -1159,9 +1159,14 @@ CERTCertificate *
|
|||
CERT_DupCertificate(CERTCertificate *c)
|
||||
{
|
||||
if (c) {
|
||||
#ifdef NSS_CLASSIC
|
||||
CERT_LockCertRefCount(c);
|
||||
++c->referenceCount;
|
||||
CERT_UnlockCertRefCount(c);
|
||||
#else
|
||||
NSSCertificate *tmp = STAN_GetNSSCertificate(c);
|
||||
nssCertificate_AddRef(tmp);
|
||||
#endif
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
|
|
@ -164,6 +164,7 @@ CERTCertificate *
|
|||
__CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
|
||||
char *nickname, PRBool isperm, PRBool copyDER)
|
||||
{
|
||||
PRStatus nssrv;
|
||||
NSSCertificate *c;
|
||||
NSSCryptoContext *context;
|
||||
NSSArena *arena;
|
||||
|
@ -174,13 +175,14 @@ __CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
|
|||
}
|
||||
c = nss_ZNEW(arena, NSSCertificate);
|
||||
if (!c) {
|
||||
goto loser;
|
||||
nssArena_Destroy(arena);
|
||||
return NULL;
|
||||
}
|
||||
NSSITEM_FROM_SECITEM(&c->encoding, derCert);
|
||||
c->object.arena = arena;
|
||||
c->object.refCount = 1;
|
||||
c->object.instanceList = nssList_Create(arena, PR_TRUE);
|
||||
c->object.instances = nssList_CreateIterator(c->object.instanceList);
|
||||
nssrv = nssPKIObject_Initialize(&c->object, arena, NULL, NULL);
|
||||
if (nssrv != PR_SUCCESS) {
|
||||
goto loser;
|
||||
}
|
||||
/* Forces a decoding of the cert in order to obtain the parts used
|
||||
* below
|
||||
*/
|
||||
|
@ -204,11 +206,14 @@ __CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
|
|||
PORT_Strlen(cc->emailAddr));
|
||||
}
|
||||
context = STAN_GetDefaultCryptoContext();
|
||||
NSSCryptoContext_ImportCertificate(context, c);
|
||||
nssrv = NSSCryptoContext_ImportCertificate(context, c);
|
||||
if (nssrv != PR_SUCCESS) {
|
||||
goto loser;
|
||||
}
|
||||
c->object.trustDomain = STAN_GetDefaultTrustDomain();
|
||||
return cc;
|
||||
loser:
|
||||
nssArena_Destroy(arena);
|
||||
nssPKIObject_Destroy(&c->object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -404,28 +409,50 @@ CERT_DestroyCertificate(CERTCertificate *cert)
|
|||
int refCount;
|
||||
CERTCertDBHandle *handle;
|
||||
if ( cert ) {
|
||||
NSSCertificate *tmp = STAN_GetNSSCertificate(cert);
|
||||
handle = cert->dbhandle;
|
||||
#ifdef NSS_CLASSIC
|
||||
CERT_LockCertRefCount(cert);
|
||||
PORT_Assert(cert->referenceCount > 0);
|
||||
refCount = --cert->referenceCount;
|
||||
CERT_UnlockCertRefCount(cert);
|
||||
if ( ( refCount == 0 ) && !cert->keepSession ) {
|
||||
PRArenaPool *arena = cert->arena;
|
||||
#else
|
||||
if (tmp) {
|
||||
/* delete the NSSCertificate */
|
||||
NSSTrustDomain *td = STAN_GetDefaultTrustDomain();
|
||||
NSSCertificate *tmp = STAN_GetNSSCertificate(cert);
|
||||
refCount = (int)tmp->object.refCount;
|
||||
if (tmp) {
|
||||
NSSCryptoContext *cc = tmp->object.cryptoContext;
|
||||
nssTrustDomain_RemoveCertFromCache(td, tmp);
|
||||
/* In 4.0, in context references of this cert would go
|
||||
* away with the context. but here the context persists,
|
||||
* so explicity remove the cert.
|
||||
/* This is a hack. For 3.4, there are persistent references
|
||||
* to 4.0 certificates during the lifetime of a cert. In the
|
||||
* case of a temp cert, the persistent reference is in the
|
||||
* cert store of the global crypto context. For a perm cert,
|
||||
* the persistent reference is in the cache. Thus, the last
|
||||
* external reference is really the penultimate NSS reference.
|
||||
* When the count drops to two, it is really one, but the
|
||||
* persistent reference must be explicitly deleted. In 4.0,
|
||||
* this ugliness will not appear. Crypto contexts will remove
|
||||
* their own cert references, and the cache will have its
|
||||
* own management code also.
|
||||
*/
|
||||
if (cc != NULL) {
|
||||
nssCertificateStore_Remove(cc->certStore, tmp);
|
||||
if (refCount == 2) {
|
||||
NSSCryptoContext *cc = tmp->object.cryptoContext;
|
||||
if (cc != NULL) {
|
||||
nssCertificateStore_Remove(cc->certStore, tmp);
|
||||
} else {
|
||||
nssTrustDomain_RemoveCertFromCache(td, tmp);
|
||||
}
|
||||
refCount = (int)tmp->object.refCount;
|
||||
}
|
||||
NSSCertificate_Destroy(tmp);
|
||||
/* another hack... the destroy *must* decrement the count */
|
||||
--refCount;
|
||||
}
|
||||
} else {
|
||||
refCount = 0;
|
||||
}
|
||||
#endif
|
||||
if ( ( refCount == 0 ) && !cert->keepSession ) {
|
||||
PRArenaPool *arena = cert->arena;
|
||||
/* zero cert before freeing. Any stale references to this cert
|
||||
* after this point will probably cause an exception. */
|
||||
PORT_Memset(cert, 0, sizeof *cert);
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: devobject.c,v $ $Revision: 1.7 $ $Date: 2001/12/20 23:17:58 $ $Name: $";
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: devobject.c,v $ $Revision: 1.8 $ $Date: 2002/01/03 20:09:18 $ $Name: $";
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifndef DEV_H
|
||||
|
@ -316,7 +316,6 @@ get_token_cert
|
|||
)
|
||||
{
|
||||
NSSCertificate *rvCert;
|
||||
struct nssPKIObjectBaseStr *object; /* XXX needs to go */
|
||||
NSSArena *arena;
|
||||
nssSession *session;
|
||||
PRStatus nssrv;
|
||||
|
@ -339,25 +338,18 @@ get_token_cert
|
|||
}
|
||||
rvCert = nss_ZNEW(arena, NSSCertificate);
|
||||
if (!rvCert) {
|
||||
goto loser;
|
||||
NSSArena_Destroy(arena);
|
||||
return NULL;
|
||||
}
|
||||
object = &rvCert->object;
|
||||
object->arena = arena;
|
||||
object->refCount = 1;
|
||||
object->instanceList = nssList_Create(arena, PR_TRUE);
|
||||
/* XXX this is only valid if tokens are unique in trust domains... */
|
||||
object->trustDomain = token->trustDomain;
|
||||
if (!object->instanceList) {
|
||||
goto loser;
|
||||
}
|
||||
object->instances = nssList_CreateIterator(object->instanceList);
|
||||
if (!object->instances) {
|
||||
nssrv = nssPKIObject_Initialize(&rvCert->object, arena,
|
||||
token->trustDomain, NULL);
|
||||
if (nssrv != PR_SUCCESS) {
|
||||
goto loser;
|
||||
}
|
||||
nssrv = nssCKObject_GetAttributes(handle,
|
||||
cert_template, template_size,
|
||||
arena, session, token->slot);
|
||||
if (nssrv) {
|
||||
if (nssrv != PR_SUCCESS) {
|
||||
goto loser;
|
||||
}
|
||||
rvCert->type = nss_cert_type_from_ck_attrib(&cert_template[0]);
|
||||
|
@ -383,7 +375,7 @@ get_token_cert
|
|||
#endif
|
||||
return rvCert;
|
||||
loser:
|
||||
nssArena_Destroy(arena);
|
||||
nssPKIObject_Destroy(&rvCert->object);
|
||||
return (NSSCertificate *)NULL;
|
||||
}
|
||||
|
||||
|
@ -494,7 +486,9 @@ retrieve_cert(NSSToken *t, nssSession *session, CK_OBJECT_HANDLE h, void *arg)
|
|||
}
|
||||
nssList_Add(cert->object.instanceList, ci);
|
||||
}
|
||||
return (*search->callback)(cert, search->cbarg);
|
||||
nssrv = (*search->callback)(cert, search->cbarg);
|
||||
NSSCertificate_Destroy(cert);
|
||||
return nssrv;
|
||||
}
|
||||
|
||||
/* traverse all certificates - this should only happen if the token
|
||||
|
@ -889,7 +883,6 @@ nssToken_FindTrustForCert
|
|||
NSSTrust *rvTrust;
|
||||
nssSession *session;
|
||||
NSSArena *arena;
|
||||
struct nssPKIObjectBaseStr *object;
|
||||
nssCryptokiInstance *instance;
|
||||
PRBool isTokenObject;
|
||||
CK_BBOOL isToken;
|
||||
|
@ -926,31 +919,23 @@ nssToken_FindTrustForCert
|
|||
nssArena_Destroy(arena);
|
||||
return NULL;
|
||||
}
|
||||
object = &rvTrust->object;
|
||||
object->arena = arena;
|
||||
object->refCount = 1;
|
||||
/* XXX this is only valid if tokens are unique in trust domains... */
|
||||
object->trustDomain = token->trustDomain;
|
||||
object->instanceList = nssList_Create(arena, PR_TRUE);
|
||||
if (!object->instanceList) {
|
||||
nssArena_Destroy(arena);
|
||||
return NULL;
|
||||
}
|
||||
object->instances = nssList_CreateIterator(object->instanceList);
|
||||
if (!object->instances) {
|
||||
nssArena_Destroy(arena);
|
||||
return NULL;
|
||||
nssrv = nssPKIObject_Initialize(&rvTrust->object, arena,
|
||||
token->trustDomain, NULL);
|
||||
if (nssrv != PR_SUCCESS) {
|
||||
goto loser;
|
||||
}
|
||||
isTokenObject = (isToken == CK_TRUE) ? PR_TRUE : PR_FALSE;
|
||||
instance = create_cryptoki_instance(arena, token, tobjID, isTokenObject);
|
||||
if (!instance) {
|
||||
nssArena_Destroy(arena);
|
||||
return NULL;
|
||||
goto loser;
|
||||
}
|
||||
rvTrust->serverAuth = saTrust;
|
||||
rvTrust->clientAuth = caTrust;
|
||||
rvTrust->emailProtection = epTrust;
|
||||
rvTrust->codeSigning = csTrust;
|
||||
return rvTrust;
|
||||
loser:
|
||||
nssPKIObject_Destroy(&rvTrust->object);
|
||||
return (NSSTrust *)NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -76,8 +76,6 @@ struct nss3_cert_cbstr {
|
|||
|
||||
/* Translate from NSSCertificate to CERTCertificate, then pass the latter
|
||||
* to a callback.
|
||||
* Question: do all callers of PK11_ functions using a CERTCertificate
|
||||
* callback understand that they must dup the cert to keep it alive?
|
||||
*/
|
||||
static PRStatus convert_cert(NSSCertificate *c, void *arg)
|
||||
{
|
||||
|
@ -87,11 +85,18 @@ static PRStatus convert_cert(NSSCertificate *c, void *arg)
|
|||
nss3cert = STAN_GetCERTCertificate(c);
|
||||
if (!nss3cert) return PR_FAILURE;
|
||||
secrv = (*nss3cb->callback)(nss3cert, nss3cb->arg);
|
||||
/* this destroy is dependent upon the question above */
|
||||
CERT_DestroyCertificate(nss3cert);
|
||||
return (secrv) ? PR_FAILURE : PR_SUCCESS;
|
||||
}
|
||||
|
||||
/* this is redeclared from trustdomain.c, but this code is just 3.4 glue
|
||||
* anyway
|
||||
*/
|
||||
static void cert_destructor(void *el)
|
||||
{
|
||||
NSSCertificate *c = (NSSCertificate *)el;
|
||||
NSSCertificate_Destroy(c);
|
||||
}
|
||||
|
||||
void
|
||||
PK11Slot_SetNSSToken(PK11SlotInfo *sl, NSSToken *nsst)
|
||||
{
|
||||
|
@ -1186,6 +1191,7 @@ PK11_FindCertFromNickname(char *nickname, void *wincx) {
|
|||
*delimit = '\0';
|
||||
/* find token by name */
|
||||
token = NSSTrustDomain_FindTokenByName(defaultTD, (NSSUTF8 *)tokenName);
|
||||
slot = PK11_ReferenceSlot(token->pk11slot);
|
||||
*delimit = ':';
|
||||
} else {
|
||||
slot = PK11_GetInternalKeySlot();
|
||||
|
@ -1194,6 +1200,12 @@ PK11_FindCertFromNickname(char *nickname, void *wincx) {
|
|||
if (token) {
|
||||
nssTokenCertSearch search;
|
||||
nssList *certList;
|
||||
if (!PK11_IsFriendly(slot)) {
|
||||
if (PK11_Authenticate(slot, PR_TRUE, wincx) != SECSuccess) {
|
||||
PK11_FreeSlot(slot);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
certList = nssList_Create(NULL, PR_FALSE);
|
||||
(void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD,
|
||||
nickname,
|
||||
|
@ -1207,6 +1219,7 @@ PK11_FindCertFromNickname(char *nickname, void *wincx) {
|
|||
nssToken_TraverseCertificatesByNickname(token, NULL,
|
||||
(NSSUTF8 *)nickname,
|
||||
&search);
|
||||
nssList_Clear(certList, cert_destructor);
|
||||
nssList_Destroy(certList);
|
||||
if (!cert) {
|
||||
certList = nssList_Create(NULL, PR_FALSE);
|
||||
|
@ -1217,9 +1230,11 @@ PK11_FindCertFromNickname(char *nickname, void *wincx) {
|
|||
nssToken_TraverseCertificatesByEmail(token, NULL,
|
||||
(NSSASCII7 *)nickname,
|
||||
&search);
|
||||
nssList_Clear(certList, cert_destructor);
|
||||
nssList_Destroy(certList);
|
||||
}
|
||||
if (cert) {
|
||||
(void)nssTrustDomain_AddCertsToCache(defaultTD, &cert, 1);
|
||||
rvCert = STAN_GetCERTCertificate(cert);
|
||||
}
|
||||
}
|
||||
|
@ -1272,43 +1287,51 @@ PK11_FindCertsFromNickname(char *nickname, void *wincx) {
|
|||
int i;
|
||||
CERTCertList *certList = NULL;
|
||||
NSSCertificate **foundCerts = NULL;
|
||||
NSSCertificate *c;
|
||||
NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain();
|
||||
NSSCertificate *c;
|
||||
NSSToken *token;
|
||||
PK11SlotInfo *slot;
|
||||
if ((delimit = PORT_Strchr(nickname,':')) != NULL) {
|
||||
NSSToken *token;
|
||||
tokenName = nickname;
|
||||
nickname = delimit + 1;
|
||||
*delimit = '\0';
|
||||
/* find token by name */
|
||||
token = NSSTrustDomain_FindTokenByName(defaultTD, (NSSUTF8 *)tokenName);
|
||||
if (token) {
|
||||
nssTokenCertSearch search;
|
||||
PRUint32 count;
|
||||
nssList *nameList;
|
||||
nameList = nssList_Create(NULL, PR_FALSE);
|
||||
(void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD,
|
||||
nickname,
|
||||
nameList);
|
||||
/* set the search criteria */
|
||||
search.callback = collect_certs;
|
||||
search.cbarg = nameList;
|
||||
search.cached = nameList;
|
||||
search.searchType = nssTokenSearchType_TokenOnly;
|
||||
nssrv = nssToken_TraverseCertificatesByNickname(token, NULL,
|
||||
nickname, &search);
|
||||
count = nssList_Count(nameList);
|
||||
foundCerts = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1);
|
||||
nssList_GetArray(nameList, (void **)foundCerts, count);
|
||||
nssList_Destroy(nameList);
|
||||
}
|
||||
slot = PK11_ReferenceSlot(token->pk11slot);
|
||||
*delimit = ':';
|
||||
} else {
|
||||
foundCerts = NSSTrustDomain_FindCertificatesByNickname(
|
||||
defaultTD,
|
||||
(NSSUTF8 *)nickname,
|
||||
NULL,
|
||||
0,
|
||||
NULL);
|
||||
slot = PK11_GetInternalKeySlot();
|
||||
token = PK11Slot_GetNSSToken(slot);
|
||||
}
|
||||
if (token) {
|
||||
nssTokenCertSearch search;
|
||||
PRUint32 count;
|
||||
nssList *nameList;
|
||||
if (!PK11_IsFriendly(slot)) {
|
||||
if (PK11_Authenticate(slot, PR_TRUE, wincx) != SECSuccess) {
|
||||
PK11_FreeSlot(slot);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
nameList = nssList_Create(NULL, PR_FALSE);
|
||||
(void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD,
|
||||
nickname,
|
||||
nameList);
|
||||
/* set the search criteria */
|
||||
search.callback = collect_certs;
|
||||
search.cbarg = nameList;
|
||||
search.cached = nameList;
|
||||
search.searchType = nssTokenSearchType_TokenOnly;
|
||||
nssrv = nssToken_TraverseCertificatesByNickname(token, NULL,
|
||||
nickname, &search);
|
||||
count = nssList_Count(nameList);
|
||||
foundCerts = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1);
|
||||
nssList_GetArray(nameList, (void **)foundCerts, count);
|
||||
nssList_Clear(nameList, cert_destructor);
|
||||
nssList_Destroy(nameList);
|
||||
}
|
||||
if (slot) {
|
||||
PK11_FreeSlot(slot);
|
||||
}
|
||||
if (foundCerts) {
|
||||
certList = CERT_NewCertList();
|
||||
|
@ -2340,6 +2363,7 @@ PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert, PK11SlotInfo *slot,
|
|||
token = PK11Slot_GetNSSToken(slot);
|
||||
nssrv = nssToken_TraverseCertificatesBySubject(token, NULL,
|
||||
&subject, &search);
|
||||
nssList_Clear(subjectList, cert_destructor);
|
||||
nssList_Destroy(subjectList);
|
||||
return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure;
|
||||
#endif
|
||||
|
@ -2410,6 +2434,7 @@ PK11_TraverseCertsForNicknameInSlot(SECItem *nickname, PK11SlotInfo *slot,
|
|||
token = PK11Slot_GetNSSToken(slot);
|
||||
nssrv = nssToken_TraverseCertificatesByNickname(token, NULL,
|
||||
nick, &search);
|
||||
nssList_Clear(nameList, cert_destructor);
|
||||
nssList_Destroy(nameList);
|
||||
if (created) nss_ZFreeIf(nick);
|
||||
return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure;
|
||||
|
@ -2466,6 +2491,7 @@ PK11_TraverseCertsInSlot(PK11SlotInfo *slot,
|
|||
} else {
|
||||
nssrv = PR_FAILURE;
|
||||
}
|
||||
nssList_Clear(certList, cert_destructor);
|
||||
nssList_Destroy(certList);
|
||||
return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure;
|
||||
#endif
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: certdecode.c,v $ $Revision: 1.7 $ $Date: 2001/12/14 17:32:18 $ $Name: $";
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: certdecode.c,v $ $Revision: 1.8 $ $Date: 2002/01/03 20:09:21 $ $Name: $";
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifndef PKIT_H
|
||||
|
@ -46,30 +46,71 @@ static const char CVS_ID[] = "@(#) $RCSfile: certdecode.c,v $ $Revision: 1.7 $ $
|
|||
/* XXX
|
||||
* move this to a more appropriate location
|
||||
*/
|
||||
NSS_IMPLEMENT void
|
||||
nssPKIObject_AddRef
|
||||
NSS_IMPLEMENT PRStatus
|
||||
nssPKIObject_Initialize
|
||||
(
|
||||
struct nssPKIObjectBaseStr *object
|
||||
struct nssPKIObjectBaseStr *object,
|
||||
NSSArena *arena,
|
||||
NSSTrustDomain *td,
|
||||
NSSCryptoContext *cc
|
||||
)
|
||||
{
|
||||
/* XXX of course this needs to be locked in 4.0! */
|
||||
object->refCount++;
|
||||
object->arena = arena;
|
||||
object->trustDomain = td;
|
||||
object->cryptoContext = cc;
|
||||
object->lock = PZ_NewLock(nssILockOther);
|
||||
if (!object->lock) {
|
||||
return PR_FAILURE;
|
||||
}
|
||||
object->instanceList = nssList_Create(arena, PR_TRUE);
|
||||
if (!object->instanceList) {
|
||||
PZ_DestroyLock(object->lock);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
object->instances = nssList_CreateIterator(object->instanceList);
|
||||
if (!object->instances) {
|
||||
nssList_Destroy(object->instanceList);
|
||||
PZ_DestroyLock(object->lock);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
object->refCount = 1;
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
/* XXX
|
||||
* move this to a more appropriate location
|
||||
*/
|
||||
NSS_IMPLEMENT void
|
||||
nssPKIObject_AddRef
|
||||
(
|
||||
struct nssPKIObjectBaseStr *object
|
||||
)
|
||||
{
|
||||
PZ_Lock(object->lock);
|
||||
object->refCount++;
|
||||
PZ_Unlock(object->lock);
|
||||
}
|
||||
|
||||
/* XXX
|
||||
* move this to a more appropriate location
|
||||
*/
|
||||
NSS_IMPLEMENT PRBool
|
||||
nssPKIObject_Destroy
|
||||
(
|
||||
struct nssPKIObjectBaseStr *object
|
||||
)
|
||||
{
|
||||
/* XXX of course this needs to be locked in 4.0! */
|
||||
if (--object->refCount == 0) {
|
||||
PRUint32 refCount;
|
||||
PZ_Lock(object->lock);
|
||||
PORT_Assert(object->refCount > 0);
|
||||
refCount = --object->refCount;
|
||||
PZ_Unlock(object->lock);
|
||||
if (refCount == 0) {
|
||||
nssList_Destroy(object->instanceList);
|
||||
nssArena_Destroy(object->arena);
|
||||
return PR_TRUE;
|
||||
}
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
#ifdef NSS_3_4_CODE
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: certificate.c,v $ $Revision: 1.21 $ $Date: 2001/12/14 17:32:18 $ $Name: $";
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: certificate.c,v $ $Revision: 1.22 $ $Date: 2002/01/03 20:09:23 $ $Name: $";
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifndef NSSPKI_H
|
||||
|
@ -67,7 +67,9 @@ nssCertificate_AddRef
|
|||
NSSCertificate *c
|
||||
)
|
||||
{
|
||||
nssPKIObject_AddRef(&c->object);
|
||||
if (c) {
|
||||
nssPKIObject_AddRef(&c->object);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
@ -77,7 +79,9 @@ NSSCertificate_Destroy
|
|||
NSSCertificate *c
|
||||
)
|
||||
{
|
||||
nssPKIObject_Destroy(&c->object);
|
||||
if (c) {
|
||||
nssPKIObject_Destroy(&c->object);
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -679,6 +683,7 @@ nssSMIMEProfile_Create
|
|||
NSSItem *profileData
|
||||
)
|
||||
{
|
||||
PRStatus nssrv;
|
||||
NSSArena *arena;
|
||||
nssSMIMEProfile *rvProfile;
|
||||
arena = nssArena_Create();
|
||||
|
@ -687,18 +692,11 @@ nssSMIMEProfile_Create
|
|||
}
|
||||
rvProfile = nss_ZNEW(arena, nssSMIMEProfile);
|
||||
if (!rvProfile) {
|
||||
goto loser;
|
||||
nssArena_Destroy(arena);
|
||||
return NULL;
|
||||
}
|
||||
rvProfile->object.arena = arena;
|
||||
rvProfile->object.refCount = 1;
|
||||
rvProfile->object.instanceList = nssList_Create(arena, PR_TRUE);
|
||||
if (!rvProfile->object.instanceList) {
|
||||
goto loser;
|
||||
}
|
||||
rvProfile->object.instances = nssList_CreateIterator(
|
||||
rvProfile->object.instanceList);
|
||||
if (!rvProfile->object.instances) {
|
||||
nssList_Destroy(rvProfile->object.instanceList);
|
||||
nssrv = nssPKIObject_Initialize(&rvProfile->object, arena, NULL, NULL);
|
||||
if (nssrv != PR_SUCCESS) {
|
||||
goto loser;
|
||||
}
|
||||
rvProfile->certificate = cert;
|
||||
|
@ -712,7 +710,7 @@ nssSMIMEProfile_Create
|
|||
}
|
||||
return rvProfile;
|
||||
loser:
|
||||
nssArena_Destroy(arena);
|
||||
nssPKIObject_Destroy(&rvProfile->object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: pki3hack.c,v $ $Revision: 1.16 $ $Date: 2001/12/20 16:20:16 $ $Name: $";
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: pki3hack.c,v $ $Revision: 1.17 $ $Date: 2002/01/03 20:09:23 $ $Name: $";
|
||||
#endif /* DEBUG */
|
||||
|
||||
/*
|
||||
|
@ -580,6 +580,7 @@ STAN_GetNSSCertificate(CERTCertificate *cc)
|
|||
NSSCertificate *c;
|
||||
nssCryptokiInstance *instance;
|
||||
NSSArena *arena;
|
||||
PRStatus nssrv;
|
||||
c = cc->nssCertificate;
|
||||
if (c) {
|
||||
return c;
|
||||
|
@ -593,15 +594,15 @@ STAN_GetNSSCertificate(CERTCertificate *cc)
|
|||
}
|
||||
c = nss_ZNEW(arena, NSSCertificate);
|
||||
if (!c) {
|
||||
goto loser;
|
||||
nssArena_Destroy(arena);
|
||||
return NULL;
|
||||
}
|
||||
NSSITEM_FROM_SECITEM(&c->encoding, &cc->derCert);
|
||||
c->type = NSSCertificateType_PKIX;
|
||||
c->object.arena = arena;
|
||||
c->object.refCount = 1;
|
||||
c->object.trustDomain = (NSSTrustDomain *)cc->dbhandle;
|
||||
c->object.instanceList = nssList_Create(arena, PR_TRUE);
|
||||
c->object.instances = nssList_CreateIterator(c->object.instanceList);
|
||||
nssrv = nssPKIObject_Initialize(&c->object, arena, cc->dbhandle, NULL);
|
||||
if (nssrv != PR_SUCCESS) {
|
||||
nssPKIObject_Destroy(&c->object);
|
||||
}
|
||||
nssItem_Create(arena,
|
||||
&c->issuer, cc->derIssuer.len, cc->derIssuer.data);
|
||||
nssItem_Create(arena,
|
||||
|
@ -637,9 +638,6 @@ STAN_GetNSSCertificate(CERTCertificate *cc)
|
|||
c->decoding = create_decoded_pkix_cert_from_nss3cert(arena, cc);
|
||||
cc->nssCertificate = c;
|
||||
return c;
|
||||
loser:
|
||||
nssArena_Destroy(arena);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NSS_EXTERN PRStatus
|
||||
|
@ -665,17 +663,9 @@ STAN_ChangeCertTrust(CERTCertificate *cc, CERTCertTrust *trust)
|
|||
arena = nssArena_Create();
|
||||
if (!arena) return PR_FAILURE;
|
||||
nssTrust = nss_ZNEW(arena, NSSTrust);
|
||||
nssTrust->object.arena = arena;
|
||||
nssTrust->object.refCount = 1;
|
||||
nssTrust->object.instanceList = nssList_Create(arena, PR_FALSE);
|
||||
if (!nssTrust->object.instanceList) {
|
||||
nssArena_Destroy(arena);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
nssTrust->object.instances = nssList_CreateIterator(
|
||||
nssTrust->object.instanceList);
|
||||
if (!nssTrust->object.instances) {
|
||||
nssArena_Destroy(arena);
|
||||
nssrv = nssPKIObject_Initialize(&nssTrust->object, arena, NULL, NULL);
|
||||
if (nssrv != PR_SUCCESS) {
|
||||
nssPKIObject_Destroy(&nssTrust->object);
|
||||
return PR_FAILURE;
|
||||
}
|
||||
nssTrust->certificate = c;
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#define PKIM_H
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char PKIM_CVS_ID[] = "@(#) $RCSfile: pkim.h,v $ $Revision: 1.12 $ $Date: 2001/12/14 17:32:20 $ $Name: $";
|
||||
static const char PKIM_CVS_ID[] = "@(#) $RCSfile: pkim.h,v $ $Revision: 1.13 $ $Date: 2002/01/03 20:09:24 $ $Name: $";
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifndef BASE_H
|
||||
|
@ -281,13 +281,22 @@ nssBestCertificate_Callback
|
|||
void *arg
|
||||
);
|
||||
|
||||
NSS_EXTERN PRStatus
|
||||
nssPKIObject_Initialize
|
||||
(
|
||||
struct nssPKIObjectBaseStr *object,
|
||||
NSSArena *arena,
|
||||
NSSTrustDomain *td,
|
||||
NSSCryptoContext *cc
|
||||
);
|
||||
|
||||
NSS_EXTERN void
|
||||
nssPKIObject_AddRef
|
||||
(
|
||||
struct nssPKIObjectBaseStr *object
|
||||
);
|
||||
|
||||
NSS_EXTERN void
|
||||
NSS_EXTERN PRBool
|
||||
nssPKIObject_Destroy
|
||||
(
|
||||
struct nssPKIObjectBaseStr *object
|
||||
|
|
|
@ -32,16 +32,20 @@
|
|||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: pkistore.c,v $ $Revision: 1.2 $ $Date: 2001/12/19 20:27:21 $ $Name: $";
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: pkistore.c,v $ $Revision: 1.3 $ $Date: 2002/01/03 20:09:24 $ $Name: $";
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifndef PKIM_H
|
||||
#include "pkim.h"
|
||||
#endif /* PKIM_H */
|
||||
|
||||
#ifndef PKIT_H
|
||||
#include "pkit.h"
|
||||
#endif /* PKIT_H */
|
||||
#ifndef PKI_H
|
||||
#include "pki.h"
|
||||
#endif /* PKI_H */
|
||||
|
||||
#ifndef NSSPKI_H
|
||||
#include "nsspki.h"
|
||||
#endif /* NSSPKI_H */
|
||||
|
||||
#ifndef BASE_H
|
||||
#include "base.h"
|
||||
|
@ -58,9 +62,6 @@ static const char CVS_ID[] = "@(#) $RCSfile: pkistore.c,v $ $Revision: 1.2 $ $Da
|
|||
* stay in until they are explicitly removed. It is only used by crypto
|
||||
* contexts at this time, but may be more generally useful...
|
||||
*
|
||||
* TODO:
|
||||
* 1) In the future (4.0), the cert entries are ref-counted. In 3.4, they
|
||||
* are managed by the CERTCertificate pointer.
|
||||
*/
|
||||
|
||||
struct nssCertificateStoreStr
|
||||
|
@ -244,7 +245,9 @@ nssCertificateStore_Add
|
|||
nssrv = add_certificate_entry(store, cert);
|
||||
if (nssrv == PR_SUCCESS) {
|
||||
nssrv = add_subject_entry(store, cert);
|
||||
if (nssrv != PR_SUCCESS) {
|
||||
if (nssrv == PR_SUCCESS) {
|
||||
nssCertificate_AddRef(cert); /* obtain a reference for the store */
|
||||
} else {
|
||||
remove_certificate_entry(store, cert);
|
||||
}
|
||||
}
|
||||
|
@ -290,7 +293,6 @@ remove_subject_entry
|
|||
if (nssList_Count(subjectList) == 0) {
|
||||
nssHash_Remove(store->subject, &cert->subject);
|
||||
nssList_Destroy(subjectList);
|
||||
/* XXX need to deref objects here in 4.0 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -306,6 +308,7 @@ nssCertificateStore_Remove
|
|||
remove_certificate_entry(store, cert);
|
||||
remove_subject_entry(store, cert);
|
||||
PZ_Unlock(store->lock);
|
||||
NSSCertificate_Destroy(cert); /* release the store's reference */
|
||||
}
|
||||
|
||||
NSS_IMPLEMENT NSSCertificate **
|
||||
|
@ -318,7 +321,7 @@ nssCertificateStore_FindCertificatesBySubject
|
|||
NSSArena *arenaOpt
|
||||
)
|
||||
{
|
||||
PRUint32 count;
|
||||
PRUint32 i, count;
|
||||
NSSCertificate **rvArray = NULL;
|
||||
nssList *subjectList;
|
||||
PZ_Lock(store->lock);
|
||||
|
@ -331,12 +334,14 @@ nssCertificateStore_FindCertificatesBySubject
|
|||
}
|
||||
if (rvOpt) {
|
||||
nssList_GetArray(subjectList, (void **)rvOpt, count);
|
||||
for (i=0; i<count; i++) nssCertificate_AddRef(rvOpt[i]);
|
||||
} else {
|
||||
rvArray = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, count + 1);
|
||||
if (!rvArray) {
|
||||
return (NSSCertificate **)NULL;
|
||||
}
|
||||
nssList_GetArray(subjectList, (void **)rvArray, count);
|
||||
for (i=0; i<count; i++) nssCertificate_AddRef(rvArray[i]);
|
||||
}
|
||||
}
|
||||
return rvArray;
|
||||
|
@ -386,7 +391,7 @@ nssCertificateStore_FindCertificatesByNickname
|
|||
NSSArena *arenaOpt
|
||||
)
|
||||
{
|
||||
PRUint32 count;
|
||||
PRUint32 i, count;
|
||||
NSSCertificate **rvArray = NULL;
|
||||
struct nickname_template_str nt;
|
||||
nt.nickname = nickname;
|
||||
|
@ -401,12 +406,14 @@ nssCertificateStore_FindCertificatesByNickname
|
|||
}
|
||||
if (rvOpt) {
|
||||
nssList_GetArray(nt.subjectList, (void **)rvOpt, count);
|
||||
for (i=0; i<count; i++) nssCertificate_AddRef(rvOpt[i]);
|
||||
} else {
|
||||
rvArray = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, count + 1);
|
||||
if (!rvArray) {
|
||||
return (NSSCertificate **)NULL;
|
||||
}
|
||||
nssList_GetArray(nt.subjectList, (void **)rvArray, count);
|
||||
for (i=0; i<count; i++) nssCertificate_AddRef(rvArray[i]);
|
||||
}
|
||||
}
|
||||
return rvArray;
|
||||
|
@ -455,7 +462,7 @@ nssCertificateStore_FindCertificatesByEmail
|
|||
NSSArena *arenaOpt
|
||||
)
|
||||
{
|
||||
PRUint32 count;
|
||||
PRUint32 i, count;
|
||||
NSSCertificate **rvArray = NULL;
|
||||
struct email_template_str et;
|
||||
et.email = email;
|
||||
|
@ -473,12 +480,14 @@ nssCertificateStore_FindCertificatesByEmail
|
|||
}
|
||||
if (rvOpt) {
|
||||
nssList_GetArray(et.emailList, (void **)rvOpt, count);
|
||||
for (i=0; i<count; i++) nssCertificate_AddRef(rvOpt[i]);
|
||||
} else {
|
||||
rvArray = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, count + 1);
|
||||
if (!rvArray) {
|
||||
return (NSSCertificate **)NULL;
|
||||
}
|
||||
nssList_GetArray(et.emailList, (void **)rvArray, count);
|
||||
for (i=0; i<count; i++) nssCertificate_AddRef(rvArray[i]);
|
||||
}
|
||||
}
|
||||
return rvArray;
|
||||
|
@ -501,7 +510,7 @@ nssCertificateStore_FindCertificateByIssuerAndSerialNumber
|
|||
nssHash_Lookup(store->issuer_and_serial, &index);
|
||||
PZ_Unlock(store->lock);
|
||||
if (entry) {
|
||||
return entry->cert;
|
||||
return nssCertificate_AddRef(entry->cert);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -548,7 +557,7 @@ nssCertificateStore_FindCertificateByEncodedCertificate
|
|||
PZ_Lock(store->lock);
|
||||
nssHash_Iterate(store->subject, match_encoding, &der);
|
||||
PZ_Unlock(store->lock);
|
||||
return der.cert;
|
||||
return nssCertificate_AddRef(der.cert);
|
||||
}
|
||||
|
||||
NSS_EXTERN PRStatus
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: tdcache.c,v $ $Revision: 1.17 $ $Date: 2001/12/14 17:32:23 $ $Name: $";
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: tdcache.c,v $ $Revision: 1.18 $ $Date: 2002/01/03 20:09:24 $ $Name: $";
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifndef PKIM_H
|
||||
|
@ -385,6 +385,7 @@ nssTrustDomain_RemoveCertFromCache
|
|||
nssHash_Remove(td->cache->subject, &cert->subject);
|
||||
}
|
||||
PZ_Unlock(td->cache->lock);
|
||||
NSSCertificate_Destroy(cert); /* release the reference */
|
||||
return;
|
||||
loser:
|
||||
/* if here, then the cache is inconsistent. For now, flush it. */
|
||||
|
@ -393,6 +394,7 @@ loser:
|
|||
PR_LOG(s_log, PR_LOG_DEBUG, ("remove cert failed, flushing"));
|
||||
#endif
|
||||
nssTrustDomain_FlushCache(td, -1.0);
|
||||
NSSCertificate_Destroy(cert); /* release the reference */
|
||||
}
|
||||
|
||||
/* This is used to remove all certs below a certain threshold, where
|
||||
|
@ -665,6 +667,7 @@ add_cert_to_cache
|
|||
#endif
|
||||
}
|
||||
PZ_Unlock(td->cache->lock);
|
||||
nssCertificate_AddRef(cert);
|
||||
return nssrv;
|
||||
loser:
|
||||
/* Remove any handles that have been created */
|
||||
|
@ -730,13 +733,13 @@ collect_subject_certs
|
|||
{
|
||||
NSSCertificate *c;
|
||||
NSSCertificate **rvArray = NULL;
|
||||
PRUint32 count;
|
||||
PRUint32 i, count;
|
||||
if (rvCertListOpt) {
|
||||
nssListIterator *iter = nssList_CreateIterator(subjectList);
|
||||
for (c = (NSSCertificate *)nssListIterator_Start(iter);
|
||||
c != (NSSCertificate *)NULL;
|
||||
c = (NSSCertificate *)nssListIterator_Next(iter)) {
|
||||
nssList_Add(rvCertListOpt, c);
|
||||
nssList_Add(rvCertListOpt, nssCertificate_AddRef(c));
|
||||
}
|
||||
nssListIterator_Finish(iter);
|
||||
nssListIterator_Destroy(iter);
|
||||
|
@ -747,6 +750,7 @@ collect_subject_certs
|
|||
return (NSSCertificate **)NULL;
|
||||
}
|
||||
nssList_GetArray(subjectList, (void **)rvArray, count);
|
||||
for (i=0; i<count; i++) nssCertificate_AddRef(rvArray[i]);
|
||||
}
|
||||
return rvArray;
|
||||
}
|
||||
|
@ -903,7 +907,7 @@ nssTrustDomain_GetCertForIssuerAndSNFromCache
|
|||
#endif
|
||||
}
|
||||
PZ_Unlock(td->cache->lock);
|
||||
return rvCert;
|
||||
return nssCertificate_AddRef(rvCert);
|
||||
}
|
||||
|
||||
#ifdef NSS_3_4_CODE
|
||||
|
@ -963,14 +967,14 @@ nssTrustDomain_GetCertByDERFromCache
|
|||
PORT_Free(issuer.data);
|
||||
PORT_Free(serial.data);
|
||||
#endif
|
||||
return rvCert;
|
||||
return nssCertificate_AddRef(rvCert);
|
||||
}
|
||||
|
||||
static void cert_iter(const void *k, void *v, void *a)
|
||||
{
|
||||
nssList *certList = (nssList *)a;
|
||||
NSSCertificate *c = (NSSCertificate *)v;
|
||||
nssList_Add(certList, c);
|
||||
NSSCertificate *c = (NSSCertificate *)k;
|
||||
nssList_Add(certList, nssCertificate_AddRef(c));
|
||||
}
|
||||
|
||||
NSS_EXTERN NSSCertificate **
|
||||
|
@ -994,6 +998,7 @@ nssTrustDomain_GetCertsFromCache
|
|||
PRUint32 count = nssList_Count(certList);
|
||||
rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count);
|
||||
nssList_GetArray(certList, (void **)rvArray, count);
|
||||
/* array takes the references */
|
||||
nssList_Destroy(certList);
|
||||
}
|
||||
return rvArray;
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: trustdomain.c,v $ $Revision: 1.22 $ $Date: 2001/12/14 20:50:59 $ $Name: $";
|
||||
static const char CVS_ID[] = "@(#) $RCSfile: trustdomain.c,v $ $Revision: 1.23 $ $Date: 2002/01/03 20:09:25 $ $Name: $";
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifndef NSSPKI_H
|
||||
|
@ -355,6 +355,12 @@ NSSTrustDomain_ImportEncodedPublicKey
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void cert_destructor(void *el)
|
||||
{
|
||||
NSSCertificate *c = (NSSCertificate *)el;
|
||||
NSSCertificate_Destroy(c);
|
||||
}
|
||||
|
||||
struct collect_arg_str {
|
||||
nssList *list;
|
||||
PRUint32 maximum;
|
||||
|
@ -411,6 +417,7 @@ NSSTrustDomain_FindBestCertificateByNickname
|
|||
name, &search);
|
||||
}
|
||||
nssListIterator_Finish(td->tokens);
|
||||
nssList_Clear(nameList, cert_destructor);
|
||||
nssList_Destroy(nameList);
|
||||
if (best.cert) {
|
||||
nssTrustDomain_AddCertsToCache(td, &best.cert, 1);
|
||||
|
@ -466,6 +473,7 @@ NSSTrustDomain_FindCertificatesByNickname
|
|||
}
|
||||
nssTrustDomain_AddCertsToCache(td, rvCerts, count);
|
||||
}
|
||||
nssList_Clear(nameList, cert_destructor);
|
||||
nssList_Destroy(nameList);
|
||||
return rvCerts;
|
||||
}
|
||||
|
@ -541,6 +549,7 @@ NSSTrustDomain_FindBestCertificateBySubject
|
|||
subject, &search);
|
||||
}
|
||||
nssListIterator_Finish(td->tokens);
|
||||
nssList_Clear(subjectList, cert_destructor);
|
||||
nssList_Destroy(subjectList);
|
||||
if (best.cert) {
|
||||
nssTrustDomain_AddCertsToCache(td, &best.cert, 1);
|
||||
|
@ -596,6 +605,7 @@ NSSTrustDomain_FindCertificatesBySubject
|
|||
}
|
||||
nssTrustDomain_AddCertsToCache(td, rvCerts, count);
|
||||
}
|
||||
nssList_Clear(subjectList, cert_destructor);
|
||||
nssList_Destroy(subjectList);
|
||||
return rvCerts;
|
||||
}
|
||||
|
@ -694,6 +704,7 @@ NSSTrustDomain_FindCertificateByEmail
|
|||
email, &search);
|
||||
}
|
||||
nssListIterator_Finish(td->tokens);
|
||||
nssList_Clear(emailList, cert_destructor);
|
||||
nssList_Destroy(emailList);
|
||||
if (best.cert) {
|
||||
nssTrustDomain_AddCertsToCache(td, &best.cert, 1);
|
||||
|
@ -848,6 +859,7 @@ NSSTrustDomain_TraverseCertificates
|
|||
nssrv = nssToken_TraverseCertificates(token, NULL, &search);
|
||||
}
|
||||
nssListIterator_Finish(td->tokens);
|
||||
nssList_Clear(certList, cert_destructor);
|
||||
nssList_Destroy(certList);
|
||||
return NULL;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче