Bug 1594931 - Stop compiling NSS' DBM legacy database r=kjacobs,keeler,mhowell,MattN

This change modifies all tests that use key3/cert8 to use the new files. It
removes test_sdr_upgraded_with_password, as without the upgrade part that is now
the same test as test_sdr_preexisting_with_password.

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

--HG--
rename : security/manager/ssl/tests/unit/test_sdr_preexisting/key4.db => security/manager/ssl/tests/unit/test_broken_fips/key4.db
extra : moz-landing-system : lando
This commit is contained in:
J.C. Jones 2019-12-14 18:52:26 +00:00
Родитель 6a4379bdc7
Коммит 4916451c01
36 изменённых файлов: 27 добавлений и 378 удалений

Просмотреть файл

@ -45,10 +45,6 @@ ifdef MOZ_SYSTEM_NSS
DEFINES += -DMOZ_SYSTEM_NSS=1
endif
ifdef NSS_DISABLE_DBM
DEFINES += -DNSS_DISABLE_DBM=1
endif
ifdef MOZ_ARTIFACT_BUILDS
DEFINES += -DMOZ_ARTIFACT_BUILDS=1
endif

Просмотреть файл

@ -361,9 +361,6 @@ bin/libfreebl_64int_3.so
#endif
@BINPATH@/@DLL_PREFIX@nss3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
#ifndef NSS_DISABLE_DBM
@BINPATH@/@DLL_PREFIX@nssdbm3@DLL_SUFFIX@
#endif
#ifndef MOZ_FOLD_LIBS
@BINPATH@/@DLL_PREFIX@nssutil3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@smime3@DLL_SUFFIX@

Просмотреть файл

@ -30,10 +30,6 @@ DEFINES += \
-DANDROID_CPU_ARCH=$(ANDROID_CPU_ARCH) \
$(NULL)
ifdef NSS_DISABLE_DBM
DEFINES += -DNSS_DISABLE_DBM=1
endif
ifdef MOZ_DEBUG
DEFINES += -DMOZ_DEBUG=1
endif

Просмотреть файл

@ -71,12 +71,6 @@
@BINPATH@/@DLL_PREFIX@freebl3.chk
@BINPATH@/@DLL_PREFIX@softokn3.chk
#endif
#ifndef NSS_DISABLE_DBM
@BINPATH@/@DLL_PREFIX@nssdbm3@DLL_SUFFIX@
#ifndef CROSS_COMPILE
@BINPATH@/@DLL_PREFIX@nssdbm3.chk
#endif
#endif
#ifndef MOZ_FOLD_LIBS
@BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@

Двоичные данные
modules/libmar/tests/unit/data/cert8.db

Двоичный файл не отображается.

Двоичные данные
modules/libmar/tests/unit/data/cert9.db Normal file

Двоичный файл не отображается.

Двоичные данные
modules/libmar/tests/unit/data/key3.db

Двоичный файл не отображается.

Двоичные данные
modules/libmar/tests/unit/data/key4.db Normal file

Двоичный файл не отображается.

Двоичные данные
modules/libmar/tests/unit/data/secmod.db

Двоичный файл не отображается.

Просмотреть файл

@ -470,7 +470,6 @@ class MacArtifactJob(ArtifactJob):
'libmozglue.dylib',
'libnss3.dylib',
'libnssckbi.dylib',
'libnssdbm3.dylib',
'libplugin_child_interpose.dylib',
# 'libreplace_jemalloc.dylib',
# 'libreplace_malloc.dylib',

Просмотреть файл

@ -228,9 +228,6 @@ GeneratedFile('nsSTSPreloadList.h',
script='../../../xpcom/ds/tools/make_dafsa.py',
inputs=['nsSTSPreloadList.inc'])
if CONFIG['NSS_DISABLE_DBM']:
DEFINES['NSS_DISABLE_DBM'] = '1'
DEFINES['SSL_DISABLE_DEPRECATED_CIPHER_SUITE_NAMES'] = 'True'
DEFINES['NSS_ENABLE_ECC'] = 'True'

Просмотреть файл

