#50795 Ticket 50794: [RFE] Use keyring to manage starting password/pin
Closed 3 years ago by spichugi. Opened 4 years ago by tbordaz.
tbordaz/389-ds-base ticket_50794  into  master

file modified
+10 -4
@@ -25,6 +25,7 @@ 

  UBSAN_CFLAGS = @ubsan_cflags@

  

  SYSTEMD_DEFINES = @systemd_defs@

+ KEYRING_DEFINES = @keyring_defs@

  

  CMOCKA_INCLUDES = $(CMOCKA_CFLAGS)

  
@@ -147,7 +148,7 @@ 

  AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(GCCSEC_CFLAGS) $(ASAN_CFLAGS) $(MSAN_CFLAGS) $(TSAN_CFLAGS) $(UBSAN_CFLAGS)

  # Flags for Directory Server

  # WARNING: This needs a clean up, because slap.h is a horrible mess and is publically exposed!

- DSPLUGIN_CPPFLAGS = $(DS_DEFINES) $(DS_INCLUDES) $(PATH_DEFINES) $(SYSTEMD_DEFINES) @openldap_inc@ $(NSS_CFLAGS) $(NSPR_INCLUDES) $(SYSTEMD_CFLAGS)

+ DSPLUGIN_CPPFLAGS = $(DS_DEFINES) $(DS_INCLUDES) $(PATH_DEFINES) $(SYSTEMD_DEFINES) $(KEYRING_DEFINES) @openldap_inc@ $(NSS_CFLAGS) $(NSPR_INCLUDES) $(SYSTEMD_CFLAGS)

  # This should give access to internal headers only for tests!!!

  DSINTERNAL_CPPFLAGS = -I$(srcdir)/include/ldaputil

  # Flags for Datastructure Library
@@ -161,6 +162,7 @@ 

  

  NSPR_LINK = $(NSPR_LIBS)

  NSS_LINK = $(NSS_LIBS)

+ SVRCORE_LINK = @keyring_links@

  

  # with recent versions of openldap - if you link with both ldap_r and ldap, the

  # shared lib _fini for one will stomp on the other, and the program will crash
@@ -831,7 +833,8 @@ 

  

  # For scripts that are "as is".

  dist_bin_SCRIPTS = ldap/admin/src/scripts/ds-replcheck \

- 	ldap/admin/src/scripts/ds-logpipe.py

+ 	ldap/admin/src/scripts/ds-logpipe.py \

+ 	ldap/admin/src/scripts/ds-keyring.py

  

  dist_bin_SCRIPTS += ldap/admin/src/logconv.pl

  
@@ -1162,11 +1165,13 @@ 

  	src/svrcore/src/std.c \

  	src/svrcore/src/systemd-ask-pass.c \

  	src/svrcore/src/std-systemd.c \

+ 	src/svrcore/src/keyring-ask-pass.c \

+ 	src/svrcore/src/std-keyring.c \

  	src/svrcore/src/user.c

  

  libsvrcore_la_LDFLAGS = $(AM_LDFLAGS)

  libsvrcore_la_CPPFLAGS = $(AM_CPPFLAGS) $(SVRCORE_INCLUDES) $(DSPLUGIN_CPPFLAGS)

- libsvrcore_la_LIBADD = $(NSS_LINK) $(NSPR_LINK)

+ libsvrcore_la_LIBADD = $(NSS_LINK) $(NSPR_LINK) $(SVRCORE_LINK)

  

  #------------------------

  # libsds
@@ -2150,7 +2155,7 @@ 

  

  ns_slapd_CPPFLAGS = $(AM_CPPFLAGS) $(DSPLUGIN_CPPFLAGS) $(SASL_CFLAGS) $(SVRCORE_INCLUDES)

  ns_slapd_LDADD = libslapd.la libldaputil.la libsvrcore.la $(LDAPSDK_LINK) $(NSS_LINK) $(LIBADD_DL) \

- 	$(NSPR_LINK) $(SASL_LINK) $(LIBNSL) $(LIBSOCKET) $(THREADLIB) $(SYSTEMD_LIBS) $(EVENT_LINK)

+ 	$(NSPR_LINK) $(SASL_LINK) $(LIBNSL) $(LIBSOCKET) $(THREADLIB) $(SYSTEMD_LIBS) $(EVENT_LINK) $(SVRCORE_LINK)

  ns_slapd_DEPENDENCIES = libslapd.la libldaputil.la

  # We need to link ns-slapd with the C++ compiler on HP-UX since we load

  # some C++ shared libraries (such as icu).
@@ -2316,6 +2321,7 @@ 

  	-e 's,@with_fhs_opt\@,@with_fhs_opt@,g' \

  	-e 's,@with_selinux\@,@with_selinux@,g' \

  	-e 's,@with_systemd\@,$(WITH_SYSTEMD),g' \

+ 	-e 's,@with_keyring\@,$(WITH_KEYRING),g' \

  	-e 's,@tmpfiles_d\@,$(tmpfiles_d),g' \

  	-e 's,@perlexec\@,@perlexec@,g' \

  	-e 's,@pythonexec\@,@pythonexec@,g' \

file modified
+16
@@ -253,6 +253,22 @@ 

  AC_SUBST([profiling_defs])

  AC_SUBST([profiling_links])

  

+ # Pull in profiling.

+ AC_MSG_CHECKING(for --disable-keyring)

+ AC_ARG_ENABLE(keyring, AS_HELP_STRING([--disable-keyring], [Disable keyring storage for sensitive data (default: no)]),

+               [], [ disable_keyring=no ])

+ AC_MSG_RESULT($disable_keyring)

+ if test "$disable_keyring" = yes ; then

+   keyring_links=""

+   keyring_defs=""

+ else

+   AC_DEFINE([WITH_KEYRING], [1], [build svrcore to use keyring])

+   keyring_links="-lkeyutils"

+   keyring_defs="-DWITH_KEYRING"

+ fi

+ AC_SUBST([keyring_links])

+ AC_SUBST([keyring_defs])

