~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/scripting/python/samba/upgrade.py

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
#
 
3
#       backend code for upgrading from Samba3
 
4
#       Copyright Jelmer Vernooij 2005-2007
 
5
#       Released under the GNU GPL v3 or later
 
6
#
 
7
 
 
8
"""Support code for upgrading from Samba 3 to Samba 4."""
 
9
 
 
10
__docformat__ = "restructuredText"
 
11
 
 
12
from provision import findnss, provision, FILL_DRS
 
13
import grp
 
14
import ldb
 
15
import time
 
16
import pwd
 
17
import uuid
 
18
import registry
 
19
from samba import Ldb
 
20
from samba.samdb import SamDB
 
21
 
 
22
def import_sam_policy(samldb, samba3_policy, domaindn):
 
23
    """Import a Samba 3 policy database."""
 
24
    samldb.modify_ldif("""
 
25
dn: %s
 
26
changetype: modify
 
27
replace: minPwdLength
 
28
minPwdLength: %d
 
29
pwdHistoryLength: %d
 
30
minPwdAge: %d
 
31
maxPwdAge: %d
 
32
lockoutDuration: %d
 
33
samba3ResetCountMinutes: %d
 
34
samba3UserMustLogonToChangePassword: %d
 
35
samba3BadLockoutMinutes: %d
 
36
samba3DisconnectTime: %d
 
37
 
 
38
""" % (dn, policy.min_password_length, 
 
39
    policy.password_history, policy.minimum_password_age,
 
40
    policy.maximum_password_age, policy.lockout_duration,
 
41
    policy.reset_count_minutes, policy.user_must_logon_to_change_password,
 
42
    policy.bad_lockout_minutes, policy.disconnect_time))
 
43
 
 
44
 
 
45
def import_sam_account(samldb,acc,domaindn,domainsid):
 
46
    """Import a Samba 3 SAM account.
 
47
    
 
48
    :param samldb: Samba 4 SAM Database handle
 
49
    :param acc: Samba 3 account
 
50
    :param domaindn: Domain DN
 
51
    :param domainsid: Domain SID."""
 
52
    if acc.nt_username is None or acc.nt_username == "":
 
53
        acc.nt_username = acc.username
 
54
 
 
55
    if acc.fullname is None:
 
56
        try:
 
57
            acc.fullname = pwd.getpwnam(acc.username)[4].split(",")[0]
 
58
        except KeyError:
 
59
            pass
 
60
 
 
61
    if acc.fullname is None:
 
62
        acc.fullname = acc.username
 
63
    
 
64
    assert acc.fullname is not None
 
65
    assert acc.nt_username is not None
 
66
 
 
67
    samldb.add({
 
68
        "dn": "cn=%s,%s" % (acc.fullname, domaindn),
 
69
        "objectClass": ["top", "user"],
 
70
        "lastLogon": str(acc.logon_time),
 
71
        "lastLogoff": str(acc.logoff_time),
 
72
        "unixName": acc.username,
 
73
        "sAMAccountName": acc.nt_username,
 
74
        "cn": acc.nt_username,
 
75
        "description": acc.acct_desc,
 
76
        "primaryGroupID": str(acc.group_rid),
 
77
        "badPwdcount": str(acc.bad_password_count),
 
78
        "logonCount": str(acc.logon_count),
 
79
        "samba3Domain": acc.domain,
 
80
        "samba3DirDrive": acc.dir_drive,
 
81
        "samba3MungedDial": acc.munged_dial,
 
82
        "samba3Homedir": acc.homedir, 
 
83
        "samba3LogonScript": acc.logon_script, 
 
84
        "samba3ProfilePath": acc.profile_path,
 
85
        "samba3Workstations": acc.workstations,
 
86
        "samba3KickOffTime": str(acc.kickoff_time),
 
87
        "samba3BadPwdTime": str(acc.bad_password_time),
 
88
        "samba3PassLastSetTime": str(acc.pass_last_set_time),
 
89
        "samba3PassCanChangeTime": str(acc.pass_can_change_time),
 
90
        "samba3PassMustChangeTime": str(acc.pass_must_change_time),
 
91
        "objectSid": "%s-%d" % (domainsid, acc.user_rid),
 
92
        "lmPwdHash:": acc.lm_password,
 
93
        "ntPwdHash:": acc.nt_password,
 
94
        })
 
95
 
 
96
 
 
97
def import_sam_group(samldb, sid, gid, sid_name_use, nt_name, comment, domaindn):
 
