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
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. """
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
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
2007-12-18 04:21:28 +03:00
def __init__ ( self , url = None , session_info = None , credentials = None ,
modules_dir = None , lp = None ) :
2007-12-30 03:14:15 +03:00
""" Open the Sam Database.
: param url : URL of the database .
"""
2008-04-14 13:51:02 +04:00
self . lp = lp
2007-12-18 04:21:28 +03:00
super ( SamDB , self ) . __init__ ( session_info = session_info , credentials = credentials ,
modules_dir = modules_dir , lp = lp )
2008-12-21 05:08:14 +03:00
glue . dsdb_set_global_schema ( self )
2007-12-18 04:21:28 +03:00
if url :
self . connect ( url )
2008-04-14 13:51:02 +04:00
else :
self . connect ( lp . get ( " sam database " ) )
def connect ( self , url ) :
2008-12-21 05:08:14 +03:00
super ( SamDB , self ) . connect ( self . lp . private_path ( url ) )
2007-12-18 04:21:14 +03:00
2007-12-17 10:20:20 +03:00
def add_foreign ( self , domaindn , sid , desc ) :
""" Add a foreign security principle. """
add = """
dn : CN = % s , CN = ForeignSecurityPrincipals , % s
objectClass : top
objectClass : foreignSecurityPrincipal
description : % s
2009-03-12 07:13:23 +03:00
""" % (sid, domaindn, desc)
2007-12-17 10:20:20 +03:00
# deliberately ignore errors from this, as the records may
# already exist
for msg in self . parse_ldif ( add ) :
self . add ( msg [ 1 ] )
2009-02-11 20:31:52 +03:00
def add_stock_foreign_sids ( self ) :
domaindn = self . domain_dn ( )
self . add_foreign ( domaindn , " S-1-5-7 " , " Anonymous " )
self . add_foreign ( domaindn , " S-1-1-0 " , " World " )
self . add_foreign ( domaindn , " S-1-5-2 " , " Network " )
self . add_foreign ( domaindn , " S-1-5-18 " , " System " )
self . add_foreign ( domaindn , " S-1-5-11 " , " Authenticated Users " )
2007-12-17 10:20:20 +03:00
def enable_account ( self , user_dn ) :
2007-12-30 03:14:15 +03:00
""" Enable an account.
2007-12-17 10:20:20 +03:00
: param user_dn : Dn of the account to enable .
"""
2008-03-28 04:08:54 +03:00
res = self . search ( user_dn , ldb . SCOPE_BASE , None , [ " userAccountControl " ] )
2007-12-17 10:20:20 +03:00
assert len ( res ) == 1
2008-03-28 04:08:54 +03:00
userAccountControl = res [ 0 ] [ " userAccountControl " ] [ 0 ]
userAccountControl = int ( userAccountControl )
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 )
2007-12-17 10:20:20 +03:00
2008-08-01 22:47:22 +04:00
def domain_dn ( self ) :
# find the DNs for the domain and the domain users group
res = self . search ( " " , scope = ldb . SCOPE_BASE ,
expression = " (defaultNamingContext=*) " ,
attrs = [ " defaultNamingContext " ] )
assert ( len ( res ) == 1 and res [ 0 ] [ " defaultNamingContext " ] is not None )
return res [ 0 ] [ " defaultNamingContext " ] [ 0 ]
2007-12-30 03:14:15 +03:00
def newuser ( self , username , unixname , password ) :
""" add a new user record.
: param username : Name of the new user .
: param unixname : Name of the unix user to map to .
: param password : Password for the new user
"""
2007-12-17 10:20:20 +03:00
# connect to the sam
self . transaction_start ( )
2008-04-14 13:51:02 +04:00
try :
2009-02-11 19:54:58 +03:00
domain_dn = self . domain_dn ( )
assert ( domain_dn is not None )
user_dn = " CN= %s ,CN=Users, %s " % ( username , domain_dn )
#
# the new user record. note the reliance on the samdb module to
# fill in a sid, guid etc
#
# now the real work
self . add ( { " dn " : user_dn ,
" sAMAccountName " : username ,
" userPassword " : password ,
" objectClass " : " user " } )
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 )
# setup ID mapping for this UID
idmap . setup_name_mapping ( user_sid , idmap . TYPE_UID , user [ 2 ] )
except KeyError :
pass
# modify the userAccountControl to remove the disabled bit
self . enable_account ( user_dn )
except :
self . transaction_cancel ( )
raise
2008-03-28 04:08:54 +03:00
self . transaction_commit ( )
def setpassword ( self , filter , password ) :
""" Set a password on a user record
: param filter : LDAP filter to find the user ( eg samccountname = name )
: param password : Password for the user
"""
# connect to the sam
self . transaction_start ( )
2009-02-11 19:54:58 +03:00
try :
# find the DNs for the domain
res = self . search ( " " , scope = ldb . SCOPE_BASE ,
expression = " (defaultNamingContext=*) " ,
attrs = [ " defaultNamingContext " ] )
assert ( len ( res ) == 1 and res [ 0 ] [ " defaultNamingContext " ] is not None )
domain_dn = res [ 0 ] [ " defaultNamingContext " ] [ 0 ]
assert ( domain_dn is not None )
res = self . search ( domain_dn , scope = ldb . SCOPE_SUBTREE ,
expression = filter ,
attrs = [ ] )
assert ( len ( res ) == 1 )
user_dn = res [ 0 ] . dn
setpw = """
2009-03-12 05:13:14 +03:00
dn : % s
changetype : modify
replace : userPassword
userPassword : % s
2009-03-12 07:13:23 +03:00
""" % (user_dn, password)
2009-02-11 19:54:58 +03:00
self . modify_ldif ( setpw )
# modify the userAccountControl to remove the disabled bit
self . enable_account ( user_dn )
except :
self . transaction_cancel ( )
raise
2007-12-17 10:20:20 +03:00
self . transaction_commit ( )
2007-12-17 14:07:51 +03:00
def set_domain_sid ( self , sid ) :
2007-12-30 03:14:15 +03:00
""" Change the domain SID used by this SamDB.
: param sid : The new domain sid to use .
"""
2008-12-21 05:08:14 +03:00
glue . samdb_set_domain_sid ( self , sid )
2007-12-17 10:20:20 +03:00
2007-12-17 14:07:51 +03:00
def attach_schema_from_ldif ( self , pf , df ) :
2008-12-21 05:08:14 +03:00
glue . dsdb_attach_schema_from_ldif_file ( self , pf , df )
2008-01-25 00:08:39 +03:00
def set_invocation_id ( self , invocation_id ) :
2009-02-24 03:02:26 +03:00
""" Set the invocation id for this SamDB handle.
: param invocation_id : GUID of the invocation id .
"""
glue . dsdb_set_ntds_invocation_id ( self , invocation_id )
2008-08-30 01:32:44 +04:00
def setexpiry ( self , user , expiry_seconds , noexpiry ) :
""" Set the password expiry for a user
: param expiry_seconds : expiry time from now in seconds
: param noexpiry : if set , then don ' t expire password
"""
2009-02-11 19:54:58 +03:00
self . transaction_start ( )
try :
res = self . search ( base = self . domain_dn ( ) , scope = ldb . SCOPE_SUBTREE ,
expression = ( " (samAccountName= %s ) " % user ) ,
attrs = [ " userAccountControl " , " accountExpires " ] )
assert len ( res ) == 1
userAccountControl = int ( res [ 0 ] [ " userAccountControl " ] [ 0 ] )
accountExpires = int ( res [ 0 ] [ " accountExpires " ] [ 0 ] )
if noexpiry :
userAccountControl = userAccountControl | 0x10000
accountExpires = 0
else :
userAccountControl = userAccountControl & ~ 0x10000
accountExpires = glue . unix2nttime ( expiry_seconds + int ( time . time ( ) ) )
mod = """
2009-03-12 05:13:14 +03:00
dn : % s
changetype : modify
replace : userAccountControl
userAccountControl : % u
replace : accountExpires
accountExpires : % u
2009-03-12 07:13:23 +03:00
""" % (res[0].dn, userAccountControl, accountExpires)
2009-02-11 19:54:58 +03:00
# now change the database
self . modify_ldif ( mod )
except :
self . transaction_cancel ( )
raise
2008-08-30 01:32:44 +04:00
self . transaction_commit ( ) ;