+ 

  # these enables are for optional or experimental features

  AC_MSG_CHECKING(for --enable-pam-passthru)

  AC_ARG_ENABLE(pam-passthru,

@@ -0,0 +1,154 @@ 

+ #!/usr/bin/env python3

This whole command should likely be a part of dsctl and assocated to the instance that way, rather than adding "yet another tool".

+ 

+ # Authors:

+ #     Dinesh Prasanth M K <dmoluguw@redhat.com>

+ #     Thierry Bordaz <tbordaz@redhat.com> for RHDS adaptations

+ #

+ # This program is free software; you can redistribute it and/or modify

+ # it under the terms of the Lesser GNU General Public License as published by

+ # the Free Software Foundation; either version 3 of the License or

+ # (at your option) any later version.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU Lesser General Public License for more details.

+ #

+ # You should have received a copy of the GNU Lesser General Public License

+ #  along with this program; if not, write to the Free Software Foundation,

+ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

+ #

+ # Copyright (C) 2019 Red Hat, Inc.

+ # All rights reserved.

+ 

+ 

+ """

+ Script that prompts user for password during server startup

+ and store them on user's keyring

+ """

+ 

+ import getopt

+ import logging

+ import os

+ import subprocess

+ import sys

+ import re

+ import pdb

+ 

+ from lib389.keyring import Keyring

+ 

+ logger = logging.getLogger(__name__)

+ 

+ logging.basicConfig(format='%(levelname)s: %(message)s')

+ 

+ # Create a instance of Keyring

+ keyring = Keyring()

+ 

+ 

+ def split_entries(entry):

+     return entry.split(',')

+ 

+ 

+ def print_help():

+     print('Usage: ds-keyring <command> [<path_file>]')

This usage needs to explain more about how /why to use this. Before I start ds? After? How does it interact with the .service script?

+     print()

+     print('      <path_file>              Instance config file (i.e. dse.ldif)')

+     print('      <command>                Action to perform')

+     print()

+     print('      ds-keyring commands:')

+     print('      --get_key  <path_file>   Get value stored in keyring')

+     print('      --set_key  <path_file>   Set value stored in keyring')

+     print('      --clear                  Clear values stored in keyring')

+     print('      --help                   Show help message')

+     print()

+ 

+ 

+ try:

+     opts, _ = getopt.getopt(sys.argv[1:], 's:g:ch', ['set_key=', 'get_key=', 'clear', 'help'])

+ 

+ except getopt.GetoptError as e:

+     logger.error(e)

+     print_help()

+     sys.exit(1)

+ 

+ for o, a in opts:

+ 

+     if o in ('-c', '--clear'):

+         keyring.clear_keyring()

+         sys.exit()

+ 

+     elif o in ('-h', '--help'):

+         print_help()

+         sys.exit()

+ 

+     elif o in ('-s', '--set_key'):

+         obj = re.compile('^.*/slapd-(.*)/.*$')

+         instance_name = obj.sub("\\1", a)

+         if not os.path.exists(a):

+             print('RHDS: dse.ldif does not exist for %s ' % instance_name)

+             sys.exit()

+ 

+         # Based on nsslapd-security parameter ('on', 'off', default 'off')

+         # If security is disabled, no need to care about NSS password

+         security_enabled = False

+         with open(a) as f:

+             datafile = f.readlines()

+             for line in datafile:

+                 if 'nsslapd-security' in line.lower():

+                     security_val = line.split(':')[1].lower()

+                     security_val = re.sub(' +','', security_val)

+                     security_val = re.sub('\n','', security_val)

+                     if security_val == 'on':

+                          security_enabled = True

+                          break

+ 

+         if not security_enabled:

+             print('RHDS (%s): NSS password ignored - security is off' % instance_name)

+             sys.exit()

+ 

+         # Security is enabled

+         #

+         # If it exists a pin.txt file, this is fine. Admin accepts to use a clear text

+         # password stored in a local file.

+         #

+         # If not, then prompt the NSS password unless it already exist one in keyring

+         token = "Internal (Software) Token"

+ 

+         pin_file = "/etc/dirsrv/slapd-%s/pin.txt" % instance_name

+         if os.path.exists(pin_file):

+             print('RHDS (%s): Keyring is not used because it exists pin.txt' % instance_name)
abbra commented 4 years ago

"Keyring is not used because pin.txt exists"

+             pass

+         else:

+             # A RHDS key description looks like "Internal (Software) Token:master1:password"

+             key_name = "%s:%s:password" % (token, instance_name)

+             try:

+                 key_id = keyring.get_key_id(key_name=key_name)

+                 print('RHDS (%s): Initialization of NSS using a password stored in keyring' % instance_name)

+             except subprocess.CalledProcessError:

+                 prompt = '[' + instance_name + '] Please provide the password for cert/key Database:'

+                 cmd_ask_password = ['systemd-ask-password', prompt]

+                 entered_pass = subprocess.check_output(cmd_ask_password)

+ 

+                 keyring.put_password(key_name=key_name, password=entered_pass)

+                 print('RHDS (%s): Prompted NSS password is now stored in keyring' % instance_name)

+                 pass

+         sys.exit()

+ 

+     elif o in ('-g', '--get_key'):

+         obj = re.compile('^.*/slapd-(.*)/.*$')

+         instance_name = obj.sub("\\1", a)

+         token = "Internal (Software) Token"

+ 

+         # A RHDS key description looks like "Internal (Software) Token:master1:password"

+         key_name = "%s:%s:password" % (token, instance_name)

+         try:

+             key_id = keyring.get_key_id(key_name=key_name)

+             print('RHDS (%s): key desc %s ==> key ID %s' % (instance_name, key_name, key_id))

+         except subprocess.CalledProcessError:

+             print("RHDS (%s): key desc \"%s\" does not exist" % key_name)

+         sys.exit()

+ 

+     else:

+         logger.error('option %s not recognized', o)

+         print_help()

+         sys.exit(1)

file modified
+3 -3
@@ -121,7 +121,7 @@ 

  }

  

  int

- detach(int slapd_exemode, int importexport_encrypt, int s_port, daemon_ports_t *ports_info)

