tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsssysinit.c (11831B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 #include "seccomon.h"
      5 #include "prio.h"
      6 #include "prprf.h"
      7 #include "plhash.h"
      8 #include "prenv.h"
      9 
     10 /*
     11 * The following provides a default example for operating systems to set up
     12 * and manage applications loading NSS on their OS globally.
     13 *
     14 * This code hooks in to the system pkcs11.txt, which controls all the loading
     15 * of pkcs11 modules common to all applications.
     16 */
     17 
     18 #ifndef LINUX
     19 #error __FILE__ only builds on Linux.
     20 #endif
     21 
     22 #include <unistd.h>
     23 #include <sys/stat.h>
     24 #include <sys/types.h>
     25 
     26 static int
     27 testdir(char *dir)
     28 {
     29    struct stat buf;
     30    memset(&buf, 0, sizeof(buf));
     31 
     32    if (stat(dir, &buf) < 0) {
     33        return 0;
     34    }
     35 
     36    return S_ISDIR(buf.st_mode);
     37 }
     38 
     39 /**
     40 * Append given @dir to @path and creates the directory with mode @mode.
     41 * Returns 0 if successful, -1 otherwise.
     42 * Assumes that the allocation for @path has sufficient space for @dir
     43 * to be added.
     44 */
     45 static int
     46 appendDirAndCreate(char *path, char *dir, mode_t mode)
     47 {
     48    PORT_Strcat(path, dir);
     49    if (!testdir(path)) {
     50        if (mkdir(path, mode)) {
     51            return -1;
     52        }
     53    }
     54    return 0;
     55 }
     56 
     57 #define XDG_NSS_USER_PATH1 "/.local"
     58 #define XDG_NSS_USER_PATH2 "/share"
     59 #define XDG_NSS_USER_PATH3 "/pki"
     60 
     61 #define NSS_USER_PATH1 "/.pki"
     62 #define NSS_USER_PATH2 "/nssdb"
     63 
     64 /**
     65 * Return the path to user's NSS database.
     66 * We search in the following dirs in order:
     67 * (1) $HOME/.pki/nssdb;
     68 * (2) $XDG_DATA_HOME/pki/nssdb if XDG_DATA_HOME is set;
     69 * (3) $HOME/.local/share/pki/nssdb (default XDG_DATA_HOME value).
     70 * If (1) does not exist, then the returned dir will be set to either
     71 * (2) or (3), depending if XDG_DATA_HOME is set.
     72 */
     73 char *
     74 getUserDB(void)
     75 {
     76    char *userdir = PR_GetEnvSecure("HOME");
     77    char *nssdir = NULL;
     78 
     79    if (userdir == NULL) {
     80        return NULL;
     81    }
     82 
     83    nssdir = PORT_Alloc(strlen(userdir) + sizeof(NSS_USER_PATH1) + sizeof(NSS_USER_PATH2));
     84    PORT_Strcpy(nssdir, userdir);
     85    PORT_Strcat(nssdir, NSS_USER_PATH1 NSS_USER_PATH2);
     86    if (testdir(nssdir)) {
     87        /* $HOME/.pki/nssdb exists */
     88        return nssdir;
     89    } else {
     90        /* either $HOME/.pki or $HOME/.pki/nssdb does not exist */
     91        PORT_Free(nssdir);
     92    }
     93    int size = 0;
     94    char *xdguserdatadir = PR_GetEnvSecure("XDG_DATA_HOME");
     95    if (xdguserdatadir) {
     96        size = strlen(xdguserdatadir);
     97    } else {
     98        size = strlen(userdir) + sizeof(XDG_NSS_USER_PATH1) + sizeof(XDG_NSS_USER_PATH2);
     99    }
    100    size += sizeof(XDG_NSS_USER_PATH3) + sizeof(NSS_USER_PATH2);
    101 
    102    nssdir = PORT_Alloc(size);
    103    if (nssdir == NULL) {
    104        return NULL;
    105    }
    106 
    107    if (xdguserdatadir) {
    108        PORT_Strcpy(nssdir, xdguserdatadir);
    109        if (!testdir(nssdir)) {
    110            PORT_Free(nssdir);
    111            return NULL;
    112        }
    113 
    114    } else {
    115        PORT_Strcpy(nssdir, userdir);
    116        if (appendDirAndCreate(nssdir, XDG_NSS_USER_PATH1, 0755) ||
    117            appendDirAndCreate(nssdir, XDG_NSS_USER_PATH2, 0755)) {
    118            PORT_Free(nssdir);
    119            return NULL;
    120        }
    121    }
    122    /* ${XDG_DATA_HOME:-$HOME/.local/share}/pki/nssdb */
    123    if (appendDirAndCreate(nssdir, XDG_NSS_USER_PATH3, 0760) ||
    124        appendDirAndCreate(nssdir, NSS_USER_PATH2, 0760)) {
    125        PORT_Free(nssdir);
    126        return NULL;
    127    }
    128    return nssdir;
    129 }
    130 
    131 #define NSS_DEFAULT_SYSTEM "/etc/pki/nssdb"
    132 static char *
    133 getSystemDB(void)
    134 {
    135    return PORT_Strdup(NSS_DEFAULT_SYSTEM);
    136 }
    137 
    138 static PRBool
    139 userIsRoot()
    140 {
    141    /* this works for linux and all unixes that we know off
    142       though it isn't stated as such in POSIX documentation */
    143    return getuid() == 0;
    144 }
    145 
    146 static PRBool
    147 userCanModifySystemDB()
    148 {
    149    return (access(NSS_DEFAULT_SYSTEM, W_OK) == 0);
    150 }
    151 
    152 #ifndef NSS_FIPS_DISABLED
    153 static PRBool
    154 getFIPSEnv(void)
    155 {
    156    char *fipsEnv = PR_GetEnvSecure("NSS_FIPS");
    157    if (!fipsEnv) {
    158        return PR_FALSE;
    159    }
    160    if ((strcasecmp(fipsEnv, "fips") == 0) ||
    161        (strcasecmp(fipsEnv, "true") == 0) ||
    162        (strcasecmp(fipsEnv, "on") == 0) ||
    163        (strcasecmp(fipsEnv, "1") == 0)) {
    164        return PR_TRUE;
    165    }
    166    return PR_FALSE;
    167 }
    168 #endif /* NSS_FIPS_DISABLED */
    169 
    170 static PRBool
    171 getFIPSMode(void)
    172 {
    173 #ifndef NSS_FIPS_DISABLED
    174    FILE *f;
    175    char d;
    176    size_t size;
    177 
    178    f = fopen("/proc/sys/crypto/fips_enabled", "r");
    179    if (!f) {
    180        /* if we don't have a proc flag, fall back to the
    181         * environment variable */
    182        return getFIPSEnv();
    183    }
    184 
    185    size = fread(&d, 1, 1, f);
    186    fclose(f);
    187    if (size != 1)
    188        return PR_FALSE;
    189    if (d != '1')
    190        return PR_FALSE;
    191    return PR_TRUE;
    192 #else
    193    return PR_FALSE;
    194 #endif /* NSS_FIPS_DISABLED */
    195 }
    196 
    197 #define NSS_DEFAULT_FLAGS "flags=readonly"
    198 
    199 /* configuration flags according to
    200 * https://developer.mozilla.org/en/PKCS11_Module_Specs
    201 * As stated there the slotParams start with a slot name which is a slotID
    202 * Slots 1 through 3 are reserved for the nss internal modules as follows:
    203 * 1 for crypto operations slot non-fips,
    204 * 2 for the key slot, and
    205 * 3 for the crypto operations slot fips
    206 */
    207 #define CIPHER_ORDER_FLAGS "cipherOrder=100"
    208 #define SLOT_FLAGS                                                  \
    209    "[slotFlags=RSA,RC4,RC2,DES,DH,SHA1,MD5,MD2,SSL,TLS,AES,RANDOM" \
    210    " askpw=any timeout=30 ]"
    211 
    212 static const char *nssDefaultFlags =
    213    CIPHER_ORDER_FLAGS " slotParams={0x00000001=" SLOT_FLAGS " }  ";
    214 
    215 static const char *nssDefaultFIPSFlags =
    216    CIPHER_ORDER_FLAGS " slotParams={0x00000003=" SLOT_FLAGS " }  ";
    217 
    218 /*
    219 * This function builds the list of databases and modules to load, and sets
    220 * their configuration. For the sample we have a fixed set.
    221 *  1. We load the user's home nss database.
    222 *  2. We load the user's custom PKCS #11 modules.
    223 *  3. We load the system nss database readonly.
    224 *
    225 * Any space allocated in get_list must be freed in release_list.
    226 * This function can use whatever information is available to the application.
    227 * it is running in the process of the application for which it is making
    228 * decisions, so it's possible to acquire the application name as part of
    229 * the decision making process.
    230 *
    231 */
    232 static char **
    233 get_list(char *filename, char *stripped_parameters)
    234 {
    235    char **module_list = PORT_ZNewArray(char *, 5);
    236    char *userdb, *sysdb;
    237    int isFIPS = getFIPSMode();
    238    const char *nssflags = isFIPS ? nssDefaultFIPSFlags : nssDefaultFlags;
    239    int next = 0;
    240 
    241    /* can't get any space */
    242    if (module_list == NULL) {
    243        return NULL;
    244    }
    245 
    246    sysdb = getSystemDB();
    247    userdb = getUserDB();
    248 
    249    /* Don't open root's user DB */
    250    if (userdb != NULL && !userIsRoot()) {
    251        /* return a list of databases to open. First the user Database */
    252        module_list[next++] = PR_smprintf(
    253            "library= "
    254            "module=\"NSS User database\" "
    255            "parameters=\"configdir='sql:%s' %s tokenDescription='NSS user database'\" "
    256            "NSS=\"trustOrder=75 %sflags=internal%s\"",
    257            userdb, stripped_parameters, nssflags,
    258            isFIPS ? ",FIPS" : "");
    259 
    260        /* now open the user's defined PKCS #11 modules */
    261        /* skip the local user DB entry */
    262        module_list[next++] = PR_smprintf(
    263            "library= "
    264            "module=\"NSS User database\" "
    265            "parameters=\"configdir='sql:%s' %s\" "
    266            "NSS=\"flags=internal,moduleDBOnly,defaultModDB,skipFirst\"",
    267            userdb, stripped_parameters);
    268    }
    269 
    270    /* now the system database (always read only unless it's root) */
    271    if (sysdb) {
    272        const char *readonly = userCanModifySystemDB() ? "" : "flags=readonly";
    273        module_list[next++] = PR_smprintf(
    274            "library= "
    275            "module=\"NSS system database\" "
    276            "parameters=\"configdir='sql:%s' tokenDescription='NSS system database' %s\" "
    277            "NSS=\"trustOrder=80 %sflags=internal,critical\"",
    278            sysdb, readonly, nssflags);
    279    }
    280 
    281    /* that was the last module */
    282    module_list[next] = 0;
    283 
    284    PORT_Free(userdb);
    285    PORT_Free(sysdb);
    286 
    287    return module_list;
    288 }
    289 
    290 static char **
    291 release_list(char **arg)
    292 {
    293    static char *success = "Success";
    294    int next;
    295 
    296    for (next = 0; arg[next]; next++) {
    297        free(arg[next]);
    298    }
    299    PORT_Free(arg);
    300    return &success;
    301 }
    302 
    303 #include "utilpars.h"
    304 
    305 #define TARGET_SPEC_COPY(new, start, end) \
    306    if (end > start) {                    \
    307        int _cnt = end - start;           \
    308        PORT_Memcpy(new, start, _cnt);    \
    309        new += _cnt;                      \
    310    }
    311 
    312 /*
    313 * According the strcpy man page:
    314 *
    315 * The strings  may  not overlap, and the destination string dest must be
    316 * large enough to receive the copy.
    317 *
    318 * This implementation allows target to overlap with src.
    319 * It does not allow the src to overlap the target.
    320 *  example: overlapstrcpy(string, string+4) is fine
    321 *           overlapstrcpy(string+4, string) is not.
    322 */
    323 static void
    324 overlapstrcpy(char *target, char *src)
    325 {
    326    while (*src) {
    327        *target++ = *src++;
    328    }
    329    *target = 0;
    330 }
    331 
    332 /* determine what options the user was trying to open this database with */
    333 /* filename is the directory pointed to by configdir= */
    334 /* stripped is the rest of the parameters with configdir= stripped out */
    335 static SECStatus
    336 parse_parameters(const char *parameters, char **filename, char **stripped)
    337 {
    338    const char *sourcePrev;
    339    const char *sourceCurr;
    340    char *targetCurr;
    341    char *newStripped;
    342    *filename = NULL;
    343    *stripped = NULL;
    344 
    345    newStripped = PORT_Alloc(PORT_Strlen(parameters) + 2);
    346    targetCurr = newStripped;
    347    sourcePrev = parameters;
    348    sourceCurr = NSSUTIL_ArgStrip(parameters);
    349    TARGET_SPEC_COPY(targetCurr, sourcePrev, sourceCurr);
    350 
    351    while (*sourceCurr) {
    352        int next;
    353        sourcePrev = sourceCurr;
    354        NSSUTIL_HANDLE_STRING_ARG(sourceCurr, *filename, "configdir=",
    355                                  sourcePrev = sourceCurr;)
    356        NSSUTIL_HANDLE_FINAL_ARG(sourceCurr);
    357        TARGET_SPEC_COPY(targetCurr, sourcePrev, sourceCurr);
    358    }
    359    *targetCurr = 0;
    360    if (*filename == NULL) {
    361        PORT_Free(newStripped);
    362        return SECFailure;
    363    }
    364    /* strip off any directives from the filename */
    365    if (strncmp("sql:", *filename, 4) == 0) {
    366        overlapstrcpy(*filename, (*filename) + 4);
    367    } else if (strncmp("dbm:", *filename, 4) == 0) {
    368        overlapstrcpy(*filename, (*filename) + 4);
    369    } else if (strncmp("extern:", *filename, 7) == 0) {
    370        overlapstrcpy(*filename, (*filename) + 7);
    371    }
    372    *stripped = newStripped;
    373    return SECSuccess;
    374 }
    375 
    376 /* entry point */
    377 char **
    378 NSS_ReturnModuleSpecData(unsigned long function, char *parameters, void *args)
    379 {
    380    char *filename = NULL;
    381    char *stripped = NULL;
    382    char **retString = NULL;
    383    SECStatus rv;
    384 
    385    rv = parse_parameters(parameters, &filename, &stripped);
    386    if (rv != SECSuccess) {
    387        /* use defaults */
    388        filename = getSystemDB();
    389        if (!filename) {
    390            return NULL;
    391        }
    392        stripped = PORT_Strdup(NSS_DEFAULT_FLAGS);
    393        if (!stripped) {
    394            free(filename);
    395            return NULL;
    396        }
    397    }
    398    switch (function) {
    399        case SECMOD_MODULE_DB_FUNCTION_FIND:
    400            retString = get_list(filename, stripped);
    401            break;
    402        case SECMOD_MODULE_DB_FUNCTION_RELEASE:
    403            retString = release_list((char **)args);
    404            break;
    405        /* can't add or delete from this module DB */
    406        case SECMOD_MODULE_DB_FUNCTION_ADD:
    407        case SECMOD_MODULE_DB_FUNCTION_DEL:
    408            retString = NULL;
    409            break;
    410        default:
    411            retString = NULL;
    412            break;
    413    }
    414 
    415    PORT_Free(filename);
    416    PORT_Free(stripped);
    417    return retString;
    418 }