98
    """Upgrade a SAM group.
 
99
    
 
100
    :param samldb: SAM database.
 
101
    :param gid: Group GID
 
102
    :param sid_name_use: SID name use
 
103
    :param nt_name: NT Group Name
 
104
    :param comment: NT Group Comment
 
105
    :param domaindn: Domain DN
 
106
    """
 
107
 
 
108
    if sid_name_use == 5: # Well-known group
 
109
        return None
 
110
 
 
111
    if nt_name in ("Domain Guests", "Domain Users", "Domain Admins"):
 
112
        return None
 
113
    
 
114
    if gid == -1:
 
115
        gr = grp.getgrnam(nt_name)
 
116
    else:
 
117
        gr = grp.getgrgid(gid)
 
118
 
 
119
    if gr is None:
 
120
        unixname = "UNKNOWN"
 
121
    else:
 
122
        unixname = gr.gr_name
 
123
 
 
124
    assert unixname is not None
 
125
    
 
126
    samldb.add({
 
127
        "dn": "cn=%s,%s" % (nt_name, domaindn),
 
128
        "objectClass": ["top", "group"],
 
129
        "description": comment,
 
130
        "cn": nt_name, 
 
131
        "objectSid": sid,
 
132
        "unixName": unixname,
 
133
        "samba3SidNameUse": str(sid_name_use)
 
134
        })
 
135
 
 
136
 
 
137
def import_idmap(samdb,samba3_idmap,domaindn):
 
138
    """Import idmap data.
 
139
 
 
140
    :param samdb: SamDB handle.
 
141
    :param samba3_idmap: Samba 3 IDMAP database to import from
 
142
    :param domaindn: Domain DN.
 
143
    """
 
144
    samdb.add({
 
145
        "dn": domaindn,
 
146
        "userHwm": str(samba3_idmap.get_user_hwm()),
 
147
        "groupHwm": str(samba3_idmap.get_group_hwm())})
 
148
 
 
149
    for uid in samba3_idmap.uids():
 
150
        samdb.add({"dn": "SID=%s,%s" % (samba3_idmap.get_user_sid(uid), domaindn),
 
151
                          "SID": samba3_idmap.get_user_sid(uid),
 
152
                          "type": "user",
 
153
                          "unixID": str(uid)})
 
154
 
 
155
    for gid in samba3_idmap.uids():
 
156
        samdb.add({"dn": "SID=%s,%s" % (samba3_idmap.get_group_sid(gid), domaindn),
 
157
                          "SID": samba3_idmap.get_group_sid(gid),
 
158
                          "type": "group",
 
159
                          "unixID": str(gid)})
 
160
 
 
161
 
 
162
def import_wins(samba4_winsdb, samba3_winsdb):
 
163
    """Import settings from a Samba3 WINS database.
 
164
    
 
165
    :param samba4_winsdb: WINS database to import to
 
166
    :param samba3_winsdb: WINS database to import from
 
167
    """
 
168
    version_id = 0
 
169
 
 
170
    for (name, (ttl, ips, nb_flags)) in samba3_winsdb.items():
 
171
        version_id+=1
 
172
 
 
173
        type = int(name.split("#", 1)[1], 16)
 
174
 
 
175
        if type == 0x1C:
 
176
            rType = 0x2
 
177
        elif type & 0x80:
 
178
            if len(ips) > 1:
 
179
                rType = 0x2
 
180
            else:
 
181
                rType = 0x1
 
182
        else:
 
183
            if len(ips) > 1:
 
184
                rType = 0x3
 
185
            else:
 
186
                rType = 0x0
 
187
 
 
188
        if ttl > time.time():
 
189
            rState = 0x0 # active
 
190
        else:
 
191
            rState = 0x1 # released
 
192
 
 
193
        nType = ((nb_flags & 0x60)>>5)
 
194
 
 
195
        samba4_winsdb.add({"dn": "name=%s,type=0x%s" % tuple(name.split("#")),
 
196
                           "type": name.split("#")[1],
 
197
                           "name": name.split("#")[0],
 
198
                           "objectClass": "winsRecord",
 
199
                           "recordType": str(rType),
 
200
                           "recordState": str(rState),
 
201
                           "nodeType": str(nType),
 
202
                           "expireTime": ldb.timestring(ttl),
 
203
                           "isStatic": "0",
 
204
                           "versionID": str(version_id),
 
205
                           "address": ips})
 
