nsssysinit.c 12 KB

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