2007-12-17 10:20:20 +03:00
#!/usr/bin/python
# Unix SMB/CIFS implementation.
2010-06-20 03:28:39 +04:00
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
2009-09-19 23:57:41 +04:00
# Copyright (C) Matthias Dieter Wallnoefer 2009
2007-12-17 10:20:20 +03:00
#
# Based on the original in EJS:
2008-01-25 03:02:13 +03:00
# Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
2007-12-17 10:20:20 +03: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/>.
#
2007-12-30 03:14:15 +03:00
""" Convenience functions for using the SAM. """
2010-04-04 05:30:03 +04:00
import dsdb
2007-12-17 10:20:20 +03:00
import samba
2007-12-18 04:21:14 +03:00
import ldb
2008-08-30 01:32:44 +04:00
import time
2009-03-12 07:20:25 +03:00
import base64
2007-12-17 10:20:20 +03:00
2008-05-22 19:42:18 +04:00
__docformat__ = " restructuredText "
2007-12-17 10:20:20 +03:00
class SamDB ( samba . Ldb ) :
2007-12-30 03:14:15 +03:00
""" The SAM database. """
2008-04-14 13:51:02 +04:00
2009-08-15 17:20:09 +04:00
def __init__ ( self , url = None , lp = None , modules_dir = None , session_info = None ,
2010-06-20 13:59:49 +04:00
credentials = None , flags = 0 , options = None , global_schema = True ,
auto_connect = True , am_rodc = False ) :
2008-04-14 13:51:02 +04:00
self . lp = lp
2010-04-20 05:48:51 +04:00
if not auto_connect :
url = None
elif url is None and lp is not None :
2010-04-04 02:30:34 +04:00
url = lp . get ( " sam database " )
2009-08-15 17:20:09 +04:00
super ( SamDB , self ) . __init__ ( url = url , lp = lp , modules_dir = modules_dir ,
session_info = session_info , credentials = credentials , flags = flags ,
options = options )
2010-04-07 14:11:12 +04:00
if global_schema :
2010-06-20 17:22:49 +04:00
dsdb . _dsdb_set_global_schema ( self )
2008-04-14 13:51:02 +04:00
2010-06-20 17:22:49 +04:00
dsdb . _dsdb_set_am_rodc ( self , am_rodc )
2010-05-17 13:49:37 +04:00
2009-08-15 17:20:09 +04:00
def connect ( self , url = None , flags = 0 , options = None ) :
2010-04-20 05:48:51 +04:00
if self . lp is not None :
url = self . lp . private_path ( url )
super ( SamDB , self ) . connect ( url = url , flags = flags ,
2009-08-15 17:20:09 +04:00
options = options )
2007-12-18 04:21:14 +03:00
2009-09-18 22:16:05 +04:00
def domain_dn ( self ) :
# find the DNs for the domain
res = self . search ( base = " " ,
scope = ldb . SCOPE_BASE ,
expression = " (defaultNamingContext=*) " ,
attrs = [ " defaultNamingContext " ] )
assert ( len ( res ) == 1 and res [ 0 ] [ " defaultNamingContext " ] is not None )
return res [ 0 ] [ " defaultNamingContext " ] [ 0 ]
def enable_account ( self , filter ) :
""" Enables an account
2007-12-17 10:20:20 +03:00
2009-09-18 22:16:05 +04:00
: param filter : LDAP filter to find the user ( eg samccountname = name )
2007-12-17 10:20:20 +03:00
"""
2009-09-18 22:16:05 +04:00
res = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
expression = filter , attrs = [ " userAccountControl " ] )
assert ( len ( res ) == 1 )
user_dn = res [ 0 ] . dn
2009-08-06 13:38:06 +04:00
userAccountControl = int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] )
2008-03-28 04:08:54 +03:00
if ( userAccountControl & 0x2 ) :
userAccountControl = userAccountControl & ~ 0x2 # remove disabled bit
if ( userAccountControl & 0x20 ) :
userAccountControl = userAccountControl & ~ 0x20 # remove 'no password required' bit
2007-12-17 10:20:20 +03:00
mod = """
dn : % s
changetype : modify
replace : userAccountControl
userAccountControl : % u
""" % (user_dn, userAccountControl)
2007-12-30 03:14:15 +03:00
self . modify_ldif ( mod )
2009-06-18 06:38:04 +04:00
2009-09-18 22:16:05 +04:00
def force_password_change_at_next_login ( self , filter ) :
""" Forces a password change at next login
2009-06-18 06:38:04 +04:00
2009-09-18 22:16:05 +04:00
: param filter : LDAP filter to find the user ( eg samccountname = name )
2009-06-18 06:38:04 +04:00
"""
2009-09-18 22:16:05 +04:00
res = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
expression = filter , attrs = [ ] )
assert ( len ( res ) == 1 )
user_dn = res [ 0 ] . dn
2009-06-18 06:38:04 +04:00
mod = """
dn : % s
changetype : modify
replace : pwdLastSet
pwdLastSet : 0
""" % (user_dn)
self . modify_ldif ( mod )
2010-06-07 20:10:28 +04:00
def newgroup ( self , groupname , groupou = None , grouptype = None ,
description = None , mailaddress = None , notes = None ) :
""" Adds a new group with additional parameters
: param groupname : Name of the new group
: param grouptype : Type of the new group
: param description : Description of the new group
: param mailaddress : Email address of the new group
: param notes : Notes of the new group
"""
2010-06-08 23:33:56 +04:00
group_dn = " CN= %s , %s , %s " % ( groupname , ( groupou or " CN=Users " ) , self . domain_dn ( ) )
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
# The new user record. Note the reliance on the SAMLDB module which
# fills in the default informations
2010-06-20 03:28:39 +04:00
ldbmessage = { " dn " : group_dn ,
2010-06-08 23:33:56 +04:00
" sAMAccountName " : groupname ,
" objectClass " : " group " }
2010-06-07 20:10:28 +04:00
2010-06-20 03:28:39 +04:00
if grouptype is not None :
2010-06-08 23:33:56 +04:00
ldbmessage [ " groupType " ] = " %d " % ( ( grouptype ) - 2 * * 32 )
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if description is not None :
ldbmessage [ " description " ] = description
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if mailaddress is not None :
ldbmessage [ " mail " ] = mailaddress
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if notes is not None :
ldbmessage [ " info " ] = notes
2010-06-07 20:10:28 +04:00
2010-06-20 13:59:49 +04:00
self . add ( ldbmessage )
2010-06-07 20:10:28 +04:00
2010-06-20 04:32:23 +04:00
def deletegroup ( self , groupname ) :
2010-06-07 20:10:28 +04:00
""" Deletes a group
: param groupname : Name of the target group
"""
groupfilter = " (&(sAMAccountName= %s )(objectCategory= %s , %s )) " % ( groupname , " CN=Group,CN=Schema,CN=Configuration " , self . domain_dn ( ) )
self . transaction_start ( )
try :
targetgroup = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
expression = groupfilter , attrs = [ ] )
if len ( targetgroup ) == 0 :
2010-06-20 04:32:23 +04:00
raise Exception ( ' Unable to find group " %s " ' % groupname )
2010-06-07 20:10:28 +04:00
assert ( len ( targetgroup ) == 1 )
2010-06-20 04:32:23 +04:00
self . delete ( targetgroup [ 0 ] . dn ) ;
2010-06-07 20:10:28 +04:00
except :
self . transaction_cancel ( )
raise
else :
self . transaction_commit ( )
2010-06-20 04:32:23 +04:00
def add_remove_group_members ( self , groupname , listofmembers ,
2010-06-07 20:10:28 +04:00
add_members_operation = True ) :
""" Adds or removes group members
: param groupname : Name of the target group
: param listofmembers : Comma - separated list of group members
: param add_members_operation : Defines if its an add or remove operation
"""
groupfilter = " (&(sAMAccountName= %s )(objectCategory= %s , %s )) " % ( groupname , " CN=Group,CN=Schema,CN=Configuration " , self . domain_dn ( ) )
groupmembers = listofmembers . split ( ' , ' )
self . transaction_start ( )
try :
targetgroup = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
expression = groupfilter , attrs = [ ' member ' ] )
if len ( targetgroup ) == 0 :
2010-06-20 04:32:23 +04:00
raise Exception ( ' Unable to find group " %s " ' % groupname )
2010-06-07 20:10:28 +04:00
assert ( len ( targetgroup ) == 1 )
modified = False
addtargettogroup = """
dn : % s
changetype : modify
""" % (str(targetgroup[0].dn))
for member in groupmembers :
targetmember = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
2010-06-08 23:33:56 +04:00
expression = " (|(sAMAccountName= %s )(CN= %s )) " % ( member , member ) , attrs = [ ] )
2010-06-07 20:10:28 +04:00
if len ( targetmember ) != 1 :
continue
if add_members_operation is True and ( targetgroup [ 0 ] . get ( ' member ' ) is None or str ( targetmember [ 0 ] . dn ) not in targetgroup [ 0 ] [ ' member ' ] ) :
modified = True
addtargettogroup + = """ add: member
member : % s
""" % (str(targetmember[0].dn))
elif add_members_operation is False and ( targetgroup [ 0 ] . get ( ' member ' ) is not None and str ( targetmember [ 0 ] . dn ) in targetgroup [ 0 ] [ ' member ' ] ) :
modified = True
addtargettogroup + = """ delete: member
member : % s
""" % (str(targetmember[0].dn))
if modified is True :
self . modify_ldif ( addtargettogroup )
except :
self . transaction_cancel ( )
raise
else :
self . transaction_commit ( )
2010-05-09 12:54:19 +04:00
def newuser ( self , username , password ,
2010-06-07 20:10:28 +04:00
force_password_change_at_next_login_req = False ,
2010-06-20 03:28:39 +04:00
useusernameascn = False , userou = None , surname = None , givenname = None , initials = None ,
profilepath = None , scriptpath = None , homedrive = None , homedirectory = None ,
jobtitle = None , department = None , company = None , description = None ,
mailaddress = None , internetaddress = None , telephonenumber = None ,
physicaldeliveryoffice = None ) :
2010-06-07 20:10:28 +04:00
""" Adds a new user with additional parameters
2009-09-18 22:16:05 +04:00
2009-09-21 01:49:05 +04:00
: param username : Name of the new user
2007-12-30 03:14:15 +03:00
: param password : Password for the new user
2009-09-21 01:49:05 +04:00
: param force_password_change_at_next_login_req : Force password change
2010-06-07 20:10:28 +04:00
: param useusernameascn : Use username as cn rather that firstname + initials + lastname
: param userou : Object container ( without domainDN postfix ) for new user
: param surname : Surname of the new user
: param givenname : First name of the new user
: param initials : Initials of the new user
: param profilepath : Profile path of the new user
: param scriptpath : Logon script path of the new user
: param homedrive : Home drive of the new user
: param homedirectory : Home directory of the new user
: param jobtitle : Job title of the new user
: param department : Department of the new user
: param company : Company of the new user
: param description : of the new user
: param mailaddress : Email address of the new user
: param internetaddress : Home page of the new user
: param telephonenumber : Phone number of the new user
: param physicaldeliveryoffice : Office location of the new user
2010-06-20 03:28:39 +04:00
"""
2010-06-07 20:10:28 +04:00
displayname = " " ;
if givenname is not None :
displayname + = givenname
if initials is not None :
displayname + = ' %s . ' % initials
if surname is not None :
displayname + = ' %s ' % surname
2010-06-20 03:28:39 +04:00
cn = username
2010-06-07 20:10:28 +04:00
if useusernameascn is None and displayname is not " " :
cn = displayname
2010-06-08 23:33:56 +04:00
user_dn = " CN= %s , %s , %s " % ( cn , ( userou or " CN=Users " ) , self . domain_dn ( ) )
2009-02-11 19:54:58 +03:00
2010-06-08 23:33:56 +04:00
# The new user record. Note the reliance on the SAMLDB module which
# fills in the default informations
2010-06-20 03:28:39 +04:00
ldbmessage = { " dn " : user_dn ,
2010-06-08 23:33:56 +04:00
" sAMAccountName " : username ,
" objectClass " : " user " }
2010-06-07 20:10:28 +04:00
2010-06-20 03:28:39 +04:00
if surname is not None :
2010-06-08 23:33:56 +04:00
ldbmessage [ " sn " ] = surname
2010-06-07 20:10:28 +04:00
2010-06-20 03:28:39 +04:00
if givenname is not None :
2010-06-08 23:33:56 +04:00
ldbmessage [ " givenName " ] = givenname
2010-06-07 20:10:28 +04:00
2010-06-20 03:28:39 +04:00
if displayname is not " " :
2010-06-08 23:33:56 +04:00
ldbmessage [ " displayName " ] = displayname
ldbmessage [ " name " ] = displayname
2010-06-07 20:10:28 +04:00
2010-06-20 03:28:39 +04:00
if initials is not None :
2010-06-08 23:33:56 +04:00
ldbmessage [ " initials " ] = ' %s . ' % initials
2010-06-07 20:10:28 +04:00
2010-06-20 03:28:39 +04:00
if profilepath is not None :
2010-06-08 23:33:56 +04:00
ldbmessage [ " profilePath " ] = profilepath
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if scriptpath is not None :
ldbmessage [ " scriptPath " ] = scriptpath
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if homedrive is not None :
ldbmessage [ " homeDrive " ] = homedrive
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if homedirectory is not None :
ldbmessage [ " homeDirectory " ] = homedirectory
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if jobtitle is not None :
ldbmessage [ " title " ] = jobtitle
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if department is not None :
ldbmessage [ " department " ] = department
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if company is not None :
ldbmessage [ " company " ] = company
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if description is not None :
ldbmessage [ " description " ] = description
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if mailaddress is not None :
ldbmessage [ " mail " ] = mailaddress
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if internetaddress is not None :
ldbmessage [ " wWWHomePage " ] = internetaddress
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if telephonenumber is not None :
ldbmessage [ " telephoneNumber " ] = telephonenumber
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
if physicaldeliveryoffice is not None :
ldbmessage [ " physicalDeliveryOfficeName " ] = physicaldeliveryoffice
2010-06-07 20:10:28 +04:00
2010-06-08 23:33:56 +04:00
self . transaction_start ( )
try :
2010-06-07 20:10:28 +04:00
self . add ( ldbmessage )
2009-02-11 19:54:58 +03:00
2009-09-19 23:57:41 +04:00
# Sets the password for it
self . setpassword ( " (dn= " + user_dn + " ) " , password ,
2009-09-21 01:49:05 +04:00
force_password_change_at_next_login_req )
2009-02-11 19:54:58 +03:00
except :
self . transaction_cancel ( )
raise
2010-04-08 23:01:17 +04:00
else :
self . transaction_commit ( )
2008-03-28 04:08:54 +03:00
2010-04-15 11:15:25 +04:00
def setpassword ( self , filter , password ,
force_change_at_next_login = False ,
username = None ) :
2009-09-18 22:16:05 +04:00
""" Sets the password for a user
2008-03-28 04:08:54 +03:00
2009-09-18 22:16:05 +04:00
Note : This call uses the " userPassword " attribute to set the password .
2009-09-19 23:57:41 +04:00
This works correctly on SAMBA 4 and on Windows DCs with
2009-09-18 22:16:05 +04:00
" 2003 Native " or higer domain function level .
2008-03-28 04:08:54 +03:00
: param filter : LDAP filter to find the user ( eg samccountname = name )
: param password : Password for the user
2010-04-04 05:30:03 +04:00
: param force_change_at_next_login : Force password change
2008-03-28 04:08:54 +03:00
"""
self . transaction_start ( )
2009-02-11 19:54:58 +03:00
try :
2009-08-15 17:20:09 +04:00
res = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
expression = filter , attrs = [ ] )
2010-04-15 11:15:25 +04:00
if len ( res ) == 0 :
2010-06-20 04:32:23 +04:00
raise Exception ( ' Unable to find user " %s " ' % ( username or filter ) )
2009-02-11 19:54:58 +03:00
assert ( len ( res ) == 1 )
user_dn = res [ 0 ] . dn
2009-09-10 02:46:51 +04:00
setpw = """
dn : % s
changetype : modify
replace : userPassword
userPassword : : % s
""" % (user_dn, base64.b64encode(password))
2009-02-11 19:54:58 +03:00
2009-09-10 02:46:51 +04:00
self . modify_ldif ( setpw )
2009-02-11 19:54:58 +03:00
2010-04-04 05:30:03 +04:00
if force_change_at_next_login :
2009-09-21 01:49:05 +04:00
self . force_password_change_at_next_login (
" (dn= " + str ( user_dn ) + " ) " )
2009-06-17 03:14:17 +04:00
2009-02-11 19:54:58 +03:00
# modify the userAccountControl to remove the disabled bit
2009-09-18 22:16:05 +04:00
self . enable_account ( filter )
2009-02-11 19:54:58 +03:00
except :
self . transaction_cancel ( )
raise
2010-04-08 23:01:17 +04:00
else :
self . transaction_commit ( )
2007-12-17 10:20:20 +03:00
2009-09-21 01:49:05 +04:00
def setexpiry ( self , filter , expiry_seconds , no_expiry_req = False ) :
2009-09-18 22:16:05 +04:00
""" Sets the account expiry for a user
2008-08-30 01:32:44 +04:00
2009-09-18 22:16:05 +04:00
: param filter : LDAP filter to find the user ( eg samccountname = name )
2008-08-30 01:32:44 +04:00
: param expiry_seconds : expiry time from now in seconds
2009-09-21 01:49:05 +04:00
: param no_expiry_req : if set , then don ' t expire password
2008-08-30 01:32:44 +04:00
"""
2009-02-11 19:54:58 +03:00
self . transaction_start ( )
try :
res = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
2009-09-21 15:53:47 +04:00
expression = filter ,
attrs = [ " userAccountControl " , " accountExpires " ] )
2009-09-21 01:49:05 +04:00
assert ( len ( res ) == 1 )
2009-09-18 22:16:05 +04:00
user_dn = res [ 0 ] . dn
2009-02-11 19:54:58 +03:00
userAccountControl = int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] )
accountExpires = int ( res [ 0 ] [ " accountExpires " ] [ 0 ] )
2009-09-21 01:49:05 +04:00
if no_expiry_req :
2009-02-11 19:54:58 +03:00
userAccountControl = userAccountControl | 0x10000
accountExpires = 0
else :
userAccountControl = userAccountControl & ~ 0x10000
2010-04-04 02:30:34 +04:00
accountExpires = samba . unix2nttime ( expiry_seconds + int ( time . time ( ) ) )
2009-02-11 19:54:58 +03:00
2009-09-18 22:16:05 +04:00
setexp = """
2009-03-12 05:13:14 +03:00
dn : % s
changetype : modify
replace : userAccountControl
userAccountControl : % u
replace : accountExpires
accountExpires : % u
2009-09-18 22:16:05 +04:00
""" % (user_dn, userAccountControl, accountExpires)
self . modify_ldif ( setexp )
2009-02-11 19:54:58 +03:00
except :
self . transaction_cancel ( )
raise
2010-04-08 23:01:17 +04:00
else :
self . transaction_commit ( )
2010-04-04 05:30:03 +04:00
def set_domain_sid ( self , sid ) :
""" Change the domain SID used by this LDB.
: param sid : The new domain sid to use .
"""
2010-06-20 17:22:49 +04:00
dsdb . _samdb_set_domain_sid ( self , sid )
2010-04-04 05:30:03 +04:00
def get_domain_sid ( self ) :
""" Read the domain SID used by this LDB.
"""
2010-06-20 17:22:49 +04:00
dsdb . _samdb_get_domain_sid ( self )
2010-04-04 05:30:03 +04:00
def set_invocation_id ( self , invocation_id ) :
""" Set the invocation id for this SamDB handle.
: param invocation_id : GUID of the invocation id .
"""
2010-06-20 17:22:49 +04:00
dsdb . _dsdb_set_ntds_invocation_id ( self , invocation_id )
2010-04-04 05:30:03 +04:00
2010-06-20 03:28:39 +04:00
def get_oid_from_attid ( self , attid ) :
2010-06-20 17:22:49 +04:00
return dsdb . _dsdb_get_oid_from_attid ( self , attid )
2010-06-20 03:28:39 +04:00
2010-04-04 05:30:03 +04:00
def get_invocation_id ( self ) :
" Get the invocation_id id "
2010-06-20 17:22:49 +04:00
return dsdb . _samdb_ntds_invocation_id ( self )
2010-04-04 05:30:03 +04:00
2010-04-20 05:48:51 +04:00
def set_ntds_settings_dn ( self , ntds_settings_dn ) :
""" Set the NTDS Settings DN, as would be returned on the dsServiceName rootDSE attribute
This allows the DN to be set before the database fully exists
: param ntds_settings_dn : The new DN to use
"""
2010-06-20 17:22:49 +04:00
dsdb . _samdb_set_ntds_settings_dn ( self , ntds_settings_dn )
2010-04-20 05:48:51 +04:00
2010-04-04 05:30:03 +04:00
invocation_id = property ( get_invocation_id , set_invocation_id )
domain_sid = property ( get_domain_sid , set_domain_sid )
def get_ntds_GUID ( self ) :
" Get the NTDS objectGUID "
2010-06-20 17:22:49 +04:00
return dsdb . _samdb_ntds_objectGUID ( self )
2010-04-04 05:30:03 +04:00
def server_site_name ( self ) :
" Get the server site name "
2010-06-20 17:22:49 +04:00
return dsdb . _samdb_server_site_name ( self )
2010-04-09 00:07:42 +04:00
def load_partition_usn ( self , base_dn ) :
2010-06-20 17:22:49 +04:00
return dsdb . _dsdb_load_partition_usn ( self , base_dn )
2010-06-20 04:32:23 +04:00
def set_schema ( self , schema ) :
self . set_schema_from_ldb ( schema . ldb )
def set_schema_from_ldb ( self , ldb ) :
2010-06-20 17:22:49 +04:00
dsdb . _dsdb_set_schema_from_ldb ( self , ldb )
2010-06-20 04:32:23 +04:00
def write_prefixes_from_schema ( self ) :
2010-06-20 17:22:49 +04:00
dsdb . _dsdb_write_prefixes_from_schema_to_ldb ( self )