206
 
 
207
    samba4_winsdb.add({"dn": "cn=VERSION",
 
208
                       "cn": "VERSION",
 
209
                       "objectClass": "winsMaxVersion",
 
210
                       "maxVersion": str(version_id)})
 
211
 
 
212
def upgrade_provision(samba3, setup_dir, message, credentials, session_info, smbconf, targetdir):
 
213
    oldconf = samba3.get_conf()
 
214
 
 
215
    if oldconf.get("domain logons") == "True":
 
216
        serverrole = "domain controller"
 
217
    else:
 
218
        if oldconf.get("security") == "user":
 
219
            serverrole = "standalone"
 
220
        else:
 
221
            serverrole = "member server"
 
222
 
 
223
    domainname = oldconf.get("workgroup")
 
224
    if domainname:
 
225
        domainname = str(domainname)
 
226
    realm = oldconf.get("realm")
 
227
    netbiosname = oldconf.get("netbios name")
 
228
 
 
229
    secrets_db = samba3.get_secrets_db()
 
230
    
 
231
    if domainname is None:
 
232
        domainname = secrets_db.domains()[0]
 
233
        message("No domain specified in smb.conf file, assuming '%s'" % domainname)
 
234
    
 
235
    if realm is None:
 
236
        realm = domainname.lower()
 
237
        message("No realm specified in smb.conf file, assuming '%s'\n" % realm)
 
238
 
 
239
    domainguid = secrets_db.get_domain_guid(domainname)
 
240
    domainsid = secrets_db.get_sid(domainname)
 
241
    if domainsid is None:
 
242
        message("Can't find domain secrets for '%s'; using random SID\n" % domainname)
 
243
    
 
244
    if netbiosname is not None:
 
245
        machinepass = secrets_db.get_machine_password(netbiosname)
 
246
    else:
 
247
        machinepass = None
 
248
    
 
249
    result = provision(setup_dir=setup_dir, message=message, 
 
250
                       samdb_fill=FILL_DRS, smbconf=smbconf, session_info=session_info, 
 
251
                       credentials=credentials, realm=realm, 
 
252
                       domain=domainname, domainsid=domainsid, domainguid=domainguid, 
 
253
                       machinepass=machinepass, serverrole=serverrole, targetdir=targetdir)
 
254
 
 
255
    import_wins(Ldb(result.paths.winsdb), samba3.get_wins_db())
 
256
 
 
257
    # FIXME: import_registry(registry.Registry(), samba3.get_registry())
 
258
 
 
259
    # FIXME: import_idmap(samdb,samba3.get_idmap_db(),domaindn)
 
260
    
 
261
    groupdb = samba3.get_groupmapping_db()
 
262
    for sid in groupdb.groupsids():
 
263
        (gid, sid_name_use, nt_name, comment) = groupdb.get_group(sid)
 
264
        # FIXME: import_sam_group(samdb, sid, gid, sid_name_use, nt_name, comment, domaindn)
 
265
 
 
266
    # FIXME: Aliases
 
267
 
 
268
    passdb = samba3.get_sam_db()
 
269
    for name in passdb:
 
270
        user = passdb[name]
 
271
        #FIXME: import_sam_account(result.samdb, user, domaindn, domainsid)
 
272
 
 
273
    if hasattr(passdb, 'ldap_url'):
 
274
        message("Enabling Samba3 LDAP mappings for SAM database")
 
275
 
 
276
        enable_samba3sam(result.samdb, passdb.ldap_url)
 
277
 
 
278
 
 
279
def enable_samba3sam(samdb, ldapurl):
 
280
    """Enable Samba 3 LDAP URL database.
 
281
 
 
282
    :param samdb: SAM Database.
 
283
    :param ldapurl: Samba 3 LDAP URL
 
284
    """
 
285
    samdb.modify_ldif("""
 
286
dn: @MODULES
 
287
changetype: modify
 
288
replace: @LIST
 
289
@LIST: samldb,operational,objectguid,rdn_name,samba3sam
 
290
""")
 
291
 
 
292
    samdb.add({"dn": "@MAP=samba3sam", "@MAP_URL": ldapurl})
 
