3 # Unix SMB/CIFS implementation.
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 """Support for reading Samba 3 data files."""
22 REGISTRY_VALUE_PREFIX = "SAMBA_REGVAL"
23 REGISTRY_DB_VERSION = 1
29 """Simple read-only support for reading the Samba3 registry."""
30 def __init__(self, file):
31 self.tdb = tdb.Tdb(file, flags=os.O_RDONLY)
37 """Return the number of keys."""
38 return len(self.keys())
41 """Return list with all the keys."""
42 return [k.rstrip("\x00") for k in self.tdb.keys() if not k.startswith(REGISTRY_VALUE_PREFIX)]
44 def subkeys(self, key):
45 data = self.tdb.get("%s\x00" % key)
51 def values(self, key):
52 """Return a dictionary with the values set for a specific key."""
53 data = self.tdb.get("%s/%s\x00" % (REGISTRY_VALUE_PREFIX, key))
61 def __init__(self, file):
62 self.tdb = tdb.Tdb(file, flags=os.O_RDONLY)
63 self.min_password_length = self.tdb.fetch_uint32("min password length\x00")
64 self.password_history = self.tdb.fetch_uint32("password history\x00")
65 self.user_must_logon_to_change_password = self.tdb.fetch_uint32("user must logon to change pasword\x00")
66 self.maximum_password_age = self.tdb.fetch_uint32("maximum password age\x00")
67 self.minimum_password_age = self.tdb.fetch_uint32("minimum password age\x00")
68 self.lockout_duration = self.tdb.fetch_uint32("lockout duration\x00")
69 self.reset_count_minutes = self.tdb.fetch_uint32("reset count minutes\x00")
70 self.bad_lockout_minutes = self.tdb.fetch_uint32("bad lockout minutes\x00")
71 self.disconnect_time = self.tdb.fetch_int32("disconnect time\x00")
72 self.refuse_machine_password_change = self.tdb.fetch_uint32("refuse machine password change\x00")
74 # FIXME: Read privileges as well
80 GROUPDB_DATABASE_VERSION_V1 = 1 # native byte format.
81 GROUPDB_DATABASE_VERSION_V2 = 2 # le format.
83 GROUP_PREFIX = "UNIXGROUP/"
85 # Alias memberships are stored reverse, as memberships. The performance
86 # critical operation is to determine the aliases a SID is member of, not
87 # listing alias members. So we store a list of alias SIDs a SID is member of
88 # hanging of the member as key.
89 MEMBEROF_PREFIX = "MEMBEROF/"
91 class GroupMappingDatabase:
92 def __init__(self, file):
93 self.tdb = tdb.Tdb(file, flags=os.O_RDONLY)
94 assert self.tdb.fetch_int32("INFO/version\x00") in (GROUPDB_DATABASE_VERSION_V1, GROUPDB_DATABASE_VERSION_V2)
97 for k in self.tdb.keys():
98 if k.startswith(GROUP_PREFIX):
99 yield k[len(GROUP_PREFIX):].rstrip("\0")
102 for k in self.tdb.keys():
103 if k.startswith(MEMBEROF_PREFIX):
104 yield k[len(MEMBEROF_PREFIX):].rstrip("\0")
110 # High water mark keys
111 IDMAP_HWM_GROUP = "GROUP HWM\0"
112 IDMAP_HWM_USER = "USER HWM\0"
114 IDMAP_GROUP_PREFIX = "GID "
115 IDMAP_USER_PREFIX = "UID "
117 # idmap version determines auto-conversion
121 def __init__(self, file):
122 self.tdb = tdb.Tdb(file, flags=os.O_RDONLY)
123 assert self.tdb.fetch_int32("IDMAP_VERSION\0") == IDMAP_VERSION_V2
126 for k in self.tdb.keys():
127 if k.startswith(IDMAP_USER_PREFIX):
128 yield int(k[len(IDMAP_USER_PREFIX):].rstrip("\0"))
131 for k in self.tdb.keys():
132 if k.startswith(IDMAP_GROUP_PREFIX):
133 yield int(k[len(IDMAP_GROUP_PREFIX):].rstrip("\0"))
135 def get_user_sid(self, uid):
136 data = self.tdb.get("%s%d\0" % (IDMAP_USER_PREFIX, uid))
139 return data.rstrip("\0")
141 def get_group_sid(self, gid):
142 data = self.tdb.get("%s%d\0" % (IDMAP_GROUP_PREFIX, gid))
145 return data.rstrip("\0")
147 def get_user_hwm(self):
148 return self.tdb.fetch_uint32(IDMAP_HWM_USER)
150 def get_group_hwm(self):
151 return self.tdb.fetch_uint32(IDMAP_HWM_GROUP)
157 class SecretsDatabase:
158 def __init__(self, file):
159 self.tdb = tdb.Tdb(file, flags=os.O_RDONLY)
161 def get_auth_password(self):
162 return self.tdb.get("SECRETS/AUTH_PASSWORD")
164 def get_auth_domain(self):
165 return self.tdb.get("SECRETS/AUTH_DOMAIN")
167 def get_auth_user(self):
168 return self.tdb.get("SECRETS/AUTH_USER")
170 def get_dom_guid(self, host):
171 return self.tdb.get("SECRETS/DOMGUID/%s" % host)
174 for k in self.tdb.keys():
175 if k.startswith("SECRETS/LDAP_BIND_PW/"):
176 yield k[len("SECRETS/LDAP_BIND_PW/"):].rstrip("\0")
179 for k in self.tdb.keys():
180 if k.startswith("SECRETS/SID/"):
181 yield k[len("SECRETS/SID/"):].rstrip("\0")
183 def get_ldap_bind_pw(self, host):
184 return self.tdb.get("SECRETS/LDAP_BIND_PW/%s" % host)
186 def get_afs_keyfile(self, host):
187 return self.tdb.get("SECRETS/AFS_KEYFILE/%s" % host)
189 def get_machine_sec_channel_type(self, host):
190 return self.tdb.fetch_uint32("SECRETS/MACHINE_SEC_CHANNEL_TYPE/%s" % host)
192 def get_machine_last_change_time(self, host):
193 return self.tdb.fetch_uint32("SECRETS/MACHINE_LAST_CHANGE_TIME/%s" % host)
195 def get_machine_password(self, host):
196 return self.tdb.get("SECRETS/MACHINE_PASSWORD/%s" % host)
198 def get_machine_acc(self, host):
199 return self.tdb.get("SECRETS/$MACHINE.ACC/%s" % host)
201 def get_domtrust_acc(self, host):
202 return self.tdb.get("SECRETS/$DOMTRUST.ACC/%s" % host)
204 def trusted_domains(self):
205 for k in self.tdb.keys():
206 if k.startswith("SECRETS/$DOMTRUST.ACC/"):
207 yield k[len("SECRETS/$DOMTRUST.ACC/"):].rstrip("\0")
209 def get_random_seed(self):
210 return self.tdb.get("INFO/random_seed")
212 def get_sid(self, host):
213 return self.tdb.get("SECRETS/SID/%s" % host.upper())
219 SHARE_DATABASE_VERSION_V1 = 1
220 SHARE_DATABASE_VERSION_V2 = 2
222 class ShareInfoDatabase:
223 def __init__(self, file):
224 self.tdb = tdb.Tdb(file, flags=os.O_RDONLY)
225 assert self.tdb.fetch_int32("INFO/version\0") in (SHARE_DATABASE_VERSION_V1, SHARE_DATABASE_VERSION_V2)
227 def get_secdesc(self, name):
228 secdesc = self.tdb.get("SECDESC/%s" % name)
229 # FIXME: Run ndr_pull_security_descriptor
237 def __init__(self, lp, shareinfo):
239 self.shareinfo = shareinfo
242 return len(self.lp) - 1
245 return self.lp.__iter__()
248 ACB_DISABLED = 0x00000001
249 ACB_HOMDIRREQ = 0x00000002
250 ACB_PWNOTREQ = 0x00000004
251 ACB_TEMPDUP = 0x00000008
252 ACB_NORMAL = 0x00000010
254 ACB_DOMTRUST = 0x00000040
255 ACB_WSTRUST = 0x00000080
256 ACB_SVRTRUST = 0x00000100
257 ACB_PWNOEXP = 0x00000200
258 ACB_AUTOLOCK = 0x00000400
259 ACB_ENC_TXT_PWD_ALLOWED = 0x00000800
260 ACB_SMARTCARD_REQUIRED = 0x00001000
261 ACB_TRUSTED_FOR_DELEGATION = 0x00002000
262 ACB_NOT_DELEGATED = 0x00004000
263 ACB_USE_DES_KEY_ONLY = 0x00008000
264 ACB_DONT_REQUIRE_PREAUTH = 0x00010000
265 ACB_PW_EXPIRED = 0x00020000
266 ACB_NO_AUTH_DATA_REQD = 0x00080000
269 'N': ACB_PWNOTREQ, # 'N'o password.
270 'D': ACB_DISABLED, # 'D'isabled.
271 'H': ACB_HOMDIRREQ, # 'H'omedir required.
272 'T': ACB_TEMPDUP, # 'T'emp account.
273 'U': ACB_NORMAL, # 'U'ser account (normal).
274 'M': ACB_MNS, # 'M'NS logon user account. What is this ?
275 'W': ACB_WSTRUST, # 'W'orkstation account.
276 'S': ACB_SVRTRUST, # 'S'erver account.
277 'L': ACB_AUTOLOCK, # 'L'ocked account.
278 'X': ACB_PWNOEXP, # No 'X'piry on password
279 'I': ACB_DOMTRUST, # 'I'nterdomain trust account.
283 def decode_acb(text):
284 assert not "[" in text and not "]" in text
287 ret |= acb_info_mapping[x]
292 def __init__(self, file):
295 for l in f.readlines():
296 if len(l) == 0 or l[0] == "#":
297 continue # Skip comments and blank lines
302 last_change_time = None
303 if parts[2] == "NO PASSWORD":
304 acct_ctrl |= ACB_PWNOTREQ
306 elif parts[2][0] in ("*", "X"):
310 lm_password = parts[2]
312 if parts[3][0] in ("*", "X"):
316 nt_password = parts[3]
318 if parts[4][0] == '[':
319 assert "]" in parts[4]
320 acct_ctrl |= decode_acb(parts[4][1:-1])
321 if parts[5].startswith("LCT-"):
322 last_change_time = int(parts[5][len("LCT-"):], 16)
323 else: # old style file
324 if username[-1] == "$":
325 acct_ctrl &= ~ACB_NORMAL
326 acct_ctrl |= ACB_WSTRUST
328 self.users[username] = (uid, lm_password, nt_password, acct_ctrl, last_change_time)
333 return len(self.users)
335 def __getitem__(self, name):
336 return self.users[name]
339 return iter(self.users)
341 def close(self): # For consistency
345 TDBSAM_FORMAT_STRING_V0 = "ddddddBBBBBBBBBBBBddBBwdwdBwwd"
346 TDBSAM_FORMAT_STRING_V1 = "dddddddBBBBBBBBBBBBddBBwdwdBwwd"
347 TDBSAM_FORMAT_STRING_V2 = "dddddddBBBBBBBBBBBBddBBBwwdBwwd"
348 TDBSAM_USER_PREFIX = "USER_"
352 def __init__(self, file):
353 self.tdb = tdb.Tdb(file, flags=os.O_RDONLY)
354 self.version = self.tdb.fetch_uint32("INFO/version") or 0
355 assert self.version in (0, 1, 2)
358 for k in self.tdb.keys():
359 if k.startswith(TDBSAM_USER_PREFIX):
360 yield k[len(TDBSAM_USER_PREFIX):].rstrip("\0")
368 def shellsplit(text):
369 """Very simple shell-like line splitting.
371 :param text: Text to split.
372 :return: List with parts of the line as strings.
379 inquotes = not inquotes
380 elif c in ("\t", "\n", " ") and not inquotes:
391 def __init__(self, file):
394 assert f.readline().rstrip("\n") == "VERSION 1 0"
395 for l in f.readlines():
396 if l[0] == "#": # skip comments
398 entries = shellsplit(l.rstrip("\n"))
400 ttl = int(entries[1])
403 while "." in entries[i]:
404 ips.append(entries[i])
406 nb_flags = entries[i]
407 assert not name in self.entries, "Name %s exists twice" % name
408 self.entries[name] = (ttl, ips, nb_flags)
411 def __getitem__(self, name):
412 return self.entries[name]
415 return len(self.entries)
418 return iter(self.entries)
420 def close(self): # for consistency
424 def __init__(self, libdir, smbconfpath):
425 self.smbconfpath = smbconfpath
428 self.lp = param.ParamFile()
429 self.lp.read(self.smbconfpath)
431 def libdir_path(self, path):
432 if path[0] == "/" or path[0] == ".":
434 return os.path.join(self.libdir, path)
439 def get_sam_db(self):
441 backends = str(lp.get("passdb backend")).split(" ")
442 if backends[0].startswith("smbpasswd"):
443 if ":" in backends[0]:
444 return SmbpasswdFile(self.libdir_path(backends[0][len("smbpasswd:"):]))
445 return SmbpasswdFile(self.libdir_path("smbpasswd"))
446 elif backends[0].startswith("tdbsam"):
447 if ":" in backends[0]:
448 return TdbSam(self.libdir_path(backends[0][len("tdbsam:"):]))
449 return TdbSam(self.libdir_path("passdb.tdb"))
451 raise NotImplementedError("unsupported passdb backend %s" % backends[0])
453 def get_policy_db(self):
454 return PolicyDatabase(self.libdir_path("account_policy.tdb"))
456 def get_registry(self):
457 return Registry(self.libdir_path("registry.tdb"))
459 def get_secrets_db(self):
460 return SecretsDatabase(self.libdir_path("secrets.tdb"))
462 def get_shareinfo_db(self):
463 return ShareInfoDatabase(self.libdir_path("share_info.tdb"))
465 def get_idmap_db(self):
466 return IdmapDatabase(self.libdir_path("winbindd_idmap.tdb"))
468 def get_wins_db(self):
469 return WinsDatabase(self.libdir_path("wins.dat"))
471 def get_shares(self):
472 return Shares(self.get_conf(), self.get_shareinfo_db())
474 def get_groupmapping_db(self):
475 return GroupMappingDatabase(self.libdir_path("group_mapping.tdb"))