3 # Unix SMB/CIFS implementation.
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
5 # Copyright (C) Matthias Dieter Wallnoefer 2009
7 # Based on the original in EJS:
8 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 3 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 """Convenience functions for using the SAM."""
29 from samba.idmap import IDmapDB
34 __docformat__ = "restructuredText"
36 class SamDB(samba.Ldb):
37 """The SAM database."""
39 def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
40 credentials=None, flags=0, options=None, global_schema=True, auto_connect=True):
44 elif url is None and lp is not None:
45 url = lp.get("sam database")
47 super(SamDB, self).__init__(url=url, lp=lp, modules_dir=modules_dir,
48 session_info=session_info, credentials=credentials, flags=flags,
52 dsdb.dsdb_set_global_schema(self)
54 def connect(self, url=None, flags=0, options=None):
55 if self.lp is not None:
56 url = self.lp.private_path(url)
58 super(SamDB, self).connect(url=url, flags=flags,
62 # find the DNs for the domain
63 res = self.search(base="",
65 expression="(defaultNamingContext=*)",
66 attrs=["defaultNamingContext"])
67 assert(len(res) == 1 and res[0]["defaultNamingContext"] is not None)
68 return res[0]["defaultNamingContext"][0]
70 def enable_account(self, filter):
73 :param filter: LDAP filter to find the user (eg samccountname=name)
75 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
76 expression=filter, attrs=["userAccountControl"])
80 userAccountControl = int(res[0]["userAccountControl"][0])
81 if (userAccountControl & 0x2):
82 userAccountControl = userAccountControl & ~0x2 # remove disabled bit
83 if (userAccountControl & 0x20):
84 userAccountControl = userAccountControl & ~0x20 # remove 'no password required' bit
89 replace: userAccountControl
90 userAccountControl: %u
91 """ % (user_dn, userAccountControl)
94 def force_password_change_at_next_login(self, filter):
95 """Forces a password change at next login
97 :param filter: LDAP filter to find the user (eg samccountname=name)
99 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
100 expression=filter, attrs=[])
101 assert(len(res) == 1)
110 self.modify_ldif(mod)
112 def newuser(self, username, password,
113 force_password_change_at_next_login_req=False):
116 :param username: Name of the new user
117 :param password: Password for the new user
118 :param force_password_change_at_next_login_req: Force password change
120 self.transaction_start()
122 user_dn = "CN=%s,CN=Users,%s" % (username, self.domain_dn())
124 # The new user record. Note the reliance on the SAMLDB module which
125 # fills in the default informations
126 self.add({"dn": user_dn,
127 "sAMAccountName": username,
128 "objectClass": "user"})
130 # Sets the password for it
131 self.setpassword("(dn=" + user_dn + ")", password,
132 force_password_change_at_next_login_req)
135 self.transaction_cancel()
138 self.transaction_commit()
140 def setpassword(self, filter, password,
141 force_change_at_next_login=False,
143 """Sets the password for a user
145 Note: This call uses the "userPassword" attribute to set the password.
146 This works correctly on SAMBA 4 and on Windows DCs with
147 "2003 Native" or higer domain function level.
149 :param filter: LDAP filter to find the user (eg samccountname=name)
150 :param password: Password for the user
151 :param force_change_at_next_login: Force password change
153 self.transaction_start()
155 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
156 expression=filter, attrs=[])
158 print('Unable to find user "%s"' % (username or filter))
160 assert(len(res) == 1)
166 replace: userPassword
168 """ % (user_dn, base64.b64encode(password))
170 self.modify_ldif(setpw)
172 if force_change_at_next_login:
173 self.force_password_change_at_next_login(
174 "(dn=" + str(user_dn) + ")")
176 # modify the userAccountControl to remove the disabled bit
177 self.enable_account(filter)
179 self.transaction_cancel()
182 self.transaction_commit()
184 def setexpiry(self, filter, expiry_seconds, no_expiry_req=False):
185 """Sets the account expiry for a user
187 :param filter: LDAP filter to find the user (eg samccountname=name)
188 :param expiry_seconds: expiry time from now in seconds
189 :param no_expiry_req: if set, then don't expire password
191 self.transaction_start()
193 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
195 attrs=["userAccountControl", "accountExpires"])
196 assert(len(res) == 1)
199 userAccountControl = int(res[0]["userAccountControl"][0])
200 accountExpires = int(res[0]["accountExpires"][0])
202 userAccountControl = userAccountControl | 0x10000
205 userAccountControl = userAccountControl & ~0x10000
206 accountExpires = samba.unix2nttime(expiry_seconds + int(time.time()))
211 replace: userAccountControl
212 userAccountControl: %u
213 replace: accountExpires
215 """ % (user_dn, userAccountControl, accountExpires)
217 self.modify_ldif(setexp)
219 self.transaction_cancel()
222 self.transaction_commit()
224 def set_domain_sid(self, sid):
225 """Change the domain SID used by this LDB.
227 :param sid: The new domain sid to use.
229 dsdb.samdb_set_domain_sid(self, sid)
231 def get_domain_sid(self):
232 """Read the domain SID used by this LDB.
235 dsdb.samdb_get_domain_sid(self)
237 def set_invocation_id(self, invocation_id):
238 """Set the invocation id for this SamDB handle.
240 :param invocation_id: GUID of the invocation id.
242 dsdb.dsdb_set_ntds_invocation_id(self, invocation_id)
244 def get_invocation_id(self):
245 "Get the invocation_id id"
246 return dsdb.samdb_ntds_invocation_id(self)
248 def set_ntds_settings_dn(self, ntds_settings_dn):
249 """Set the NTDS Settings DN, as would be returned on the dsServiceName rootDSE attribute
251 This allows the DN to be set before the database fully exists
253 :param ntds_settings_dn: The new DN to use
255 dsdb.samdb_set_ntds_settings_dn(self, ntds_settings_dn)
257 invocation_id = property(get_invocation_id, set_invocation_id)
259 domain_sid = property(get_domain_sid, set_domain_sid)
261 def get_ntds_GUID(self):
262 "Get the NTDS objectGUID"
263 return dsdb.samdb_ntds_objectGUID(self)
265 def set_ntds_GUID(self, object_guid):
266 "Set the NTDS objectGUID"
267 return dsdb.dsdb_set_ntds_objectGUID(self, object_guid)
269 def server_site_name(self):
270 "Get the server site name"
271 return dsdb.samdb_server_site_name(self)
273 def load_partition_usn(self, base_dn):
274 return dsdb.dsdb_load_partition_usn(self, base_dn)