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 }