def editShare(name, path, comment, browseable, permAll, usergroups, users): samba = SambaConf() samba.addShare(name, path, comment, browseable, permAll, usergroups, users, mod=True) return samba.save()
def isSamba4Provisioned(): """ @return: check if Samba4 has been provisioned already @rtype: boolean """ global_info = SambaConf().getGlobalInfo() if global_info["realm"] and global_info["server role"]: return True return False
def backupShare(share, media, login): """ Launch as a background process the backup of a share """ r = AF().log(PLUGIN_NAME, AA.SAMBA4_BACKUP_SHARE, [(share, AT.SHARE), (login, AT.USER)], media) config = BasePluginConfig("base") cmd = os.path.join(config.backuptools, "backup.sh") if share == "homes": # FIXME: Maybe we should have a configuration directive to tell that # all users home are stored into /home savedir = "/home/" else: savedir = SambaConf().getContent(share, "path") # Run backup process in background shlaunchBackground( cmd + " " + share + " " + savedir + " " + config.backupdir + " " + login + " " + media + " " + config.backuptools, "backup share " + share, progressBackup) r.commit() return os.path.join(config.backupdir, "%s-%s-%s" % (login, share, strftime("%Y%m%d")))
class SambaAD: """ Handle sam.ldb: users and computers """ def __init__(self): self.smb_conf = SambaConf() self.samdb_url = os.path.join(self.smb_conf.private_dir(), 'sam.ldb') self.samdb = SamDB(url=self.samdb_url, session_info=system_session(), lp=LoadParm()) # v Users --------------------------------------------------------------------- def isUserEnabled(self, username): search_filter = "(&(objectClass=user)(sAMAccountName=%s))" % ldb.binary_encode( to_str(username)) userlist = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=search_filter, attrs=["userAccountControl"]) if not userlist: return False uac_flags = int(userlist[0]["userAccountControl"][0]) return 0 == (uac_flags & dsdb.UF_ACCOUNTDISABLE) def existsUser(self, username): return to_str(username) in self._samba_tool("user list") def updateUserPassword(self, username, password): self._samba_tool("user setpassword %s --newpassword='******'" % (username, password)) return True def createUser(self, username, password, given_name=None, surname=None): cmd = "user create %s '%s'" % (username, password) if given_name and surname: cmd += " --given-name='%s' --surname='%s'" % (to_str(given_name), to_str(surname)) self._samba_tool(cmd) return True def createGroup(self, name, description): cmd = 'group add ' + name if description: cmd += ' --description=' + description self._samba_tool(cmd) return True def enableUser(self, username): self._samba_tool("user enable %s" % username) return True def disableUser(self, username): self._samba_tool("user disable %s" % username) return True def deleteUser(self, username): self._samba_tool("user delete %s" % username) return True def _samba_tool(self, cmd): samba_tool = os.path.join(self.smb_conf.prefix, "bin/samba-tool") cmd = samba_tool + " " + cmd exit_code, std_out, std_err = shlaunch(cmd) if exit_code != 0: error_msg = "Error processing `%s`:\n" % cmd if std_err: error_msg += "\n".join(std_err) if std_out: error_msg += "\n".join(std_out) logger.error(error_msg) raise SambaToolException(error_msg) return std_out # v Machines ------------------------------------------------------------------ def _listComputersInContainer(self, container_dn, name_suffix=''): computers = self.samdb.search( base=container_dn, scope=ldb.SCOPE_ONELEVEL, expression="(objectClass=computer)", attrs=["name", "description", "operatingSystem"]) res = [] if computers: for computer in computers: description = computer.get("description", computer.get("operatingSystem", "")) res.append({ "name": str(computer["name"]) + name_suffix, "description": str(description), "enabled": 1 # TODO: get what the state actually is }) return res def listDomainMembers(self): """ Returns list of Computer objects description @return: list of dicts with Computer name and description @rtype: list """ dcs = self._listComputersInContainer( "OU=Domain Controllers,%s" % self.samdb.domain_dn(), ' (dc)') computers = self._listComputersInContainer("CN=Computers,%s" % self.samdb.domain_dn()) return dcs + computers def deleteMachine(self, name): # TODO return True def getMachine(self, name): container_dn = "CN=Computers,%s" % self.samdb.domain_dn() computers = self.samdb.search( base=container_dn, scope=ldb.SCOPE_ONELEVEL, expression="(&(objectClass=computer)(name=%s))" % name, attrs=["description", "operatingSystem"]) if not computers or len(computers) < 1: return {'name': name, 'description': 'Unknown', 'enabled': False} c = computers[0] description = str(c.get('description', c.get('operatingSystem'))) return {'name': name, 'description': description, 'enabled': True} def editMachine(self, name, description, enabled): # TODO return True
def getSamba4GlobalInfo(): """ @return: values from [global] section in smb.conf @rtype: dict """ return SambaConf().getGlobalInfo()
def saveOptions(options): return SambaConf().saveOptions(options)
def __init__(self): self.smb_conf = SambaConf() self.samdb_url = os.path.join(self.smb_conf.private_dir(), 'sam.ldb') self.samdb = SamDB(url=self.samdb_url, session_info=system_session(), lp=LoadParm())
def getShare(name): return SambaConf().getDetailedShare(name)
def addShare(name, path, comment, browseable, permAll, usergroups, users): samba = SambaConf() samba.addShare(name, path, comment, browseable, permAll, usergroups, users) samba.save() return name
def getShares(): return SambaConf().getDetailedShares()
def getACLOnShare(name): if name: return SambaConf().getACLOnShare(name) else: return []
def deleteShare(name, file): samba = SambaConf() samba.delShare(name, file) return samba.save()
class SambaAD: """ Handle sam.ldb: users and computers """ def __init__(self): self.smb_conf = SambaConf() self.samdb_url = os.path.join(self.smb_conf.private_dir(), 'sam.ldb') self.samdb = SamDB(url=self.samdb_url, session_info=system_session(), lp=LoadParm()) # v Users --------------------------------------------------------------------- def isUserEnabled(self, username): if type(username) != type(''): raise TypeError("username is expected to be string") search_filter = "(&(objectClass=user)(sAMAccountName=%s))" % ldb.binary_encode( username) userlist = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression=search_filter, attrs=["userAccountControl"]) if not userlist: return False uac_flags = int(userlist[0]["userAccountControl"][0]) return 0 == (uac_flags & dsdb.UF_ACCOUNTDISABLE) def existsUser(self, username): return username in self._samba_tool("user list") def updateUserPassword(self, username, password): self._samba_tool("user setpassword %s --newpassword='******'" % (username, password)) return True def createUser(self, username, password, given_name=None, surname=None): cmd = "user create %s '%s'" % (username, password) if given_name and surname: cmd += " --given-name='%s' --surname='%s'" % (given_name, surname) self._samba_tool(cmd) return True def createGroup(self, name, description): cmd = 'group add ' + name if description: cmd += ' --description=' + description self._samba_tool(cmd) return True def enableUser(self, username): self._samba_tool("user enable %s" % username) return True def disableUser(self, username): self._samba_tool("user disable %s" % username) return True def deleteUser(self, username): self._samba_tool("user delete %s" % username) return True def _samba_tool(self, cmd): samba_tool = os.path.join(self.smb_conf.prefix, "bin/samba-tool") cmd = samba_tool + " " + cmd exit_code, std_out, std_err = shlaunch(cmd) if exit_code != 0: error_msg = "Error processing `%s`:\n" % cmd if std_err: error_msg += "\n".join(std_err) if std_out: error_msg += "\n".join(std_out) logger.error(error_msg) raise SambaToolException(error_msg) return std_out # v Machines ------------------------------------------------------------------ def _listComputersInContainer(self, container_dn, name_suffix=''): computers = self.samdb.search(base=container_dn, scope=ldb.SCOPE_ONELEVEL, expression="(objectClass=computer)", attrs=["name", "description", "operatingSystem"]) res = [] if computers: for computer in computers: try: description = computer["description"] except KeyError: description = computer["operatingSystem"] res.append({ "name": str(computer["name"]) + name_suffix, "description": str(description), "enabled": 1 # TODO: get what the state actually is }) return res def listDomainMembers(self): """ Returns list of Computer objects description @return: list of dicts with Computer name and description @rtype: list """ dcs = self._listComputersInContainer( "OU=Domain Controllers,%s" % self.samdb.domain_dn(), ' (dc)') computers = self._listComputersInContainer( "CN=Computers,%s" % self.samdb.domain_dn()) return dcs + computers def deleteMachine(self, name): # TODO return True def getMachine(self, name): container_dn = "CN=Computers,%s" % self.samdb.domain_dn() computers = self.samdb.search(base=container_dn, scope=ldb.SCOPE_ONELEVEL, expression="(&(objectClass=computer)(name=%s))" % name, attrs=["description", "operatingSystem"]) if not computers or len(computers) < 1: return {'name': name, 'description': 'Unknown', 'enabled': False} c = computers[0] description = str(c.get('description', c.get('operatingSystem'))) return {'name': name, 'description': description, 'enabled': True} def editMachine(self, name, description, enabled): # TODO return True
def isAuthorizedSharePath(path): return not path or SambaConf().isAuthorizedSharePath(path)
def provision_samba4(mode, realm, admin, admin_password, iface, dns_ip, dns_forwarder): if mode not in ['dc', 'bdc', 'robdc']: fail_provisioning_samba4( "We can only provision samba4 as Domain Controller") print('Provisionning samba mode: %s, realm: %s, admin_password:%s' % (mode, realm, admin_password)) bdc = False rodc = False if mode == 'bdc': mode = 'dc' bdc = True elif mode == 'robdc': mode = 'dc' bdc = True rodc = True samba = SambaConf() params = {'realm': realm, 'prefix': samba.prefix, 'role': mode, 'adminpass': admin_password, 'workgroup': samba.workgroupFromRealm(realm), 'dns_forwarder': dns_forwarder} def provision_domain(): print("Provisioning domain") cmd = ("%(prefix)s/bin/samba-tool domain provision" " --adminpass='******'" " --domain='%(workgroup)s'" " --workgroup='%(workgroup)s'" " --realm='%(realm)s'" " --use-xattr=yes" " --use-rfc2307" " --server-role='%(role)s'" % params) shlaunch(cmd) def join_domain(): print('Joining domain') if bdc is True: if rodc is True: mode_bdc = 'RODC' else: mode_bdc = 'DC' else: fail_provisioning_samba4('Unsupported DC mode.') par = {'realm': realm, 'mode_bdc': mode_bdc, 'prefix': samba.prefix, 'username': admin, 'adminpass': admin_password} cmd = ("%(prefix)s/bin/samba-tool domain join %(realm)s %(mode_bdc)s" " --username='******'" " --realm='%(realm)s'" " --password='******'" % par) # shlaunch(cmd) run(cmd, shell=True) def write_config_files(): print("provision: domain_provision_cb") netbios_domain_name = gethostname() samba.writeSambaConfig(mode, netbios_domain_name, realm, DESCRIPTION, dns_forwarder=dns_forwarder) samba.writeKrb5Config(realm) def disable_password_complexity(): print("provision: disable_password_complexity") cmd = ("%s/bin/samba-tool domain passwordsettings set" " --complexity=off" " --min-pwd-length=0" " --min-pwd-age=0" " --max-pwd-age=999" % samba.prefix) shlaunch(cmd) def check_ldap_is_running(): # Number of times we will check whether ldap is running on 389 port max_checkings_ldap_running = 10 tries = 1 while tries < max_checkings_ldap_running: print("Checking ldap is running") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) result = sock.connect_ex(('127.0.0.1', 389)) if result == 0: return True sleep(SLEEP_TIME) tries += 1 fail_provisioning_samba4("Ldap is not running after waiting long time") def openldap_smb5pwd_config(): with open('/etc/openldap/slapd.conf', 'r') as h: for line in h: if line.lstrip().startswith('moduleload') and 'smbk5pwd' in line: return state = 0 conf_file = '/etc/openldap/slapd.conf' backup_file = backup(conf_file) with open(conf_file, 'w') as file_out, open(backup_file, 'r') as file_in: for line in file_in: file_out.write(line) if state == 0: if line.lstrip().startswith('moduleload'): file_out.write('moduleload smbk5pwd.so\n') state = 1 elif state == 1: if line.lstrip().startswith('database'): file_out.write('overlay smbk5pwd\n') state = 2 # TODO: fix this strange chmodS shlaunch("chown root:ldap /etc/openldap/slapd.conf") def reconfig_ldap_service(): print("Reconfiguring ldap service") openldap_smb5pwd_config() should_reconfing = True f = None try: f = open('/etc/sysconfig/slapd', 'r') for line in f: if line.lstrip().startswith('SLAPDURLLIST='): should_reconfing = False if should_reconfing: f.close() f = open('/etc/sysconfig/slapd', 'a') f.write(os.linesep) f.write('SLAPDURLLIST="ldap://127.0.0.1"') f.write(os.linesep) f.close() shlaunch("systemctl restart slapd") except Exception as e: print(">>>> %s" % e) fail_provisioning_samba4(e.message) sleep(SLEEP_TIME) check_ldap_is_running() def clean_etc_host(): cmd = "sed -i -e 's/.*%s.*//' /etc/hosts" % gethostname() shlaunch(cmd) def configure_network(): def configure_ntpd(): shlaunch("systemctl stop ntpd") state = 0 for line in fileinput.input('/etc/ntp.conf', inplace=1): # replace first server if line.startswith('fudge'): print(line,) state = 1 if line.startswith('server') and state == 1: print('server %s' % dns_ip) state = 2 else: print line, shlaunch("ntpdate %s" % dns_ip, False, subprocess.PIPE) shlaunch("systemctl start ntpd") def update_resolvconf(): with open('/etc/dhclient-enter-hooks', 'w') as f: fic = """make_resolv_conf() { echo "nameserver %s" > /etc/resolv.conf echo "nameserver 127.0.0.1" >> /etc/resolv.conf echo "search %s" >> /etc/resolv.conf }""" % (dns_ip, realm) f.write(fic) def configure_shorewall(): print("Configure shorewall") src = os.path.join(os.getcwd(), 'templates', 'shorewall_macro.Samba4AD') dst = os.path.join('/etc/shorewall/', 'macro.Samba4AD') shutil.copy(src, dst) os.chmod(dst, 0o600) zones = get_zones('lan') for zone in zones: add_rule('Samba4AD/ACCEPT', zone, "fw") shlaunch("systemctl restart shorewall") def configure_dhcp(): subnet = getSubnet(network) if not subnet: addSubnet(network, netmask, realm) setSubnetDescription(network, 'AD network') setSubnetAuthoritative(network) setSubnetOption(network, 'domain-name', '\"' + realm + '\"') setSubnetOption(network, 'domain-name-servers', addr) start = IP(ip.net().int() + 100) end = IP(start.int() + 99) addPool(network, 'ADpool', str(start), str(end)) shlaunch("systemctl restart dhcpd") print("### Done configure_dhcp") if_detail = netifaces.ifaddresses(iface)[netifaces.AF_INET][0] addr = if_detail['addr'] netmask = if_detail['netmask'] ip = IP(addr).make_net(netmask) network = str(ip.net()) netmask = ip.prefixlen() if not bdc: configure_dhcp() else: configure_ntpd() configure_shorewall() print("### Done configure_shorewall") def start_samba4_service(): print("Starting samba service") shlaunch("systemctl restart samba") sleep(SLEEP_TIME) def start_s4sync_service(): print("Starting s4sync daemon") shlaunch("systemctl start s4sync") # Clean up previous provisions if os.path.exists(samba.smb_conf_path): os.unlink(samba.smb_conf_path) if os.path.exists(os.path.join(samba.db_dir, 'private/sam.ldb')): for root, dirs, files in os.walk(os.path.join(samba.db_dir, 'private/')): for f in files: os.unlink(os.path.join(root, f)) for d in dirs: shutil.rmtree(os.path.join(root, d)) write_config_files() print("### Done write_config_files") if not bdc: provision_domain() print("### Done provision_domain") disable_password_complexity() print("### Done disable_password_complexity") else: clean_etc_host() join_domain() reconfig_ldap_service() print("### Done reconfig_ldap_service") configure_network() start_samba4_service() print("### Done start_samba4_service") start_s4sync_service() print("### Done start_s4sync_service")