s4:samba-tool/gpo: use the dns_domain from the server when creating gpos
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / netcmd / gpo.py
blob5169085635012b35eaf03819929d1617c04dc288
1 # implement samba_tool gpo commands
3 # Copyright Andrew Tridgell 2010
4 # Copyright Amitay Isaacs 2011-2012 <amitay@gmail.com>
6 # based on C implementation by Guenther Deschner and Wilco Baan Hofman
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 import os
23 import samba.getopt as options
24 import ldb
26 from samba.auth import system_session
27 from samba.netcmd import (
28 Command,
29 CommandError,
30 Option,
31 SuperCommand,
33 from samba.samdb import SamDB
34 from samba import dsdb
35 from samba.dcerpc import security
36 from samba.ndr import ndr_unpack
37 import samba.security
38 import samba.auth
39 from samba.auth import AUTH_SESSION_INFO_DEFAULT_GROUPS, AUTH_SESSION_INFO_AUTHENTICATED, AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
40 from samba.netcmd.common import netcmd_finddc
41 from samba import policy
42 from samba import smb
43 import uuid
44 from samba.ntacls import dsacl2fsacl
45 from samba.dcerpc import nbt
46 from samba.net import Net
49 def samdb_connect(ctx):
50 '''make a ldap connection to the server'''
51 try:
52 ctx.samdb = SamDB(url=ctx.url,
53 session_info=system_session(),
54 credentials=ctx.creds, lp=ctx.lp)
55 except Exception, e:
56 raise CommandError("LDAP connection to %s failed " % ctx.url, e)
59 def attr_default(msg, attrname, default):
60 '''get an attribute from a ldap msg with a default'''
61 if attrname in msg:
62 return msg[attrname][0]
63 return default
66 def gpo_flags_string(value):
67 '''return gpo flags string'''
68 flags = policy.get_gpo_flags(value)
69 if not flags:
70 ret = 'NONE'
71 else:
72 ret = ' '.join(flags)
73 return ret
76 def gplink_options_string(value):
77 '''return gplink options string'''
78 options = policy.get_gplink_options(value)
79 if not options:
80 ret = 'NONE'
81 else:
82 ret = ' '.join(options)
83 return ret
86 def parse_gplink(gplink):
87 '''parse a gPLink into an array of dn and options'''
88 ret = []
89 a = gplink.split(']')
90 for g in a:
91 if not g:
92 continue
93 d = g.split(';')
94 if len(d) != 2 or not d[0].startswith("[LDAP://"):
95 raise RuntimeError("Badly formed gPLink '%s'" % g)
96 ret.append({ 'dn' : d[0][8:], 'options' : int(d[1])})
97 return ret
100 def encode_gplink(gplist):
101 '''Encode an array of dn and options into gPLink string'''
102 ret = ''
103 for g in gplist:
104 ret += "[LDAP://%s;%d]" % (g['dn'], g['options'])
105 return ret
108 def dc_url(lp, creds, url=None, dc=None):
109 '''If URL is not specified, return URL for writable DC.
110 If dc is provided, use that to construct ldap URL'''
112 if url is None:
113 if dc is None:
114 try:
115 dc = netcmd_finddc(lp, creds)
116 except Exception, e:
117 raise RuntimeError("Could not find a DC for domain", e)
118 url = 'ldap://' + dc
119 return url
122 def get_gpo_dn(samdb, gpo):
123 '''Construct the DN for gpo'''
125 dn = samdb.get_default_basedn()
126 dn.add_child(ldb.Dn(samdb, "CN=Policies,CN=System"))
127 dn.add_child(ldb.Dn(samdb, "CN=%s" % gpo))
128 return dn
131 def get_gpo_info(samdb, gpo=None, displayname=None, dn=None,
132 sd_flags=security.SECINFO_OWNER|security.SECINFO_GROUP|security.SECINFO_DACL|security.SECINFO_SACL):
133 '''Get GPO information using gpo, displayname or dn'''
135 policies_dn = samdb.get_default_basedn()
136 policies_dn.add_child(ldb.Dn(samdb, "CN=Policies,CN=System"))
138 base_dn = policies_dn
139 search_expr = "(objectClass=groupPolicyContainer)"
140 search_scope = ldb.SCOPE_ONELEVEL
142 if gpo is not None:
143 search_expr = "(&(objectClass=groupPolicyContainer)(name=%s))" % ldb.binary_encode(gpo)
145 if displayname is not None:
146 search_expr = "(&(objectClass=groupPolicyContainer)(displayname=%s))" % ldb.binary_encode(displayname)
148 if dn is not None:
149 base_dn = dn
150 search_scope = ldb.SCOPE_BASE
152 try:
153 msg = samdb.search(base=base_dn, scope=search_scope,
154 expression=search_expr,
155 attrs=['nTSecurityDescriptor',
156 'versionNumber',
157 'flags',
158 'name',
159 'displayName',
160 'gPCFileSysPath'],
161 controls=['sd_flags:1:%d' % sd_flags])
162 except Exception, e:
163 if gpo is not None:
164 mesg = "Cannot get information for GPO %s" % gpo
165 else:
166 mesg = "Cannot get information for GPOs"
167 raise CommandError(mesg, e)
169 return msg
172 def get_gpo_containers(samdb, gpo):
173 '''lists dn of containers for a GPO'''
175 search_expr = "(&(objectClass=*)(gPLink=*%s*))" % gpo
176 try:
177 msg = samdb.search(expression=search_expr, attrs=['gPLink'])
178 except Exception, e:
179 raise CommandError("Could not find container(s) with GPO %s" % gpo, e)
181 return msg
184 def del_gpo_link(samdb, container_dn, gpo):
185 '''delete GPO link for the container'''
186 # Check if valid Container DN and get existing GPlinks
187 try:
188 msg = samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
189 expression="(objectClass=*)",
190 attrs=['gPLink'])[0]
191 except Exception, e:
192 raise CommandError("Container '%s' does not exist" % container_dn, e)
194 found = False
195 gpo_dn = str(get_gpo_dn(samdb, gpo))
196 if 'gPLink' in msg:
197 gplist = parse_gplink(msg['gPLink'][0])
198 for g in gplist:
199 if g['dn'].lower() == gpo_dn.lower():
200 gplist.remove(g)
201 found = True
202 break
203 else:
204 raise CommandError("No GPO(s) linked to this container")
206 if not found:
207 raise CommandError("GPO '%s' not linked to this container" % gpo)
209 m = ldb.Message()
210 m.dn = container_dn
211 if gplist:
212 gplink_str = encode_gplink(gplist)
213 m['r0'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_REPLACE, 'gPLink')
214 else:
215 m['d0'] = ldb.MessageElement(msg['gPLink'][0], ldb.FLAG_MOD_DELETE, 'gPLink')
216 try:
217 samdb.modify(m)
218 except Exception, e:
219 raise CommandError("Error removing GPO from container", e)
222 def parse_unc(unc):
223 '''Parse UNC string into a hostname, a service, and a filepath'''
224 if unc.startswith('\\\\') and unc.startswith('//'):
225 raise ValueError("UNC doesn't start with \\\\ or //")
226 tmp = unc[2:].split('/', 2)
227 if len(tmp) == 3:
228 return tmp
229 tmp = unc[2:].split('\\', 2)
230 if len(tmp) == 3:
231 return tmp
232 raise ValueError("Invalid UNC string: %s" % unc)
235 def copy_directory_remote_to_local(conn, remotedir, localdir):
236 if not os.path.isdir(localdir):
237 os.mkdir(localdir)
238 r_dirs = [ remotedir ]
239 l_dirs = [ localdir ]
240 while r_dirs:
241 r_dir = r_dirs.pop()
242 l_dir = l_dirs.pop()
244 dirlist = conn.list(r_dir)
245 for e in dirlist:
246 r_name = r_dir + '\\' + e['name']
247 l_name = os.path.join(l_dir, e['name'])
249 if e['attrib'] & smb.FILE_ATTRIBUTE_DIRECTORY:
250 r_dirs.append(r_name)
251 l_dirs.append(l_name)
252 os.mkdir(l_name)
253 else:
254 data = conn.loadfile(r_name)
255 file(l_name, 'w').write(data)
258 def copy_directory_local_to_remote(conn, localdir, remotedir):
259 if not conn.chkpath(remotedir):
260 conn.mkdir(remotedir)
261 l_dirs = [ localdir ]
262 r_dirs = [ remotedir ]
263 while l_dirs:
264 l_dir = l_dirs.pop()
265 r_dir = r_dirs.pop()
267 dirlist = os.listdir(l_dir)
268 for e in dirlist:
269 l_name = os.path.join(l_dir, e)
270 r_name = r_dir + '\\' + e
272 if os.path.isdir(l_name):
273 l_dirs.append(l_name)
274 r_dirs.append(r_name)
275 conn.mkdir(r_name)
276 else:
277 data = file(l_name, 'r').read()
278 conn.savefile(r_name, data)
281 def create_directory_hier(conn, remotedir):
282 elems = remotedir.replace('/', '\\').split('\\')
283 path = ""
284 for e in elems:
285 path = path + '\\' + e
286 if not conn.chkpath(path):
287 conn.mkdir(path)
290 class cmd_listall(Command):
291 """List all GPOs."""
293 synopsis = "%prog [options]"
295 takes_optiongroups = {
296 "sambaopts": options.SambaOptions,
297 "versionopts": options.VersionOptions,
298 "credopts": options.CredentialsOptions,
301 takes_options = [
302 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
303 metavar="URL", dest="H")
306 def run(self, H=None, sambaopts=None, credopts=None, versionopts=None):
308 self.lp = sambaopts.get_loadparm()
309 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
311 self.url = dc_url(self.lp, self.creds, H)
313 samdb_connect(self)
315 msg = get_gpo_info(self.samdb, None)
317 for m in msg:
318 self.outf.write("GPO : %s\n" % m['name'][0])
319 self.outf.write("display name : %s\n" % m['displayName'][0])
320 self.outf.write("path : %s\n" % m['gPCFileSysPath'][0])
321 self.outf.write("dn : %s\n" % m.dn)
322 self.outf.write("version : %s\n" % attr_default(m, 'versionNumber', '0'))
323 self.outf.write("flags : %s\n" % gpo_flags_string(int(attr_default(m, 'flags', 0))))
324 self.outf.write("\n")
327 class cmd_list(Command):
328 """List GPOs for an account."""
330 synopsis = "%prog <username> [options]"
332 takes_args = ['username']
333 takes_optiongroups = {
334 "sambaopts": options.SambaOptions,
335 "versionopts": options.VersionOptions,
336 "credopts": options.CredentialsOptions,
339 takes_options = [
340 Option("-H", "--URL", help="LDB URL for database or target server",
341 type=str, metavar="URL", dest="H")
344 def run(self, username, H=None, sambaopts=None, credopts=None, versionopts=None):
346 self.lp = sambaopts.get_loadparm()
347 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
349 self.url = dc_url(self.lp, self.creds, H)
351 samdb_connect(self)
353 try:
354 msg = self.samdb.search(expression='(&(|(samAccountName=%s)(samAccountName=%s$))(objectClass=User))' %
355 (ldb.binary_encode(username),ldb.binary_encode(username)))
356 user_dn = msg[0].dn
357 except Exception:
358 raise CommandError("Failed to find account %s" % username)
360 # check if its a computer account
361 try:
362 msg = self.samdb.search(base=user_dn, scope=ldb.SCOPE_BASE, attrs=['objectClass'])[0]
363 is_computer = 'computer' in msg['objectClass']
364 except Exception:
365 raise CommandError("Failed to find objectClass for user %s" % username)
367 session_info_flags = ( AUTH_SESSION_INFO_DEFAULT_GROUPS |
368 AUTH_SESSION_INFO_AUTHENTICATED )
370 # When connecting to a remote server, don't look up the local privilege DB
371 if self.url is not None and self.url.startswith('ldap'):
372 session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
374 session = samba.auth.user_session(self.samdb, lp_ctx=self.lp, dn=user_dn,
375 session_info_flags=session_info_flags)
377 token = session.security_token
379 gpos = []
381 inherit = True
382 dn = ldb.Dn(self.samdb, str(user_dn)).parent()
383 while True:
384 msg = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['gPLink', 'gPOptions'])[0]
385 if 'gPLink' in msg:
386 glist = parse_gplink(msg['gPLink'][0])
387 for g in glist:
388 if not inherit and not (g['options'] & dsdb.GPLINK_OPT_ENFORCE):
389 continue
390 if g['options'] & dsdb.GPLINK_OPT_DISABLE:
391 continue
393 try:
394 sd_flags=security.SECINFO_OWNER|security.SECINFO_GROUP|security.SECINFO_DACL
395 gmsg = self.samdb.search(base=g['dn'], scope=ldb.SCOPE_BASE,
396 attrs=['name', 'displayName', 'flags',
397 'nTSecurityDescriptor'],
398 controls=['sd_flags:1:%d' % sd_flags])
399 secdesc_ndr = gmsg[0]['nTSecurityDescriptor'][0]
400 secdesc = ndr_unpack(security.descriptor, secdesc_ndr)
401 except Exception:
402 self.outf.write("Failed to fetch gpo object with nTSecurityDescriptor %s\n" %
403 g['dn'])
404 continue
406 try:
407 samba.security.access_check(secdesc, token,
408 security.SEC_STD_READ_CONTROL |
409 security.SEC_ADS_LIST |
410 security.SEC_ADS_READ_PROP)
411 except RuntimeError:
412 self.outf.write("Failed access check on %s\n" % msg.dn)
413 continue
415 # check the flags on the GPO
416 flags = int(attr_default(gmsg[0], 'flags', 0))
417 if is_computer and (flags & dsdb.GPO_FLAG_MACHINE_DISABLE):
418 continue
419 if not is_computer and (flags & dsdb.GPO_FLAG_USER_DISABLE):
420 continue
421 gpos.append((gmsg[0]['displayName'][0], gmsg[0]['name'][0]))
423 # check if this blocks inheritance
424 gpoptions = int(attr_default(msg, 'gPOptions', 0))
425 if gpoptions & dsdb.GPO_BLOCK_INHERITANCE:
426 inherit = False
428 if dn == self.samdb.get_default_basedn():
429 break
430 dn = dn.parent()
432 if is_computer:
433 msg_str = 'computer'
434 else:
435 msg_str = 'user'
437 self.outf.write("GPOs for %s %s\n" % (msg_str, username))
438 for g in gpos:
439 self.outf.write(" %s %s\n" % (g[0], g[1]))
442 class cmd_show(Command):
443 """Show information for a GPO."""
445 synopsis = "%prog <gpo> [options]"
447 takes_optiongroups = {
448 "sambaopts": options.SambaOptions,
449 "versionopts": options.VersionOptions,
450 "credopts": options.CredentialsOptions,
453 takes_args = ['gpo']
455 takes_options = [
456 Option("-H", help="LDB URL for database or target server", type=str)
459 def run(self, gpo, H=None, sambaopts=None, credopts=None, versionopts=None):
461 self.lp = sambaopts.get_loadparm()
462 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
464 self.url = dc_url(self.lp, self.creds, H)
466 samdb_connect(self)
468 try:
469 msg = get_gpo_info(self.samdb, gpo)[0]
470 except Exception:
471 raise CommandError("GPO '%s' does not exist" % gpo)
473 try:
474 secdesc_ndr = msg['nTSecurityDescriptor'][0]
475 secdesc = ndr_unpack(security.descriptor, secdesc_ndr)
476 secdesc_sddl = secdesc.as_sddl()
477 except Exception:
478 secdesc_sddl = "<hidden>"
480 self.outf.write("GPO : %s\n" % msg['name'][0])
481 self.outf.write("display name : %s\n" % msg['displayName'][0])
482 self.outf.write("path : %s\n" % msg['gPCFileSysPath'][0])
483 self.outf.write("dn : %s\n" % msg.dn)
484 self.outf.write("version : %s\n" % attr_default(msg, 'versionNumber', '0'))
485 self.outf.write("flags : %s\n" % gpo_flags_string(int(attr_default(msg, 'flags', 0))))
486 self.outf.write("ACL : %s\n" % secdesc_sddl)
487 self.outf.write("\n")
490 class cmd_getlink(Command):
491 """List GPO Links for a container."""
493 synopsis = "%prog <container_dn> [options]"
495 takes_optiongroups = {
496 "sambaopts": options.SambaOptions,
497 "versionopts": options.VersionOptions,
498 "credopts": options.CredentialsOptions,
501 takes_args = ['container_dn']
503 takes_options = [
504 Option("-H", help="LDB URL for database or target server", type=str)
507 def run(self, container_dn, H=None, sambaopts=None, credopts=None,
508 versionopts=None):
510 self.lp = sambaopts.get_loadparm()
511 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
513 self.url = dc_url(self.lp, self.creds, H)
515 samdb_connect(self)
517 try:
518 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
519 expression="(objectClass=*)",
520 attrs=['gPLink'])[0]
521 except Exception:
522 raise CommandError("Container '%s' does not exist" % container_dn)
524 if msg['gPLink']:
525 self.outf.write("GPO(s) linked to DN %s\n" % container_dn)
526 gplist = parse_gplink(msg['gPLink'][0])
527 for g in gplist:
528 msg = get_gpo_info(self.samdb, dn=g['dn'])
529 self.outf.write(" GPO : %s\n" % msg[0]['name'][0])
530 self.outf.write(" Name : %s\n" % msg[0]['displayName'][0])
531 self.outf.write(" Options : %s\n" % gplink_options_string(g['options']))
532 self.outf.write("\n")
533 else:
534 self.outf.write("No GPO(s) linked to DN=%s\n" % container_dn)
537 class cmd_setlink(Command):
538 """Add or update a GPO link to a container."""
540 synopsis = "%prog <container_dn> <gpo> [options]"
542 takes_optiongroups = {
543 "sambaopts": options.SambaOptions,
544 "versionopts": options.VersionOptions,
545 "credopts": options.CredentialsOptions,
548 takes_args = ['container_dn', 'gpo']
550 takes_options = [
551 Option("-H", help="LDB URL for database or target server", type=str),
552 Option("--disable", dest="disabled", default=False, action='store_true',
553 help="Disable policy"),
554 Option("--enforce", dest="enforced", default=False, action='store_true',
555 help="Enforce policy")
558 def run(self, container_dn, gpo, H=None, disabled=False, enforced=False,
559 sambaopts=None, credopts=None, versionopts=None):
561 self.lp = sambaopts.get_loadparm()
562 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
564 self.url = dc_url(self.lp, self.creds, H)
566 samdb_connect(self)
568 gplink_options = 0
569 if disabled:
570 gplink_options |= dsdb.GPLINK_OPT_DISABLE
571 if enforced:
572 gplink_options |= dsdb.GPLINK_OPT_ENFORCE
574 # Check if valid GPO DN
575 try:
576 msg = get_gpo_info(self.samdb, gpo=gpo)[0]
577 except Exception:
578 raise CommandError("GPO '%s' does not exist" % gpo)
579 gpo_dn = str(get_gpo_dn(self.samdb, gpo))
581 # Check if valid Container DN
582 try:
583 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
584 expression="(objectClass=*)",
585 attrs=['gPLink'])[0]
586 except Exception:
587 raise CommandError("Container '%s' does not exist" % container_dn)
589 # Update existing GPlinks or Add new one
590 existing_gplink = False
591 if 'gPLink' in msg:
592 gplist = parse_gplink(msg['gPLink'][0])
593 existing_gplink = True
594 found = False
595 for g in gplist:
596 if g['dn'].lower() == gpo_dn.lower():
597 g['options'] = gplink_options
598 found = True
599 break
600 if found:
601 raise CommandError("GPO '%s' already linked to this container" % gpo)
602 else:
603 gplist.insert(0, { 'dn' : gpo_dn, 'options' : gplink_options })
604 else:
605 gplist = []
606 gplist.append({ 'dn' : gpo_dn, 'options' : gplink_options })
608 gplink_str = encode_gplink(gplist)
610 m = ldb.Message()
611 m.dn = ldb.Dn(self.samdb, container_dn)
613 if existing_gplink:
614 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_REPLACE, 'gPLink')
615 else:
616 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_ADD, 'gPLink')
618 try:
619 self.samdb.modify(m)
620 except Exception, e:
621 raise CommandError("Error adding GPO Link", e)
623 self.outf.write("Added/Updated GPO link\n")
624 cmd_getlink().run(container_dn, H, sambaopts, credopts, versionopts)
627 class cmd_dellink(Command):
628 """Delete GPO link from a container."""
630 synopsis = "%prog <container_dn> <gpo> [options]"
632 takes_optiongroups = {
633 "sambaopts": options.SambaOptions,
634 "versionopts": options.VersionOptions,
635 "credopts": options.CredentialsOptions,
638 takes_args = ['container', 'gpo']
640 takes_options = [
641 Option("-H", help="LDB URL for database or target server", type=str),
644 def run(self, container, gpo, H=None, sambaopts=None, credopts=None,
645 versionopts=None):
647 self.lp = sambaopts.get_loadparm()
648 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
650 self.url = dc_url(self.lp, self.creds, H)
652 samdb_connect(self)
654 # Check if valid GPO
655 try:
656 get_gpo_info(self.samdb, gpo=gpo)[0]
657 except Exception:
658 raise CommandError("GPO '%s' does not exist" % gpo)
660 container_dn = ldb.Dn(self.samdb, container)
661 del_gpo_link(self.samdb, container_dn, gpo)
662 self.outf.write("Deleted GPO link.\n")
663 cmd_getlink().run(container_dn, H, sambaopts, credopts, versionopts)
666 class cmd_listcontainers(Command):
667 """List all linked containers for a GPO."""
669 synopsis = "%prog <gpo> [options]"
671 takes_optiongroups = {
672 "sambaopts": options.SambaOptions,
673 "versionopts": options.VersionOptions,
674 "credopts": options.CredentialsOptions,
677 takes_args = ['gpo']
679 takes_options = [
680 Option("-H", help="LDB URL for database or target server", type=str)
683 def run(self, gpo, H=None, sambaopts=None, credopts=None,
684 versionopts=None):
686 self.lp = sambaopts.get_loadparm()
687 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
689 self.url = dc_url(self.lp, self.creds, H)
691 samdb_connect(self)
693 msg = get_gpo_containers(self.samdb, gpo)
694 if len(msg):
695 self.outf.write("Container(s) using GPO %s\n" % gpo)
696 for m in msg:
697 self.outf.write(" DN: %s\n" % m['dn'])
698 else:
699 self.outf.write("No Containers using GPO %s\n" % gpo)
702 class cmd_getinheritance(Command):
703 """Get inheritance flag for a container."""
705 synopsis = "%prog <container_dn> [options]"
707 takes_optiongroups = {
708 "sambaopts": options.SambaOptions,
709 "versionopts": options.VersionOptions,
710 "credopts": options.CredentialsOptions,
713 takes_args = ['container_dn']
715 takes_options = [
716 Option("-H", help="LDB URL for database or target server", type=str)
719 def run(self, container_dn, H=None, sambaopts=None, credopts=None,
720 versionopts=None):
722 self.lp = sambaopts.get_loadparm()
723 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
725 self.url = dc_url(self.lp, self.creds, H)
727 samdb_connect(self)
729 try:
730 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
731 expression="(objectClass=*)",
732 attrs=['gPOptions'])[0]
733 except Exception:
734 raise CommandError("Container '%s' does not exist" % container_dn)
736 inheritance = 0
737 if 'gPOptions' in msg:
738 inheritance = int(msg['gPOptions'][0])
740 if inheritance == dsdb.GPO_BLOCK_INHERITANCE:
741 self.outf.write("Container has GPO_BLOCK_INHERITANCE\n")
742 else:
743 self.outf.write("Container has GPO_INHERIT\n")
746 class cmd_setinheritance(Command):
747 """Set inheritance flag on a container."""
749 synopsis = "%prog <container_dn> <block|inherit> [options]"
751 takes_optiongroups = {
752 "sambaopts": options.SambaOptions,
753 "versionopts": options.VersionOptions,
754 "credopts": options.CredentialsOptions,
757 takes_args = [ 'container_dn', 'inherit_state' ]
759 takes_options = [
760 Option("-H", help="LDB URL for database or target server", type=str)
763 def run(self, container_dn, inherit_state, H=None, sambaopts=None, credopts=None,
764 versionopts=None):
766 if inherit_state.lower() == 'block':
767 inheritance = dsdb.GPO_BLOCK_INHERITANCE
768 elif inherit_state.lower() == 'inherit':
769 inheritance = dsdb.GPO_INHERIT
770 else:
771 raise CommandError("Unknown inheritance state (%s)" % inherit_state)
773 self.lp = sambaopts.get_loadparm()
774 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
776 self.url = dc_url(self.lp, self.creds, H)
778 samdb_connect(self)
779 try:
780 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
781 expression="(objectClass=*)",
782 attrs=['gPOptions'])[0]
783 except Exception:
784 raise CommandError("Container '%s' does not exist" % container_dn)
786 m = ldb.Message()
787 m.dn = ldb.Dn(self.samdb, container_dn)
789 if 'gPOptions' in msg:
790 m['new_value'] = ldb.MessageElement(str(inheritance), ldb.FLAG_MOD_REPLACE, 'gPOptions')
791 else:
792 m['new_value'] = ldb.MessageElement(str(inheritance), ldb.FLAG_MOD_ADD, 'gPOptions')
794 try:
795 self.samdb.modify(m)
796 except Exception, e:
797 raise CommandError("Error setting inheritance state %s" % inherit_state, e)
800 class cmd_fetch(Command):
801 """Download a GPO."""
803 synopsis = "%prog <gpo> [options]"
805 takes_optiongroups = {
806 "sambaopts": options.SambaOptions,
807 "versionopts": options.VersionOptions,
808 "credopts": options.CredentialsOptions,
811 takes_args = ['gpo']
813 takes_options = [
814 Option("-H", help="LDB URL for database or target server", type=str),
815 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
818 def run(self, gpo, H=None, tmpdir=None, sambaopts=None, credopts=None, versionopts=None):
820 self.lp = sambaopts.get_loadparm()
821 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
823 # We need to know writable DC to setup SMB connection
824 if H and H.startswith('ldap://'):
825 dc_hostname = H[7:]
826 self.url = H
827 else:
828 dc_hostname = netcmd_finddc(self.lp, self.creds)
829 self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
831 samdb_connect(self)
832 try:
833 msg = get_gpo_info(self.samdb, gpo)[0]
834 except Exception:
835 raise CommandError("GPO '%s' does not exist" % gpo)
837 # verify UNC path
838 unc = msg['gPCFileSysPath'][0]
839 try:
840 [dom_name, service, sharepath] = parse_unc(unc)
841 except ValueError:
842 raise CommandError("Invalid GPO path (%s)" % unc)
844 # SMB connect to DC
845 try:
846 conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
847 except Exception:
848 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname)
850 # Copy GPT
851 if tmpdir is None:
852 tmpdir = "/tmp"
853 if not os.path.isdir(tmpdir):
854 raise CommandError("Temoprary directory '%s' does not exist" % tmpdir)
856 localdir = os.path.join(tmpdir, "policy")
857 if not os.path.isdir(localdir):
858 os.mkdir(localdir)
860 gpodir = os.path.join(localdir, gpo)
861 if os.path.isdir(gpodir):
862 raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir)
864 try:
865 os.mkdir(gpodir)
866 copy_directory_remote_to_local(conn, sharepath, gpodir)
867 except Exception, e:
868 # FIXME: Catch more specific exception
869 raise CommandError("Error copying GPO from DC", e)
870 self.outf.write('GPO copied to %s\n' % gpodir)
873 class cmd_create(Command):
874 """Create an empty GPO."""
876 synopsis = "%prog <displayname> [options]"
878 takes_optiongroups = {
879 "sambaopts": options.SambaOptions,
880 "versionopts": options.VersionOptions,
881 "credopts": options.CredentialsOptions,
884 takes_args = ['displayname']
886 takes_options = [
887 Option("-H", help="LDB URL for database or target server", type=str),
888 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
891 def run(self, displayname, H=None, tmpdir=None, sambaopts=None, credopts=None,
892 versionopts=None):
894 self.lp = sambaopts.get_loadparm()
895 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
897 net = Net(creds=self.creds, lp=self.lp)
899 # We need to know writable DC to setup SMB connection
900 if H and H.startswith('ldap://'):
901 dc_hostname = H[7:]
902 self.url = H
903 flags = (nbt.NBT_SERVER_LDAP |
904 nbt.NBT_SERVER_DS |
905 nbt.NBT_SERVER_WRITABLE)
906 cldap_ret = net.finddc(address=dc_hostname, flags=flags)
907 else:
908 flags = (nbt.NBT_SERVER_LDAP |
909 nbt.NBT_SERVER_DS |
910 nbt.NBT_SERVER_WRITABLE)
911 cldap_ret = net.finddc(domain=self.lp.get('realm'), flags=flags)
912 dc_hostname = cldap_ret.pdc_dns_name
913 self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
915 samdb_connect(self)
917 msg = get_gpo_info(self.samdb, displayname=displayname)
918 if msg.count > 0:
919 raise CommandError("A GPO already existing with name '%s'" % displayname)
921 # Create new GUID
922 guid = str(uuid.uuid4())
923 gpo = "{%s}" % guid.upper()
924 realm = cldap_ret.dns_domain
925 unc_path = "\\\\%s\\sysvol\\%s\\Policies\\%s" % (realm, realm, gpo)
927 # Create GPT
928 if tmpdir is None:
929 tmpdir = "/tmp"
930 if not os.path.isdir(tmpdir):
931 raise CommandError("Temporary directory '%s' does not exist" % tmpdir)
933 localdir = os.path.join(tmpdir, "policy")
934 if not os.path.isdir(localdir):
935 os.mkdir(localdir)
937 gpodir = os.path.join(localdir, gpo)
938 if os.path.isdir(gpodir):
939 raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir)
941 try:
942 os.mkdir(gpodir)
943 os.mkdir(os.path.join(gpodir, "Machine"))
944 os.mkdir(os.path.join(gpodir, "User"))
945 gpt_contents = "[General]\r\nVersion=0\r\n"
946 file(os.path.join(gpodir, "GPT.INI"), "w").write(gpt_contents)
947 except Exception, e:
948 raise CommandError("Error Creating GPO files", e)
950 # Connect to DC over SMB
951 [dom_name, service, sharepath] = parse_unc(unc_path)
952 try:
953 conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
954 except Exception, e:
955 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname, e)
957 self.samdb.transaction_start()
958 try:
959 # Add cn=<guid>
960 gpo_dn = get_gpo_dn(self.samdb, gpo)
962 m = ldb.Message()
963 m.dn = gpo_dn
964 m['a01'] = ldb.MessageElement("groupPolicyContainer", ldb.FLAG_MOD_ADD, "objectClass")
965 m['a02'] = ldb.MessageElement(displayname, ldb.FLAG_MOD_ADD, "displayName")
966 m['a03'] = ldb.MessageElement(unc_path, ldb.FLAG_MOD_ADD, "gPCFileSysPath")
967 m['a04'] = ldb.MessageElement("0", ldb.FLAG_MOD_ADD, "flags")
968 m['a05'] = ldb.MessageElement("0", ldb.FLAG_MOD_ADD, "versionNumber")
969 m['a06'] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_ADD, "showInAdvancedViewOnly")
970 m['a07'] = ldb.MessageElement("2", ldb.FLAG_MOD_ADD, "gpcFunctionalityVersion")
971 self.samdb.add(m)
973 # Add cn=User,cn=<guid>
974 m = ldb.Message()
975 m.dn = ldb.Dn(self.samdb, "CN=User,%s" % str(gpo_dn))
976 m['a01'] = ldb.MessageElement("container", ldb.FLAG_MOD_ADD, "objectClass")
977 m['a02'] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_ADD, "showInAdvancedViewOnly")
978 self.samdb.add(m)
980 # Add cn=Machine,cn=<guid>
981 m = ldb.Message()
982 m.dn = ldb.Dn(self.samdb, "CN=Machine,%s" % str(gpo_dn))
983 m['a01'] = ldb.MessageElement("container", ldb.FLAG_MOD_ADD, "objectClass")
984 m['a02'] = ldb.MessageElement("TRUE", ldb.FLAG_MOD_ADD, "showInAdvancedViewOnly")
985 self.samdb.add(m)
987 # Copy GPO files over SMB
988 create_directory_hier(conn, sharepath)
989 copy_directory_local_to_remote(conn, gpodir, sharepath)
991 # Get new security descriptor
992 msg = get_gpo_info(self.samdb, gpo=gpo)[0]
993 ds_sd_ndr = msg['nTSecurityDescriptor'][0]
994 ds_sd = ndr_unpack(security.descriptor, ds_sd_ndr).as_sddl()
996 # Create a file system security descriptor
997 domain_sid = security.dom_sid(self.samdb.get_domain_sid())
998 sddl = dsacl2fsacl(ds_sd, domain_sid)
999 fs_sd = security.descriptor.from_sddl(sddl, domain_sid)
1001 # Set ACL
1002 sio = ( security.SECINFO_OWNER |
1003 security.SECINFO_GROUP |
1004 security.SECINFO_DACL |
1005 security.SECINFO_PROTECTED_DACL )
1006 conn.set_acl(sharepath, fs_sd, sio)
1007 except Exception:
1008 self.samdb.transaction_cancel()
1009 raise
1010 else:
1011 self.samdb.transaction_commit()
1013 self.outf.write("GPO '%s' created as %s\n" % (displayname, gpo))
1016 class cmd_del(Command):
1017 """Delete a GPO."""
1019 synopsis = "%prog <gpo> [options]"
1021 takes_optiongroups = {
1022 "sambaopts": options.SambaOptions,
1023 "versionopts": options.VersionOptions,
1024 "credopts": options.CredentialsOptions,
1027 takes_args = ['gpo']
1029 takes_options = [
1030 Option("-H", help="LDB URL for database or target server", type=str),
1033 def run(self, gpo, H=None, sambaopts=None, credopts=None,
1034 versionopts=None):
1036 self.lp = sambaopts.get_loadparm()
1037 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
1039 # We need to know writable DC to setup SMB connection
1040 if H and H.startswith('ldap://'):
1041 dc_hostname = H[7:]
1042 self.url = H
1043 else:
1044 dc_hostname = netcmd_finddc(self.lp, self.creds)
1045 self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
1047 samdb_connect(self)
1049 # Check if valid GPO
1050 try:
1051 get_gpo_info(self.samdb, gpo=gpo)[0]
1052 except Exception:
1053 raise CommandError("GPO '%s' does not exist" % gpo)
1055 realm = self.lp.get('realm')
1056 unc_path = "\\\\%s\\sysvol\\%s\\Policies\\%s" % (realm, realm, gpo)
1058 # Connect to DC over SMB
1059 [dom_name, service, sharepath] = parse_unc(unc_path)
1060 try:
1061 conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
1062 except Exception, e:
1063 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname, e)
1065 self.samdb.transaction_start()
1066 try:
1067 # Check for existing links
1068 msg = get_gpo_containers(self.samdb, gpo)
1070 if len(msg):
1071 self.outf.write("GPO %s is linked to containers\n" % gpo)
1072 for m in msg:
1073 del_gpo_link(self.samdb, m['dn'], gpo)
1074 self.outf.write(" Removed link from %s.\n" % m['dn'])
1076 # Remove LDAP entries
1077 gpo_dn = get_gpo_dn(self.samdb, gpo)
1078 self.samdb.delete(ldb.Dn(self.samdb, "CN=User,%s" % str(gpo_dn)))
1079 self.samdb.delete(ldb.Dn(self.samdb, "CN=Machine,%s" % str(gpo_dn)))
1080 self.samdb.delete(gpo_dn)
1082 # Remove GPO files
1083 conn.deltree(sharepath)
1085 except Exception:
1086 self.samdb.transaction_cancel()
1087 raise
1088 else:
1089 self.samdb.transaction_commit()
1091 self.outf.write("GPO %s deleted.\n" % gpo)
1094 class cmd_aclcheck(Command):
1095 """Check all GPOs have matching LDAP and DS ACLs."""
1097 synopsis = "%prog [options]"
1099 takes_optiongroups = {
1100 "sambaopts": options.SambaOptions,
1101 "versionopts": options.VersionOptions,
1102 "credopts": options.CredentialsOptions,
1105 takes_options = [
1106 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1107 metavar="URL", dest="H")
1110 def run(self, H=None, sambaopts=None, credopts=None, versionopts=None):
1112 self.lp = sambaopts.get_loadparm()
1113 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
1115 self.url = dc_url(self.lp, self.creds, H)
1117 # We need to know writable DC to setup SMB connection
1118 if H and H.startswith('ldap://'):
1119 dc_hostname = H[7:]
1120 self.url = H
1121 else:
1122 dc_hostname = netcmd_finddc(self.lp, self.creds)
1123 self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
1125 samdb_connect(self)
1127 msg = get_gpo_info(self.samdb, None)
1129 for m in msg:
1130 # verify UNC path
1131 unc = m['gPCFileSysPath'][0]
1132 try:
1133 [dom_name, service, sharepath] = parse_unc(unc)
1134 except ValueError:
1135 raise CommandError("Invalid GPO path (%s)" % unc)
1137 # SMB connect to DC
1138 try:
1139 conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
1140 except Exception:
1141 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname)
1143 fs_sd = conn.get_acl(sharepath, security.SECINFO_OWNER | security.SECINFO_GROUP | security.SECINFO_DACL, security.SEC_FLAG_MAXIMUM_ALLOWED)
1145 ds_sd_ndr = m['nTSecurityDescriptor'][0]
1146 ds_sd = ndr_unpack(security.descriptor, ds_sd_ndr).as_sddl()
1148 # Create a file system security descriptor
1149 domain_sid = security.dom_sid(self.samdb.get_domain_sid())
1150 expected_fs_sddl = dsacl2fsacl(ds_sd, domain_sid)
1152 if (fs_sd.as_sddl(domain_sid) != expected_fs_sddl):
1153 raise CommandError("Invalid GPO ACL %s on path (%s), should be %s" % (fs_sd.as_sddl(domain_sid), sharepath, expected_fs_sddl))
1156 class cmd_gpo(SuperCommand):
1157 """Group Policy Object (GPO) management."""
1159 subcommands = {}
1160 subcommands["listall"] = cmd_listall()
1161 subcommands["list"] = cmd_list()
1162 subcommands["show"] = cmd_show()
1163 subcommands["getlink"] = cmd_getlink()
1164 subcommands["setlink"] = cmd_setlink()
1165 subcommands["dellink"] = cmd_dellink()
1166 subcommands["listcontainers"] = cmd_listcontainers()
1167 subcommands["getinheritance"] = cmd_getinheritance()
1168 subcommands["setinheritance"] = cmd_setinheritance()
1169 subcommands["fetch"] = cmd_fetch()
1170 subcommands["create"] = cmd_create()
1171 subcommands["del"] = cmd_del()
1172 subcommands["aclcheck"] = cmd_aclcheck()