293
 
 
294
 
 
295
smbconf_keep = [
 
296
    "dos charset", 
 
297
    "unix charset",
 
298
    "display charset",
 
299
    "comment",
 
300
    "path",
 
301
    "directory",
 
302
    "workgroup",
 
303
    "realm",
 
304
    "netbios name",
 
305
    "netbios aliases",
 
306
    "netbios scope",
 
307
    "server string",
 
308
    "interfaces",
 
309
    "bind interfaces only",
 
310
    "security",
 
311
    "auth methods",
 
312
    "encrypt passwords",
 
313
    "null passwords",
 
314
    "obey pam restrictions",
 
315
    "password server",
 
316
    "smb passwd file",
 
317
    "private dir",
 
318
    "passwd chat",
 
319
    "password level",
 
320
    "lanman auth",
 
321
    "ntlm auth",
 
322
    "client NTLMv2 auth",
 
323
    "client lanman auth",
 
324
    "client plaintext auth",
 
325
    "read only",
 
326
    "hosts allow",
 
327
    "hosts deny",
 
328
    "log level",
 
329
    "debuglevel",
 
330
    "log file",
 
331
    "smb ports",
 
332
    "large readwrite",
 
333
    "max protocol",
 
334
    "min protocol",
 
335
    "unicode",
 
336
    "read raw",
 
337
    "write raw",
 
338
    "disable netbios",
 
339
    "nt status support",
 
340
    "announce version",
 
341
    "announce as",
 
342
    "max mux",
 
343
    "max xmit",
 
344
    "name resolve order",
 
345
    "max wins ttl",
 
346
    "min wins ttl",
 
347
    "time server",
 
348
    "unix extensions",
 
349
    "use spnego",
 
350
    "server signing",
 
351
    "client signing",
 
352
    "max connections",
 
353
    "paranoid server security",
 
354
    "socket options",
 
355
    "strict sync",
 
356
    "max print jobs",
 
357
    "printable",
 
358
    "print ok",
 
359
    "printer name",
 
360
    "printer",
 
361
    "map system",
 
362
    "map hidden",
 
363
    "map archive",
 
364
    "preferred master",
 
365
    "prefered master",
 
366
    "local master",
 
367
    "browseable",
 
368
    "browsable",
 
369
    "wins server",
 
370
    "wins support",
 
371
    "csc policy",
 
372
    "strict locking",
 
373
    "preload",
 
374
    "auto services",
 
375
    "lock dir",
 
376
    "lock directory",
 
377
    "pid directory",
 
378
    "socket address",
 
379
    "copy",
 
380
    "include",
 
381
    "available",
 
382
    "volume",
 
383
    "fstype",
 
384
    "panic action",
 
385
    "msdfs root",
 
386
    "host msdfs",
 
387
    "winbind separator"]
 
388
 
 
389
def upgrade_smbconf(oldconf,mark):
 
390
    """Remove configuration variables not present in Samba4
 
391
 
 
392
    :param oldconf: Old configuration structure
 
393
    :param mark: Whether removed configuration variables should be 
 
394
        kept in the new configuration as "samba3:<name>"
 
395
    """
 
396
    data = oldconf.data()
 
397
    newconf = param_init()
 
398
 
 
399
    for s in data:
 
400
        for p in data[s]:
 
401
            keep = False
 
402
            for k in smbconf_keep:
 
403
                if smbconf_keep[k] == p:
 
404
                    keep = True
 
405
                    break
 
406
 
 
407
            if keep:
 
408
                newconf.set(s, p, oldconf.get(s, p))
 
409
            elif mark:
 
410
                newconf.set(s, "samba3:"+p, oldconf.get(s,p))
 
411
 
 
412
    return newconf
 
413
 
 
414
SAMBA3_PREDEF_NAMES = {
 
415
        'HKLM': registry.HKEY_LOCAL_MACHINE,
 
416
}
 
417
 
 
418
def import_registry(samba4_registry, samba3_regdb):
 
419
    """Import a Samba 3 registry database into the Samba 4 registry.
 
420
 
 
421
    :param samba4_registry: Samba 4 registry handle.
 
422
    :param samba3_regdb: Samba 3 registry database handle.
 
423
    """
 
424
    def ensure_key_exists(keypath):
 
425
        (predef_name, keypath) = keypath.split("/", 1)
 
426
        predef_id = SAMBA3_PREDEF_NAMES[predef_name]
 
427
        keypath = keypath.replace("/", "\\")
 
428
        return samba4_registry.create_key(predef_id, keypath)
 
429
 
 
430
    for key in samba3_regdb.keys():
 
431
        key_handle = ensure_key_exists(key)
 
432
        for subkey in samba3_regdb.subkeys(key):
 
433
            ensure_key_exists(subkey)
 
434
        for (value_name, (value_type, value_data)) in samba3_regdb.values(key).items():
 
435
            key_handle.set_value(value_name, value_type, value_data)
 
436
 
 
437