@ -8,19 +8,19 @@
// Tests that if Firefox attempts and fails to load a PKCS#11 module DB that was
// in FIPS mode, Firefox can still make use of keys in the key database.
// secomd.db can be created via `certutil -N -d <dir>`. Putting it in FIPS mode
// involves running `modutil -fips true -dbdir <dir>`. key3.db is from
// test_sdr_preexisting/key3.db.
// involves running `modutil -fips true -dbdir <dir>`. key4.db is from
// test_sdr_preexisting/key4.db.
function run_test() {
let profile = do_get_profile();
let keyDBName = "key3.db";
let keyDBName = "key4.db";
let keyDBFile = do_get_file(`test_broken_fips/${keyDBName}`);
keyDBFile.copyTo(profile, keyDBName);
let secmodDBName = "secmod.db";
let secmodDBFile = do_get_file(`test_broken_fips/${secmodDBName}`);
secmodDBFile.copyTo(profile, secmodDBName);
let pkcs11modDBName = "pkcs11.txt";
let pkcs11modDBFile = do_get_file(`test_broken_fips/${pkcs11modDBName}`);
pkcs11modDBFile.copyTo(profile, pkcs11modDBName);
let moduleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
Ci.nsIPKCS11ModuleDB
@ -41,7 +41,10 @@ function run_test() {
"decrypted ciphertext should match expected plaintext"
);
let secmodDBFileFIPS = do_get_profile();
secmodDBFileFIPS.append(`${secmodDBName}.fips`);
ok(secmodDBFileFIPS.exists(), "backed-up PKCS#11 module db should now exist");
let pkcs11modDBFileFIPS = do_get_profile();
pkcs11modDBFileFIPS.append(`${pkcs11modDBName}.fips`);
ok(
pkcs11modDBFileFIPS.exists(),
"backed-up PKCS#11 module db should now exist"
);
}

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,5 @@
library=
name=NSS Internal FIPS PKCS #11 Module
parameters=configdir='.' certPrefix='' keyPrefix='' secmod='' flags= updatedir='' updateCertPrefix='' updateKeyPrefix='' updateid='' updateTokenDescription=''
NSS=slotParams={0x00000003=[slotFlags=RSA,RC4,RC2,DES,DH,SHA1,MD5,MD2,SSL,TLS,AES,SHA256,SHA512,Camellia,SEED,RANDOM ] } Flags=internal,FIPS,critical

Двоичный файл не отображается.

Просмотреть файл

