2007-12-17 10:20:20 +03:00
#!/usr/bin/python
# Unix SMB/CIFS implementation.
2008-01-25 03:02:13 +03:00
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
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
2008-12-21 05:08:14 +03:00
import glue
2007-12-18 04:21:14 +03:00
import ldb
2008-04-14 13:51:02 +04:00
from samba . idmap import IDmapDB
import pwd
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-04-07 14:11:12 +04:00
credentials = None , flags = 0 , options = None , global_schema = False ) :
2008-04-14 13:51:02 +04:00
self . lp = lp
2009-08-15 17:20:09 +04:00
if url is 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 :
glue . dsdb_set_global_schema ( self )
2008-04-14 13:51:02 +04:00
2009-08-15 17:20:09 +04:00
def connect ( self , url = None , flags = 0 , options = None ) :
super ( SamDB , self ) . connect ( url = self . lp . private_path ( url ) , flags = flags ,
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-04-04 05:30:03 +04:00
def newuser ( self , username , unixname , password ,
force_password_change_at_next_login_req = False ) :
2009-09-18 22:16:05 +04:00
""" Adds a new user
2009-09-19 23:57:41 +04:00
Note : This call adds also the ID mapping for winbind ; therefore it works
* only * on SAMBA 4.
2007-12-30 03:14:15 +03:00
2009-09-21 01:49:05 +04:00
: param username : Name of the new user
: param unixname : Name of the unix user to map to
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
2007-12-30 03:14:15 +03:00
"""
2007-12-17 10:20:20 +03:00
self . transaction_start ( )
2008-04-14 13:51:02 +04:00
try :
2009-08-15 17:20:09 +04:00
user_dn = " CN= %s ,CN=Users, %s " % ( username , self . domain_dn ( ) )
2009-02-11 19:54:58 +03:00
2009-09-18 22:16:05 +04:00
# The new user record. Note the reliance on the SAMLDB module which
# fills in the default informations
2009-02-11 19:54:58 +03:00
self . add ( { " dn " : user_dn ,
" sAMAccountName " : username ,
" objectClass " : " user " } )
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-09-19 23:57:41 +04:00
# Gets the user SID (for the account mapping setup)
2009-02-11 19:54:58 +03:00
res = self . search ( user_dn , scope = ldb . SCOPE_BASE ,
expression = " objectclass=* " ,
attrs = [ " objectSid " ] )
assert len ( res ) == 1
user_sid = self . schema_format_value ( " objectSid " , res [ 0 ] [ " objectSid " ] [ 0 ] )
2008-04-14 13:51:02 +04:00
2009-02-11 19:54:58 +03:00
try :
idmap = IDmapDB ( lp = self . lp )
user = pwd . getpwnam ( unixname )
2009-09-18 22:16:05 +04:00
2009-02-11 19:54:58 +03:00
# setup ID mapping for this UID
idmap . setup_name_mapping ( user_sid , idmap . TYPE_UID , user [ 2 ] )
except KeyError :
pass
except :
self . transaction_cancel ( )
raise
2008-03-28 04:08:54 +03:00
self . transaction_commit ( )
2010-04-04 05:30:03 +04:00
def setpassword ( self , filter , password , force_change_at_next_login = False ) :
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 = [ ] )
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
2007-12-17 10:20:20 +03:00
self . transaction_commit ( )
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-03-01 07:04:23 +03:00
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 .
"""
dsdb . samdb_set_domain_sid ( self , sid )
def get_domain_sid ( self ) :
""" Read the domain SID used by this LDB.
"""
dsdb . samdb_get_domain_sid ( self )
def set_invocation_id ( self , invocation_id ) :
""" Set the invocation id for this SamDB handle.
: param invocation_id : GUID of the invocation id .
"""
dsdb . dsdb_set_ntds_invocation_id ( self , invocation_id )
def get_invocation_id ( self ) :
" Get the invocation_id id "
return dsdb . samdb_ntds_invocation_id ( self )
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 "
return dsdb . samdb_ntds_objectGUID ( self )
def server_site_name ( self ) :
" Get the server site name "
return dsdb . samdb_server_site_name ( self )