+ detach(int slapd_exemode, int importexport_encrypt, int s_port, daemon_ports_t *ports_info, char *nss_default_slot_token, char *keyring_nss_password)

  {

      int i, sd;

  
@@ -150,7 +150,7 @@ 

          vattr_global_lock_create();

  

          /* call this right after the fork, but before closing stdin */

-         if (slapd_do_all_nss_ssl_init(slapd_exemode, importexport_encrypt, s_port, ports_info)) {

+         if (slapd_do_all_nss_ssl_init(slapd_exemode, importexport_encrypt, s_port, ports_info, nss_default_slot_token, keyring_nss_password)) {

              return 1;

          }

  
@@ -183,7 +183,7 @@ 

           */

          vattr_global_lock_create();

  

-         if (slapd_do_all_nss_ssl_init(slapd_exemode, importexport_encrypt, s_port, ports_info)) {

+         if (slapd_do_all_nss_ssl_init(slapd_exemode, importexport_encrypt, s_port, ports_info, nss_default_slot_token, keyring_nss_password)) {

              return 1;

          }

          if (set_workingdir()) {

file modified
+135 -4
@@ -50,6 +50,7 @@ 

  #include <sys/utsname.h>

  #include <sys/systeminfo.h>

  #endif

+ #define NEED_TOK_PBE /* see slap.h - defines tokPBE and ptokPBE */

  #include "slap.h"

  #include "slapi-plugin.h"

  #include "prinit.h"
@@ -59,6 +60,7 @@ 

  #include "getopt_ext.h"

  #include "fe.h"

  #include <nss.h>

+ #include <keyutils.h>

  

  #ifdef LINUX

  /* For mallopt. Should be removed soon. */
@@ -508,11 +510,129 @@ 

      return -1;

  }

  

+ static int

+ keyring_get_password(char *configdir, char **nss_default_slot_token, char **keyring_nss_password)

I'm not sure I like this in main. If we still use svrcore, the point of svrcore is not just to get the password, but to make the decision about which password we use. I think that perhaps this should be in svrcore.

It needs to be in main just before svrcore_setup is called. That way DS can read the password from keyring before setuid.

It is expected to retrieve the NSS password from svrcore. I do not get the meaning of 'which password' ?

+ {

+     char pin_data[4096] = {0};

+     int ret = -1; /* by default it fails */

+     long keyid;

+     char *token = NULL;

+     char *serverId = NULL;

+     char key_type[] = "password";

+     char *keyring_key = NULL; /* concatenate of 'token:serverId:key_type */

+ 

+     if (configdir == NULL) {

+         slapi_log_err(SLAPI_LOG_ERR, "keyring_get_password", "No configdir\n");

+         goto cleanup;

+     }

+ 

+     if (keyring_nss_password == NULL) {

+         slapi_log_err(SLAPI_LOG_ERR, "keyring_get_password", "not possible to store retrieved password\n");

+         goto cleanup;

+     }

+     *keyring_nss_password = NULL;

+ 

+     if (nss_default_slot_token == NULL) {

+         slapi_log_err(SLAPI_LOG_ERR, "keyring_get_password", "not possible to store NSS default slot token\n");

+         goto cleanup;

+     }

+     *nss_default_slot_token = NULL;

+ 

+     /* Retrieve the default slot token: Internal (Software) Token */

+     token = slapi_ch_strdup(ptokPBE);

+     if (token == NULL) {

+         slapi_log_err(SLAPI_LOG_ERR, "keyring_get_password", "not possible to copy NSS default slot token: %s\n", ptokPBE);

+         goto cleanup;

+     }

+     /* ptokPBE contains tailing ' ' that we need to remove */

+     for (int i = (strlen(token) - 1); token[i] == ' '; i--) token[i] = '\0';

+ 

+     /* retrieve the serverId: /etc/dirsrv/slapd-<serverId> */

+     serverId = PL_strstr(configdir, "slapd-");

+     if (serverId) {

+         serverId = slapi_ch_strdup(serverId + PL_strlen("slapd-"));

+     } else {

+         slapi_log_err(SLAPI_LOG_ERR, "keyring_get_password", "not possible to retrieve serverID from %s (missing \"slapd-\")\n",

+                 configdir);

+         goto cleanup;

+     }

+ 

+     /*

+      * Keyring stores key/pin pairs

+      *

+      * The format of the key is <default_slot_token>:<instance_serverId>:<key_type>

+      * for example:

+      *    <default_slot_token>: Internal (Software) Token

+      *    <instance_serverId>: master1

+      *    <key_type>: password

+      *

+      * svrcore is the framework used by DS to get the NSS password. So the pin retrieved

+      * from keyring is stored in a svrcore plugin: keyring plugin.

+      *

+      * In svrcore the pin is stored/retrieved using the token of the NSS db default slot: 'Internal (Software) Token'.

+      * The NSS db default slot is created with this token (ptokPBE) during slapd_nss_init.

+      * When authenticating to NSS db (slapd_pk11_authenticate) it lookup the default slot

+      * and use the same token (ptokPBE) (without tailing spaces), to retrieve the pin (getPin svrcore)

+      */

+     keyring_key = slapi_ch_smprintf("%s:%s:%s", token, serverId, key_type);

+     if (keyring_key == NULL) {

+         slapi_log_err(SLAPI_LOG_ERR, "keyring_get_password", "Fail to create the keyring key (\"%s:%s:%s\")\n",

+                 token, serverId, key_type);

+         goto cleanup;

+     }

+ 

+     /* retrieve the keyid from the key description

+      * there is no destination else it requires a Selinux

+      * module to grant Write right

+      */

+     keyid = keyctl_search(KEY_SPEC_USER_KEYRING, "user", keyring_key, 0 /* no destination */);

+     if (keyid != -1) {

+         long rc;

+ 

+         /* selinux permissive => EACCES */

+         rc = keyctl_read((key_serial_t) keyid, pin_data, sizeof (pin_data));

+ 

+         if (rc <= 0) {

+             slapi_log_err(SLAPI_LOG_ERR, "keyring_get_password", "Fail to read NSS database password pin_data=%s (%ld)\n", pin_data, rc);

+         } else if (rc > 0) {

+             slapi_log_err(SLAPI_LOG_ERR, "keyring_get_password", "pin_data=%s (%ld)\n", pin_data, rc);

+             pin_data[rc] = '\0';

+             if (pin_data[rc - 1] == '\n') {

+                 /* in case of systemd-tty-ask-password-agent

+                  * the returned string ends with '\n'

+                  */

+                 pin_data[rc - 1] = '\0';

+             }

+             *nss_default_slot_token = token;

+             *keyring_nss_password = slapi_ch_strdup(pin_data);

+             ret = 0;

+         }

+     } else {

+         slapi_log_err(SLAPI_LOG_INFO, "keyring_get_password", "Keyring does not contain NSS database password or access denied (rc=%ld)\n", keyid);

+     }

+ 

+ cleanup:

+     if (token && ret) {

+         /* in case of failure (ret==-1) the token

+          * is NOT referenced by nss_default_slot_token. It can be freed

+          */

+         slapi_ch_free((void **) &token);

+     }

+     if (serverId) slapi_ch_free((void **) &serverId);

+     if (keyring_key) slapi_ch_free((void **) &keyring_key);

+ 

+     return ret;

+ }

+ 

  int

  main(int argc, char **argv)

  {

      int return_value = 0;

      struct main_config mcfg = {0};

+ 	char *nss_default_slot_token = NULL; /* contains the "Internal (Software) Token" */

+ 	char *keyring_nss_password = NULL;   /* contains the password of the NSS db

+                                           * it is retrieved from keyring before detach

+                                           */

  

      /* Set a number of defaults */

      mcfg.slapd_exemode = SLAPD_EXEMODE_UNKNOWN;
@@ -786,6 +906,16 @@ 

          }

      }

  

+     /* check keyring only in case security was enabled */

+     if (slapdFrontendConfig->security) {

+         /* nss_default_slot_token and keyring_nss_password are retrieved before detach

+          * Then the are transmitted to the deamon that will use them to initialize security

+          */

+         if (keyring_get_password(slapdFrontendConfig->configdir, &nss_default_slot_token, &keyring_nss_password)) {

+             slapi_log_err(SLAPI_LOG_INFO, "main", "Password of NSS database not retrieved from keyring\n");

+         }

+     }

+ 

      /* Now, sockets are open, so we can safely change identity now */

      return_value = main_setuid(slapdFrontendConfig->localuser);

      if (0 != return_value) {
@@ -793,7 +923,6 @@ 

                        slapdFrontendConfig->localuser);

          exit(1);

      }

- 

      /*

       * Detach ourselves from the terminal (unless running in debug mode).

       * We must detach before we start any threads since detach forks() on
@@ -801,7 +930,7 @@ 

       * Have to detach after ssl_init - the user may be prompted for the PIN

       * on the terminal, so it must be open.

       */

-     if (detach(mcfg.slapd_exemode, mcfg.importexport_encrypt, mcfg.s_port, &ports_info)) {

+     if (detach(mcfg.slapd_exemode, mcfg.importexport_encrypt, mcfg.s_port, &ports_info, nss_default_slot_token, keyring_nss_password)) {

          return_value = 1;

          goto cleanup;

      }
@@ -2841,7 +2970,7 @@ 

    modes (or just not detaching).

  */

  int

- slapd_do_all_nss_ssl_init(int slapd_exemode, int importexport_encrypt, int s_port, daemon_ports_t *ports_info)

+ slapd_do_all_nss_ssl_init(int slapd_exemode, int importexport_encrypt, int s_port, daemon_ports_t *ports_info, char *nss_default_slot_token, char *keyring_nss_password)

  {

      /*

       * Initialise NSS once for the whole slapd process, whether SSL
@@ -2860,7 +2989,9 @@ 

       * modules can assume NSS is available

       */

      if (slapd_nss_init((slapd_exemode == SLAPD_EXEMODE_SLAPD),

-                        (slapd_exemode != SLAPD_EXEMODE_REFERRAL) /* have config? */)) {

+                        (slapd_exemode != SLAPD_EXEMODE_REFERRAL) /* have config? */,

+ 					   nss_default_slot_token,

+ 					   keyring_nss_password)) {

          if (force_to_disable_security("NSS", &init_ssl, ports_info)) {

              return 1;

          }

@@ -629,7 +629,7 @@ 

  /*

   * detach.c

   */

- int detach(int slapd_exemode, int importexport_encrypt, int s_port, daemon_ports_t *ports_info);

+ int detach(int slapd_exemode, int importexport_encrypt, int s_port, daemon_ports_t *ports_info, char *nss_default_slot_token, char *keyring_nss_password);

  void close_all_files(void);

  void raise_process_limits(void);

  
@@ -1088,7 +1088,7 @@ 

  /*

   * ssl.c

   */

- int slapd_nss_init(int init_ssl, int config_available);

+ int slapd_nss_init(int init_ssl, int config_available, char *nss_default_slot_token, char *keyring_nss_password);

  int slapd_ssl_init(void);

  int slapd_ssl_init2(PRFileDesc **fd, int startTLS);

  int slapd_security_library_is_initialized(void);
@@ -1503,7 +1503,7 @@ 

  #if defined(hpux)

  void signal2sigaction(int s, void *a);

  #endif

- int slapd_do_all_nss_ssl_init(int slapd_exemode, int importexport_encrypt, int s_port, daemon_ports_t *ports_info);

+ int slapd_do_all_nss_ssl_init(int slapd_exemode, int importexport_encrypt, int s_port, daemon_ports_t *ports_info, char *nss_default_slot_token, char *keyring_nss_password);

  

  /*

   * pagedresults.c

file modified
+37 -4
@@ -62,6 +62,16 @@ 

     and use the same naming scheme

  */

  static char *dongle_file_name = NULL;

+ static char *nss_token = NULL; /* contains the "Internal (Software) Token"

+                                 * the value is set before detach()

+                                 * to be used by the daemon to initialize security

+                                 */

+ static char *nss_password = NULL; /* contains the password of the NSS db that was

+                                    * stored in keyring.

+                                    * It was set before detach() (because of

+                                    * because of keyring access) to be used by the

+                                    * daemon to initialized security

+                                    */

  

  static int _security_library_initialized = 0;

  static int _ssl_listener_initialized = 0;
@@ -925,7 +935,7 @@ 

   * we do not need a prefix any more.

   */

  int

- slapd_nss_init(int init_ssl __attribute__((unused)), int config_available __attribute__((unused)))

+ slapd_nss_init(int init_ssl __attribute__((unused)), int config_available __attribute__((unused)), char *nss_default_slot_token, char *keyring_nss_password)

  {

      SECStatus secStatus;

      PRErrorCode errorCode;
@@ -987,6 +997,12 @@ 

  

      dongle_file_name = slapi_ch_smprintf("%s/pin.txt", certdir);

  

+     /* it consums the string */

+     nss_token = nss_default_slot_token;

+ 

+     /* it consums the string */

+     nss_password = keyring_nss_password;

+ 

      if (secStatus != SECSuccess) {

          errorCode = PR_GetError();

          slapd_SSL_error("NSS initialization failed (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s): "
@@ -1068,12 +1084,27 @@ 

      return rv;

  }

  

+ /* Function used by svrcore plugins to log messages in DS logs */

+ static void

+ svrcore_log_err(char *fmt, char *arg1, char *arg2, char *arg3)

+ {

+ 	slapi_log_err(SLAPI_LOG_ERR, "Security Initialization",

+                               fmt,

+ 			arg1 ? arg1 : "",

+ 			arg2 ? arg2 : "",

+ 			arg3 ? arg3 : "");

+ }

+ 

  static int

  svrcore_setup(void)

  {

      PRErrorCode errorCode;

      int rv = 0;

- #ifdef WITH_SYSTEMD

+ #ifdef WITH_KEYRING

+ 	SVRCOREStdKeyringPinObj *StdPinObj;

+     StdPinObj = (SVRCOREStdKeyringPinObj *)SVRCORE_GetRegisteredPinObj();

+ 

+ #elif WITH_SYSTEMD

      SVRCOREStdSystemdPinObj *StdPinObj;

      StdPinObj = (SVRCOREStdSystemdPinObj *)SVRCORE_GetRegisteredPinObj();

  #else
@@ -1084,11 +1115,13 @@ 

      if (StdPinObj) {

          return 0; /* already registered */

      }

- #ifdef WITH_SYSTEMD

+ #ifdef WITH_KEYRING

+ 	if (SVRCORE_CreateStdKeyringPinObj(&StdPinObj, dongle_file_name, nss_token, nss_password, PR_TRUE, PR_TRUE, PR_TRUE /* Keyring */, 90, svrcore_log_err) != SVRCORE_Success) {

+ #elif WITH_SYSTEMD

      if (SVRCORE_CreateStdSystemdPinObj(&StdPinObj, dongle_file_name, PR_TRUE, PR_TRUE, 90) != SVRCORE_Success) {

  #else

      if (SVRCORE_CreateStdPinObj(&StdPinObj, dongle_file_name, PR_TRUE) != SVRCORE_Success) {

- #endif

+ #endif /* WITH_KEYRING */

          errorCode = PR_GetError();

          slapd_SSL_warn("Unable to create PinObj (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",

                         errorCode, slapd_pr_strerror(errorCode));

file modified
+1
@@ -607,6 +607,7 @@ 

  %{_mandir}/man1/dbscan.1.gz

  %{_bindir}/ds-replcheck

  %{_mandir}/man1/ds-replcheck.1.gz

+ %{_bindir}/ds-keyring.py

  %{_bindir}/ds-logpipe.py

  %{_mandir}/man1/ds-logpipe.py.1.gz

  %{_bindir}/ldclt

@@ -0,0 +1,108 @@ 

+ # Authors:

+ #     Dinesh Prasanth M K <dmoluguw@redhat.com>

+ #

+ # This program is free software; you can redistribute it and/or modify

+ # it under the terms of the Lesser GNU General Public License as published by

+ # the Free Software Foundation; either version 3 of the License or

+ # (at your option) any later version.

+ #

+ # This program is distributed in the hope that it will be useful,

+ # but WITHOUT ANY WARRANTY; without even the implied warranty of

+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+ # GNU Lesser General Public License for more details.

+ #

+ # You should have received a copy of the GNU Lesser General Public License

+ #  along with this program; if not, write to the Free Software Foundation,

+ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

+ #

+ # Copyright (C) 2019 Red Hat, Inc.

+ # All rights reserved.

+ #

+ 

+ 

+ """

+ Module that provides interface to access Kernel Keyring

+ """

+ import subprocess

+ 

+ 

+ class Keyring(object):

+     """

+     Utility class to deal with keyrings. This class is a simple wrapper

+     against the `keyutils` package. Defaults to user keyring `@u` and key

+     type `user`

+     """

+ 

+     def __init__(self, keyring='@u', key_type='user'):

+         self.keyring = keyring

+         self.key_type = key_type

+ 

+     def put_password(self, key_name, password):

+         """

+         Save a password to the keyring

+ 

+         :param key_name: Name of they key

+         :type key_name: str

+         :param password: Password value to be stored

+         :type password: bytearray

+         :return: Key ID, Error (if any)

+         :rtype: (bytearray, bytearray)

+         """

+ 

+         cmd = ['keyctl', 'padd', self.key_type, key_name, self.keyring]

+ 

+         p = subprocess.Popen(cmd,

+                              stdin=subprocess.PIPE,

+                              stdout=subprocess.PIPE,

+                              stderr=subprocess.STDOUT)

+ 

+         return p.communicate(input=password)

+ 

+     def get_key_id(self, key_name):

+         """

+         Retrieve key ID from the provided key name

+ 

+         :param key_name: Name of the key to search

+         :type key_name: str

+         :return: key_ID

+         :rtype: int

+         """

+ 

+         cmd = ['keyctl', 'search', self.keyring, self.key_type, key_name]

+ 

+         return subprocess.check_output(cmd).decode('utf-8').strip()

+ 

+     def get_password(self, key_name, output_format='raw'):

+         """

+         Retrieve password in the given format

+ 

+         :param key_name: The value of the key to be retrieved

+         :type key_name: str

+         :param output_format: Retrieval format: hex or raw (default)

+         :type output_format: str

+         :return: Value of the key in specified format

+         :rtype: str

+         """

+ 

+         if output_format.lower() == 'raw':

+             mode = 'pipe'

+         elif output_format.lower() == 'hex':

+             mode = 'read'

+         else:

+             raise AttributeError('output_format must be one of [\'raw\', \'hex\'].')

+ 

+         key_id = self.get_key_id(key_name)

+ 

+         cmd = ['keyctl', mode, key_id]

+ 

+         return subprocess.check_output(cmd).decode('utf-8')

+ 

+     def clear_keyring(self):

+         """

+         Clear the default keyring

+ 

+         :return: Return code

+         :rtype: int

+         """

+         cmd = ['keyctl', 'clear', self.keyring]

+         return subprocess.check_call(cmd)

file modified
+3 -1
@@ -24,7 +24,9 @@ 

  			std.c \

  			systemd-ask-pass.c \

  			std-systemd.c \

+ 			keyring-ask-pass.c \

+ 			std-keyring.c \

  			user.c

  

- libsvrcore_la_LDFLAGS = -version-info 0:0:0 @NSS_LIBS@ @NSPR_LIBS@

+ libsvrcore_la_LDFLAGS = -version-info 0:0:0 @NSS_LIBS@ @NSPR_LIBS@ -lkeyutils

  libsvrcore_la_CFLAGS = @NSS_CFLAGS@ @NSPR_CFLAGS@ @SYSTEMD_CFLAGS@

@@ -0,0 +1,166 @@ 

+ /*

+  * Copyright (C) 1998 Netscape Communications Corporation.

+  * All Rights Reserved.

+  *

+  * Copyright 2016 Red Hat, Inc. and/or its affiliates.

+  *

+  * 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 https://mozilla.org/MPL/2.0/.

+  */

+ 

+ /*

+  * systemd-ask-pass.c - SVRCORE module for reading the PIN from systemd integrations.

+  */

+ 

+ #if HAVE_CONFIG_H

+ #include <config.h>

+ #endif

+ 

+ /* For socket.h to work correct, we need to define __USE_GNU */

+ #define _GNU_SOURCE 1

+ 

+ #include <stdio.h>

+ #include <stdlib.h>

+ #include <string.h>

+ #include <errno.h>

+ #include <sys/stat.h>

+ #include <sys/types.h>

+ #include <unistd.h>

+ #include <sys/socket.h>

+ #include <sys/un.h>

+ #include <sys/uio.h>

+ #include <time.h>

+ #include <inttypes.h>

+ #include <svrcore.h>

+ #include <keyutils.h>

+ 

+ 

+ #define PASS_MAX 256 * sizeof(char)

+ #define NSEC_PER_USEC ((uint64_t) 1000ULL)

+ #define USEC_PER_SEC ((uint64_t) 1000000ULL)

+ 

+ struct SVRCOREKeyringPinObj

+ {

+     SVRCOREPinObj base;

+ 	char *nss_token;

+ 	char *pin;

+     svrcore_log_err_t log_callback;

+     uint64_t timeout;

+ };

+ static const struct SVRCOREPinMethods vtable;

+ 

+ SVRCOREError

+ SVRCORE_CreateKeyringPinObj(SVRCOREKeyringPinObj **out, char *nss_token, char *nss_password, uint64_t timeout, svrcore_log_err_t log_callback)

+ {

+ #ifdef WITH_KEYRING

+ 

+     SVRCOREError err = SVRCORE_Success;

+     SVRCOREKeyringPinObj *obj = NULL;

+ 

+     do { // This is used like a "with" statement, to avoid a goto.

+         obj = (SVRCOREKeyringPinObj *)malloc(sizeof(SVRCOREKeyringPinObj));

+         if (obj == NULL) {

+             err = SVRCORE_NoMemory_Error;

+             break;

+         }

+ 

+         obj->base.methods = &vtable;

+ 		obj->nss_token = strdup(nss_token);

+ 		obj->pin = strdup(nss_password);

+         obj->log_callback = log_callback;

+         if (timeout == 0) {

+             obj->timeout = 90;

+         } else {

+             obj->timeout = timeout;

+         }

+ 

+     } while (0);

+ 

+     // If error, destrop it, and return err

+     if (err != SVRCORE_Success) {

+         SVRCORE_DestroyKeyringPinObj(obj);

+         obj = NULL;

+     }

+ 

+     *out = obj;

+     return err;

+ #else // keyring

+     return SVRCORE_MissingFeature;

+ #endif // keyring

+ }

+ 

+ static char *

+ getPin(SVRCOREPinObj *obj, const char *token, PRBool retry)

+ {

+     char *pin = NULL;

+     SVRCOREKeyringPinObj *kobj = (SVRCOREKeyringPinObj *) obj;

+     int max_retry = 10; /* used to validate the retrieved password */

+ 

+ 

+ #ifdef WITH_KEYRING

+     if (!strcmp(token, kobj->nss_token)) {

+         pin = strdup(kobj->pin);

+     }

+ 

+     /* Attempt to create a Pin Storage object.  This checks the

+      * password.

+      */

+     do {

+         SVRCOREPk11PinStore *store;

+         SVRCOREError err;

+ 

+         err = SVRCORE_CreatePk11PinStore(&store, token, pin);

+         SVRCORE_DestroyPk11PinStore(store);

+ 

+         if (err == SVRCORE_Success) {

+             break;

+         } else if (err == SVRCORE_IncorrectPassword_Error) {

+             /* retry*/

+             max_retry--;

+         } else {

+             break;

+         }

+ 

+     } while(max_retry);

+ 

+     if ((max_retry == 0) && kobj->log_callback) {

+         /* The password retrieved from keyring is invalid

+          * It should be changed

+          */

+         kobj->log_callback("Keyring fails to check the NSS password: \"%s\"\n",

+ 					pin, 0, 0);

+         kobj->log_callback("You may try: keyctl search @u user \"%s\" --> <key_id>\n",

+ 					token, 0, 0);

+         kobj->log_callback("Then: keyctl unlink <key_id> @u\n",

+ 					0, 0, 0);

+         pin = NULL;

+     }

+ 

+     return pin;

+ #else // Keyring

+     return NULL;

+ #endif // Keyring

+ }

+ 

+ void

+ SVRCORE_DestroyKeyringPinObj(SVRCOREKeyringPinObj *obj)

+ {

+ #ifdef WITH_KEYRING

+     if (obj) {

+ 		if (obj->nss_token) free(obj->nss_token);

+ 		if (obj->pin) free(obj->pin);

+         free(obj);

+     }

+ #endif // Keyring

+ }

+ 

+ static void

+ destroyObject(SVRCOREPinObj *obj)

+ {

+     SVRCORE_DestroyKeyringPinObj((SVRCOREKeyringPinObj*)obj);

+ }

+ 

+ static const SVRCOREPinMethods vtable =

+ { 0, 0, destroyObject, getPin };

+ 

@@ -0,0 +1,270 @@ 

+ /*

+  * Copyright (C) 1998 Netscape Communications Corporation.

+  * All Rights Reserved.

+  *

+  * Copyright 2016 Red Hat, Inc. and/or its affiliates.

+  *

+  * 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 https://mozilla.org/MPL/2.0/.

+  */

+ 

+ /*

+  * std-keyring.c - Extension of the STD module to integrate file, tty and keyring

+  */

+ 

+ 

+ #if HAVE_CONFIG_H

+ #include <config.h>

+ #endif

+ 

+ #include <stdio.h>

+ #include <string.h>

+ #include <svrcore.h>

+ #include <unistd.h>

+ 

+ 

+ /* ------------------------------------------------------------ */

+ /*

+  * SVRCOREStdKeyringPinObj implementation

+  */

+ struct SVRCOREStdKeyringPinObj

+ {

+     SVRCOREPinObj base;

+     SVRCORECachedPinObj *cache;

+     SVRCOREAltPinObj *alt;

+     SVRCOREFilePinObj *file;

+     SVRCOREUserPinObj *user;

+     SVRCOREAltPinObj *systemdalt;

+     SVRCORESystemdPinObj *systemd;

+ 	SVRCOREAltPinObj *keyringalt;

+ 	SVRCOREKeyringPinObj *keyring;

+ 	char *keyring_nss_token;

+ 	char *keyring_nss_password;

+ 	svrcore_log_err_t log_callback;

+ 

+     SVRCOREPinObj *top;

+ };

+ static const SVRCOREPinMethods vtable;

+ 

+ /* ------------------------------------------------------------ */

+ SVRCOREError

+ SVRCORE_CreateStdKeyringPinObj(

+     SVRCOREStdKeyringPinObj **out,

+     const char *filename,  const char *nss_token, const char *nss_password, PRBool cachePINs,

+     PRBool systemdPINs, PRBool keyringPINs, uint64_t timeout, svrcore_log_err_t log_callback)

+ {

+ 

+     SVRCOREError err = SVRCORE_Success;

+     SVRCOREStdKeyringPinObj *obj = 0;

+ 

+     do {

+         SVRCOREPinObj *top;

+ 

+         obj = (SVRCOREStdKeyringPinObj *)malloc(sizeof (SVRCOREStdKeyringPinObj));

+         if (!obj) { err = SVRCORE_NoMemory_Error; break; }

+ 

+         obj->base.methods = &vtable;

+ 

+         obj->cache = 0;

+         obj->alt = 0;

+         obj->file = 0;

+         obj->user = 0;

+         obj->systemdalt = 0;

+         obj->systemd = 0;

+ 		obj->keyringalt = 0;

+ 		obj->keyring = 0;

+ 		obj->keyring_nss_token = 0;

+ 		obj->keyring_nss_password = 0;

+ 		obj->log_callback = log_callback;

+ 

+         err = SVRCORE_CreateUserPinObj(&obj->user);

+         if (err) {

+             break;

+         }

+         // Automatically detect if we are on an interactive session or not

+ 

+ #ifdef DEBUG

+         printf("std-keyring:create() -> interactive %d \n", isatty(fileno(stdin)));

+ #endif

+ 

+         // During testing, we want to disable this sometimes ...

+         // SVRCORE_SetUserPinInteractive(obj->user, PR_FALSE);

+         SVRCORE_SetUserPinInteractive(obj->user, isatty(fileno(stdin)));

+ 

+         top = (SVRCOREPinObj*)obj->user;

+ 

+         /* If filename is provided, splice it into the chain */

+         if (filename)

+         {

+             err = SVRCORE_CreateFilePinObj(&obj->file, filename);

+             if (err) {

+                 break;

+             }

+ 

+             err = SVRCORE_CreateAltPinObj(&obj->alt,

+                   (SVRCOREPinObj*)obj->file, top);

+             if (err) {

+                 break;

+             }

+ 

+             top = (SVRCOREPinObj*)obj->alt;

+         }

+ 

+ #ifdef WITH_SYSTEMD

+         if (systemdPINs) {

+             err = SVRCORE_CreateSystemdPinObj(&obj->systemd, timeout);

+             if (err) {

+                 break;

+             }

+             // Now make a second "alt" object. If pin and user fail, we call systemd

+ #ifdef DEBUG

+             printf("std-keyring:create() -> Creating systemd alt pin object \n");

+ #endif

+             err = SVRCORE_CreateAltPinObj(&obj->systemdalt,

+                     top, (SVRCOREPinObj*)obj->systemd);

+             if (err) {

+                 break;

+             }

+             top = (SVRCOREPinObj *)obj->systemdalt;

+         }

+ #endif /* WITH_SYTEMD */

+ 

+ 		if (keyringPINs && nss_token && nss_password) {

+             /* Use of nss_token/nss_password (key/value pair) retrieved in keyring */

+ 			if (nss_token) {

+ 				obj->keyring_nss_token = strdup(nss_token);

+ 			}

+ 			if (nss_password) {

+ 				obj->keyring_nss_password = strdup(nss_password);

+ 			}

+ 			if (log_callback) log_callback("Keyring enabled: NSS password registered for slot token \"%s\"\n",

+ 					obj->keyring_nss_token, 0, 0);

+ #ifdef DEBUG

+             printf("std-keyring:create() -> Creating keyring pin object \n");

+ #endif

+             err = SVRCORE_CreateKeyringPinObj(&obj->keyring, obj->keyring_nss_token, obj->keyring_nss_password, timeout, log_callback);

+             if (err) {

+                 break;

+             }

+             // Now make a second "alt" object. if keyring fails let's try in that order file/user/systemd

+             err = SVRCORE_CreateAltPinObj(&obj->keyringalt,

+                     (SVRCOREPinObj*)obj->keyring, top);

+             if (err) {

+                 break;

+             }

+             top = (SVRCOREPinObj *)obj->keyringalt;

+         }

+ 

+         /* Create cache object if requested */

+         if (cachePINs)

+         {

+             err = SVRCORE_CreateCachedPinObj(&obj->cache, top);

+             if (err) {

+                 break;

+             }

+ 

+             top = (SVRCOREPinObj*)obj->cache;

+         }

+ 

+         obj->top = top;

+     } while(0);

+ 

+     *out = obj;

+ 

+     if (err != SVRCORE_Success)

+     {

+         SVRCORE_DestroyStdKeyringPinObj(obj);

+         *out = NULL;

+     }

+ 

+     return err;

+ }

+ 

+ void

+ SVRCORE_DestroyStdKeyringPinObj(

+   SVRCOREStdKeyringPinObj *obj)

+ {

+ #ifdef WITH_KEYRING

+     if (!obj) return;

+ 

+     if (obj->user) SVRCORE_DestroyUserPinObj(obj->user);

+     if (obj->file) SVRCORE_DestroyFilePinObj(obj->file);

+     if (obj->alt) SVRCORE_DestroyAltPinObj(obj->alt);

+     if (obj->cache) SVRCORE_DestroyCachedPinObj(obj->cache);

+     if (obj->systemd) SVRCORE_DestroySystemdPinObj(obj->systemd);

+     if (obj->systemdalt) SVRCORE_DestroyAltPinObj(obj->systemdalt);

+ 	if (obj->keyring) SVRCORE_DestroyKeyringPinObj(obj->keyring);

+ 	if (obj->keyringalt) SVRCORE_DestroyAltPinObj(obj->keyringalt);

+ 	if (obj->keyring_nss_token) free(obj->keyring_nss_token);

+ 	if (obj->keyring_nss_password) free(obj->keyring_nss_password);

+ 

+     free(obj);

+ #endif // Keyring

+ }

+ 

+ /* ------------------------------------------------------------ */

+ 

+ void

+ SVRCORE_SetStdKeyringPinInteractive(SVRCOREStdKeyringPinObj *obj, PRBool i)

+ {

+ #ifdef WITH_KEYRING

+     SVRCORE_SetUserPinInteractive(obj->user, i);

+ #endif // Systemd

+ }

+ 

+ /* ------------------------------------------------------------ */

+ /*

+  * SVRCORE_StdKeyringPinGetPin

+  */

+ SVRCOREError

+ SVRCORE_StdKeyringPinGetPin(char **pin, SVRCOREStdKeyringPinObj *obj,

+   const char *nss_password)

+ {

+ #ifdef WITH_KEYRING

+ #ifdef DEBUG

+     printf("std-keyring:stdkeyring-getpin() -> starting \n");

+ #endif

+     /* Make sure caching is turned on */

+     if (!obj->cache)

+     {

+         *pin = 0;

+         return SVRCORE_NoSuchToken_Error;

+     }

+ 

+     return SVRCORE_CachedPinGetPin(pin, obj->cache, nss_password);

+ #else // keyring

+     return SVRCORE_MissingFeature;

+ #endif // keyring

+ }

+ 

+ /* ------------------------------------------------------------ */

+ /*

+  * vtable methods

+  */

+ static void

+ destroyObject(SVRCOREPinObj *obj)

+ {

+     SVRCORE_DestroyStdKeyringPinObj((SVRCOREStdKeyringPinObj*)obj);

+ }

+ 

+ static char *

+ getPin(SVRCOREPinObj *pinObj, const char *tokenName, PRBool retry)

+ {

+ 	char *pin = NULL;

+ 	char *fullToken;

+ #ifdef DEBUG

+     printf("std-keyring:getpin() -> starting \n");

+ #endif

+     SVRCOREStdKeyringPinObj *obj = (SVRCOREStdKeyringPinObj*)pinObj;

+ 	pin = SVRCORE_GetPin(obj->top, tokenName, retry);

+ 

+ 	return pin;

+ }

+ 

+ /*

+  * VTable

+  */

+ static const SVRCOREPinMethods vtable =

+ { 0, 0, destroyObject, getPin };

+ 

file modified
+31
@@ -263,6 +263,37 @@ 

  SVRCORE_DestroyStdSystemdPinObj(SVRCOREStdSystemdPinObj *obj);

  

  /* ------------------------------------------------------------ */

+ typedef void (*svrcore_log_err_t)(char *fmt, char *arg1, char *arg2, char *arg3);

+ 

+ 

+ typedef struct SVRCOREKeyringPinObj SVRCOREKeyringPinObj;

+ 

+ SVRCOREError

+ SVRCORE_CreateKeyringPinObj(SVRCOREKeyringPinObj **out, char *nss_token, char *nss_password, uint64_t timeout, svrcore_log_err_t log_callback);

+ 

+ void

+ SVRCORE_DestroyKeyringPinObj(SVRCOREKeyringPinObj *obj);

+ 

+ typedef struct SVRCOREStdKeyringPinObj SVRCOREStdKeyringPinObj;

+ typedef void (*svrcore_log_err_t)(char *fmt, char *arg1, char *arg2, char *arg3);

+ 

+ SVRCOREError

+ SVRCORE_CreateStdKeyringPinObj(

+     SVRCOREStdKeyringPinObj **out,

+     const char *filename,  const char *nss_token, const char *nss_password, PRBool cachePINs,

+     PRBool systemdPINs, PRBool keyringPINs, uint64_t timeout, svrcore_log_err_t log_callback);

+ 

+ void

+ SVRCORE_SetStdKeyringPinInteractive(SVRCOREStdKeyringPinObj *obj, PRBool i);

+ 

+ SVRCOREError

+ SVRCORE_StdKeyringPinGetPin(char **pin, SVRCOREStdKeyringPinObj *obj,

+   const char *nss_password);

+ 

+ void

+ SVRCORE_DestroyStdKeyringPinObj(

+   SVRCOREStdKeyringPinObj *obj);

+ /* ------------------------------------------------------------ */

  /*

   * Implements SVRCORESecurePinStore interface

   */

@@ -14,21 +14,28 @@ 

  # If we don't do this, we can't prompt for the password!

  # If you want this script to go away, fix the bugzilla so we don't need it!

  

+ PHASE=${2:-pre}

  # Make sure we have the path to the dse.ldif

  if [ -z $1 ]

  then

-     echo "usage: ${0} /etc/dirsrv/slapd-<instance>/dse.ldif"

+     echo "usage: ${0} /etc/dirsrv/slapd-<instance>/dse.ldif [pre|post]"

      exit 1

  fi

  

  # Grep the user out

  

  DS_USER=`grep 'nsslapd-localuser: ' $1 | awk '{print $2}'`

+ if [ "${PHASE}" = "pre" ]

+ then

+         ACTION="-m u:${DS_USER}:rwx"

+ else

+         ACTION="-x u:${DS_USER}"

+ fi

  

  # Now apply the acl

  

  if [ -d /var/run/systemd/ask-password ]

  then

-     setfacl -m u:${DS_USER}:rwx /var/run/systemd/ask-password

+     echo setfacl ${ACTION} /var/run/systemd/ask-password

  fi

  

@@ -13,8 +13,11 @@ 

  EnvironmentFile=-@initconfigdir@/@package_name@

  EnvironmentFile=-@initconfigdir@/@package_name@-%i

  PIDFile=/run/@package_name@/slapd-%i.pid

- ExecStartPre=@libexecdir@/ds_systemd_ask_password_acl @instconfigdir@/slapd-%i/dse.ldif

+ ExecStartPre=@libexecdir@/ds_systemd_ask_password_acl  @instconfigdir@/slapd-%i/dse.ldif pre

+ ExecStartPost=@libexecdir@/ds_systemd_ask_password_acl @instconfigdir@/slapd-%i/dse.ldif post

+ ExecStartPre=/usr/bin/ds-keyring.py --set_key @instconfigdir@/slapd-%i/dse.ldif

  ExecStart=@sbindir@/ns-slapd -D @instconfigdir@/slapd-%i -i /run/@package_name@/slapd-%i.pid

+ KeyringMode=shared

  

  [Install]

  WantedBy=multi-user.target

Bug Description:
Currently an instance with security enabled can be automatically
restarted only if it exists a pin.txt file.
This RFE is to allow to restart a without pin.txt and without prompting
the NSS password

Fix Description:
See http://www.port389.org/docs/389ds/design/protect-NSS-db.html

https://pagure.io/389-ds-base/issue/50794

Reviewed by: ?

Platforms tested: F30

Flag Day: no

Doc impact: no

@tbordaz, I see there is a new CLI tool (ds-keyring.py), but would it be very difficult to add this to dsctl? Then from the instance name you can automatically get the dse.ldif location, etc.

"Keyring is not used because pin.txt exists"

@mreynols, merging ds-keyring.py into dsctl looks a very good idea. Initially it was keyring focus but later had to gather more and more data from the instance (serverId, security, pin file). Thanks for this idea, I will implement (try) it.

@tbordaz I have a concern about use of this functionality in containers. Keyring is not namespaced, so you should be able to disable its use in a container. The patch, as it is now, doesn't allow that. Once you built with keyring support, there is no fallback to use of systemd services, for example.

@abbra, at the moment, if pin.txt exists it assumes that the admin accepts the risk and then there is no need to store the password in keyring.
It could be an option, to store it even if pin.txt exist and even to remove the pin.txt.

@abbra, would it be acceptable to use pin.txt (so no keyring) in container ?
If you build with keyring flag, the server will try to retrieve the password from keyring but if it is not available it will fallback to pin.txt and then to systemd prompt.

My understanding exactly the fallback is not happening because you made it compile time #ifdef, not a runtime 'if'.

also, I see in Fedora 31 that dirsrv has both pwdfile.txt and pin.txt, with pretty much the same password in both.

@abbra you are right. IIRC pwdfile.txt is produced by dscreate or lib389 (from pin.txt) when creating NSS db/keys/ssca . I think it is a leftover from security initialization and could/should likely be removed.

@abbra I misunderstood your question regarding keyring fallback. I answered regarding svrcore fallbacks :(

Right it is currently compile option, I did not make keyring runtime option. Is it what you are asking for ?

@tbordaz yes, I think it needs to be a runtime option. Whether it would be driven by a configuration or by a runtime decision as a fallback, I don't know. Keyring access would not fail in container, the keyring storage is just not isolated so would theoretically be accessible from other containers.

I'm not going to oppose this feature because I think it comes from customer requests. But at a high level I'm going to say "it doesn't enhance security at all" and represents a communication issue where we are unable to express threat and risk models to our consumers so they can understand the value of their assets.

To explain, if someone has filesystem access to your directory server instance they don't care about "getting the TLS keys". If someone has filesystem access to your directory server they can access the database that contains every single user hash (pbkdf2, or md4 aka cleartext in ipa with adtrust enabled). They can also then modify this content, read dse.ldif, reset directory manager pw, and then do anything they like such as adding their own user account, insert them to admin groups, reset other account pws .... The risk of compromising an IDM system like 389 is that people target it because it grants access to everything ELSE of business value.

There is a difference between a theoretical risk (IE cryptographic attacks and people stealing TLS keys) and a real risk (gaining access to business applications of value, finding personal data etc). Any serious attacker wants access to your accounts and other business applications - and won't care about your TLS keys as gaining access to the LDAP server at all already represents a complete compromise of all attached applications and thus, game over.

So with this in mind, I'm going to point out - pin.txt on the same filesystem as the dirsrv db/dse.ldif, has low risk because everyone wants the dirsrv db not the TLS keys. Framed differently - your directory server is only as secure as the dirsrv db.

I'm also going to highlight linux's extensive record of failure to prevent priv esc on a machine, meaning that any serious business will isolate their LDAP on unique hardware or in a container in unique hardware. In a container the situation is actually somewhat better due to containers getting selinux/apparmor confinement labels to protect the dirsrv db too.

Now this isn't just my own opinion today - I contacted current sysadmins and pentesters who helped me to ensure this idea is grounded in reality.

I think that we really should be improving our understanding of threat and risk models, and improving our customer communication so that we can cut through the difference between "security" and "compliance checkboxes".

This usage needs to explain more about how /why to use this. Before I start ds? After? How does it interact with the .service script?

This whole command should likely be a part of dsctl and assocated to the instance that way, rather than adding "yet another tool".

I'm not sure I like this in main. If we still use svrcore, the point of svrcore is not just to get the password, but to make the decision about which password we use. I think that perhaps this should be in svrcore.

You need to test a pin of 4097 to show there isn't a buffer overflow in reading a pin that large.

So I think here something that really isn't clear in the logic and order of WHICH pin we'll use. SVRCore has logic to say "okay this source, then that, then this ....". I think there is a fair bit from main.c that you added that probably doesn't belong, and same with detach. I think this logic should have been in svrcore and it's up to svrcore to do the logic to get that material.

As well, youneed to define clearly what order we attempt various types of pin check in and their failure modes.

For example, currently we check for pin.txt and if not found we attempt ask-pass. Only both their failures, stops start up.

With this added what's the flow? keyring first? And if keyring fails do we check pin.txt?

Or do we do pin.txt, then if not found keyring, then ask pass?

I'm not going to oppose this feature because I think it comes from customer requests. But at a high level I'm going to say "it doesn't enhance security at all" and represents a communication issue where we are unable to express threat and risk models to our consumers so they can understand the value of their assets.
To explain, if someone has filesystem access to your directory server instance they don't care about "getting the TLS keys". If someone has filesystem access to your directory server they can access the database that contains every single user hash (pbkdf2, or md4 aka cleartext in ipa with adtrust enabled). They can also then modify this content, read dse.ldif, reset directory manager pw, and then do anything they like such as adding their own user account, insert them to admin groups, reset other account pws .... The risk of compromising an IDM system like 389 is that people target it because it grants access to everything ELSE of business value.
There is a difference between a theoretical risk (IE cryptographic attacks and people stealing TLS keys) and a real risk (gaining access to business applications of value, finding personal data etc). Any serious attacker wants access to your accounts and other business applications - and won't care about your TLS keys as gaining access to the LDAP server at all already represents a complete compromise of all attached applications and thus, game over.
So with this in mind, I'm going to point out - pin.txt on the same filesystem as the dirsrv db/dse.ldif, has low risk because everyone wants the dirsrv db not the TLS keys. Framed differently - your directory server is only as secure as the dirsrv db.
I'm also going to highlight linux's extensive record of failure to prevent priv esc on a machine, meaning that any serious business will isolate their LDAP on unique hardware or in a container in unique hardware. In a container the situation is actually somewhat better due to containers getting selinux/apparmor confinement labels to protect the dirsrv db too.
Now this isn't just my own opinion today - I contacted current sysadmins and pentesters who helped me to ensure this idea is grounded in reality.
I think that we really should be improving our understanding of threat and risk models, and improving our customer communication so that we can cut through the difference between "security" and "compliance checkboxes".

The point of this ticket is to remove password in clear on the file system (pin.txt). The purpose of that file is to allow automatic restart. This PR is just a solution for that.
I do agree that there are many others threats if someone has access to the file system.

It needs to be in main just before svrcore_setup is called. That way DS can read the password from keyring before setuid.

It is expected to retrieve the NSS password from svrcore. I do not get the meaning of 'which password' ?

So I think here something that really isn't clear in the logic and order of WHICH pin we'll use. SVRCore has logic to say "okay this source, then that, then this ....". I think there is a fair bit from main.c that you added that probably doesn't belong, and same with detach. I think this logic should have been in svrcore and it's up to svrcore to do the logic to get that material.
As well, youneed to define clearly what order we attempt various types of pin check in and their failure modes.
For example, currently we check for pin.txt and if not found we attempt ask-pass. Only both their failures, stops start up.
With this added what's the flow? keyring first? And if keyring fails do we check pin.txt?
Or do we do pin.txt, then if not found keyring, then ask pass?

The order is defined in SVRCORE_CreateStdKeyringPinObj: first svrcore cache, then keyring, then pin file, then ask-password.
theoretically we should not have keyring key if a pin.txt exist. But if it happens, keyring is returned first.

The point of this ticket is to remove password in clear on the file system (pin.txt). The purpose of that file is to allow automatic restart. This PR is just a solution for that.
I do agree that there are many others threats if someone has access to the file system.

Maybe a more constructive and actionable feedback is that it should be documented that other risks exist within these systems?

It needs to be in main just before svrcore_setup is called. That way DS can read the password from keyring before setuid.

Okay, I think my concern here is that the code in main.c seems to be what talks to the keyring, when it should be the module in svrcore. So either the call to svrcore_setup needs to be earlier when we are before the setuid so that svrcore is contacting the keyring, or we need to think about this differently. Or I'm really misunderstanding the code ....

The order is defined in SVRCORE_CreateStdKeyringPinObj: first svrcore cache, then keyring, then pin file, then ask-password.
theoretically we should not have keyring key if a pin.txt exist. But if it happens, keyring is returned first.

We need to make sure this order is well defined in docs :)

@tbordaz Is there anything else here?

@firstyear, this PR has been on hold for a while. Sorry for that. In addition to the remarks in this PR, it also need an improvement to be usable with containers (sharing keyring). I updated the doc but not this PR.
I hope to be back on this one soon but at the moment this feature is not that urgent.

389-ds-base is moving from Pagure to Github. This means that new issues and pull requests
will be accepted only in 389-ds-base's github repository.

This pull request has been cloned to Github as issue and is available here:
- https://github.com/389ds/389-ds-base/issues/3849

If you want to continue to work on the PR, please navigate to the github issue,
download the patch from the attachments and file a new pull request.

Thank you for understanding. We apologize for all inconvenience.

Pull-Request has been closed by spichugi

3 years ago