@ -17,9 +17,7 @@
// `certutil -d . -A -n "Let's Encrypt Authority X1" -t ,, -a \
// -i LetsEncrypt.pem`
//
// This should create cert8.db and key3.db files for use on non-Android
// platforms. Perform the same steps with "sql:." as the argument to the "-d"
// flag to create cert9.db and key4.db for use with Android.
// This should create the cert9.db and key4.db files.
//
// (The crucial property of the first certificate is that it is a built-in trust
// anchor, so any replacement must also have this property. The second
@ -77,9 +75,8 @@
"use strict";
function run_test() {
const isAndroid = AppConstants.platform == "android";
const certDBName = isAndroid ? "cert9.db" : "cert8.db";
const keyDBName = isAndroid ? "key4.db" : "key3.db";
const certDBName = "cert9.db";
const keyDBName = "key4.db";
let profile = do_get_profile();
let certDBFile = do_get_file(`test_cert_isBuiltInRoot_reload/${certDBName}`);
certDBFile.copyTo(profile, certDBName);

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -9,171 +9,11 @@
// a preexisting NSS key database. Creating the database is straight-forward:
// simply run Firefox (or xpcshell) and encrypt something using
// nsISecretDecoderRing (e.g. by saving a password or directly using the
// interface). The resulting key3.db file (in the profile directory) now
// interface). The resulting key4.db file (in the profile directory) now
// contains the private key used to encrypt the data.
// "Upgrading" a key3.db with certutil to use on Android appears not to work.
// Because the keys have to be the same for this test to work the way it does,
// the key from key3.db must be extracted and added to a new key4.db. This can
// be done with NSS' PK11_* APIs like so (although note that the following code
// is not guaranteed to compile or work, but is more of a guideline for how to
// do this in the future if necessary):
//
// #include <stdio.h>
//
// #include "nss.h"
// #include "pk11pub.h"
// #include "prerror.h"
// #include "secerr.h"
//
// void printPRError(const char* message) {
// fprintf(stderr, "%s: %s\n", message, PR_ErrorToString(PR_GetError(), 0));
// }
//
// int main(int argc, char* argv[]) {
// if (NSS_Initialize(".", "", "", "", NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT)
// != SECSuccess) {
// printPRError("NSS_Initialize failed");
// return 1;
// }
//
// PK11SlotInfo* slot = PK11_GetInternalKeySlot();
// if (!slot) {
// printPRError("PK11_GetInternalKeySlot failed");
// return 1;
// }
//
// // Create a key to wrap the SDR key to export it.
// unsigned char wrappingKeyIDBytes[] = { 0 };
// SECItem wrappingKeyID = {
// siBuffer,
// wrappingKeyIDBytes,
// sizeof(wrappingKeyIDBytes)
// };
// PK11SymKey* wrappingKey = PK11_TokenKeyGen(slot, CKM_DES3_CBC, 0, 0,
// &wrappingKeyID, PR_FALSE, NULL);
// if (!wrappingKey) {
// printPRError("PK11_TokenKeyGen failed");
// return 1;
// }
//
// // This is the magic identifier NSS uses for the SDR key.
// unsigned char sdrKeyIDBytes[] = {
// 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
// };
// SECItem sdrKeyID = { siBuffer, sdrKeyIDBytes, sizeof(sdrKeyIDBytes) };
// PK11SymKey* sdrKey = PK11_FindFixedKey(slot, CKM_DES3_CBC, &sdrKeyID,
// NULL);
// if (!sdrKey) {
// printPRError("PK11_FindFixedKey failed");
// return 1;
// }
//
// // Wrap the SDR key.
// unsigned char wrappedKeyBuf[1024];
// SECItem wrapped = { siBuffer, wrappedKeyBuf, sizeof(wrappedKeyBuf) };
// if (PK11_WrapSymKey(CKM_DES3_ECB, NULL, wrappingKey, sdrKey, &wrapped)
// != SECSuccess) {
// printPRError("PK11_WrapSymKey failed");
// return 1;
// }
//
// // Unwrap the SDR key (NSS considers the SDR key "sensitive" and so won't
// // just export it as raw key material - we have to export it and then
// // re-import it as non-sensitive to get that data.
// PK11SymKey* unwrapped = PK11_UnwrapSymKey(wrappingKey, CKM_DES3_ECB, NULL,
// &wrapped, CKM_DES3_CBC,
// CKA_ENCRYPT, 0);
// if (!unwrapped) {
// printPRError("PK11_UnwrapSymKey failed");
// return 1;
// }
// if (PK11_ExtractKeyValue(unwrapped) != SECSuccess) {
// printPRError("PK11_ExtractKeyValue failed");
// return 1;
// }
// SECItem* keyData = PK11_GetKeyData(unwrapped);
// if (!keyData) {
// printPRError("PK11_GetKeyData failed");
// return 1;
// }
// for (int i = 0; i < keyData->len; i++) {
// printf("0x%02hhx, ", keyData->data[i]);
// }
// printf("\n");
//
// PK11_FreeSymKey(unwrapped);
// PK11_FreeSymKey(sdrKey);
// PK11_FreeSymKey(wrappingKey);
// PK11_FreeSlot(slot);
//
// if (NSS_Shutdown() != SECSuccess) {
// printPRError("NSS_Shutdown failed");
// return 1;
// }
// return 0;
// }
//
// The output of compiling and running the above should be the bytes of the SDR
// key. Given that, create a key4.db with an empty password using
// `certutil -N -d sql:.` and then compile and run the following:
//
// #include <stdio.h>
//
// #include "nss.h"
// #include "pk11pub.h"
// #include "prerror.h"
// #include "secerr.h"
// #include "secmod.h"
//
// void printPRError(const char* message) {
// fprintf(stderr, "%s: %s\n", message, PR_ErrorToString(PR_GetError(), 0));
// }
//
// int main(int argc, char* argv[]) {
// if (NSS_Initialize("sql:.", "", "", "",
// NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT) != SECSuccess) {
// printPRError("NSS_Initialize failed");
// return 1;
// }
//
// PK11SlotInfo* slot = PK11_GetInternalKeySlot();
// if (!slot) {
// printPRError("PK11_GetInternalKeySlot failed");
// return 1;
// }
//
// // These are the bytes of the SDR key from the previous step:
// unsigned char keyBytes[] = {
// 0x70, 0xab, 0xea, 0x1f, 0x8f, 0xe3, 0x4a, 0x7a, 0xb5, 0xb0, 0x43, 0xe5,
// 0x51, 0x83, 0x86, 0xe5, 0xb3, 0x43, 0xa8, 0x1f, 0xc1, 0x57, 0x86, 0x46
// };
// SECItem keyItem = { siBuffer, keyBytes, sizeof(keyBytes) };
// PK11SymKey* key = PK11_ImportSymKey(slot, CKM_DES3_CBC, PK11_OriginUnwrap,
// CKA_ENCRYPT, &keyItem, NULL);
// if (!key) {
// printPRError("PK11_ImportSymKey failed");
// return 1;
// }
//
// PK11_FreeSymKey(key);
// PK11_FreeSlot(slot);
//
// if (NSS_Shutdown() != SECSuccess) {
// printPRError("NSS_Shutdown failed");
// return 1;
// }
// return 0;
// }
//
// This should create a key4.db file with the SDR key. (Note however that this
// does not set the magic key ID for the SDR key. Currently this is not a
// problem because the NSS implementation that backs the SDR simply tries all
// applicable keys it has when decrypting, so this still works.)
function run_test() {
const isAndroid = AppConstants.platform == "android";
const keyDBName = isAndroid ? "key4.db" : "key3.db";
const keyDBName = "key4.db";
let profile = do_get_profile();
let keyDBFile = do_get_file(`test_sdr_preexisting/${keyDBName}`);
keyDBFile.copyTo(profile, keyDBName);

Двоичный файл не отображается.

Просмотреть файл

@ -58,8 +58,8 @@ function run_test() {
});
let profile = do_get_profile();
let keyDBFile = do_get_file("test_sdr_preexisting_with_password/key3.db");
keyDBFile.copyTo(profile, "key3.db");
let keyDBFile = do_get_file("test_sdr_preexisting_with_password/key4.db");
keyDBFile.copyTo(profile, "key4.db");
let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
Ci.nsISecretDecoderRing

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -1,149 +0,0 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
"use strict";
// Tests that the SDR implementation is able to decrypt strings encrypted using
// a preexisting NSS key database that a) has a password and b) has already been
// upgraded from the old dbm format in a previous run of Firefox.
// To create such a database, run the xpcshell test
// `test_sdr_preexisting_with_password.js` and locate the file `key4.db` created
// in the xpcshell test profile directory.
// This does not apply to Android as the dbm implementation was never enabled on
// that platform.
var gMockPrompter = {
passwordToTry: "password",
numPrompts: 0,
// This intentionally does not use arrow function syntax to avoid an issue
// where in the context of the arrow function, |this != gMockPrompter| due to
// how objects get wrapped when going across xpcom boundaries.
promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
this.numPrompts++;
if (this.numPrompts > 1) {
// don't keep retrying a bad password
return false;
}
equal(
text,
"Please enter your master password.",
"password prompt text should be as expected"
);
equal(checkMsg, null, "checkMsg should be null");
ok(this.passwordToTry, "passwordToTry should be non-null");
password.value = this.passwordToTry;
return true;
},
QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
};
// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
// to call promptPassword. We return the mock one, above.
var gWindowWatcher = {
getNewPrompter: () => gMockPrompter,
QueryInterface: ChromeUtils.generateQI([Ci.nsIWindowWatcher]),
};
function run_test() {
let windowWatcherCID = MockRegistrar.register(
"@mozilla.org/embedcomp/window-watcher;1",
gWindowWatcher
);
registerCleanupFunction(() => {
MockRegistrar.unregister(windowWatcherCID);
});
let profile = do_get_profile();
let key3DBFile = do_get_file("test_sdr_upgraded_with_password/key3.db");
key3DBFile.copyTo(profile, "key3.db");
let key4DBFile = do_get_file("test_sdr_upgraded_with_password/key4.db");
key4DBFile.copyTo(profile, "key4.db");
// Unfortunately we have to also copy the certificate databases as well.
// Otherwise, NSS will think it has to create them, which will cause NSS to
// think it has to also do a migration, which will open key3.db and not close
// it until shutdown, which means that on Windows removing the file just after
// startup fails. Luckily users profiles will have both key and certificate
// databases anyway, so this is an accurate reflection of normal use.
let cert8DBFile = do_get_file("test_sdr_upgraded_with_password/cert8.db");
cert8DBFile.copyTo(profile, "cert8.db");
let cert9DBFile = do_get_file("test_sdr_upgraded_with_password/cert9.db");
cert9DBFile.copyTo(profile, "cert9.db");
let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
Ci.nsISecretDecoderRing
);
let testcases = [
// a full padding block
{
ciphertext:
"MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECGeDHwVfyFqzBBAYvqMq/kDMsrARVNdC1C8d",
plaintext: "password",
},
// 7 bytes of padding
{
ciphertext:
"MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECCAzLDVmYG2/BAh3IoIsMmT8dQ==",
plaintext: "a",
},
// 6 bytes of padding
{
ciphertext:
"MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECPN8zlZzn8FdBAiu2acpT8UHsg==",
plaintext: "bb",
},
// 1 byte of padding
{
ciphertext:
"MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECD5px1eMKkJQBAgUPp35GlrDvQ==",
plaintext: "!seven!",
},
// 2 bytes of padding
{
ciphertext:
"MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECMh0hLtKDyUdBAixw9UZsMt+vA==",
plaintext: "sixsix",
},
// long plaintext requiring more than two blocks
{
ciphertext:
"MFoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECDRX1qi+/FX1BDATFIcIneQjvBuq3wdFxzllJt2VtUD69ACdOKAXH3eA87oHDvuHqOeCDwRy4UzoG5s=",
plaintext: "thisismuchlongerandsotakesupmultipleblocks",
},
// this differs from the previous ciphertext by one bit and demonstrates
// that this implementation does not enforce message integrity
{
ciphertext:
"MFoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECDRX1qi+/FX1BDAbFIcIneQjvBuq3wdFxzllJt2VtUD69ACdOKAXH3eA87oHDvuHqOeCDwRy4UzoG5s=",
plaintext: "nnLbuwLRkhlongerandsotakesupmultipleblocks",
},
];
for (let testcase of testcases) {
let decrypted = sdr.decryptString(testcase.ciphertext);
equal(
decrypted,
testcase.plaintext,
"decrypted ciphertext should match expected plaintext"
);
}
equal(
gMockPrompter.numPrompts,
1,
"Should have been prompted for a password once"
);
// NSS does not close the old database when performing an upgrade. Thus, on
// Windows, we can't delete the old database file on the run that we perform
// an upgrade. However, we can delete it on subsequent runs.
let key3DBInProfile = do_get_profile();
key3DBInProfile.append("key3.db");
ok(
!key3DBInProfile.exists(),
"key3.db should not exist after running with key4.db with a password"
);
}

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -39,7 +39,6 @@ support-files =
test_sanctions/**
test_sdr_preexisting/**
test_sdr_preexisting_with_password/**
test_sdr_upgraded_with_password/**
test_self_signed_certs/**
test_signed_apps/**
test_startcom_wosign/**
@ -190,10 +189,9 @@ run-sequentially = hardcoded ports
run-sequentially = hardcoded ports
[test_sdr.js]
[test_sdr_preexisting.js]
[test_sdr_preexisting_with_password.js]
# Not relevant to Android. See the comment in the test.
skip-if = toolkit == 'android'
[test_sdr_upgraded_with_password.js]
[test_sdr_preexisting_with_password.js]
# Not relevant to Android. See the comment in the test.
skip-if = toolkit == 'android'
[test_self_signed_certs.js]

Просмотреть файл

@ -83,8 +83,7 @@ gyp_vars['nss_public_dist_dir'] = '$PRODUCT_DIR/dist'
gyp_vars['nss_dist_obj_dir'] = '$PRODUCT_DIR/dist/bin'
# We don't currently build NSS tests.
gyp_vars['disable_tests'] = 1
if CONFIG['NSS_DISABLE_DBM']:
gyp_vars['disable_dbm'] = 1
gyp_vars['disable_dbm'] = 1
gyp_vars['disable_libpkix'] = 1
gyp_vars['enable_sslkeylogfile'] = 1
# pkg-config won't reliably find zlib on our builders, so just force it.

Двоичный файл не отображается.

Просмотреть файл

@ -80,8 +80,7 @@ add_task(async function test_common_initialize() {
// Before initializing the service for the first time, we should copy the key
// file required to decrypt the logins contained in the SQLite databases used
// by migration tests. This file is not required for the other tests.
const isAndroid = AppConstants.platform == "android";
const keyDBName = isAndroid ? "key4.db" : "key3.db";
const keyDBName = "key4.db";
await OS.File.copy(
do_get_file(`data/${keyDBName}`).path,
OS.Path.join(OS.Constants.Path.profileDir, keyDBName)

Просмотреть файл

@ -67,9 +67,6 @@ with Files('moz.*'):
with Files('toolkit.mozbuild'):
BUG_COMPONENT = ('Firefox Build System', 'General')
with Files('nss.configure'):
BUG_COMPONENT = ('Firefox Build System', 'General')
with Files('library/**'):
BUG_COMPONENT = ('Firefox Build System', 'General')

Просмотреть файл

@ -815,8 +815,6 @@ option('--enable-ipdl-tests', help='Enable expensive IPDL tests')
set_config('MOZ_IPDL_TESTS',
depends_if('--enable-ipdl-tests')(lambda _: True))
include('nss.configure')
# Graphics
# ==============================================================
option('--disable-skia', help='Disable use of Skia')

Просмотреть файл

@ -1,17 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# DBM support in NSS
# ==============================================================
@depends(build_project)
def dbm_default(build_project):
return build_project != 'mobile/android'
option('--enable-dbm', default=dbm_default,
help='{Enable|Disable} building DBM')
set_config('NSS_DISABLE_DBM', depends('--enable-dbm')(lambda x: not x))