mirror of
https://github.com/samba-team/samba.git
synced 2025-01-11 05:18:09 +03:00
s3_upgrade: Update commandline options and use updated samba3 python module
upgrade_from_s3 script now requires samba3 configuration file and target directory for samba4 database. In addition, it either uses --libdir option or --testparm option to correctly guess the paths for samba3 databases (private dir and state directory). Usage: upgrade_from_s3 [options] <configuration_file> <targetdir> Input arguments are: <configuration_file> - path to existing smb.conf <targetdir> - directory in which samba4 database will be created In addition, specify either samba3 database directory (with --libdir) or samba3 testparm utility (with --testparm). Before using passdb interface, initialize s3 loadparm context using correct path settings for private dir and state directory. Export account policy from s3 to s4. Signed-off-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
parent
d2536b3120
commit
d8465f2a91
@ -35,119 +35,39 @@ from samba import dsdb
|
||||
from samba.ndr import ndr_pack
|
||||
|
||||
|
||||
def import_sam_policy(samldb, policy, dn):
|
||||
"""Import a Samba 3 policy database."""
|
||||
samldb.modify_ldif("""
|
||||
dn: %s
|
||||
changetype: modify
|
||||
replace: minPwdLength
|
||||
minPwdLength: %d
|
||||
pwdHistoryLength: %d
|
||||
minPwdAge: %d
|
||||
maxPwdAge: %d
|
||||
lockoutDuration: %d
|
||||
samba3ResetCountMinutes: %d
|
||||
samba3UserMustLogonToChangePassword: %d
|
||||
samba3BadLockoutMinutes: %d
|
||||
samba3DisconnectTime: %d
|
||||
def import_sam_policy(samdb, policy, logger):
|
||||
"""Import a Samba 3 policy.
|
||||
|
||||
""" % (dn, policy.min_password_length,
|
||||
policy.password_history, policy.minimum_password_age,
|
||||
policy.maximum_password_age, policy.lockout_duration,
|
||||
policy.reset_count_minutes, policy.user_must_logon_to_change_password,
|
||||
policy.bad_lockout_minutes, policy.disconnect_time))
|
||||
|
||||
|
||||
def import_sam_account(samldb,acc,domaindn,domainsid):
|
||||
"""Import a Samba 3 SAM account.
|
||||
|
||||
:param samldb: Samba 4 SAM Database handle
|
||||
:param acc: Samba 3 account
|
||||
:param domaindn: Domain DN
|
||||
:param domainsid: Domain SID."""
|
||||
if acc.nt_username is None or acc.nt_username == "":
|
||||
acc.nt_username = acc.username
|
||||
|
||||
if acc.fullname is None:
|
||||
try:
|
||||
acc.fullname = pwd.getpwnam(acc.username)[4].split(",")[0]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if acc.fullname is None:
|
||||
acc.fullname = acc.username
|
||||
|
||||
assert acc.fullname is not None
|
||||
assert acc.nt_username is not None
|
||||
|
||||
samldb.add({
|
||||
"dn": "cn=%s,%s" % (acc.fullname, domaindn),
|
||||
"objectClass": ["top", "user"],
|
||||
"lastLogon": str(acc.logon_time),
|
||||
"lastLogoff": str(acc.logoff_time),
|
||||
"unixName": acc.username,
|
||||
"sAMAccountName": acc.nt_username,
|
||||
"cn": acc.nt_username,
|
||||
"description": acc.acct_desc,
|
||||
"primaryGroupID": str(acc.group_rid),
|
||||
"badPwdcount": str(acc.bad_password_count),
|
||||
"logonCount": str(acc.logon_count),
|
||||
"samba3Domain": acc.domain,
|
||||
"samba3DirDrive": acc.dir_drive,
|
||||
"samba3MungedDial": acc.munged_dial,
|
||||
"samba3Homedir": acc.homedir,
|
||||
"samba3LogonScript": acc.logon_script,
|
||||
"samba3ProfilePath": acc.profile_path,
|
||||
"samba3Workstations": acc.workstations,
|
||||
"samba3KickOffTime": str(acc.kickoff_time),
|
||||
"samba3BadPwdTime": str(acc.bad_password_time),
|
||||
"samba3PassLastSetTime": str(acc.pass_last_set_time),
|
||||
"samba3PassCanChangeTime": str(acc.pass_can_change_time),
|
||||
"samba3PassMustChangeTime": str(acc.pass_must_change_time),
|
||||
"objectSid": "%s-%d" % (domainsid, acc.user_rid),
|
||||
"lmPwdHash:": acc.lm_password,
|
||||
"ntPwdHash:": acc.nt_password,
|
||||
})
|
||||
|
||||
|
||||
def import_sam_group(samldb, sid, gid, sid_name_use, nt_name, comment, domaindn):
|
||||
"""Upgrade a SAM group.
|
||||
|
||||
:param samldb: SAM database.
|
||||
:param gid: Group GID
|
||||
:param sid_name_use: SID name use
|
||||
:param nt_name: NT Group Name
|
||||
:param comment: NT Group Comment
|
||||
:param domaindn: Domain DN
|
||||
:param samdb: Samba4 SAM database
|
||||
:param policy: Samba3 account policy
|
||||
:param logger: Logger object
|
||||
"""
|
||||
|
||||
if sid_name_use == 5: # Well-known group
|
||||
return None
|
||||
# Following entries are used -
|
||||
# min password length, password history, minimum password age,
|
||||
# maximum password age, lockout duration
|
||||
#
|
||||
# Following entries are not used -
|
||||
# reset count minutes, user must logon to change password,
|
||||
# bad lockout minutes, disconnect time
|
||||
|
||||
if nt_name in ("Domain Guests", "Domain Users", "Domain Admins"):
|
||||
return None
|
||||
m = ldb.Message()
|
||||
m.dn = samdb.get_default_basedn()
|
||||
m['a01'] = ldb.MessageElement(str(policy['min password length']), ldb.FLAG_MOD_REPLACE,
|
||||
'minPwdLength')
|
||||
m['a02'] = ldb.MessageElement(str(policy['password history']), ldb.FLAG_MOD_REPLACE,
|
||||
'pwdHistoryLength')
|
||||
m['a03'] = ldb.MessageElement(str(policy['minimum password age']), ldb.FLAG_MOD_REPLACE,
|
||||
'minPwdAge')
|
||||
m['a04'] = ldb.MessageElement(str(policy['maximum password age']), ldb.FLAG_MOD_REPLACE,
|
||||
'maxPwdAge')
|
||||
m['a05'] = ldb.MessageElement(str(policy['lockout duration']), ldb.FLAG_MOD_REPLACE,
|
||||
'lockoutDuration')
|
||||
|
||||
if gid == -1:
|
||||
gr = grp.getgrnam(nt_name)
|
||||
else:
|
||||
gr = grp.getgrgid(gid)
|
||||
|
||||
if gr is None:
|
||||
unixname = "UNKNOWN"
|
||||
else:
|
||||
unixname = gr.gr_name
|
||||
|
||||
assert unixname is not None
|
||||
|
||||
samldb.add({
|
||||
"dn": "cn=%s,%s" % (nt_name, domaindn),
|
||||
"objectClass": ["top", "group"],
|
||||
"description": comment,
|
||||
"cn": nt_name,
|
||||
"objectSid": sid,
|
||||
"unixName": unixname,
|
||||
"samba3SidNameUse": str(sid_name_use)
|
||||
})
|
||||
try:
|
||||
samdb.modify(m)
|
||||
except ldb.LdbError, e:
|
||||
logger.warn("Could not set account policy, (%s)", str(e))
|
||||
|
||||
|
||||
def add_idmap_entry(idmapdb, sid, xid, xid_type, logger):
|
||||
@ -487,36 +407,37 @@ def import_registry(samba4_registry, samba3_regdb):
|
||||
key_handle.set_value(value_name, value_type, value_data)
|
||||
|
||||
|
||||
def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir):
|
||||
def upgrade_from_samba3(samba3, logger, targetdir, session_info=None):
|
||||
"""Upgrade from samba3 database to samba4 AD database
|
||||
|
||||
:param samba3: samba3 object
|
||||
:param logger: Logger object
|
||||
:param targetdir: samba4 database directory
|
||||
:param session_info: Session information
|
||||
"""
|
||||
|
||||
# Read samba3 smb.conf
|
||||
oldconf = s3param.get_context();
|
||||
oldconf.load(smbconf)
|
||||
|
||||
if oldconf.get("domain logons"):
|
||||
if samba3.lp.get("domain logons"):
|
||||
serverrole = "domain controller"
|
||||
else:
|
||||
if oldconf.get("security") == "user":
|
||||
if samba3.lp.get("security") == "user":
|
||||
serverrole = "standalone"
|
||||
else:
|
||||
serverrole = "member server"
|
||||
|
||||
domainname = oldconf.get("workgroup")
|
||||
realm = oldconf.get("realm")
|
||||
netbiosname = oldconf.get("netbios name")
|
||||
domainname = samba3.lp.get("workgroup")
|
||||
realm = samba3.lp.get("realm")
|
||||
netbiosname = samba3.lp.get("netbios name")
|
||||
|
||||
# secrets db
|
||||
secrets_db = samba3.get_secrets_db()
|
||||
|
||||
if not domainname:
|
||||
domainname = secrets_db.domains()[0]
|
||||
logger.warning("No domain specified in smb.conf file, assuming '%s'",
|
||||
logger.warning("No workgroup specified in smb.conf file, assuming '%s'",
|
||||
domainname)
|
||||
|
||||
if not realm:
|
||||
if oldconf.get("domain logons"):
|
||||
if serverrole == "domain controller":
|
||||
logger.warning("No realm specified in smb.conf file and being a DC. That upgrade path doesn't work! Please add a 'realm' directive to your old smb.conf to let us know which one you want to use (generally it's the upcased DNS domainname).")
|
||||
return
|
||||
else:
|
||||
@ -538,7 +459,9 @@ def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir):
|
||||
# We must close the direct pytdb database before the C code loads it
|
||||
secrets_db.close()
|
||||
|
||||
passdb.set_secrets_dir(samba3.privatedir)
|
||||
# Connect to old password backend
|
||||
passdb.set_secrets_dir(samba3.lp.get("private dir"))
|
||||
s3db = samba3.get_sam_db()
|
||||
|
||||
# Get domain sid
|
||||
try:
|
||||
@ -548,17 +471,18 @@ def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir):
|
||||
|
||||
# Get machine account, sid, rid
|
||||
try:
|
||||
machineacct = old_passdb.getsampwnam('%s$' % netbiosname)
|
||||
machineacct = s3db.getsampwnam('%s$' % netbiosname)
|
||||
machinesid, machinerid = machineacct.user_sid.split()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Connect to old password backend
|
||||
old_passdb = passdb.PDB(oldconf.get('passdb backend'))
|
||||
# Export account policy
|
||||
logger.info("Exporting account policy")
|
||||
policy = s3db.get_account_policy()
|
||||
|
||||
# Import groups from old passdb backend
|
||||
# Export groups from old passdb backend
|
||||
logger.info("Exporting groups")
|
||||
grouplist = old_passdb.enum_group_mapping()
|
||||
grouplist = s3db.enum_group_mapping()
|
||||
groupmembers = {}
|
||||
for group in grouplist:
|
||||
sid, rid = group.sid.split()
|
||||
@ -568,10 +492,10 @@ def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir):
|
||||
|
||||
# Get members for each group/alias
|
||||
if group.sid_name_use == lsa.SID_NAME_ALIAS or group.sid_name_use == lsa.SID_NAME_WKN_GRP:
|
||||
members = old_passdb.enum_aliasmem(group.sid)
|
||||
members = s3db.enum_aliasmem(group.sid)
|
||||
elif group.sid_name_use == lsa.SID_NAME_DOM_GRP:
|
||||
try:
|
||||
members = old_passdb.enum_group_members(group.sid)
|
||||
members = s3db.enum_group_members(group.sid)
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
@ -581,9 +505,9 @@ def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir):
|
||||
groupmembers[group.nt_name] = members
|
||||
|
||||
|
||||
# Import users from old passdb backend
|
||||
# Export users from old passdb backend
|
||||
logger.info("Exporting users")
|
||||
userlist = old_passdb.search_users(0)
|
||||
userlist = s3db.search_users(0)
|
||||
userdata = {}
|
||||
uids = {}
|
||||
admin_user = None
|
||||
@ -597,9 +521,9 @@ def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir):
|
||||
if entry['rid'] >= next_rid:
|
||||
next_rid = entry['rid'] + 1
|
||||
|
||||
userdata[username] = old_passdb.getsampwnam(username)
|
||||
userdata[username] = s3db.getsampwnam(username)
|
||||
try:
|
||||
uids[username] = old_passdb.sid_to_id(userdata[username].user_sid)[0]
|
||||
uids[username] = s3db.sid_to_id(userdata[username].user_sid)[0]
|
||||
except:
|
||||
try:
|
||||
uids[username] = pwd.getpwnam(username).pw_uid
|
||||
@ -611,7 +535,6 @@ def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir):
|
||||
if username.lower() == 'administrator':
|
||||
admin_user = username
|
||||
|
||||
|
||||
logger.info("Next rid = %d", next_rid)
|
||||
|
||||
# Do full provision
|
||||
@ -622,19 +545,26 @@ def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir):
|
||||
hostname=netbiosname, machinepass=machinepass,
|
||||
serverrole=serverrole, samdb_fill=FILL_FULL)
|
||||
|
||||
logger.info("Import WINS")
|
||||
# Import WINS database
|
||||
logger.info("Importing WINS database")
|
||||
import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db())
|
||||
|
||||
new_smbconf = result.lp.configfile
|
||||
newconf = s3param.get_context()
|
||||
newconf.load(new_smbconf)
|
||||
# Set Account policy
|
||||
logger.info("Importing Account policy")
|
||||
import_sam_policy(result.samdb, policy, logger)
|
||||
|
||||
# Migrate idmap
|
||||
logger.info("Migrating idmap database")
|
||||
# Migrate IDMAP database
|
||||
logger.info("Importing idmap database")
|
||||
import_idmap(result.idmap, samba3.get_idmap_db(), logger)
|
||||
|
||||
# Set the s3 context for samba4 configuration
|
||||
new_lp_ctx = s3param.get_context()
|
||||
new_lp_ctx.load(result.lp.configfile)
|
||||
new_lp_ctx.set("private dir", result.lp.get("private dir"))
|
||||
new_lp_ctx.set("state directory", result.lp.get("state directory"))
|
||||
|
||||
# Connect to samba4 backend
|
||||
new_passdb = passdb.PDB('samba4')
|
||||
s4_passdb = passdb.PDB(new_lp_ctx.get("passdb backend"))
|
||||
|
||||
# Export groups to samba4 backend
|
||||
logger.info("Importing groups")
|
||||
@ -649,7 +579,7 @@ def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir):
|
||||
for username in userdata:
|
||||
if username.lower() == 'administrator' or username.lower() == 'root':
|
||||
continue
|
||||
new_passdb.add_sam_account(userdata[username])
|
||||
s4_passdb.add_sam_account(userdata[username])
|
||||
if username in uids:
|
||||
add_idmap_entry(result.idmap, userdata[username].user_sid, uids[username], "UID", logger)
|
||||
|
||||
@ -661,14 +591,15 @@ def upgrade_from_samba3(samba3, logger, session_info, smbconf, targetdir):
|
||||
# Set password for administrator
|
||||
if admin_user:
|
||||
logger.info("Setting password for administrator")
|
||||
admin_userdata = new_passdb.getsampwnam("administrator")
|
||||
admin_userdata = s4_passdb.getsampwnam("administrator")
|
||||
admin_userdata.nt_passwd = userdata[admin_user].nt_passwd
|
||||
if userdata[admin_user].lanman_passwd:
|
||||
admin_userdata.lanman_passwd = userdata[admin_user].lanman_passwd
|
||||
admin_userdata.pass_last_set_time = userdata[admin_user].pass_last_set_time
|
||||
if userdata[admin_user].pw_history:
|
||||
admin_userdata.pw_history = userdata[admin_user].pw_history
|
||||
new_passdb.update_sam_account(admin_userdata)
|
||||
s4_passdb.update_sam_account(admin_userdata)
|
||||
logger.info("Administrator password has been set to password of user '%s'", admin_user)
|
||||
|
||||
# FIXME: import_registry(registry.Registry(), samba3.get_registry())
|
||||
# FIXME: shares
|
||||
|
@ -35,7 +35,7 @@ cat - > $PREFIX/samba3-upgrade/samba3/smb1.conf <<EOF
|
||||
debug level = 0
|
||||
EOF
|
||||
|
||||
testit "samba3-upgrade-member" $PYTHON $SRCDIR/source4/setup/upgrade_from_s3 --targetdir=$PREFIX/samba3-upgrade/s4_1 --configfile=$PREFIX/samba3-upgrade/samba3/smb1.conf $PREFIX/samba3-upgrade/samba3
|
||||
testit "samba3-upgrade-member" $PYTHON $SRCDIR/source4/setup/upgrade_from_s3 $PREFIX/samba3-upgrade/samba3/smb1.conf $PREFIX/samba3-upgrade/s4_1 --libdir=$PREFIX/samba3-upgrade/samba3
|
||||
|
||||
# Test 2 (s3 dc)
|
||||
cat - > $PREFIX/samba3-upgrade/samba3/smb2.conf <<EOF
|
||||
@ -55,7 +55,7 @@ cat - > $PREFIX/samba3-upgrade/samba3/smb2.conf <<EOF
|
||||
domain logons = yes
|
||||
EOF
|
||||
|
||||
testit "samba3-upgrade-dc" $PYTHON $SRCDIR/source4/setup/upgrade_from_s3 --targetdir=$PREFIX/samba3-upgrade/s4_2 --configfile=$PREFIX/samba3-upgrade/samba3/smb2.conf $PREFIX/samba3-upgrade/samba3
|
||||
testit "samba3-upgrade-dc" $PYTHON $SRCDIR/source4/setup/upgrade_from_s3 $PREFIX/samba3-upgrade/samba3/smb2.conf $PREFIX/samba3-upgrade/s4_2 --libdir=$PREFIX/samba3-upgrade/samba3
|
||||
|
||||
rm -rf $PREFIX/samba3-upgrade
|
||||
|
||||
|
@ -28,16 +28,57 @@ import samba.getopt as options
|
||||
from samba.auth import system_session
|
||||
from samba.upgrade import upgrade_from_samba3
|
||||
from samba.samba3 import Samba3
|
||||
parser = optparse.OptionParser("upgrade_from_s3 [options] <libdir>")
|
||||
sambaopts = options.SambaOptions(parser)
|
||||
parser.add_option_group(sambaopts)
|
||||
from samba.samba3 import param as s3param
|
||||
|
||||
def get_testparm_var(testparm, varname):
|
||||
cmd = "%s -s -l --parameter-name='%s' 2>/dev/null" % (testparm, varname)
|
||||
output = os.popen(cmd, 'r').readline()
|
||||
return output.strip()
|
||||
|
||||
cmd_help = """upgrade_from_s3 [options] <configuration_file> <targetdir>
|
||||
|
||||
Input arguments are:
|
||||
<configuration_file> - path to existing smb.conf
|
||||
<targetdir> - directory in which samba4 database will be created
|
||||
|
||||
In addition, specify either samba3 database directory (with --libdir) or
|
||||
samba3 testparm utility (with --testparm). """
|
||||
|
||||
parser = optparse.OptionParser(cmd_help)
|
||||
parser.add_option_group(options.VersionOptions(parser))
|
||||
parser.add_option("--quiet", help="Be quiet")
|
||||
parser.add_option("--targetdir", type="string", metavar="DIR",
|
||||
help="Set target directory")
|
||||
parser.add_option("--libdir", type="string", metavar="DIR",
|
||||
help="samba3 database directory")
|
||||
parser.add_option("--testparm", type="string", metavar="PATH",
|
||||
help="samba3 testparm utility")
|
||||
|
||||
opts, args = parser.parse_args()
|
||||
|
||||
if len(args) < 2:
|
||||
parser.print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
smbconf = args[0]
|
||||
if not os.path.exists(smbconf):
|
||||
print("error: %s does not exist" % smbconf)
|
||||
sys.exit(1)
|
||||
|
||||
targetdir = args[1]
|
||||
if not os.path.isdir(targetdir):
|
||||
print("error: %s is not a directory" % targetdir)
|
||||
sys.exit(1)
|
||||
|
||||
libdir = opts.libdir
|
||||
testparm = opts.testparm
|
||||
|
||||
if not libdir and not testparm:
|
||||
print("error: please specify either libdir or testparm")
|
||||
sys.exit(1)
|
||||
|
||||
if libdir and testparm:
|
||||
print("warning: both libdir and testparm specified, libdir will be ignored.")
|
||||
libdir = None
|
||||
|
||||
logger = logging.getLogger("upgrade")
|
||||
logger.addHandler(logging.StreamHandler(sys.stdout))
|
||||
if opts.quiet:
|
||||
@ -45,23 +86,25 @@ if opts.quiet:
|
||||
else:
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
if len(args) < 1:
|
||||
parser.print_usage()
|
||||
sys.exit(1)
|
||||
s3conf = s3param.get_context()
|
||||
#s3conf.load_default()
|
||||
|
||||
logger.info("Reading Samba3 databases and smb.conf")
|
||||
# Set correct default values from libdir or testparm
|
||||
paths = {}
|
||||
if libdir:
|
||||
paths["state directory"] = libdir
|
||||
paths["private dir"] = libdir
|
||||
else:
|
||||
paths["state directory"] = get_testparm_var(testparm, "state directory")
|
||||
paths["private dir"] = get_testparm_var(testparm, "private dir")
|
||||
|
||||
libdir = args[0]
|
||||
if not os.path.isdir(libdir):
|
||||
print "error: %s is not a directory"
|
||||
sys.exit(1)
|
||||
for p in paths:
|
||||
s3conf.set(p, paths[p])
|
||||
|
||||
lp = sambaopts.get_loadparm()
|
||||
smbconf = lp.configfile
|
||||
|
||||
samba3 = Samba3(libdir, smbconf)
|
||||
# load smb.conf parameters
|
||||
logger.info("Reading smb.conf")
|
||||
s3conf.load(smbconf)
|
||||
samba3 = Samba3(smbconf, s3conf)
|
||||
|
||||
logger.info("Provisioning")
|
||||
|
||||
upgrade_from_samba3(samba3, logger, session_info=system_session(),
|
||||
smbconf=smbconf, targetdir=opts.targetdir)
|
||||
upgrade_from_samba3(samba3, logger, targetdir, session_info=system_session())
|
||||
|
Loading…
Reference in New Issue
Block a user