2010-12-08 08:20:54 +11:00
# common functions for samba-tool python commands
#
# Copyright Andrew Tridgell 2010
2011-09-07 11:11:38 -04:00
# Copyright Giampaolo Lauria 2011 <lauria2@yahoo.com>
2010-12-08 08:20:54 +11:00
#
# 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 <http://www.gnu.org/licenses/>.
#
2011-09-07 11:11:38 -04:00
import re
2011-07-26 13:46:17 +10:00
from samba . dcerpc import nbt
from samba . net import Net
2022-02-24 11:05:57 +13:00
from samba . netcmd import CommandError
2019-03-15 14:20:05 +01:00
import ldb
2011-07-26 13:46:17 +10:00
2020-11-19 11:19:04 +13:00
# In MS AD, setting a timeout to '(never)' corresponds to this value
NEVER_TIMESTAMP = int ( - 0x8000000000000000 )
2022-02-24 11:05:57 +13:00
def _get_user_realm_domain ( user , sam = None ) :
2018-04-11 16:32:19 +12:00
r """ get the realm or the domain and the base user
2011-09-07 11:11:38 -04:00
from user like :
* username
* DOMAIN \username
* username @REALM
2022-02-24 11:05:57 +13:00
A SamDB object can also be passed in to check
our domain or realm against the obtained ones .
2011-09-07 11:11:38 -04:00
"""
baseuser = user
m = re . match ( r " ( \ w+) \\ ( \ w+$) " , user )
if m :
domain = m . group ( 1 )
baseuser = m . group ( 2 )
2022-02-24 11:05:57 +13:00
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 = " "
2011-09-07 11:11:38 -04:00
m = re . match ( r " ( \ w+)@( \ w+) " , user )
if m :
baseuser = m . group ( 1 )
realm = m . group ( 2 )
2022-02-24 11:05:57 +13:00
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 ( ) , " " )
2011-09-07 11:11:38 -04:00
2010-12-08 08:20:54 +11:00
def netcmd_dnsname ( lp ) :
2023-12-14 15:14:27 +13:00
""" return the full DNS name of our own host. Used as a default
for hostname when running status queries """
2010-12-08 08:20:54 +11:00
return lp . get ( ' netbios name ' ) . lower ( ) + " . " + lp . get ( ' realm ' ) . lower ( )
2011-07-26 13:46:17 +10:00
2011-11-22 22:26:06 +01:00
def netcmd_finddc ( lp , creds , realm = None ) :
2023-12-14 15:14:27 +13:00
""" Return domain-name of a writable/ldap-capable DC for the default
2011-11-22 22:26:06 +01:00
domain ( parameter " realm " in smb . conf ) unless another realm has been
2023-12-14 15:14:27 +13:00
specified as argument """
2011-07-26 13:46:17 +10:00
net = Net ( creds = creds , lp = lp )
2011-11-22 22:26:06 +01:00
if realm is None :
realm = lp . get ( ' realm ' )
cldap_ret = net . finddc ( domain = realm ,
2018-07-30 18:16:12 +12:00
flags = nbt . NBT_SERVER_LDAP | nbt . NBT_SERVER_DS | nbt . NBT_SERVER_WRITABLE )
2011-07-26 13:46:17 +10:00
return cldap_ret . pdc_dns_name
2011-11-22 22:26:38 +01:00
def netcmd_get_domain_infos_via_cldap ( lp , creds , address = None ) :
2023-12-14 15:14:27 +13:00
""" Return domain information (CLDAP record) of the ldap-capable
DC with the specified address """
2011-11-22 22:26:38 +01:00
net = Net ( creds = creds , lp = lp )
cldap_ret = net . finddc ( address = address ,
2018-07-30 18:16:12 +12:00
flags = nbt . NBT_SERVER_LDAP | nbt . NBT_SERVER_DS )
2011-11-22 22:26:38 +01:00
return cldap_ret
2019-03-15 14:20:05 +01:00
def is_printable_attr_val ( val ) :
import unicodedata
2023-06-06 13:17:58 +02:00
# The value must be convertible to a string value.
2019-03-15 14:20:05 +01:00
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
2020-11-19 11:19:04 +13:00
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 )
2020-11-19 11:24:25 +13:00
def attr_default ( msg , attrname , default ) :
2023-12-14 15:14:27 +13:00
""" get an attribute from a ldap msg with a default """
2020-11-19 11:24:25 +13:00
if attrname in msg :
return msg [ attrname ] [ 0 ]
return default