1
0
mirror of https://github.com/samba-team/samba.git synced 2025-08-02 00:22:11 +03:00

r26496: Move some provision functions to a new SamDB class, support setting session_info on a ldb context from python.

(This used to be commit 75cfb0d609)
This commit is contained in:
Jelmer Vernooij
2007-12-17 08:20:20 +01:00
committed by Stefan Metzmacher
parent 7c34f48827
commit b0360e3a86
9 changed files with 449 additions and 365 deletions

View File

@ -35,40 +35,78 @@ if _in_source_tree():
import misc
import ldb
ldb.ldb.set_credentials = misc.ldb_set_credentials
#FIXME: ldb.ldb.set_session_info = misc.ldb_set_session_info
ldb.ldb.set_loadparm = misc.ldb_set_loadparm
ldb.Ldb.set_credentials = misc.ldb_set_credentials
ldb.Ldb.set_session_info = misc.ldb_set_session_info
ldb.Ldb.set_loadparm = misc.ldb_set_loadparm
def Ldb(url, session_info=None, credentials=None, modules_dir=None, lp=None):
"""Open a Samba Ldb file.
:param url: LDB Url to open
:param session_info: Optional session information
:param credentials: Optional credentials, defaults to anonymous.
:param modules_dir: Modules directory, automatically set if not specified.
:param lp: Loadparm object, optional.
This is different from a regular Ldb file in that the Samba-specific
modules-dir is used by default and that credentials and session_info
can be passed through (required by some modules).
class Ldb(ldb.Ldb):
"""Simple Samba-specific LDB subclass that takes care
of setting up the modules dir, credentials pointers, etc.
Please note that this is intended to be for all Samba LDB files,
not necessarily the Sam database. For Sam-specific helper
functions see samdb.py.
"""
import ldb
ret = ldb.Ldb()
if modules_dir is None:
modules_dir = default_ldb_modules_dir
if modules_dir is not None:
ret.set_modules_dir(modules_dir)
def samba_debug(level,text):
print "%d %s" % (level, text)
if credentials is not None:
ldb.set_credentials(credentials)
if session_info is not None:
ldb.set_session_info(session_info)
if lp is not None:
ldb.set_loadparm(lp)
#ret.set_debug(samba_debug)
ret.connect(url)
return ret
def __init__(url, session_info=None, credentials=None, modules_dir=None,
lp=None):
"""Open a Samba Ldb file.
:param url: LDB Url to open
:param session_info: Optional session information
:param credentials: Optional credentials, defaults to anonymous.
:param modules_dir: Modules directory, automatically set if not specified.
:param lp: Loadparm object, optional.
This is different from a regular Ldb file in that the Samba-specific
modules-dir is used by default and that credentials and session_info
can be passed through (required by some modules).
"""
super(self, Ldb).__init__()
import ldb
ret = ldb.Ldb()
if modules_dir is None:
modules_dir = default_ldb_modules_dir
if modules_dir is not None:
ret.set_modules_dir(modules_dir)
def samba_debug(level,text):
print "%d %s" % (level, text)
if credentials is not None:
ldb.set_credentials(credentials)
if session_info is not None:
ldb.set_session_info(session_info)
if lp is not None:
ldb.set_loadparm(lp)
#ret.set_debug(samba_debug)
ret.connect(url)
return ret
def searchone(self, basedn, expression, attribute):
"""Search for one attribute as a string."""
res = self.search(basedn, SCOPE_SUBTREE, expression, [attribute])
if len(res) != 1 or res[0][attribute] is None:
return None
return res[0][attribute]
def erase(self):
"""Erase an ldb, removing all records."""
# delete the specials
for attr in ["@INDEXLIST", "@ATTRIBUTES", "@SUBCLASSES", "@MODULES",
"@OPTIONS", "@PARTITION", "@KLUDGEACL"]:
try:
self.delete(Dn(self, attr))
except LdbError, (LDB_ERR_NO_SUCH_OBJECT, _):
# Ignore missing dn errors
pass
basedn = Dn(self, "")
# and the rest
for msg in self.search(basedn, SCOPE_SUBTREE,
"(&(|(objectclass=*)(dn=*))(!(dn=@BASEINFO)))",
["dn"]):
self.delete(msg.dn)
res = self.search(basedn, SCOPE_SUBTREE, "(&(|(objectclass=*)(dn=*))(!(dn=@BASEINFO)))", ["dn"])
assert len(res) == 0
def substitute_var(text, values):

View File

@ -15,6 +15,7 @@ from socket import gethostname, gethostbyname
import param
import registry
from samba import Ldb, substitute_var, valid_netbios_name
from samba.samdb import SamDB
from ldb import Dn, SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError, \
LDB_ERR_NO_SUCH_OBJECT, timestring
@ -164,32 +165,6 @@ def findnss(nssfn, *names):
pass
raise Exception("Unable to find user/group for %s" % arguments[1])
def add_foreign(ldb, subobj, sid, desc):
"""Add a foreign security principle."""
add = """
dn: CN=%s,CN=ForeignSecurityPrincipals,%s
objectClass: top
objectClass: foreignSecurityPrincipal
description: %s
""" % (sid, subobj.domaindn, desc)
# deliberately ignore errors from this, as the records may
# already exist
for msg in ldb.parse_ldif(add):
ldb.add(msg[1])
def setup_name_mapping(subobj, ldb, sid, unixname):
"""Setup a mapping between a sam name and a unix name."""
res = ldb.search(Dn(ldb, subobj.domaindn), SCOPE_SUBTREE,
"objectSid=%s" % sid, ["dn"])
assert len(res) == 1, "Failed to find record for objectSid %s" % sid
mod = """
dn: %s
changetype: modify
replace: unixName
unixName: %s
""" % (res[0].dn, unixname)
ldb.modify(ldb.parse_ldif(mod).next()[1])
def hostip():
@ -214,57 +189,6 @@ def ldb_delete(ldb):
ldb.connect(ldb.filename)
def ldb_erase(ldb):
"""Erase an ldb, removing all records."""
# delete the specials
for attr in ["@INDEXLIST", "@ATTRIBUTES", "@SUBCLASSES", "@MODULES",
"@OPTIONS", "@PARTITION", "@KLUDGEACL"]:
try:
ldb.delete(Dn(ldb, attr))
except LdbError, (LDB_ERR_NO_SUCH_OBJECT, _):
# Ignore missing dn errors
pass
basedn = Dn(ldb, "")
# and the rest
for msg in ldb.search(basedn, SCOPE_SUBTREE,
"(&(|(objectclass=*)(dn=*))(!(dn=@BASEINFO)))",
["dn"]):
ldb.delete(msg.dn)
res = ldb.search(basedn, SCOPE_SUBTREE, "(&(|(objectclass=*)(dn=*))(!(dn=@BASEINFO)))", ["dn"])
assert len(res) == 0
def ldb_erase_partitions(subobj, message, ldb, ldapbackend):
"""Erase an ldb, removing all records."""
assert ldb is not None
res = ldb.search(Dn(ldb, ""), SCOPE_BASE, "(objectClass=*)",
["namingContexts"])
assert len(res) == 1
if not "namingContexts" in res[0]:
return
for basedn in res[0]["namingContexts"]:
anything = "(|(objectclass=*)(dn=*))"
previous_remaining = 1
current_remaining = 0
if ldapbackend and (basedn == subobj.domaindn):
# Only delete objects that were created by provision
anything = "(objectcategory=*)"
k = 0
while ++k < 10 and (previous_remaining != current_remaining):
# and the rest
res2 = ldb.search(Dn(ldb, basedn), SCOPE_SUBTREE, anything, ["dn"])
previous_remaining = current_remaining
current_remaining = len(res2)
for msg in res2:
try:
ldb.delete(msg.dn)
except LdbError, (_, text):
message("Unable to delete %s: %s" % (msg.dn, text))
def open_ldb(session_info, credentials, dbname):
assert session_info is not None
@ -374,30 +298,30 @@ def setup_name_mappings(subobj, ldb):
sid = list(res[0]["objectSid"])[0]
# add some foreign sids if they are not present already
add_foreign(ldb, subobj, "S-1-5-7", "Anonymous")
add_foreign(ldb, subobj, "S-1-1-0", "World")
add_foreign(ldb, subobj, "S-1-5-2", "Network")
add_foreign(ldb, subobj, "S-1-5-18", "System")
add_foreign(ldb, subobj, "S-1-5-11", "Authenticated Users")
ldb.add_foreign(subobj.domaindn, "S-1-5-7", "Anonymous")
ldb.add_foreign(subobj.domaindn, "S-1-1-0", "World")
ldb.add_foreign(subobj.domaindn, "S-1-5-2", "Network")
ldb.add_foreign(subobj.domaindn, "S-1-5-18", "System")
ldb.add_foreign(subobj.domaindn, "S-1-5-11", "Authenticated Users")
# some well known sids
setup_name_mapping(subobj, ldb, "S-1-5-7", subobj.nobody)
setup_name_mapping(subobj, ldb, "S-1-1-0", subobj.nogroup)
setup_name_mapping(subobj, ldb, "S-1-5-2", subobj.nogroup)
setup_name_mapping(subobj, ldb, "S-1-5-18", subobj.root)
setup_name_mapping(subobj, ldb, "S-1-5-11", subobj.users)
setup_name_mapping(subobj, ldb, "S-1-5-32-544", subobj.wheel)
setup_name_mapping(subobj, ldb, "S-1-5-32-545", subobj.users)
setup_name_mapping(subobj, ldb, "S-1-5-32-546", subobj.nogroup)
setup_name_mapping(subobj, ldb, "S-1-5-32-551", subobj.backup)
ldb.setup_name_mapping(subobj.domaindn, "S-1-5-7", subobj.nobody)
ldb.setup_name_mapping(subobj.domaindn, "S-1-1-0", subobj.nogroup)
ldb.setup_name_mapping(subobj.domaindn, "S-1-5-2", subobj.nogroup)
ldb.setup_name_mapping(subobj.domaindn, "S-1-5-18", subobj.root)
ldb.setup_name_mapping(subobj.domaindn, "S-1-5-11", subobj.users)
ldb.setup_name_mapping(subobj.domaindn, "S-1-5-32-544", subobj.wheel)
ldb.setup_name_mapping(subobj.domaindn, "S-1-5-32-545", subobj.users)
ldb.setup_name_mapping(subobj.domaindn, "S-1-5-32-546", subobj.nogroup)
ldb.setup_name_mapping(subobj.domaindn, "S-1-5-32-551", subobj.backup)
# and some well known domain rids
setup_name_mapping(subobj, ldb, sid + "-500", subobj.root)
setup_name_mapping(subobj, ldb, sid + "-518", subobj.wheel)
setup_name_mapping(subobj, ldb, sid + "-519", subobj.wheel)
setup_name_mapping(subobj, ldb, sid + "-512", subobj.wheel)
setup_name_mapping(subobj, ldb, sid + "-513", subobj.users)
setup_name_mapping(subobj, ldb, sid + "-520", subobj.wheel)
ldb.setup_name_mapping(subobj.domaindn, sid + "-500", subobj.root)
ldb.setup_name_mapping(subobj.domaindn, sid + "-518", subobj.wheel)
ldb.setup_name_mapping(subobj.domaindn, sid + "-519", subobj.wheel)
ldb.setup_name_mapping(subobj.domaindn, sid + "-512", subobj.wheel)
ldb.setup_name_mapping(subobj.domaindn, sid + "-513", subobj.users)
ldb.setup_name_mapping(subobj.domaindn, sid + "-520", subobj.wheel)
def provision_become_dc(setup_dir, subobj, message, paths, session_info,
@ -414,7 +338,8 @@ def provision_become_dc(setup_dir, subobj, message, paths, session_info,
setup_ldb(setup_dir, "provision_partitions.ldif", session_info,
credentials, subobj, paths.samdb)
samdb = open_ldb(session_info, credentials, paths.samdb)
samdb = SamDB(paths.samdb, session_info=session_info,
credentials=credentials)
ldb.transaction_start()
try:
message("Setting up %s attributes" % paths.samdb)
@ -424,7 +349,7 @@ def provision_become_dc(setup_dir, subobj, message, paths, session_info,
setup_add_ldif(setup_dir, "provision_rootdse_add.ldif", subobj, samdb)
message("Erasing data from partitions")
ldb_erase_partitions(subobj, message, samdb, undefined)
ldb_erase_partitions(subobj, message, samdb, None)
message("Setting up %s indexes" % paths.samdb)
setup_add_ldif(setup_dir, "provision_index.ldif", subobj, samdb)
@ -603,7 +528,7 @@ def provision_dns(setup_dir, subobj, message, paths, session_info, credentials):
"""Write out a DNS zone file, from the info in the current database."""
message("Setting up DNS zone: %s" % subobj.dnsdomain)
# connect to the sam
ldb = Ldb(paths.samdb, session_info=session_info, credentials=credentials)
ldb = SamDB(paths.samdb, session_info=session_info, credentials=credentials)
# These values may have changed, due to an incoming SamSync,
# or may not have been specified, so fetch them from the database
@ -614,7 +539,7 @@ def provision_dns(setup_dir, subobj, message, paths, session_info, credentials):
assert(res[0]["objectGUID"] is not None)
subobj.domainguid = res[0]["objectGUID"]
subobj.host_guid = searchone(ldb, subobj.domaindn,
subobj.host_guid = ldb.searchone(subobj.domaindn,
"(&(objectClass=computer)(cn=%s))" % subobj.netbiosname, "objectGUID")
assert subobj.host_guid is not None
@ -716,13 +641,6 @@ def provision_guess(lp):
return subobj
def searchone(ldb, basedn, expression, attribute):
"""search for one attribute as a string."""
res = ldb.search(basedn, SCOPE_SUBTREE, expression, [attribute])
if len(res) != 1 or res[0][attribute] is None:
return None
return res[0][attribute]
def load_schema(setup_dir, subobj, samdb):
"""Load schema."""
@ -745,70 +663,6 @@ def load_schema(setup_dir, subobj, samdb):
samdb.attach_dsdb_schema_from_ldif(head_data, schema_data)
def enable_account(ldb, user_dn):
"""enable the account."""
res = ldb.search(user_dn, SCOPE_ONELEVEL, None, ["userAccountControl"])
assert len(res) == 1
userAccountControl = res[0].userAccountControl
userAccountControl = userAccountControl - 2 # remove disabled bit
mod = """
dn: %s
changetype: modify
replace: userAccountControl
userAccountControl: %u
""" % (user_dn, userAccountControl)
ldb.modify(mod)
def newuser(sam, username, unixname, password, message, session_info,
credentials):
"""add a new user record"""
# connect to the sam
ldb.transaction_start()
# find the DNs for the domain and the domain users group
res = ldb.search("", SCOPE_BASE, "defaultNamingContext=*",
["defaultNamingContext"])
assert(len(res) == 1 and res[0].defaultNamingContext is not None)
domain_dn = res[0].defaultNamingContext
assert(domain_dn is not None)
dom_users = searchone(ldb, domain_dn, "name=Domain Users", "dn")
assert(dom_users 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
#
ldif = """
dn: %s
sAMAccountName: %s
unixName: %s
sambaPassword: %s
objectClass: user
""" % (user_dn, username, unixname, password)
# add the user to the users group as well
modgroup = """
dn: %s
changetype: modify
add: member
member: %s
""" % (dom_users, user_dn)
# now the real work
message("Adding user %s" % user_dn)
ldb.add(ldif)
message("Modifying group %s" % dom_users)
ldb.modify(modgroup)
# modify the userAccountControl to remove the disabled bit
enable_account(ldb, user_dn)
ldb.transaction_commit()
def join_domain(domain, netbios_name, join_type, creds, message):
ctx = NetContext(creds)
joindom = object()
@ -835,3 +689,35 @@ def vampire(domain, session_info, credentials, message):
vampire_ctx.session_info = session_info
if not ctx.SamSyncLdb(vampire_ctx):
raise Exception("Migration of remote domain to Samba failed: %s " % vampire_ctx.error_string)
def ldb_erase_partitions(subobj, message, ldb, ldapbackend):
"""Erase an ldb, removing all records."""
assert ldb is not None
res = ldb.search(Dn(ldb, ""), SCOPE_BASE, "(objectClass=*)",
["namingContexts"])
assert len(res) == 1
if not "namingContexts" in res[0]:
return
for basedn in res[0]["namingContexts"]:
anything = "(|(objectclass=*)(dn=*))"
previous_remaining = 1
current_remaining = 0
if ldapbackend and (basedn == subobj.domaindn):
# Only delete objects that were created by provision
anything = "(objectcategory=*)"
k = 0
while ++k < 10 and (previous_remaining != current_remaining):
# and the rest
res2 = ldb.search(Dn(ldb, basedn), SCOPE_SUBTREE, anything, ["dn"])
previous_remaining = current_remaining
current_remaining = len(res2)
for msg in res2:
try:
ldb.delete(msg.dn)
except LdbError, (_, text):
message("Unable to delete %s: %s" % (msg.dn, text))

View File

@ -0,0 +1,117 @@
#!/usr/bin/python
# Unix SMB/CIFS implementation.
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
#
# Based on the original in EJS:
# Copyright (C) Andrew Tridgell 2005
#
# 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/>.
#
import samba
class SamDB(samba.Ldb):
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
""" % (sid, domaindn, desc)
# deliberately ignore errors from this, as the records may
# already exist
for msg in self.parse_ldif(add):
self.add(msg[1])
def setup_name_mapping(self, domaindn, sid, unixname):
"""Setup a mapping between a sam name and a unix name."""
res = self.search(Dn(ldb, domaindn), SCOPE_SUBTREE,
"objectSid=%s" % sid, ["dn"])
assert len(res) == 1, "Failed to find record for objectSid %s" % sid
mod = """
dn: %s
changetype: modify
replace: unixName
unixName: %s
""" % (res[0].dn, unixname)
self.modify(self.parse_ldif(mod).next()[1])
def enable_account(self, user_dn):
"""enable the account.
:param user_dn: Dn of the account to enable.
"""
res = self.search(user_dn, SCOPE_ONELEVEL, None, ["userAccountControl"])
assert len(res) == 1
userAccountControl = res[0].userAccountControl
userAccountControl = userAccountControl - 2 # remove disabled bit
mod = """
dn: %s
changetype: modify
replace: userAccountControl
userAccountControl: %u
""" % (user_dn, userAccountControl)
self.modify(mod)
def newuser(self, username, unixname, password, message):
"""add a new user record"""
# connect to the sam
self.transaction_start()
# find the DNs for the domain and the domain users group
res = self.search("", SCOPE_BASE, "defaultNamingContext=*",
["defaultNamingContext"])
assert(len(res) == 1 and res[0].defaultNamingContext is not None)
domain_dn = res[0].defaultNamingContext
assert(domain_dn is not None)
dom_users = searchone(self, domain_dn, "name=Domain Users", "dn")
assert(dom_users 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
#
ldif = """
dn: %s
sAMAccountName: %s
unixName: %s
sambaPassword: %s
objectClass: user
""" % (user_dn, username, unixname, password)
# add the user to the users group as well
modgroup = """
dn: %s
changetype: modify
add: member
member: %s
""" % (dom_users, user_dn)
# now the real work
message("Adding user %s" % user_dn)
self.add(ldif)
message("Modifying group %s" % dom_users)
self.modify(modgroup)
# modify the userAccountControl to remove the disabled bit
enable_account(self, user_dn)
self.transaction_commit()