2007-12-10 11:29:00 +03:00
#!/usr/bin/python
#
# backend code for upgrading from Samba3
# Copyright Jelmer Vernooij 2005-2007
# Released under the GNU GPL v3 or later
#
""" Support code for upgrading from Samba 3 to Samba 4. """
from provision import findnss
import provision
import grp
import pwd
2007-12-17 14:19:45 +03:00
import uuid
2007-12-10 11:29:00 +03:00
def regkey_to_dn ( name ) :
2007-12-20 01:27:31 +03:00
""" Convert a registry key to a DN.
: name : The registry key name .
: return : A matching DN . """
2007-12-17 14:19:45 +03:00
dn = " hive=NONE "
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
if name == " " :
return dn
for el in name . split ( " / " ) :
2007-12-10 11:29:00 +03:00
dn = " key= %s , " % el + dn
2007-12-17 14:19:45 +03:00
return dn
2007-12-10 11:29:00 +03:00
# Where prefix is any of:
# - HKLM
# HKU
# HKCR
# HKPD
# HKPT
#
def upgrade_registry ( regdb , prefix , ldb ) :
""" Migrate registry contents. """
2007-12-17 14:19:45 +03:00
assert regdb is not None
prefix_up = prefix . upper ( )
ldif = [ ]
2007-12-10 11:29:00 +03:00
for rk in regdb . keys :
2007-12-17 14:19:45 +03:00
pts = rk . name . split ( " / " )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
# Only handle selected hive
2007-12-10 11:29:00 +03:00
if pts [ 0 ] . upper ( ) != prefix_up :
2007-12-17 14:19:45 +03:00
continue
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
keydn = regkey_to_dn ( rk . name )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
pts = rk . name . split ( " / " )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
# Convert key name to dn
ldif [ rk . name ] = """
2007-12-10 11:29:00 +03:00
dn : % s
name : % s
""" % (keydn, pts[0])
2007-12-17 14:19:45 +03:00
2007-12-10 11:29:00 +03:00
for rv in rk . values :
2007-12-17 14:19:45 +03:00
ldif [ rk . name + " ( " + rv . name + " ) " ] = """
2007-12-10 11:29:00 +03:00
dn : % s , value = % s
value : % s
type : % d
data : : % s """ % (keydn, rv.name, rv.name, rv.type, ldb.encode(rv.data))
2007-12-17 14:19:45 +03:00
return ldif
2007-12-10 11:29:00 +03:00
def upgrade_sam_policy ( samba3 , dn ) :
2007-12-17 14:19:45 +03:00
ldif = """
2007-12-10 11:29:00 +03:00
dn : % s
changetype : modify
replace : minPwdLength
minPwdLength : % d
pwdHistoryLength : % d
minPwdAge : % d
maxPwdAge : % d
lockoutDuration : % d
samba3ResetCountMinutes : % d
samba3UserMustLogonToChangePassword : % d
samba3BadLockoutMinutes : % d
samba3DisconnectTime : % d
""" % (dn, samba3.policy.min_password_length,
2007-12-17 14:19:45 +03:00
samba3 . policy . password_history , samba3 . policy . minimum_password_age ,
samba3 . policy . maximum_password_age , samba3 . policy . lockout_duration ,
samba3 . policy . reset_count_minutes , samba3 . policy . user_must_logon_to_change_password ,
samba3 . policy . bad_lockout_minutes , samba3 . policy . disconnect_time )
return ldif
2007-12-10 11:29:00 +03:00
def upgrade_sam_account ( ldb , acc , domaindn , domainsid ) :
""" Upgrade a SAM account. """
if acc . nt_username is None or acc . nt_username == " " :
2007-12-17 14:19:45 +03:00
acc . nt_username = acc . username
2007-12-10 11:29:00 +03:00
if acc . fullname is None :
2007-12-17 14:19:45 +03:00
acc . fullname = pwd . getpwnam ( acc . fullname ) [ 4 ]
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
acc . fullname = acc . fullname . split ( " , " ) [ 0 ]
2007-12-10 11:29:00 +03:00
if acc . fullname is None :
2007-12-17 14:19:45 +03:00
acc . fullname = acc . username
assert acc . fullname is not None
assert acc . nt_username is not None
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
ldif = """ dn: cn= %s , %s
2007-12-10 11:29:00 +03:00
objectClass : top
objectClass : user
lastLogon : % d
lastLogoff : % d
unixName : % s
sAMAccountName : % s
cn : % s
description : % s
primaryGroupID : % d
badPwdcount : % d
logonCount : % d
samba3Domain : % s
samba3DirDrive : % s
samba3MungedDial : % s
samba3Homedir : % s
samba3LogonScript : % s
samba3ProfilePath : % s
samba3Workstations : % s
samba3KickOffTime : % d
samba3BadPwdTime : % d
samba3PassLastSetTime : % d
samba3PassCanChangeTime : % d
samba3PassMustChangeTime : % d
objectSid : % s - % d
lmPwdHash : : % s
ntPwdHash : : % s
""" % (ldb.dn_escape(acc.fullname), domaindn, acc.logon_time, acc.logoff_time, acc.username, acc.nt_username, acc.nt_username,
acc . acct_desc , acc . group_rid , acc . bad_password_count , acc . logon_count ,
acc . domain , acc . dir_drive , acc . munged_dial , acc . homedir , acc . logon_script ,
acc . profile_path , acc . workstations , acc . kickoff_time , acc . bad_password_time ,
acc . pass_last_set_time , acc . pass_can_change_time , acc . pass_must_change_time , domainsid , acc . user_rid ,
2007-12-17 14:19:45 +03:00
ldb . encode ( acc . lm_pw ) , ldb . encode ( acc . nt_pw ) )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
return ldif
2007-12-10 11:29:00 +03:00
def upgrade_sam_group ( group , domaindn ) :
""" Upgrade a SAM group. """
2007-12-17 14:19:45 +03:00
if group . sid_name_use == 5 : # Well-known group
return None
2007-12-10 11:29:00 +03:00
if group . nt_name in ( " Domain Guests " , " Domain Users " , " Domain Admins " ) :
2007-12-17 14:19:45 +03:00
return None
2007-12-10 11:29:00 +03:00
if group . gid == - 1 :
2007-12-17 14:19:45 +03:00
gr = grp . getgrnam ( grp . nt_name )
2007-12-10 11:29:00 +03:00
else :
2007-12-17 14:19:45 +03:00
gr = grp . getgrgid ( grp . gid )
2007-12-10 11:29:00 +03:00
if gr is None :
2007-12-17 14:19:45 +03:00
group . unixname = " UNKNOWN "
2007-12-10 11:29:00 +03:00
else :
2007-12-17 14:19:45 +03:00
group . unixname = gr . gr_name
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
assert group . unixname is not None
ldif = """ dn: cn= %s , %s
2007-12-10 11:29:00 +03:00
objectClass : top
objectClass : group
description : % s
cn : % s
objectSid : % s
unixName : % s
samba3SidNameUse : % d
""" % (group.nt_name, domaindn,
group . comment , group . nt_name , group . sid , group . unixname , group . sid_name_use )
2007-12-17 14:19:45 +03:00
return ldif
2007-12-10 11:29:00 +03:00
def upgrade_winbind ( samba3 , domaindn ) :
2007-12-17 14:19:45 +03:00
ldif = """
2007-12-10 11:29:00 +03:00
dn : dc = none
userHwm : % d
groupHwm : % d
""" % (samba3.idmap.user_hwm, samba3.idmap.group_hwm)
for m in samba3 . idmap . mappings :
2007-12-17 14:19:45 +03:00
ldif + = """
2007-12-10 11:29:00 +03:00
dn : SID = % s , % s
SID : % s
type : % d
unixID : % d """ % (m.sid, domaindn, m.sid, m.type, m.unix_id)
2007-12-17 14:19:45 +03:00
return ldif
2007-12-10 11:29:00 +03:00
def upgrade_wins ( samba3 ) :
2007-12-10 12:29:20 +03:00
""" Upgrade the WINS database. """
2007-12-17 14:19:45 +03:00
ldif = " "
version_id = 0
2007-12-10 11:29:00 +03:00
for e in samba3 . winsentries :
2007-12-17 14:19:45 +03:00
now = sys . nttime ( )
ttl = sys . unix2nttime ( e . ttl )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
version_id + = 1
2007-12-10 11:29:00 +03:00
numIPs = len ( e . ips )
if e . type == 0x1C :
2007-12-17 14:19:45 +03:00
rType = 0x2
2007-12-10 11:29:00 +03:00
elif e . type & 0x80 :
if numIPs > 1 :
2007-12-17 14:19:45 +03:00
rType = 0x2
2007-12-10 11:29:00 +03:00
else :
2007-12-17 14:19:45 +03:00
rType = 0x1
2007-12-10 11:29:00 +03:00
else :
if numIPs > 1 :
2007-12-17 14:19:45 +03:00
rType = 0x3
2007-12-10 11:29:00 +03:00
else :
2007-12-17 14:19:45 +03:00
rType = 0x0
2007-12-10 11:29:00 +03:00
if ttl > now :
2007-12-17 14:19:45 +03:00
rState = 0x0 # active
2007-12-10 11:29:00 +03:00
else :
2007-12-17 14:19:45 +03:00
rState = 0x1 # released
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
nType = ( ( e . nb_flags & 0x60 ) >> 5 )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
ldif + = """
2007-12-10 11:29:00 +03:00
dn : name = % s , type = 0 x % 02 X
type : 0 x % 02 X
name : % s
objectClass : winsRecord
recordType : % u
recordState : % u
nodeType : % u
isStatic : 0
expireTime : % s
versionID : % llu
""" % (e.name, e.type, e.type, e.name,
rType , rState , nType ,
ldaptime ( ttl ) , version_id )
for ip in e . ips :
2007-12-17 14:19:45 +03:00
ldif + = " address: %s \n " % ip
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
ldif + = """
2007-12-10 11:29:00 +03:00
dn : CN = VERSION
objectClass : winsMaxVersion
maxVersion : % llu
""" % version_id
2007-12-17 14:19:45 +03:00
return ldif
2007-12-10 11:29:00 +03:00
def upgrade_provision ( lp , samba3 ) :
2007-12-17 14:19:45 +03:00
domainname = samba3 . configuration . get ( " workgroup " )
2007-12-10 11:29:00 +03:00
if domainname is None :
2007-12-17 14:19:45 +03:00
domainname = samba3 . secrets . domains [ 0 ] . name
print " No domain specified in smb.conf file, assuming ' %s ' \n " % domainname
domsec = samba3 . find_domainsecrets ( domainname )
hostsec = samba3 . find_domainsecrets ( hostname ( ) )
realm = samba3 . configuration . get ( " realm " )
2007-12-10 11:29:00 +03:00
if realm is None :
2007-12-17 14:19:45 +03:00
realm = domainname
print " No realm specified in smb.conf file, assuming ' %s ' \n " % realm
random_init ( local )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
subobj . realm = realm
subobj . domain = domainname
2007-12-10 11:29:00 +03:00
if domsec is not None :
2007-12-17 14:19:45 +03:00
subobj . DOMAINGUID = domsec . guid
subobj . DOMAINSID = domsec . sid
2007-12-10 11:29:00 +03:00
else :
2007-12-17 14:19:45 +03:00
print " Can ' t find domain secrets for ' %s ' ; using random SID and GUID \n " % domainname
subobj . DOMAINGUID = uuid . random ( )
subobj . DOMAINSID = randsid ( )
2007-12-10 11:29:00 +03:00
if hostsec :
2007-12-20 01:27:31 +03:00
hostguid = hostsec . guid
2007-12-17 14:19:45 +03:00
subobj . krbtgtpass = randpass ( 12 )
subobj . machinepass = randpass ( 12 )
subobj . adminpass = randpass ( 12 )
subobj . datestring = datestring ( )
subobj . root = findnss ( pwd . getpwnam , " root " ) [ 4 ]
subobj . nobody = findnss ( pwd . getpwnam , " nobody " ) [ 4 ]
subobj . nogroup = findnss ( grp . getgrnam , " nogroup " , " nobody " ) [ 2 ]
subobj . wheel = findnss ( grp . getgrnam , " wheel " , " root " ) [ 2 ]
subobj . users = findnss ( grp . getgrnam , " users " , " guest " , " other " ) [ 2 ]
subobj . dnsdomain = subobj . realm . lower ( )
subobj . dnsname = " %s . %s " % ( subobj . hostname . lower ( ) , subobj . dnsdomain )
subobj . basedn = " DC= " + " ,DC= " . join ( subobj . realm . split ( " . " ) )
rdn_list = subobj . dnsdomain . split ( " . " )
subobj . domaindn = " DC= " + " ,DC= " . join ( rdn_list )
subobj . domaindn_ldb = " users.ldb "
subobj . rootdn = subobj . domaindn
modules_list = [ " rootdse " ,
" kludge_acl " ,
" paged_results " ,
" server_sort " ,
" extended_dn " ,
" asq " ,
" samldb " ,
" password_hash " ,
" operational " ,
" objectclass " ,
" rdn_name " ,
" show_deleted " ,
" partition " ]
subobj . modules_list = " , " . join ( modules_list )
return subobj
2007-12-10 11:29:00 +03:00
smbconf_keep = [
2007-12-17 14:19:45 +03:00
" dos charset " ,
" unix charset " ,
" display charset " ,
" comment " ,
" path " ,
" directory " ,
" workgroup " ,
" realm " ,
" netbios name " ,
" netbios aliases " ,
" netbios scope " ,
" server string " ,
" interfaces " ,
" bind interfaces only " ,
" security " ,
" auth methods " ,
" encrypt passwords " ,
" null passwords " ,
" obey pam restrictions " ,
" password server " ,
" smb passwd file " ,
" private dir " ,
" passwd chat " ,
" password level " ,
" lanman auth " ,
" ntlm auth " ,
" client NTLMv2 auth " ,
" client lanman auth " ,
" client plaintext auth " ,
" read only " ,
" hosts allow " ,
" hosts deny " ,
" log level " ,
" debuglevel " ,
" log file " ,
" smb ports " ,
" large readwrite " ,
" max protocol " ,
" min protocol " ,
" unicode " ,
" read raw " ,
" write raw " ,
" disable netbios " ,
" nt status support " ,
" announce version " ,
" announce as " ,
" max mux " ,
" max xmit " ,
" name resolve order " ,
" max wins ttl " ,
" min wins ttl " ,
" time server " ,
" unix extensions " ,
" use spnego " ,
" server signing " ,
" client signing " ,
" max connections " ,
" paranoid server security " ,
" socket options " ,
" strict sync " ,
" max print jobs " ,
" printable " ,
" print ok " ,
" printer name " ,
" printer " ,
" map system " ,
" map hidden " ,
" map archive " ,
" preferred master " ,
" prefered master " ,
" local master " ,
" browseable " ,
" browsable " ,
" wins server " ,
" wins support " ,
" csc policy " ,
" strict locking " ,
" preload " ,
" auto services " ,
" lock dir " ,
" lock directory " ,
" pid directory " ,
" socket address " ,
" copy " ,
" include " ,
" available " ,
" volume " ,
" fstype " ,
" panic action " ,
" msdfs root " ,
" host msdfs " ,
" winbind separator " ]
2007-12-10 11:29:00 +03:00
def upgrade_smbconf ( oldconf , mark ) :
2007-12-17 14:19:45 +03:00
""" Remove configuration variables not present in Samba4
: param oldconf : Old configuration structure
: param mark : Whether removed configuration variables should be
kept in the new configuration as " samba3:<name> "
"""
data = oldconf . data ( )
newconf = param_init ( )
for s in data :
for p in data [ s ] :
keep = False
for k in smbconf_keep :
2007-12-10 11:29:00 +03:00
if smbconf_keep [ k ] == p :
2007-12-17 14:19:45 +03:00
keep = True
break
2007-12-10 11:29:00 +03:00
if keep :
2007-12-17 14:19:45 +03:00
newconf . set ( s , p , oldconf . get ( s , p ) )
2007-12-10 11:29:00 +03:00
elif mark :
2007-12-17 14:19:45 +03:00
newconf . set ( s , " samba3: " + p , oldconf . get ( s , p ) )
2007-12-10 11:29:00 +03:00
if oldconf . get ( " domain logons " ) == " True " :
2007-12-17 14:19:45 +03:00
newconf . set ( " server role " , " domain controller " )
2007-12-10 11:29:00 +03:00
else :
if oldconf . get ( " security " ) == " user " :
2007-12-17 14:19:45 +03:00
newconf . set ( " server role " , " standalone " )
2007-12-10 11:29:00 +03:00
else :
2007-12-17 14:19:45 +03:00
newconf . set ( " server role " , " member server " )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
return newconf
2007-12-10 11:29:00 +03:00
def upgrade ( subobj , samba3 , message , paths , session_info , credentials ) :
2007-12-17 14:19:45 +03:00
ret = 0
lp = loadparm_init ( )
samdb = Ldb ( paths . samdb , session_info = session_info , credentials = credentials )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
message ( " Writing configuration " )
newconf = upgrade_smbconf ( samba3 . configuration , True )
newconf . save ( paths . smbconf )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
message ( " Importing account policies " )
ldif = upgrade_sam_policy ( samba3 , subobj . BASEDN )
samdb . modify ( ldif )
regdb = Ldb ( paths . hklm )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
regdb . modify ( """
2007-12-10 11:29:00 +03:00
dn : value = RefusePasswordChange , key = Parameters , key = Netlogon , key = Services , key = CurrentControlSet , key = System , HIVE = NONE
replace : type
type : 4
replace : data
data : % d
2007-12-17 14:19:45 +03:00
""" % s amba3.policy.refuse_machine_password_change)
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
message ( " Importing users " )
2007-12-10 11:29:00 +03:00
for account in samba3 . samaccounts :
2007-12-17 14:19:45 +03:00
msg = " ... " + account . username
ldif = upgrade_sam_account ( samdb , accounts , subobj . BASEDN , subobj . DOMAINSID )
2007-12-10 11:29:00 +03:00
try :
samdb . add ( ldif )
except LdbError , e :
# FIXME: Ignore 'Record exists' errors
2007-12-17 14:19:45 +03:00
msg + = " ... error: " + str ( e )
ret + = 1 ;
message ( msg )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
message ( " Importing groups " )
2007-12-10 11:29:00 +03:00
for mapping in samba3 . groupmappings :
2007-12-17 14:19:45 +03:00
msg = " ... " + mapping . nt_name
ldif = upgrade_sam_group ( mapping , subobj . BASEDN )
2007-12-10 11:29:00 +03:00
if ldif is not None :
try :
2007-12-17 14:19:45 +03:00
samdb . add ( ldif )
2007-12-10 11:29:00 +03:00
except LdbError , e :
# FIXME: Ignore 'Record exists' errors
2007-12-17 14:19:45 +03:00
msg + = " ... error: " + str ( e )
ret + = 1
message ( msg )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
message ( " Importing registry data " )
2007-12-10 11:29:00 +03:00
for hive in [ " hkcr " , " hkcu " , " hklm " , " hkpd " , " hku " , " hkpt " ] :
2007-12-17 14:19:45 +03:00
message ( " ... " + hive )
regdb = Ldb ( paths [ hive ] )
ldif = upgrade_registry ( samba3 . registry , hive , regdb )
for j in ldif :
msg = " ... ... " + j
2007-12-10 11:29:00 +03:00
try :
regdb . add ( ldif [ j ] )
except LdbError , e :
# FIXME: Ignore 'Record exists' errors
2007-12-17 14:19:45 +03:00
msg + = " ... error: " + str ( e )
ret + = 1
message ( msg )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
message ( " Importing WINS data " )
winsdb = Ldb ( paths . winsdb )
ldb_erase ( winsdb )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
ldif = upgrade_wins ( samba3 )
winsdb . add ( ldif )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
# figure out ldapurl, if applicable
ldapurl = None
pdb = samba3 . configuration . get_list ( " passdb backend " )
2007-12-10 11:29:00 +03:00
if pdb is not None :
for backend in pdb :
if len ( backend ) > = 7 and backend [ 0 : 7 ] == " ldapsam " :
ldapurl = backend [ 7 : ]
2007-12-17 14:19:45 +03:00
# URL was not specified in passdb backend but ldap /is/ used
2007-12-10 11:29:00 +03:00
if ldapurl == " " :
2007-12-17 14:19:45 +03:00
ldapurl = " ldap:// %s " % samba3 . configuration . get ( " ldap server " )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
# Enable samba3sam module if original passdb backend was ldap
2007-12-10 11:29:00 +03:00
if ldapurl is not None :
2007-12-17 14:19:45 +03:00
message ( " Enabling Samba3 LDAP mappings for SAM database " )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
samdb . modify ( """
2007-12-10 11:29:00 +03:00
dn : @MODULES
changetype : modify
replace : @LIST
@LIST : samldb , operational , objectguid , rdn_name , samba3sam
""" )
2007-12-20 01:27:38 +03:00
samdb . add ( { " dn " : " @MAP=samba3sam " , " @MAP_URL " : ldapurl } )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
return ret
2007-12-10 11:29:00 +03:00
def upgrade_verify ( subobj , samba3 , paths , message ) :
2007-12-17 14:19:45 +03:00
message ( " Verifying account policies " )
2007-12-10 11:29:00 +03:00
2007-12-17 14:19:45 +03:00
samldb = Ldb ( paths . samdb )
2007-12-10 11:29:00 +03:00
for account in samba3 . samaccounts :
2007-12-17 14:19:45 +03:00
msg = samldb . search ( " (&(sAMAccountName= " + account . nt_username + " )(objectclass=user)) " )
assert ( len ( msg ) > = 1 )
# FIXME