e4d72e
# HG changeset patch
e4d72e
# User EdĂȘnis Freindorfer Azevedo <edenisfa@gmail.com>
e4d72e
# Date 1547073505 -39600
e4d72e
#      Thu Jan 10 09:38:25 2019 +1100
e4d72e
# Node ID da45424cb9a0b4d8e45e5040e2e3b574d994e254
e4d72e
# Parent  f7187a33fad7b9cafe0c2947c6d48618fdda57e4
e4d72e
Bug 818686 - XDG Base Directory Specification support with fallback, r=mt
e4d72e
e4d72e
Summary:
e4d72e
We check if $HOME/.pki and $HOME/.pki/nssdb exist; if they do, then we use
e4d72e
this path. Otherwise, use ${XDG_DATA_HOME:-$HOME/.local/share}/pki/nssdb
e4d72e
e4d72e
Test Plan:
e4d72e
Create dummy empty dir and set HOME to it. Then, check if getUserDb returns:
e4d72e
1. $HOME/.pki/nssdb when this path exists;
e4d72e
2. $HOME/.local/share/pki/nssdb when $HOME/.pki/nssdb does not and XDG_DATA_HOME is not defined;
e4d72e
3. $XDG_DATA_HOME/pki/nssdb when $HOME/.pki/nssdb does not exist and XDG_DATA_HOME is defined.
e4d72e
e4d72e
Reviewers: mt
e4d72e
e4d72e
Reviewed By: mt
e4d72e
e4d72e
Bug #: 818686
e4d72e
e4d72e
Differential Revision: https://phabricator.services.mozilla.com/D14007
e4d72e
e4d72e
diff --git a/lib/sysinit/nsssysinit.c b/lib/sysinit/nsssysinit.c
e4d72e
--- a/lib/sysinit/nsssysinit.c
e4d72e
+++ b/lib/sysinit/nsssysinit.c
e4d72e
@@ -37,9 +37,41 @@ testdir(char *dir)
e4d72e
     return S_ISDIR(buf.st_mode);
e4d72e
 }
e4d72e
 
e4d72e
+/**
e4d72e
+ * Append given @dir to @path and creates the directory with mode @mode.
e4d72e
+ * Returns 0 if successful, -1 otherwise.
e4d72e
+ * Assumes that the allocation for @path has sufficient space for @dir
e4d72e
+ * to be added.
e4d72e
+ */
e4d72e
+static int
e4d72e
+appendDirAndCreate(char *path, char *dir, mode_t mode)
e4d72e
+{
e4d72e
+    PORT_Strcat(path, dir);
e4d72e
+    if (!testdir(path)) {
e4d72e
+        if (mkdir(path, mode)) {
e4d72e
+            return -1;
e4d72e
+        }
e4d72e
+    }
e4d72e
+    return 0;
e4d72e
+}
e4d72e
+
e4d72e
+#define XDG_NSS_USER_PATH1 "/.local"
e4d72e
+#define XDG_NSS_USER_PATH2 "/share"
e4d72e
+#define XDG_NSS_USER_PATH3 "/pki"
e4d72e
+
e4d72e
 #define NSS_USER_PATH1 "/.pki"
e4d72e
 #define NSS_USER_PATH2 "/nssdb"
e4d72e
-static char *
e4d72e
+
e4d72e
+/**
e4d72e
+ * Return the path to user's NSS database.
e4d72e
+ * We search in the following dirs in order:
e4d72e
+ * (1) $HOME/.pki/nssdb;
e4d72e
+ * (2) $XDG_DATA_HOME/pki/nssdb if XDG_DATA_HOME is set;
e4d72e
+ * (3) $HOME/.local/share/pki/nssdb (default XDG_DATA_HOME value).
e4d72e
+ * If (1) does not exist, then the returned dir will be set to either
e4d72e
+ * (2) or (3), depending if XDG_DATA_HOME is set.
e4d72e
+ */
e4d72e
+char *
e4d72e
 getUserDB(void)
e4d72e
 {
e4d72e
     char *userdir = PR_GetEnvSecure("HOME");
e4d72e
@@ -50,22 +82,47 @@ getUserDB(void)
e4d72e
     }
e4d72e
 
e4d72e
     nssdir = PORT_Alloc(strlen(userdir) + sizeof(NSS_USER_PATH1) + sizeof(NSS_USER_PATH2));
e4d72e
+    PORT_Strcpy(nssdir, userdir);
e4d72e
+    PORT_Strcat(nssdir, NSS_USER_PATH1 NSS_USER_PATH2);
e4d72e
+    if (testdir(nssdir)) {
e4d72e
+        /* $HOME/.pki/nssdb exists */
e4d72e
+        return nssdir;
e4d72e
+    } else {
e4d72e
+        /* either $HOME/.pki or $HOME/.pki/nssdb does not exist */
e4d72e
+        PORT_Free(nssdir);
e4d72e
+    }
e4d72e
+    int size = 0;
e4d72e
+    char *xdguserdatadir = PR_GetEnvSecure("XDG_DATA_HOME");
e4d72e
+    if (xdguserdatadir) {
e4d72e
+        size = strlen(xdguserdatadir);
e4d72e
+    } else {
e4d72e
+        size = strlen(userdir) + sizeof(XDG_NSS_USER_PATH1) + sizeof(XDG_NSS_USER_PATH2);
e4d72e
+    }
e4d72e
+    size += sizeof(XDG_NSS_USER_PATH3) + sizeof(NSS_USER_PATH2);
e4d72e
+
e4d72e
+    nssdir = PORT_Alloc(size);
e4d72e
     if (nssdir == NULL) {
e4d72e
         return NULL;
e4d72e
     }
e4d72e
-    PORT_Strcpy(nssdir, userdir);
e4d72e
-    /* verify it exists */
e4d72e
-    if (!testdir(nssdir)) {
e4d72e
-        PORT_Free(nssdir);
e4d72e
-        return NULL;
e4d72e
+
e4d72e
+    if (xdguserdatadir) {
e4d72e
+        PORT_Strcpy(nssdir, xdguserdatadir);
e4d72e
+        if (!testdir(nssdir)) {
e4d72e
+            PORT_Free(nssdir);
e4d72e
+            return NULL;
e4d72e
+        }
e4d72e
+
e4d72e
+    } else {
e4d72e
+        PORT_Strcpy(nssdir, userdir);
e4d72e
+        if (appendDirAndCreate(nssdir, XDG_NSS_USER_PATH1, 0755) ||
e4d72e
+            appendDirAndCreate(nssdir, XDG_NSS_USER_PATH2, 0755)) {
e4d72e
+            PORT_Free(nssdir);
e4d72e
+            return NULL;
e4d72e
+        }
e4d72e
     }
e4d72e
-    PORT_Strcat(nssdir, NSS_USER_PATH1);
e4d72e
-    if (!testdir(nssdir) && mkdir(nssdir, 0760)) {
e4d72e
-        PORT_Free(nssdir);
e4d72e
-        return NULL;
e4d72e
-    }
e4d72e
-    PORT_Strcat(nssdir, NSS_USER_PATH2);
e4d72e
-    if (!testdir(nssdir) && mkdir(nssdir, 0760)) {
e4d72e
+    /* ${XDG_DATA_HOME:-$HOME/.local/share}/pki/nssdb */
e4d72e
+    if (appendDirAndCreate(nssdir, XDG_NSS_USER_PATH3, 0760) ||
e4d72e
+        appendDirAndCreate(nssdir, NSS_USER_PATH2, 0760)) {
e4d72e
         PORT_Free(nssdir);
e4d72e
         return NULL;
e4d72e
     }