# common functions for samba-tool python commands # # Copyright Andrew Tridgell 2010 # Copyright Giampaolo Lauria 2011 # # This program is free software; you can redistribute it and/or modify # it under the terms of the 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import re from samba.dcerpc import nbt from samba.net import Net from samba.netcmd import CommandError import ldb # In MS AD, setting a timeout to '(never)' corresponds to this value NEVER_TIMESTAMP = int(-0x8000000000000000) def _get_user_realm_domain(user, sam=None): r""" get the realm or the domain and the base user from user like: * username * DOMAIN\username * username@REALM A SamDB object can also be passed in to check our domain or realm against the obtained ones. """ baseuser = user m = re.match(r"(\w+)\\(\w+$)", user) if m: domain = m.group(1) baseuser = m.group(2) if sam is not None: our_domain = sam.domain_netbios_name() if domain.lower() != our_domain.lower(): raise CommandError(f"Given domain '{domain}' does not match " f"our domain '{our_domain}'!") return (baseuser.lower(), "", domain.upper()) realm = "" m = re.match(r"(\w+)@(\w+)", user) if m: baseuser = m.group(1) realm = m.group(2) if sam is not None: our_realm = sam.domain_dns_name() our_realm_initial = our_realm.split('.', 1)[0] if realm.lower() != our_realm_initial.lower(): raise CommandError(f"Given realm '{realm}' does not match our " f"realm '{our_realm}'!") return (baseuser.lower(), realm.upper(), "") def netcmd_dnsname(lp): '''return the full DNS name of our own host. Used as a default for hostname when running status queries''' return lp.get('netbios name').lower() + "." + lp.get('realm').lower() def netcmd_finddc(lp, creds, realm=None): '''Return domain-name of a writable/ldap-capable DC for the default domain (parameter "realm" in smb.conf) unless another realm has been specified as argument''' net = Net(creds=creds, lp=lp) if realm is None: realm = lp.get('realm') cldap_ret = net.finddc(domain=realm, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS | nbt.NBT_SERVER_WRITABLE) return cldap_ret.pdc_dns_name def netcmd_get_domain_infos_via_cldap(lp, creds, address=None): '''Return domain information (CLDAP record) of the ldap-capable DC with the specified address''' net = Net(creds=creds, lp=lp) cldap_ret = net.finddc(address=address, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS) return cldap_ret def is_printable_attr_val(val): import unicodedata # The value must be convertible to a string value. try: str_val = str(val) except: return False # Characters of the Unicode Character Category "C" ("Other") are # supposed to be not printable. The category "C" includes control # characters, format specifier and others. for c in str_val: if unicodedata.category(c)[0] == 'C': return False return True def get_ldif_for_editor(samdb, msg): # Copy the given message, because we do not # want to modify the original message. m = ldb.Message() m.dn = msg.dn for k in msg.keys(): if k == "dn": continue vals = msg[k] m[k] = vals need_base64 = False for v in vals: if is_printable_attr_val(v): continue need_base64 = True break if not need_base64: m[k].set_flags(ldb.FLAG_FORCE_NO_BASE64_LDIF) result_ldif = samdb.write_ldif(m, ldb.CHANGETYPE_NONE) return result_ldif def timestamp_to_mins(timestamp_str): """Converts a timestamp in -100 nanosecond units to minutes""" # treat a timestamp of 'never' the same as zero (this should work OK for # most settings, and it displays better than trying to convert # -0x8000000000000000 to minutes) if int(timestamp_str) == NEVER_TIMESTAMP: return 0 else: return abs(int(timestamp_str)) / (1e7 * 60) def timestamp_to_days(timestamp_str): """Converts a timestamp in -100 nanosecond units to days""" return timestamp_to_mins(timestamp_str) / (60 * 24) def attr_default(msg, attrname, default): '''get an attribute from a ldap msg with a default''' if attrname in msg: return msg[attrname][0] return default