Skip to content

Commit

Permalink
Bug 1427276 - Fix sdb to handle UTF-8 paths correctly on Windows. r=f…
Browse files Browse the repository at this point in the history
…kiefer
  • Loading branch information
vyv03354 committed Dec 29, 2017
1 parent 788cbad commit c39e0d6
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 16 deletions.
34 changes: 33 additions & 1 deletion gtests/softoken_gtest/softoken_gtest.cc
@@ -1,4 +1,8 @@
#include <cstdlib>
#if defined(_WIN32)
#include <windows.h>
#include <codecvt>
#endif

#include "cert.h"
#include "certdb.h"
Expand Down Expand Up @@ -34,13 +38,15 @@ class ScopedUniqueDirectory {
~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) {
Expand All @@ -60,6 +66,18 @@ ScopedUniqueDirectory::ScopedUniqueDirectory(const std::string &prefix) {
}
}
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) {
Expand All @@ -84,10 +102,11 @@ bool ScopedUniqueDirectory::TryMakingDirectory(std::string &prefix) {
class SoftokenTest : public ::testing::Test {
protected:
SoftokenTest() : mNSSDBDir("SoftokenTest.d-") {}
SoftokenTest(const std::string &prefix) : mNSSDBDir(prefix) {}

virtual void SetUp() {
std::string nssInitArg("sql:");
nssInitArg.append(mNSSDBDir.GetPath());
nssInitArg.append(mNSSDBDir.GetUTF8Path());
ASSERT_EQ(SECSuccess, NSS_Initialize(nssInitArg.c_str(), "", "", SECMOD_DB,
NSS_INIT_NOROOTINIT));
}
Expand Down Expand Up @@ -202,6 +221,19 @@ TEST_F(SoftokenTest, CreateObjectChangeToEmptyPassword) {
EXPECT_NE(nullptr, obj);
}

class SoftokenNonAsciiTest : public SoftokenTest {
protected:
SoftokenNonAsciiTest() : SoftokenTest("SoftokenTest.\xF7-") {}
};

TEST_F(SoftokenNonAsciiTest, NonAsciiPathWorking) {
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
EXPECT_EQ(SECSuccess, PK11_ResetToken(slot.get(), nullptr));
EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
}

// This is just any X509 certificate. Its contents don't matter.
static unsigned char certDER[] = {
0x30, 0x82, 0x01, 0xEF, 0x30, 0x82, 0x01, 0x94, 0xA0, 0x03, 0x02, 0x01,
Expand Down
33 changes: 31 additions & 2 deletions lib/softoken/sdb.c
Expand Up @@ -37,6 +37,7 @@
#elif defined(XP_UNIX)
#include <unistd.h>
#endif
#include "utilpars.h"

#ifdef SQLITE_UNSAFE_THREADS
#include "prlock.h"
Expand Down Expand Up @@ -190,6 +191,34 @@ sdb_done(int err, int *count)
return 0;
}

#if defined(_WIN32)
/*
* NSPR functions and narrow CRT functions do not handle UTF-8 file paths that
* sqlite3 expects.
*/

static int
sdb_chmod(const char *filename, int pmode)
{
int result;

if (!filename) {
return -1;
}

wchar_t *filenameWide = _NSSUTIL_UTF8ToWide(filename);
if (!filenameWide) {
return -1;
}
result = _wchmod(filenameWide, pmode);
PORT_Free(filenameWide);

return result;
}
#else
#define sdb_chmod(filename, pmode) chmod((filename), (pmode))
#endif

/*
* find out where sqlite stores the temp tables. We do this by replicating
* the logic from sqlite.
Expand Down Expand Up @@ -1739,7 +1768,7 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate,
* sqlite3 will always create it.
*/
LOCK_SQLITE();
create = (PR_Access(dbname, PR_ACCESS_EXISTS) != PR_SUCCESS);
create = (_NSSUTIL_Access(dbname, PR_ACCESS_EXISTS) != PR_SUCCESS);
if ((flags == SDB_RDONLY) && create) {
error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
goto loser;
Expand All @@ -1756,7 +1785,7 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate,
*
* NO NSPR call for chmod? :(
*/
if (create && chmod(dbname, 0600) != 0) {
if (create && sdb_chmod(dbname, 0600) != 0) {
error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
goto loser;
}
Expand Down
4 changes: 4 additions & 0 deletions lib/softoken/sdb.h
Expand Up @@ -83,6 +83,10 @@ CK_RV s_open(const char *directory, const char *certPrefix,
int flags, SDB **certdb, SDB **keydb, int *newInit);
CK_RV s_shutdown();

#if defined(_WIN32)
wchar_t *sdb_UTF8ToWide(const char *buf);
#endif

/* flags */
#define SDB_RDONLY 1
#define SDB_RDWR 2
Expand Down
68 changes: 64 additions & 4 deletions lib/softoken/sftkdb.c
Expand Up @@ -28,6 +28,9 @@
#include "utilpars.h"
#include "secerr.h"
#include "softoken.h"
#if defined(_WIN32)
#include <windows.h>
#endif

/*
* We want all databases to have the same binary representation independent of
Expand Down Expand Up @@ -2509,6 +2512,53 @@ sftk_oldVersionExists(const char *dir, int version)
return PR_FALSE;
}

#if defined(_WIN32)
/*
* Convert an sdb path (encoded in UTF-8) to a legacy path (encoded in the
* current system codepage). Fails if the path contains a character outside
* the current system codepage.
*/
static char *
sftk_legacyPathFromSDBPath(const char *confdir)
{
wchar_t *confdirWide;
DWORD size;
char *nconfdir;
BOOL unmappable;

if (!confdir) {
return NULL;
}
confdirWide = _NSSUTIL_UTF8ToWide(confdir);
if (!confdirWide) {
return NULL;
}

size = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, confdirWide, -1,
NULL, 0, NULL, &unmappable);
if (size == 0 || unmappable) {
PORT_Free(confdirWide);
return NULL;
}
nconfdir = PORT_Alloc(sizeof(char) * size);
if (!nconfdir) {
PORT_Free(confdirWide);
return NULL;
}
size = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, confdirWide, -1,
nconfdir, size, NULL, &unmappable);
PORT_Free(confdirWide);
if (size == 0 || unmappable) {
PORT_Free(nconfdir);
return NULL;
}

return nconfdir;
}
#else
#define sftk_legacyPathFromSDBPath(confdir) PORT_Strdup((confdir))
#endif

static PRBool
sftk_hasLegacyDB(const char *confdir, const char *certPrefix,
const char *keyPrefix, int certVersion, int keyVersion)
Expand Down Expand Up @@ -2568,6 +2618,7 @@ sftk_DBInit(const char *configdir, const char *certPrefix,
int flags = SDB_RDONLY;
PRBool newInit = PR_FALSE;
PRBool needUpdate = PR_FALSE;
char *nconfdir = NULL;

if (!readOnly) {
flags = SDB_CREATE;
Expand Down Expand Up @@ -2606,11 +2657,14 @@ sftk_DBInit(const char *configdir, const char *certPrefix,
* the exists.
*/
if (crv != CKR_OK) {
if (((flags & SDB_RDONLY) == SDB_RDONLY) &&
sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
if ((flags & SDB_RDONLY) == SDB_RDONLY) {
nconfdir = sftk_legacyPathFromSDBPath(confdir);
}
if (nconfdir &&
sftk_hasLegacyDB(nconfdir, certPrefix, keyPrefix, 8, 3)) {
/* we have legacy databases, if we failed to open the new format
* DB's read only, just use the legacy ones */
crv = sftkdbCall_open(confdir, certPrefix,
crv = sftkdbCall_open(nconfdir, certPrefix,
keyPrefix, 8, 3, flags,
noCertDB ? NULL : &certSDB, noKeyDB ? NULL : &keySDB);
}
Expand Down Expand Up @@ -2639,7 +2693,10 @@ sftk_DBInit(const char *configdir, const char *certPrefix,
/* if the new format DB was also a newly created DB, and we
* succeeded, then need to update that new database with data
* from the existing legacy DB */
if (sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
nconfdir = sftk_legacyPathFromSDBPath(confdir);
if (nconfdir &&
sftk_hasLegacyDB(nconfdir, certPrefix, keyPrefix, 8, 3)) {
confdir = nconfdir;
needUpdate = PR_TRUE;
}
}
Expand Down Expand Up @@ -2712,6 +2769,9 @@ sftk_DBInit(const char *configdir, const char *certPrefix,
if (appName) {
PORT_Free(appName);
}
if (nconfdir) {
PORT_Free(nconfdir);
}
return forceOpen ? CKR_OK : crv;
}

Expand Down
8 changes: 8 additions & 0 deletions lib/util/nssutil.def
Expand Up @@ -315,3 +315,11 @@ NSS_SecureMemcmpZero;
;+ local:
;+ *;
;+};
;-NSSUTIL_3.35 { # NSS Utilities 3.35 release
;- global:
;-# private exports for softoken
_NSSUTIL_UTF8ToWide;-
_NSSUTIL_Access;-
;- local:
;- *;
;-};

0 comments on commit c39e0d6

Please sign in to comment.