Commit 984b7610 authored by Dana Keeler's avatar Dana Keeler

bug 1552262 - add PK11_FindRawCertsBySubject to search a given slot for...

bug 1552262 - add PK11_FindRawCertsBySubject to search a given slot for certificates with a given subject r=jcj,RyanSleevi.sleevi

Differential Revision: https://phabricator.services.mozilla.com/D32067

--HG--
extra : source : 3c2aceba7ae819c25fe372d9506af1e7e3a524fb
parent fbc7f55e
1 Added function:
'function SECStatus PK11_FindRawCertsWithSubject(PK11SlotInfo*, SECItem*, CERTCertificateList**)' {PK11_FindRawCertsWithSubject@@NSS_3.45}
1 Added function:
'function SECStatus CERT_GetCertificateDer(const CERTCertificate*, SECItem*)' {CERT_GetCertificateDer@@NSS_3.44}
1 function with some indirect sub-type change:
[C]'function SECStatus CERT_AddOCSPAcceptableResponses(CERTOCSPRequest*, SECOidTag, ...)' at ocsp.c:2203:1 has some indirect sub-type changes:
parameter 2 of type 'typedef SECOidTag' has sub-type changes:
underlying type 'enum __anonymous_enum__3' at secoidt.h:34:1 changed:
type size hasn't changed
3 enumerator insertions:
'__anonymous_enum__3::SEC_OID_EXT_KEY_USAGE_IPSEC_END' value '361'
'__anonymous_enum__3::SEC_OID_EXT_KEY_USAGE_IPSEC_TUNNEL' value '362'
'__anonymous_enum__3::SEC_OID_EXT_KEY_USAGE_IPSEC_USER' value '363'
1 enumerator change:
'__anonymous_enum__3::SEC_OID_TOTAL' from value '361' to '364' at secoidt.h:34:1
......@@ -14,6 +14,7 @@
#include "pk11pqg.h"
#include "pk11pub.h"
#include "pkcs11uri.h"
#include "secmod.h"
struct ScopedDelete {
void operator()(CERTCertificate* cert) { CERT_DestroyCertificate(cert); }
......@@ -47,6 +48,7 @@ struct ScopedDelete {
SEC_PKCS12DecoderFinish(dcx);
}
void operator()(CERTDistNames* names) { CERT_FreeDistNames(names); }
void operator()(SECMODModule* module) { SECMOD_DestroyModule(module); }
};
template <class T>
......@@ -82,6 +84,7 @@ SCOPED(PK11Context);
SCOPED(PK11GenericObject);
SCOPED(SEC_PKCS12DecoderContext);
SCOPED(CERTDistNames);
SCOPED(SECMODModule);
#undef SCOPED
......
......@@ -8,7 +8,21 @@
#define util_h__
#include <cassert>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <sys/stat.h>
#include <vector>
#if defined(_WIN32)
#include <windows.h>
#include <codecvt>
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "nspr.h"
static inline std::vector<uint8_t> hex_string_to_bytes(std::string s) {
std::vector<uint8_t> bytes;
......@@ -18,4 +32,81 @@ static inline std::vector<uint8_t> hex_string_to_bytes(std::string s) {
return bytes;
}
// Given a prefix, attempts to create a unique directory that the user can do
// work in without impacting other tests. For example, if given the prefix
// "scratch", a directory like "scratch05c17b25" will be created in the current
// working directory (or the location specified by NSS_GTEST_WORKDIR, if
// defined).
// Upon destruction, the implementation will attempt to delete the directory.
// However, no attempt is made to first remove files in the directory - the
// user is responsible for this. If the directory is not empty, deleting it will
// fail.
// Statistically, it is technically possible to fail to create a unique
// directory name, but this is extremely unlikely given the expected workload of
// this implementation.
class ScopedUniqueDirectory {
public:
explicit ScopedUniqueDirectory(const std::string &prefix) {
std::string path;
const char *workingDirectory = PR_GetEnvSecure("NSS_GTEST_WORKDIR");
if (workingDirectory) {
path.assign(workingDirectory);
}
path.append(prefix);
for (int i = 0; i < RETRY_LIMIT; i++) {
std::string pathCopy(path);
// TryMakingDirectory will modify its input. If it fails, we want to throw
// away the modified result.
if (TryMakingDirectory(pathCopy)) {
mPath.assign(pathCopy);
break;
}
}
assert(mPath.length() > 0);
#if defined(_WIN32)
// sqldb always uses UTF-8 regardless of the current system locale.
DWORD len =
MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), nullptr, 0);
std::vector<wchar_t> buf(len, L'\0');
MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), buf.data(),
buf.size());
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
mUTF8Path = converter.to_bytes(std::wstring(buf.begin(), buf.end()));
#else
mUTF8Path = mPath;
#endif
}
// NB: the directory must be empty upon destruction
~ScopedUniqueDirectory() { assert(rmdir(mPath.c_str()) == 0); }
const std::string &GetPath() { return mPath; }
const std::string &GetUTF8Path() { return mUTF8Path; }
private:
static const int RETRY_LIMIT = 5;
static void GenerateRandomName(/*in/out*/ std::string &prefix) {
std::stringstream ss;
ss << prefix;
// RAND_MAX is at least 32767.
ss << std::setfill('0') << std::setw(4) << std::hex << rand() << rand();
// This will overwrite the value of prefix. This is a little inefficient,
// but at least it makes the code simple.
ss >> prefix;
}
static bool TryMakingDirectory(/*in/out*/ std::string &prefix) {
GenerateRandomName(prefix);
#if defined(_WIN32)
return _mkdir(prefix.c_str()) == 0;
#else
return mkdir(prefix.c_str(), 0777) == 0;
#endif
}
std::string mPath;
std::string mUTF8Path;
};
#endif // util_h__
......@@ -15,6 +15,7 @@ CPPSRCS = \
pk11_ecdsa_unittest.cc \
pk11_encrypt_derive_unittest.cc \
pk11_export_unittest.cc \
pk11_find_certs_unittest.cc \
pk11_import_unittest.cc \
pk11_pbkdf2_unittest.cc \
pk11_prf_unittest.cc \
......
This diff is collapsed.
......@@ -19,6 +19,7 @@
'pk11_curve25519_unittest.cc',
'pk11_ecdsa_unittest.cc',
'pk11_encrypt_derive_unittest.cc',
'pk11_find_certs_unittest.cc',
'pk11_import_unittest.cc',
'pk11_pbkdf2_unittest.cc',
'pk11_prf_unittest.cc',
......
......@@ -12,6 +12,7 @@ CPPSRCS = \
INCLUDES += \
-I$(CORE_DEPTH)/gtests/google_test/gtest/include \
-I$(CORE_DEPTH)/gtests/common \
-I$(CORE_DEPTH)/cpputil \
$(NULL)
......
#include <cstdlib>
#if defined(_WIN32)
#include <windows.h>
#include <codecvt>
#endif
#include "cert.h"
#include "certdb.h"
#include "nspr.h"
......@@ -12,93 +6,13 @@
#include "secerr.h"
#include "nss_scoped_ptrs.h"
#include "util.h"
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
namespace nss_test {
// Given a prefix, attempts to create a unique directory that the user can do
// work in without impacting other tests. For example, if given the prefix
// "scratch", a directory like "scratch05c17b25" will be created in the current
// working directory (or the location specified by NSS_GTEST_WORKDIR, if
// defined).
// Upon destruction, the implementation will attempt to delete the directory.
// However, no attempt is made to first remove files in the directory - the
// user is responsible for this. If the directory is not empty, deleting it will
// fail.
// Statistically, it is technically possible to fail to create a unique
// directory name, but this is extremely unlikely given the expected workload of
// this implementation.
class ScopedUniqueDirectory {
public:
explicit ScopedUniqueDirectory(const std::string &prefix);
// NB: the directory must be empty upon destruction
~ScopedUniqueDirectory() { assert(rmdir(mPath.c_str()) == 0); }
const std::string &GetPath() { return mPath; }
const std::string &GetUTF8Path() { return mUTF8Path; }
private:
static const int RETRY_LIMIT = 5;
static void GenerateRandomName(/*in/out*/ std::string &prefix);
static bool TryMakingDirectory(/*in/out*/ std::string &prefix);
std::string mPath;
std::string mUTF8Path;
};
ScopedUniqueDirectory::ScopedUniqueDirectory(const std::string &prefix) {
std::string path;
const char *workingDirectory = PR_GetEnvSecure("NSS_GTEST_WORKDIR");
if (workingDirectory) {
path.assign(workingDirectory);
}
path.append(prefix);
for (int i = 0; i < RETRY_LIMIT; i++) {
std::string pathCopy(path);
// TryMakingDirectory will modify its input. If it fails, we want to throw
// away the modified result.
if (TryMakingDirectory(pathCopy)) {
mPath.assign(pathCopy);
break;
}
}
assert(mPath.length() > 0);
#if defined(_WIN32)
// sqldb always uses UTF-8 regardless of the current system locale.
DWORD len =
MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), nullptr, 0);
std::vector<wchar_t> buf(len, L'\0');
MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), buf.data(),
buf.size());
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
mUTF8Path = converter.to_bytes(std::wstring(buf.begin(), buf.end()));
#else
mUTF8Path = mPath;
#endif
}
void ScopedUniqueDirectory::GenerateRandomName(std::string &prefix) {
std::stringstream ss;
ss << prefix;
// RAND_MAX is at least 32767.
ss << std::setfill('0') << std::setw(4) << std::hex << rand() << rand();
// This will overwrite the value of prefix. This is a little inefficient, but
// at least it makes the code simple.
ss >> prefix;
}
bool ScopedUniqueDirectory::TryMakingDirectory(std::string &prefix) {
GenerateRandomName(prefix);
#if defined(_WIN32)
return _mkdir(prefix.c_str()) == 0;
#else
return mkdir(prefix.c_str(), 0777) == 0;
#endif
}
class SoftokenTest : public ::testing::Test {
protected:
SoftokenTest() : mNSSDBDir("SoftokenTest.d-") {}
......
......@@ -1151,3 +1151,9 @@ CERT_GetCertificateDer;
;+ local:
;+ *;
;+};
;+NSS_3.45 { # NSS 3.45 release
;+ global:
PK11_FindRawCertsWithSubject;
;+ local:
;+ *;
;+};
......@@ -4,6 +4,8 @@
/*
* This file manages object type indepentent functions.
*/
#include <limits.h>
#include "seccomon.h"
#include "secmod.h"
#include "secmodi.h"
......@@ -1883,6 +1885,96 @@ pk11_FindObjectsByTemplate(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate,
*object_count = -1;
return objID;
}
SECStatus
PK11_FindRawCertsWithSubject(PK11SlotInfo *slot, SECItem *derSubject,
CERTCertificateList **results)
{
if (!slot || !derSubject || !results) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
*results = NULL;
// derSubject->data may be null. If so, derSubject->len must be 0.
if (!derSubject->data && derSubject->len != 0) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
CK_CERTIFICATE_TYPE ckc_x_509 = CKC_X_509;
CK_OBJECT_CLASS cko_certificate = CKO_CERTIFICATE;
CK_ATTRIBUTE subjectTemplate[] = {
{ CKA_CERTIFICATE_TYPE, &ckc_x_509, sizeof(ckc_x_509) },
{ CKA_CLASS, &cko_certificate, sizeof(cko_certificate) },
{ CKA_SUBJECT, derSubject->data, derSubject->len },
};
int templateCount = sizeof(subjectTemplate) / sizeof(subjectTemplate[0]);
int handleCount = 0;
CK_OBJECT_HANDLE *handles =
pk11_FindObjectsByTemplate(slot, subjectTemplate, templateCount,
&handleCount);
if (!handles) {
// pk11_FindObjectsByTemplate indicates there was an error by setting
// handleCount to -1 (and it has set an error with PORT_SetError).
if (handleCount == -1) {
return SECFailure;
}
return SECSuccess;
}
PORT_Assert(handleCount > 0);
if (handleCount <= 0) {
PORT_Free(handles);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
if (handleCount > INT_MAX / sizeof(SECItem)) {
PORT_Free(handles);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
PORT_Free(handles);
return SECFailure;
}
CERTCertificateList *rawCertificates =
PORT_ArenaNew(arena, CERTCertificateList);
if (!rawCertificates) {
PORT_Free(handles);
PORT_FreeArena(arena, PR_FALSE);
return SECFailure;
}
rawCertificates->arena = arena;
rawCertificates->certs = PORT_ArenaNewArray(arena, SECItem, handleCount);
if (!rawCertificates->certs) {
PORT_Free(handles);
PORT_FreeArena(arena, PR_FALSE);
return SECFailure;
}
rawCertificates->len = handleCount;
int handleIndex;
for (handleIndex = 0; handleIndex < handleCount; handleIndex++) {
SECStatus rv =
PK11_ReadAttribute(slot, handles[handleIndex], CKA_VALUE, arena,
&rawCertificates->certs[handleIndex]);
if (rv != SECSuccess) {
PORT_Free(handles);
PORT_FreeArena(arena, PR_FALSE);
return SECFailure;
}
if (!rawCertificates->certs[handleIndex].data) {
PORT_Free(handles);
PORT_FreeArena(arena, PR_FALSE);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
}
PORT_Free(handles);
*results = rawCertificates;
return SECSuccess;
}
/*
* given a PKCS #11 object, match it's peer based on the KeyID. searchID
* is typically a privateKey or a certificate while the peer is the opposite
......
......@@ -875,6 +875,17 @@ SECStatus PK11_WriteRawAttribute(PK11ObjectType type, void *object,
PK11SlotList *
PK11_GetAllSlotsForCert(CERTCertificate *cert, void *arg);
/*
* Finds all certificates on the given slot with the given subject distinguished
* name and returns them as DER bytes. If no such certificates can be found,
* returns SECSuccess and sets *results to NULL. If a failure is encountered
* while fetching any of the matching certificates, SECFailure is returned and
* *results will be NULL.
*/
SECStatus
PK11_FindRawCertsWithSubject(PK11SlotInfo *slot, SECItem *derSubject,
CERTCertificateList **results);
/**********************************************************************
* New functions which are already deprecated....
**********************************************************************/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment