From df3ee388b8e9827c5bab6258aca690e533e1ca2c Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 8 Mar 2019 09:05:20 -0500 Subject: [PATCH] tkt-78015: Switch from SSSD to NSLCD (#2687) * Switch from SSSD to NSLCD - Begin using NSLCD for authenticated LDAP. More work is needed to bring in / validate kerberos support post-sssd. --- .../migrations/0007_migrate_to_nslcd.py | 22 + gui/directoryservice/models.py | 2 +- .../etc/directoryservice/ActiveDirectory/ctl | 42 - src/freenas/etc/directoryservice/LDAP/ctl | 62 +- src/freenas/etc/ix.rc.d/ix-smbpasswd | 50 - src/freenas/etc/ix.rc.d/ix-sssd | 22 - src/freenas/etc/ix.rc.d/ix_pf_late | 2 +- src/freenas/etc/rc.conf.local | 33 +- src/freenas/etc/rc.freenas | 1 - .../active_directory/active_directory.sh | 7 - .../domain_controller/domain_controller.sh | 7 - .../local/libexec/freenas-debug/ldap/ldap.sh | 6 +- .../local/libexec/nas/generate_sssd_conf.py | 907 ------------------ .../middlewared/etc_files/local/nslcd.conf | 60 +- .../middlewared/etc_files/local/smb4.conf | 32 +- .../middlewared/etc_files/nsswitch.conf | 15 +- .../middlewared/etc_files/pam.d/pam.inc | 32 +- src/middlewared/middlewared/plugins/smb.py | 19 +- 18 files changed, 124 insertions(+), 1197 deletions(-) create mode 100644 gui/directoryservice/migrations/0007_migrate_to_nslcd.py delete mode 100755 src/freenas/etc/ix.rc.d/ix-smbpasswd delete mode 100755 src/freenas/etc/ix.rc.d/ix-sssd delete mode 100755 src/freenas/usr/local/libexec/nas/generate_sssd_conf.py diff --git a/gui/directoryservice/migrations/0007_migrate_to_nslcd.py b/gui/directoryservice/migrations/0007_migrate_to_nslcd.py new file mode 100644 index 000000000000..9e32557f3633 --- /dev/null +++ b/gui/directoryservice/migrations/0007_migrate_to_nslcd.py @@ -0,0 +1,22 @@ +import django.core.validators +from django.db import migrations, models +import freenasUI.freeadmin.models.fields + +def remove_sssd_aux_params(apps, schema_editor): + LDAP = apps.get_model('directoryservice.LDAP') + for o in LDAP.objects.all(): + if not o.ldap_anonbind: + o.ldap_auxiliary_parameters = "" + o.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('directoryservice', '0006_certificate_model'), + ] + + operations = [ + migrations.RunPython( + remove_sssd_aux_params + ), + ] diff --git a/gui/directoryservice/models.py b/gui/directoryservice/models.py index 671592a1c104..d191060fed2b 100644 --- a/gui/directoryservice/models.py +++ b/gui/directoryservice/models.py @@ -1279,7 +1279,7 @@ class LDAP(DirectoryServiceBase): ldap_auxiliary_parameters = models.TextField( verbose_name=_("Auxiliary Parameters"), blank=True, - help_text=_("These parameters are added to sssd.conf") + help_text=_("These parameters are added to nslcd.conf") ) ldap_schema = models.CharField( verbose_name=("Schema"), diff --git a/src/freenas/etc/directoryservice/ActiveDirectory/ctl b/src/freenas/etc/directoryservice/ActiveDirectory/ctl index 3395cd9037b8..88a05c67b078 100755 --- a/src/freenas/etc/directoryservice/ActiveDirectory/ctl +++ b/src/freenas/etc/directoryservice/ActiveDirectory/ctl @@ -23,31 +23,6 @@ adctl_cmd() return 0 } -sssd_running() -{ - ${service} sssd onestatus >/dev/null 2>&1 - return $? -} - -sssd_start() -{ - adctl_cmd ${service} sssd onestart - return $? -} - -sssd_stop() -{ - adctl_cmd ${service} sssd onestop - return $? -} - -sssd_restart() -{ - adctl_cmd ${service} sssd onestop - adctl_cmd ${service} sssd onestart - return $? -} - cifs_enabled() { srv_enabled cifs && return 0 @@ -123,17 +98,6 @@ adctl_start() return 1 fi - if AD_has_unix_extensions && AD_has_keytab - then - adctl_cmd ${service} ix-sssd start - if sssd_running - then - sssd_restart - else - sssd_start - fi - fi - cifs_start if ! adctl_cmd ${service} ix-activedirectory quietstart @@ -180,12 +144,6 @@ adctl_stop() cifs_stop fi - if sssd_running - then - sssd_stop - adctl_cmd ${service} ix-sssd start - fi - if [ "${prev_cifs_started}" = "0" -a "${cifs_started}" = "0" ] then adctl_cmd ${service} samba_server forcestop diff --git a/src/freenas/etc/directoryservice/LDAP/ctl b/src/freenas/etc/directoryservice/LDAP/ctl index 852a04e6c257..ecc06b50564f 100755 --- a/src/freenas/etc/directoryservice/LDAP/ctl +++ b/src/freenas/etc/directoryservice/LDAP/ctl @@ -22,31 +22,6 @@ ldapctl_cmd() return 0 } -sssd_running() -{ - ${service} sssd onestatus >/dev/null 2>&1 - return $? -} - -sssd_start() -{ - ldapctl_cmd ${service} sssd onestart - return $? -} - -sssd_stop() -{ - ldapctl_cmd ${service} sssd onestop - return $? -} - -sssd_restart() -{ - ldapctl_cmd ${service} sssd onestop - ldapctl_cmd ${service} sssd onestart - return $? -} - nslcd_running() { ${service} nslcd onestatus >/dev/null 2>&1 @@ -87,7 +62,7 @@ cifs_restart() cifs_reset() { - ldapctl_cmd ${python} ${notifier} call etc.generate smb + ldapctl_cmd ${python} ${notifier} call etc.generate smb > /dev/null if cifs_enabled; then @@ -150,26 +125,14 @@ ldapctl_start() fi fi - anonbind="$(LDAP_get ldap_anonbind)" - if [ "${anonbind}" = "0" ] - then - ldapctl_cmd ${service} ix-sssd start - if sssd_running - then - sssd_restart - else - sssd_start - fi - - elif [ "${anonbind}" = "1" ] + + ldapctl_cmd ${python} ${notifier} call etc.generate nss > /dev/null + ldapctl_cmd ${service} ix-pam quietstart + if nslcd_running then - ldapctl_cmd ${service} ix-pam quietstart - if nslcd_running - then - nslcd_restart - else - nslcd_start - fi + nslcd_restart + else + nslcd_start fi if ! ldapctl_cmd ${service} ix-ldap status @@ -181,6 +144,8 @@ ldapctl_start() if cifs_enabled && LDAP_has_samba_schema then + ldapctl_cmd ${python} ${notifier} call etc.generate smb > /dev/null + ldapctl_cmd ${python} ${notifier} call smb.store_ldap_admin_password > /dev/null cifs_restart fi @@ -200,11 +165,6 @@ ldapctl_stop() ldap_set 1 fi - if sssd_running - then - sssd_stop - ldapctl_cmd ${service} ix-sssd start - fi if nslcd_running then nslcd_stop @@ -212,7 +172,7 @@ ldapctl_stop() fi ldapctl_cmd ${service} ix-ldap forcestop - ldapctl_cmd ${service} ix-nsswitch quietstop + ldapctl_cmd ${python} ${notifier} call etc.generate nss > /dev/null ldapctl_cmd ${service} ix-pam quietstop ldapctl_cmd "${service} ix-cache quietstop &" diff --git a/src/freenas/etc/ix.rc.d/ix-smbpasswd b/src/freenas/etc/ix.rc.d/ix-smbpasswd deleted file mode 100755 index bd992d58cbe2..000000000000 --- a/src/freenas/etc/ix.rc.d/ix-smbpasswd +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh -# -# $FreeBSD$ -# - -# PROVIDE: ix-smbpasswd -# REQUIRE: samba_server - -. /etc/rc.subr - -generate_smbpasswd() -{ - local IFS="|" - local f="ldap_bindpw" - eval local $f - local sf=$(var_to_sf $f) - RO_FREENAS_CONFIG=$(ro_sqlite ${name} 2> /tmp/${name}.fail && rm /tmp/${name}.fail) - trap 'rm -f ${RO_FREENAS_CONFIG}' EXIT - - ${FREENAS_SQLITE_CMD} ${RO_FREENAS_CONFIG} " - SELECT - $sf - - FROM - services_services, - directoryservice_ldap - - WHERE ( - srv_service = 'ldap' and - srv_enable = 1 - ) - - ORDER BY - -directoryservice_ldap.id - - LIMIT 1 " | \ - while eval read $f - do - if [ -n "${ldap_bindpw}" ]; then - /usr/local/bin/smbpasswd -w "$(/usr/local/bin/midclt call pwenc.decrypt ${ldap_bindpw})" >/dev/null 2>&1 - fi - done -} - -name="ix-smbpasswd" -start_cmd='generate_smbpasswd' -stop_cmd=':' - -load_rc_config $name -run_rc_command "$1" diff --git a/src/freenas/etc/ix.rc.d/ix-sssd b/src/freenas/etc/ix.rc.d/ix-sssd deleted file mode 100755 index 28b9931fa9b3..000000000000 --- a/src/freenas/etc/ix.rc.d/ix-sssd +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# -# $FreeBSD$ -# - -# PROVIDE: ix-sssd -# REQUIRE: DAEMON -# BEFORE: sssd - -. /etc/rc.freenas - -generate_sssd_config() -{ - /usr/local/libexec/nas/generate_sssd_conf.py -} - -name="ix-sssd" -start_cmd='generate_sssd_config' -stop_cmd=':' - -load_rc_config $name -run_rc_command "$1" diff --git a/src/freenas/etc/ix.rc.d/ix_pf_late b/src/freenas/etc/ix.rc.d/ix_pf_late index 028ad6a06d4e..882f42db186d 100755 --- a/src/freenas/etc/ix.rc.d/ix_pf_late +++ b/src/freenas/etc/ix.rc.d/ix_pf_late @@ -4,7 +4,7 @@ # # PROVIDE: ix_pf_late -# REQUIRE: ix-smbpasswd +# REQUIRE: samba_server # KEYWORD: nojail shutdown . /etc/rc.subr diff --git a/src/freenas/etc/rc.conf.local b/src/freenas/etc/rc.conf.local index b000c55657cc..6d717391c9ab 100755 --- a/src/freenas/etc/rc.conf.local +++ b/src/freenas/etc/rc.conf.local @@ -444,42 +444,13 @@ _snmp_config() _ldap_config() { - # Should we ditch sssd now that we have nslcd in? TBD - sssd_enable="NO" + # Should we ditch sssd now that we have nslcd in? Yes nslcd_enable="NO" if dirsrv_enabled ldap ; then - local anonymous_bind=$(${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} " - SELECT - ldap_anonbind - FROM - directoryservice_ldap - ORDER BY - -id - LIMIT 1 - ") - if [ "${anonymous_bind}" = "1" ]; then - nslcd_enable="YES" - fi - sssd_enable="YES" + nslcd_enable="YES" fi - # This needs to go away, winbind is perfectly fine for this use case nowdays - if dirsrv_enabled activedirectory; then - local ad_unix=$(${FREENAS_SQLITE_CMD} ${FREENAS_CONFIG} " - SELECT - ad_unix_extensions - FROM - directoryservice_activedirectory - ORDER BY - -id - LIMIT 1 - ") - if [ "${ad_unix}" = "1" ]; then - sssd_enable="YES" - fi - fi - echo "sssd_enable=\"${sssd_enable}\"" echo "nslcd_enable=\"${nslcd_enable}\"" } diff --git a/src/freenas/etc/rc.freenas b/src/freenas/etc/rc.freenas index 6d71cfa3225a..c86b40073ff9 100644 --- a/src/freenas/etc/rc.freenas +++ b/src/freenas/etc/rc.freenas @@ -60,7 +60,6 @@ # # LDAP settings # -: ${SSSD_CONF:="/usr/local/etc/sssd/sssd.conf"} : ${LDAP_CONF:="/usr/local/etc/openldap/ldap.conf"} : ${CERT_FILE:="/usr/local/etc/certs/cacert.crt"} : ${LDAP_TIMEOUT:="0"} diff --git a/src/freenas/usr/local/libexec/freenas-debug/active_directory/active_directory.sh b/src/freenas/usr/local/libexec/freenas-debug/active_directory/active_directory.sh index 5582c4d7ffb6..211566793812 100755 --- a/src/freenas/usr/local/libexec/freenas-debug/active_directory/active_directory.sh +++ b/src/freenas/usr/local/libexec/freenas-debug/active_directory/active_directory.sh @@ -195,13 +195,6 @@ __EOF__ klist section_footer - # - # Dump Active Directory SSSD configuration - # - section_header "${SSSD_CONF}" - sc "${SSSD_CONF}" | grep -iv ldap_default_authtok - section_footer - # # Dump generated AD config file # diff --git a/src/freenas/usr/local/libexec/freenas-debug/domain_controller/domain_controller.sh b/src/freenas/usr/local/libexec/freenas-debug/domain_controller/domain_controller.sh index abddc5d464ea..ff377053ec25 100755 --- a/src/freenas/usr/local/libexec/freenas-debug/domain_controller/domain_controller.sh +++ b/src/freenas/usr/local/libexec/freenas-debug/domain_controller/domain_controller.sh @@ -165,13 +165,6 @@ __EOF__ klist section_footer - # - # Dump Domain Controller SSSD configuration - # - section_header "${SSSD_CONF}" - sc "${SSSD_CONF}" | grep -iv ldap_default_authtok - section_footer - # # Dump generated DC config file # diff --git a/src/freenas/usr/local/libexec/freenas-debug/ldap/ldap.sh b/src/freenas/usr/local/libexec/freenas-debug/ldap/ldap.sh index 8f2daa4fcf0a..65cbec099bd1 100755 --- a/src/freenas/usr/local/libexec/freenas-debug/ldap/ldap.sh +++ b/src/freenas/usr/local/libexec/freenas-debug/ldap/ldap.sh @@ -142,10 +142,10 @@ __EOF__ section_footer # - # Dump SSSD configuration + # Dump NSLCD configuration # - section_header "${SSSD_CONF}" - sc "${SSSD_CONF}" | grep -iv ldap_default_authtok + section_header "${NSLCD_CONF}" + sc "${NSLCD_CONF}" | grep -iv ldap_default_authtok section_footer # diff --git a/src/freenas/usr/local/libexec/nas/generate_sssd_conf.py b/src/freenas/usr/local/libexec/nas/generate_sssd_conf.py deleted file mode 100755 index 110f639181e9..000000000000 --- a/src/freenas/usr/local/libexec/nas/generate_sssd_conf.py +++ /dev/null @@ -1,907 +0,0 @@ -#!/usr/local/bin/python -from middlewared.client import Client -from middlewared.client.utils import Struct - -import os -import re -import sys -import tempfile - - -SSSD_CONFIGFILE = "/usr/local/etc/sssd/sssd.conf" - - -class SSSDBase(object): - keys = [ - 'stdin', - 'stdout', - 'stderr', - 'path', - 'domain', - 'cookie', - '_config', - ] - - def __init__(self, *args, **kwargs): - self.path = SSSD_CONFIGFILE - self.stdin = sys.stdin - self.stdout = sys.stdout - self.stderr = sys.stderr - self.cookie = None - self._config = [] - - for key in SSSDBase.keys: - if key in kwargs and kwargs[key]: - self.__dict__[key] = kwargs[key] - - def parse(self, line): - if not line: - self._config.append(line) - elif line.startswith(';'): - self._config.append(line) - elif line.startswith('#'): - self._config.append(line) - else: - parts = line.split(' ') - if len(parts) > 2: - key = parts[0] - val = ' '.join(parts[2:]) - self._config.append({key: val}) - - def get_section_type(self): - return None - - def get_header(self): - return None - - def get_config(self): - str = None - if not self.is_empty(): - for pair in self._config: - lines = [] - if isinstance(pair, dict): - for key in list(pair.keys()): - line = "%s = %s" % (key, pair[key]) - elif isinstance(pair, str): - line = "%s" % pair - lines.append(line.strip()) - str = '\n'.join(lines) - return "%s" % str - - def generate_header(self): - if not self.is_empty(): - print(self.get_header(), file=self.stdout) - - def generate_config(self): - if not self.is_empty(): - for pair in self._config: - try: - for key in list(pair.keys()): - print("%s = %s" % (key, pair[key]), file=self.stdout) - except: - print("%s" % pair, file=self.stdout) - - def is_empty(self): - ret = True - if len(self._config): - ret = False - return ret - - def __str__(self): - out = None - - if self.is_empty(): - out = self.get_header() - else: - lines = [] - header = self.get_header() - if header: - lines.append(header.strip()) - for pair in self._config: - if isinstance(pair, dict): - for key in list(pair.keys()): - line = "%s = %s" % (key, pair[key]) - elif isinstance(pair, str): - line = "%s" % pair - lines.append(line.strip()) - out = '\n'.join(lines) - - return "%s\n" % out - - def __len__(self): - len = 0 - if not self.is_empty(): - for pair in self._config: - len += 1 - return len - - def __delitem__(self, name): - pass - - -class SSSDSectionBase(SSSDBase): - def section_types(self): - return ('nss', 'pam', 'sudo', 'ssh') - - def add_newline(self): - self._config.append('\n') - - def add_comment(self, comment, delim='#'): - self._config.append('%s %s' % (delim, comment.strip())) - - def __setattr__(self, name, value): - if name in self.keys: - super(SSSDSectionBase, self).__setattr__(name, value) - else: - i = 0 - found = False - length = len(self._config) - - while i < length: - pair = self._config[i] - if isinstance(pair, dict): - if name in pair: - pair[name] = value - found = True - i += 1 - - if not found: - self._config.append({name: value}) - - def __getattr__(self, name): - attr = None - if name in self.keys: - attr = super(SSSDSectionBase, self).__getattr__(name) - else: - for pair in self._config: - if isinstance(pair, dict) and name in pair: - attr = pair[name] - return attr - - def __setitem__(self, name, value): - return self.__setattr__(name, value) - - def __getitem__(self, name): - return self.__getattr__(name) - - def __iter__(self): - for pair in self._config: - if isinstance(pair, dict): - yield list(pair.keys())[0] - else: - yield pair - - def get_header(self): - return "[%s]" % self.get_section_type() - - -class SSSDSectionNULL(SSSDSectionBase): - def __str__(self): - if self.is_empty(): - return "" - else: - return super(SSSDSectionNULL, self).__str__() - - -class SSSDSectionSSSD(SSSDSectionBase): - def get_section_type(self): - return "sssd" - - def add_domain(self, domain): - if not self.domains: - self.domains = domain - else: - domains = [] - self_domains = self.domains.split(',') - for d in self_domains: - d = d.strip() - domains.append(d) - if d == domain: - return - domains.append(domain) - self.domains = ','.join(domains) - - def remove_domain(self, domain): - domains = [] - self_domains = self.domains.split(',') - for d in self_domains: - d = d.strip() - if d == domain: - continue - else: - domains.append(domain) - self.domains = ','.join(domains) - - def get_domains(self): - domains = [] - self_domains = self.domains.split(',') - for s in self_domains: - s = s.strip() - domains.append(s) - return domains - - def add_service(self, service): - if service not in self.section_types(): - return - if not self.services: - self.services = service - else: - services = [] - self_services = self.services.split(',') - for s in self_services: - s = s.strip() - services.append(s) - if s == service: - return - services.append(service) - self.services = ','.join(services) - - def remove_service(self, service): - services = [] - self_services = self.services.split(',') - for s in self_services: - s = s.strip() - if s == service: - continue - else: - services.append(service) - self.services = ','.join(services) - - def get_services(self): - services = [] - self_services = self.services.split(',') - for s in self_services: - s = s.strip() - services.append(s) - return services - - -class SSSDServiceSectionBase(SSSDSectionBase): - pass - - -class SSSDSectionNSS(SSSDServiceSectionBase): - def get_section_type(self): - return "nss" - - -class SSSDSectionPAM(SSSDServiceSectionBase): - def get_section_type(self): - return "pam" - - -class SSSDSectionSUDO(SSSDServiceSectionBase): - def get_section_type(self): - return "sudo" - - -class SSSDSectionSSH(SSSDServiceSectionBase): - def get_section_type(self): - return "ssh" - - -class SSSDSectionDomain(SSSDSectionBase): - def __init__(self, *args, **kwargs): - super(SSSDSectionDomain, self).__init__(*args, **kwargs) - - self.domain = None - if args and args[0]: - parts = args[0].split('/') - if parts and len(parts) > 1: - self.domain = parts[1] - - def is_empty(self): - if not self.domain: - return True - return super(SSSDSectionDomain, self).is_empty() - - def get_section_type(self): - if self.domain: - return "domain/%s" % self.domain - return super(SSSDSectionDomain, self).get_section_type() - - -class SSSDSection(object): - def __new__(cls, *args, **kwargs): - obj = None - if not args: - return SSSDSectionNULL(*args, **kwargs) - - section = args[0] - if (section.lower() == 'sssd'): - obj = SSSDSectionSSSD(*args, **kwargs) - elif (section.lower() == 'nss'): - obj = SSSDSectionNSS(*args, **kwargs) - elif (section.lower() == 'pam'): - obj = SSSDSectionPAM(*args, **kwargs) - elif (section.lower() == 'sudo'): - obj = SSSDSectionSUDO(*args, **kwargs) - elif (section.lower() == 'ssh'): - obj = SSSDSectionSSH(*args, **kwargs) - elif (section.lower().startswith('domain')): - obj = SSSDSectionDomain(*args, **kwargs) - else: - obj = SSSDSectionNULL(*args, **kwargs) - - return obj - - -class SSSDSectionContainer(object): - def __init__(self): - super(SSSDSectionContainer, self).__init__() - self.sections = [] - - def __setattr__(self, name, value): - if name != 'sections': - self.sections.append({name: value}) - else: - super(SSSDSectionContainer, self).__setattr__(name, value) - - def __setitem__(self, name, value): - if name != 'sections': - for section in self.sections: - key = list(section.keys())[0] - if key == name: - section[key] = value - return - self.sections.append({name: value}) - self.order() - - else: - super(SSSDSectionContainer, self).__setitem__(name, value) - - def __getattr__(self, name): - if name != 'sections': - section = None - for s in self.sections: - key = list(s.keys())[0] - if key == name: - section = s[key] - return section - - else: - return super(SSSDSectionContainer, self).__getattr__(name) - - def __getitem__(self, name): - return self.__getattr__(name) - - def __delitem__(self, name): - i = 0 - while i < len(self.sections): - s = self.sections[i] - key = list(s.keys())[0] - if key == name: - del self.sections[i] - break - i += 1 - - def __iter__(self): - for section in self.sections: - yield section[list(section.keys())[0]] - - def keys(self): - keys = [] - for section in self.sections: - keys.append(list(section.keys())[0]) - return keys - - def order(self): - section_types = ['sssd', 'nss', 'pam', 'sudo', 'ssh', 'domain'] - - sections = [] - for st in section_types: - for s in self.sections: - if st in s: - sections.append({st: s[st]}) - elif st == 'domain': - key = list(s.keys())[0] - if key.startswith('domain'): - sections.append({key: s[key]}) - - self.sections = sections - - -class SSSDConf(SSSDBase): - def __init__(self, *args, **kwargs): - super(SSSDConf, self).__init__(*args, **kwargs) - self.sections = SSSDSectionContainer() - - if 'parse' in kwargs: - self.parse = kwargs.pop('parse') - - self.parse() - - def add_sssd_section(self): - if not self.sections['sssd']: - self.sections['sssd'] = SSSDSectionSSSD() - self.sections['sssd'].config_file_version = 2 - self.sections['sssd'].full_name_format = r"%2$s\%1$s" - self.sections['sssd'].re_expression = r"(((?P[^\\]+)\\(?P.+$))" \ - r"|((?P[^@]+)@(?P.+$))|(^(?P[^@\\]+)$))" - - def add_nss_section(self): - if not self.sections['nss']: - self.sections['nss'] = SSSDSectionNSS() - self.sections['sssd'].add_service('nss') - - def add_pam_section(self): - if not self.sections['pam']: - self.sections['pam'] = SSSDSectionPAM() - self.sections['sssd'].add_service('pam') - - def merge_config(self, sc): - domains = self.sections['sssd'].get_domains() - services = self.sections['sssd'].get_services() - - domains_override = False - - for s in sc.sections: - st = s.get_section_type() - self_s = self.sections[st] - - if st.startswith('domain'): - if self_s: - for var in s: - if s[var]: - self_s[var] = s[var] - - else: - self.sections[st] = s - - if s.domain not in domains: - domains.append(s.domain) - - else: - if self_s: - if st == 'sssd' and s.domains: - domains_override = True - - for var in s: - if s[var]: - self_s[var] = s[var] - - else: - self.sections[st] = s - - if st not in services: - services.append(st) - - for s in services: - self.sections['sssd'].add_service(s) - if not domains_override: - for d in domains: - self.sections['sssd'].add_domain(d) - - def num_sections(self): - lines = [] - - with open(self.path, 'r') as f: - lines = f.readlines() - - nsections = 0 - for line in lines: - line = line.strip() - - if line.startswith('['): - nsections += 1 - - return nsections - - def parse(self): - nsections = self.num_sections() - - with open(self.path, "r") as f: - lines = f.readlines() - - section = None - - if not nsections: - self.add_sssd_section() - - cookie = 'domain/%s' % self.cookie - section = SSSDSectionDomain(cookie) - - self.sections[cookie] = section - self.sections['sssd'].add_domain(self.cookie) - self.sections['sssd'].add_newline() - - for line in lines: - line = line.strip() - if not line: - continue - - if line.startswith('['): - r = re.match('^\[([^\s]+)\]', line) - if r: - s = r.group(1) - section = SSSDSection(s) - self.sections[s] = section - if not s.startswith('domain'): - if not self.sections['sssd']: - self.add_sssd_section() - self.sections['sssd'].add_service(s) - - elif section is not None: - section.parse(line) - - def keys(self): - keys = [] - for section in self.sections: - key = section.get_section_type() - if key: - keys.append(key) - return keys - - def __iter__(self): - for key in list(self.keys()): - yield self.sections[key] - - def __getitem__(self, name): - if name != 'sections': - return self.sections[name] - else: - return super(SSSDConf, self).__getitem__(name) - - def __setitem__(self, name, value): - if name != 'sections': - self.sections[name] = value - else: - super(SSSDConf, self).__setitem__(name, value) - - def __str__(self): - str = None - for s in self.sections: - if not str: - str = "%s" % s - else: - str = "%s\n%s" % (str, s) - return "%s" % str - - def save(self, path=None): - stdout = self.stdout - if path: - stdout = open(path, "w") - - print(self.__str__(), file=stdout) - - if path: - stdout.close() - os.chmod(path, 0o600) - - -def activedirectory_has_unix_extensions(client): - ad_unix_extensions = False - - try: - ad_unix_extensions = client.call('datastore.query', 'directoryservice.activedirectory', None, {'get': True})['ad_unix_extensions'] - except: - pass - - return ad_unix_extensions - - -def sssd_mkdir(dir): - try: - os.makedirs(dir) - except Exception: - pass - - -def sssd_setup(): - sssd_mkdir("/var/log/sssd") - sssd_mkdir("/var/db/sss") - sssd_mkdir("/var/db/sss_mc") - sssd_mkdir("/var/run/sss/private") - - if os.path.exists(SSSD_CONFIGFILE): - os.chown(SSSD_CONFIGFILE, 0, 0) - os.chmod(SSSD_CONFIGFILE, 0o600) - - -def add_ldap_section(client, sc): - ldap = Struct(client.call('datastore.query', 'directoryservice.ldap', None, {'get': True})) - - ldap_hostname = ldap.ldap_hostname.upper() - parts = ldap_hostname.split('.') - ldap_hostname = parts[0] - - ldap_cookie = ldap_hostname - ldap_domain = 'domain/%s' % ldap_cookie - - ldap_section = None - for key in list(sc.keys()): - if key == ldap_domain: - ldap_section = sc[key] - break - - if not ldap_section: - ldap_section = SSSDSectionDomain(ldap_domain) - ldap_section.description = ldap_cookie - if ldap_section.description != ldap_cookie: - return - - ldap_defaults = [ - {'enumerate': 'true'}, - {'cache_credentials': 'true'}, - {'id_provider': 'ldap'}, - {'auth_provider': 'ldap'}, - {'chpass_provider': 'ldap'}, - {'ldap_schema': 'rfc2307bis'}, - {'ldap_force_upper_case_realm': 'true'}, - {'use_fully_qualified_names': 'false'} - ] - - for d in ldap_defaults: - key = list(d.keys())[0] - if key not in ldap_section: - setattr(ldap_section, key, d[key]) - - ldap_section.ldap_schema = ldap.ldap_schema - ldap_section.ldap_uri = "%s://%s" % ( - "ldaps" if ldap.ldap_ssl == 'on' else "ldap", - ldap.ldap_hostname - ) - ldap_section.ldap_search_base = ldap.ldap_basedn - - if ldap.ldap_usersuffix: - ldap_section.ldap_user_search_base = "%s,%s" % ( - ldap.ldap_usersuffix, ldap.ldap_basedn - ) - else: - ldap_section.ldap_user_search_base = "%s%s" % ( - ldap.ldap_basedn, - "?subtree?(objectclass=posixAccount)" - ) - - if ldap.ldap_groupsuffix: - ldap_section.ldap_group_search_base = "%s,%s" % ( - ldap.ldap_groupsuffix, ldap.ldap_basedn - ) - else: - ldap_section.ldap_group_search_base = "%s%s" % ( - ldap.ldap_basedn, - "?subtree?(objectclass=posixGroup)" - ) - - if ldap.ldap_sudosuffix: - if not sc['sudo']: - sc['sudo'] = SSSDSectionSUDO() - sc['sssd'].add_service('sudo') - ldap_section.sudo_provider = 'ldap' - ldap_section.ldap_sudo_search_base = "%s,%s" % ( - ldap.ldap_sudosuffix, - ldap.ldap_basedn - ) - - if ldap.ldap_ssl == 'on': - cert = client.call('certificate.query', [('id', '=', ldap.ldap_certificate.id)], {'get': True}) - certpath = cert['certificate_path'] - if certpath: - ldap_section.ldap_tls_cacert = certpath - - elif ldap.ldap_ssl == 'start_tls': - ldap_section.tls_reqcert = 'allow' - cert = client.call('certificate.query', [('id', '=', ldap.ldap_certificate.id)], {'get': True}) - certpath = cert['certificate_path'] - if certpath: - ldap_section.ldap_tls_cacert = certpath - ldap_section.ldap_id_use_start_tls = 'true' - - ldap_save = ldap - ldap = Struct(client.call('notifier.directoryservice', 'LDAP')) - - if ldap.keytab_file and ldap.keytab_principal: - ldap_section.auth_provider = 'krb5' - ldap_section.chpass_provider = 'krb5' - ldap_section.ldap_sasl_mech = 'GSSAPI' - ldap_section.ldap_sasl_authid = ldap.keytab_principal - ldap_section.ldap_krb5_keytab = ldap.keytab_file - ldap_section.krb5_server = ldap.krb_kdc - ldap_section.krb5_realm = ldap.krb_realm - ldap_section.krb5_canonicalize = 'false' - - else: - ldap_section.ldap_default_bind_dn = ldap.binddn - ldap_section.ldap_default_authtok_type = 'password' - ldap_section.ldap_default_authtok = ldap.bindpw - - homedir_path = None - - try: - for share in client.call('datastore.query', 'sharing.CIFS_Share'): - share = Struct(share) - if share.cifs_home and share.cifs_path: - homedir_path = share.cifs_path - break - except: - pass - - if homedir_path: - sssd_mkdir("%s/%s" % (homedir_path, ldap_cookie)) - ldap_section.override_homedir = "%s/%%d/%%u" % homedir_path - - sc[ldap_domain] = ldap_section - sc['sssd'].add_domain(ldap_cookie) - sc['sssd'].add_newline() - - ldap = ldap_save - if ldap.ldap_auxiliary_parameters: - path = tempfile.mktemp(dir='/tmp') - with open(path, 'wb+') as f: - f.write(ldap.ldap_auxiliary_parameters.encode('utf-8')) - - aux_sc = SSSDConf(path=path, cookie=sc.cookie) - os.unlink(path) - - sc.merge_config(aux_sc) - - -def add_activedirectory_section(client, sc): - activedirectory = Struct(client.call('datastore.query', 'directoryservice.activedirectory', None, {'get': True})) - ad = client.call('notifier.directoryservice', 'AD') - use_ad_provider = False - - ad_cookie = ad.netbiosname - ad_domain = 'domain/%s' % ad_cookie - - ad_section = None - for key in list(sc.keys()): - if key == ad_domain: - ad_section = sc[key] - break - - if not ad_section: - ad_section = SSSDSectionDomain(ad_domain) - ad_section.description = ad_cookie - if ad_section.description != ad_cookie: - return - - ad_defaults = [ - {'enumerate': 'true'}, - {'id_provider': 'ldap'}, - {'auth_provider': 'ldap'}, - {'access_provider': 'ldap'}, - {'chpass_provider': 'ldap'}, - {'ldap_schema': 'rfc2307bis'}, - {'ldap_user_object_class': 'person'}, - {'ldap_user_name': 'msSFU30Name'}, - {'ldap_user_uid_number': 'uidNumber'}, - {'ldap_user_gid_number': 'gidNumber'}, - {'ldap_user_home_directory': 'unixHomeDirectory'}, - {'ldap_user_shell': 'loginShell'}, - {'ldap_user_principal': 'userPrincipalName'}, - {'ldap_group_object_class': 'group'}, - {'ldap_group_name': 'msSFU30Name'}, - {'ldap_group_gid_number': 'gidNumber'}, - {'ldap_force_upper_case_realm': 'true'}, - {'use_fully_qualified_names': 'true'} - ] - - __, hostname, __ = os.uname()[0:3] - - if ad.keytab_file and ad.keytab_principal: - use_ad_provider = True - - if use_ad_provider: - for d in ad_defaults: - key = list(d.keys())[0] - if key.startswith("ldap_") and key in d: - del d[key] - elif key.endswith("_provider"): - d[key] = 'ad' - - ad_section.ad_hostname = hostname - ad_section.ad_domain = ad.domainname - ad_section.ldap_id_mapping = False - - for d in ad_defaults: - if not d: - continue - key = list(d.keys())[0] - if key not in ad_section: - setattr(ad_section, key, d[key]) - - if activedirectory.ad_use_default_domain: - ad_section.use_fully_qualified_names = 'false' - - try: - for share in client.call('datastore.query', 'sharing.cifs_share'): - share = Struct(share) - if share.cifs_home and share.cifs_path: - homedir_path = "%s/%%d/%%u" % share.cifs_path - ad_section.override_homedir = homedir_path - break - - except Exception: - pass - - if use_ad_provider: - pass - -# ad_section.auth_provider = 'krb5' -# ad_section.chpass_provider = 'krb5' -# ad_section.ldap_sasl_mech = 'GSSAPI' -# ad_section.ldap_sasl_authid = ad.keytab_principal -# ad_section.krb5_server = ad.krb_kdc -# ad_section.krb5_realm = ad.krb_realm -# ad_section.krb5_canonicalize = 'false' - - else: - ad_section.ldap_uri = "ldap://%s" % ad.dchost - ad_section.ldap_search_base = ad.basedn - - ad_section.ldap_default_bind_dn = ad.binddn - ad_section.ldap_default_authtok_type = 'password' - ad_section.ldap_default_authtok = ad.bindpw - - sc[ad_domain] = ad_section - sc['sssd'].add_domain(ad_cookie) - sc['sssd'].add_newline() - - -def get_activedirectory_cookie(client): - cookie = '' - - if client.call('notifier.common', 'system', 'activedirectory_enabled'): - smb = Struct(client.call('smb.config')) - cookie = smb.netbiosname.upper() - parts = cookie.split('.') - cookie = parts[0] - - return cookie - - -def get_ldap_cookie(client): - cookie = '' - - if client.call('notifier.common', 'system', 'ldap_enabled'): - ldap = Struct(client.call('datastore.query', 'directoryservice.ldap', None, {'get': True})) - cookie = ldap.ldap_hostname.upper() - parts = cookie.split('.') - cookie = parts[0] - - return cookie - - -def get_directoryservice_cookie(client): - if client.call('notifier.common', 'system', 'activedirectory_enabled'): - return get_activedirectory_cookie(client) - if client.call('notifier.common', 'system', 'ldap_enabled'): - return get_ldap_cookie(client) - - return None - - -def main(): - client = Client() - sssd_conf = None - - if client.call('notifier.common', 'system', 'ldap_enabled') and client.call('notifier.common', 'system', 'ldap_anonymous_bind'): - sys.exit(1) - - sssd_setup() - if os.path.exists(SSSD_CONFIGFILE): - sssd_conf = SSSD_CONFIGFILE - - cookie = get_directoryservice_cookie(client) - if not cookie: - sys.exit(1) - - def nullfunc(): - pass - sc = SSSDConf(client=client, path=sssd_conf, parse=nullfunc, cookie=cookie) - - sc.add_sssd_section() - sc.add_nss_section() - sc.add_pam_section() - - if client.call('notifier.common', 'system', 'activedirectory_enabled') and activedirectory_has_unix_extensions(client): - add_activedirectory_section(client, sc) - if client.call('notifier.common', 'system', 'ldap_enabled'): - add_ldap_section(client, sc) - - sc.save(SSSD_CONFIGFILE) - - -if __name__ == '__main__': - main() diff --git a/src/middlewared/middlewared/etc_files/local/nslcd.conf b/src/middlewared/middlewared/etc_files/local/nslcd.conf index 8b09a570584b..88b1b99edba6 100644 --- a/src/middlewared/middlewared/etc_files/local/nslcd.conf +++ b/src/middlewared/middlewared/etc_files/local/nslcd.conf @@ -1,42 +1,46 @@ +# +# NSLCD.CONF(5) The configuration file for LDAP nameservice daemon +# $FreeBSD$ +# <% - def safe_call(*args): - try: - val = middleware.call_sync(*args) - except: - val = False - return val - - ldap = safe_call('datastore.query', 'directoryservice.LDAP') - if ldap and ldap[0]: - ldap = ldap[0] + ldap = middleware.call_sync('datastore.config', 'directoryservice.LDAP') + if ldap: certpath = None if ldap['ldap_certificate']: - cert = safe_call('certificate.query', [('id', '=', ldap['ldap_certificate']['id'])], {'get': True}) + cert = middleware.call_sync('certificate.query', [('id', '=', ldap['ldap_certificate']['id'])], {'get': True}) if cert: certpath = cert['certificate_path'] else: ldap = None - ldap_enabled = safe_call('notifier.common', 'system', 'ldap_enabled') + ldap_enabled = ldap['ldap_enable'] ldap_uri = "%s://%s" % ("ldaps" if ldap['ldap_ssl'] == "on" else "ldap", ldap['ldap_hostname']) %> -% if ldap_enabled and ldap: -uri ${ldap_uri} -base ${ldap['ldap_basedn']} - % if ldap['ldap_ssl'] in ('start_tls', 'on'): -ssl ${ldap['ldap_ssl']} - % if certpath: -tls_cacert ${certpath} - % endif -tls_reqcert allow - % endif -scope sub -timelimit 30 -bind_timelimit 30 -map passwd loginShell /bin/sh - % if ldap['ldap_auxiliary_parameters']: -${ldap['ldap_auxiliary_parameters']} +% if ldap_enabled: + uri ${ldap_uri} + base ${ldap['ldap_basedn']} + % if ldap['ldap_ssl'] in ('start_tls', 'on'): + ssl ${ldap['ldap_ssl']} + % if certpath: + tls_cacert ${certpath} % endif + tls_reqcert allow + % endif + % if ldap['ldap_binddn'] and ldap['ldap_bindpw']: + binddn ${ldap['ldap_binddn']} + bindpw ${ldap['ldap_bindpw']} + % endif + % if ldap['ldap_kerberos_principal'] and ldap['ldap_kerberos_realm']: + sasl_mech GSSAPI + sasl_realm ${ldap['ldap_kerberos_realm']} + % endif + scope sub + timelimit 30 + bind_timelimit 30 + map passwd loginShell /bin/sh + % if ldap['ldap_auxiliary_parameters']: + ${ldap['ldap_auxiliary_parameters']} + % endif % endif diff --git a/src/middlewared/middlewared/etc_files/local/smb4.conf b/src/middlewared/middlewared/etc_files/local/smb4.conf index 58a0df198d4d..2262f5dae0a3 100644 --- a/src/middlewared/middlewared/etc_files/local/smb4.conf +++ b/src/middlewared/middlewared/etc_files/local/smb4.conf @@ -177,19 +177,21 @@ for hostname in db['ldap']['ldap_hostname'].split() ]) else: - ldap_pdb_backend = f'ldapsam:\"{"ldaps" if db["ldap"]["ldap_ssl"] is not "off" else "ldap"}\"//{db["ldap"]["ldap_hostname"]}' + ldap_pdb_backend = f"ldapsam:{'ldaps' if db['ldap']['ldap_ssl'] == 'on' else 'ldap'}://{db['ldap']['ldap_hostname']}" + pc.update({'passdb backend': ldap_pdb_backend}) pc.update({'ldap admin dn': db['ldap']['ldap_binddn']}) pc.update({'ldap suffix': db['ldap']['ldap_basedn']}) - pc.update({'ldap user suffix': db['ldap']['ldap_user_suffix']}) - pc.update({'ldap group suffix': db['ldap']['ldap_group_suffix']}) - pc.update({'ldap machine suffix': db['ldap']['ldap_machine_suffix']}) + pc.update({'ldap user suffix': db['ldap']['ldap_usersuffix']}) + pc.update({'ldap group suffix': db['ldap']['ldap_groupsuffix']}) + pc.update({'ldap machine suffix': db['ldap']['ldap_machinesuffix']}) + pc.update({'workgroup': db['cifs']['workgroup'].upper()}) if db['ldap']['ldap_ssl'] is not "start_tls": pc.update({'ldap ssl': 'off'}) pc.update({'ldap replication sleep': '1000'}) pc.update({'ldap passwd sync': 'Yes'}) pc.update({'ldapsam trusted': 'Yes'}) - pc.update({'ldap workgroup': db['ldap']['ldap_workgroup']}) + pc.update({'domain logons': 'Yes'}) elif db['role'] == "domain_controller": @@ -212,8 +214,11 @@ return pc - def add_idmap_domain(pc,db, domain, backend): - if domain == pc['workgroup']: + def add_idmap_domain(pc, db, domain, backend): + """ + Preparation for eventual support of trusted domains in AD. + """ + if domain == pc['workgroup'] and db['ad']['ad_enable']: idmap = middleware.call_sync('datastore.config', f'directoryservice.idmap_{db["ad"]["ad_idmap_backend"]}') else: idmap = middleware.call_sync('datastore.config', f'directoryservice.idmap_{backend}') @@ -241,6 +246,15 @@ pc.update({f'idmap config {domain}: unix_nss_info': idmap['idmap_ad_unix_nss_info']}) if idmap['idmap_ad_unix_nss_info']: pc.update({f'idmap config {domain}: unix_primary_group': idmap['idmap_ad_unix_primary_group']}) + + elif backend == "ldap": + if idmap['idmap_ldap_ldap_base_dn']: + pc.update({f'idmap config {domain}: ldap_base_dn': idmap['idmap_ldap_ldap_base_dn']}) + if idmap['idmap_ldap_ldap_user_dn']: + pc.update({f'idmap config {domain}: ldap_user_dn': idmap['idmap_ldap_ldap_user_dn']}) + if idmap['idmap_ldap_ldap_url']: + pc.update({f'idmap config {domain}: ldap_url': idmap['idmap_ldap_ldap_url']}) + elif backend == "rfc2307": pc.update({f'idmap config {domain}: ldap_server': idmap['idmap_rfc2307_ldap_server']}) if idmap['idmap_rfc2307_ldap_server_url']: @@ -271,9 +285,9 @@ def add_idmap_params(pc, db): if db['role'] == "ad_member": add_idmap_domain(pc, db, pc['workgroup'], db['ad']['ad_idmap_backend']) - elif db['role'] == "ldap_member": + elif db['role'] == 'ldap_member': add_idmap_domain(pc, db, pc['workgroup'], db['ldap']['ldap_idmap_backend']) - elif db['role'] == "standalone": + else: add_default_idmap_domain(pc, db) return pc diff --git a/src/middlewared/middlewared/etc_files/nsswitch.conf b/src/middlewared/middlewared/etc_files/nsswitch.conf index a8534e57e0f5..20fa8a800dd7 100644 --- a/src/middlewared/middlewared/etc_files/nsswitch.conf +++ b/src/middlewared/middlewared/etc_files/nsswitch.conf @@ -21,17 +21,8 @@ sudoers = ['files'] if ldap_enabled: - ldap_anonymous_bind = safe_call('notifier.common', 'system', 'ldap_anonymous_bind') - ldap_sudo_configured = safe_call('notifier.common', 'system', 'ldap_sudo_configured') - - if ldap_anonymous_bind: - group.append('ldap') - passwd.append('ldap') - else: - group.append('sss') - passwd.append('sss') - if ldap_sudo_configured: - sudoers.append('sss') + group.append('ldap') + passwd.append('ldap') if nis_enabled: group.append('nis') @@ -51,4 +42,4 @@ shells: files services: files protocols: files rpc: files -sudoers: ${' '.join(sudoers)} +sudoers: files diff --git a/src/middlewared/middlewared/etc_files/pam.d/pam.inc b/src/middlewared/middlewared/etc_files/pam.d/pam.inc index 4327147b8f71..93222020685d 100644 --- a/src/middlewared/middlewared/etc_files/pam.d/pam.inc +++ b/src/middlewared/middlewared/etc_files/pam.d/pam.inc @@ -3,7 +3,6 @@ def __init__(self, **kwargs): self.middleware = kwargs.get('middleware') self.pam_mkhomedir = "/usr/local/lib/pam_mkhomedir.so" - self.pam_sss = "/usr/local/lib/pam_sss.so" self.pam_ldap = "/usr/local/lib/pam_ldap.so" self.pam_winbind = "/usr/local/lib/pam_winbind.so" @@ -78,29 +77,18 @@ def enabled(self): return self.safe_call('notifier.common', 'system', 'ldap_enabled') - def __anonymous_bind(self): - return self.safe_call('notifier.common', 'system', 'ldap_anonymous_bind') - def pam_auth(self): - module = self.pam_sss - args = ["ignore_authinfo_unavail", "quiet"] - - if self.__anonymous_bind(): - module = self.pam_ldap - args = ["try_first_pass", "ignore_unknown_user", - "ignore_authinfo_unavail", "no_warn"] + module = self.pam_ldap + args = ["try_first_pass", "ignore_unknown_user", + "ignore_authinfo_unavail", "no_warn"] module_args = " ".join(args) return f"auth\t\tsufficient\t{module}\t{module_args}" def pam_account(self): - module = self.pam_sss - args = ["ignore_authinfo_unavail", "quiet"] - - if self.__anonymous_bind(): - module = self.pam_ldap - args = ["ignore_unknown_user", "ignore_authinfo_unavail", "no_warn"] + module = self.pam_ldap + args = ["ignore_unknown_user", "ignore_authinfo_unavail", "no_warn"] module_args = " ".join(args) @@ -110,13 +98,9 @@ return f"session\t\trequired\t{self.pam_mkhomedir}" def pam_password(self): - module = self.pam_sss - args = ["use_authtok", "quiet"] - - if self.__anonymous_bind(): - module = self.pam_ldap - args = ["use_authtok", "ignore_unknown_user", - "ignore_authinfo_unavail", "no_warn"] + module = self.pam_ldap + args = ["use_authtok", "ignore_unknown_user", + "ignore_authinfo_unavail", "no_warn"] module_args = " ".join(args) diff --git a/src/middlewared/middlewared/plugins/smb.py b/src/middlewared/middlewared/plugins/smb.py index 43036809ab46..ab65c59c82f6 100644 --- a/src/middlewared/middlewared/plugins/smb.py +++ b/src/middlewared/middlewared/plugins/smb.py @@ -3,7 +3,7 @@ accepts, private, CRUDService) from middlewared.async_validators import check_path_resides_within_volume from middlewared.service_exception import CallError -from middlewared.utils import Popen +from middlewared.utils import Popen, run import asyncio import codecs @@ -100,6 +100,23 @@ def check_codec(encoding): return encodings + @private + async def store_ldap_admin_password(self): + """ + This is required if the LDAP directory service is enabled. The ldap admin dn and + password are stored in private/secrets.tdb file. + """ + ldap = await self.middleware.call('datastore.config', 'directoryservice.ldap') + if not ldap['ldap_enable']: + return True + + set_pass = await run(['usr/local/bin/smbpasswd', '-w', ldap['ldap_bindpw']], check=False) + if set_pass.returncode != 0: + self.logger.debug(f"Failed to set set ldap bindpw in secrets.tdb: {set_pass.stdout.decode()}") + return False + + return True + @accepts(Dict( 'smb_update', Str('netbiosname'),