nfs4acls: Use talloc_realloc()
[Samba.git] / python / samba / netcmd / gpo.py
blob23b562eb6358fba17dd5151203ff4ca67c2ea1d7
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 self.samdb.add(m)
967 # Add cn=User,cn=<guid>
968 m = ldb.Message()
969 m.dn = ldb.Dn(self.samdb, "CN=User,%s" % str(gpo_dn))
970 m['a01'] = ldb.MessageElement("container", ldb.FLAG_MOD_ADD, "objectClass")
971 self.samdb.add(m)
973 # Add cn=Machine,cn=<guid>
974 m = ldb.Message()
975 m.dn = ldb.Dn(self.samdb, "CN=Machine,%s" % str(gpo_dn))
976 m['a01'] = ldb.MessageElement("container", ldb.FLAG_MOD_ADD, "objectClass")
977 self.samdb.add(m)
979 # Get new security descriptor
980 ds_sd_flags = ( security.SECINFO_OWNER |
981 security.SECINFO_GROUP |
982 security.SECINFO_DACL )
983 msg = get_gpo_info(self.samdb, gpo=gpo, sd_flags=ds_sd_flags)[0]
984 ds_sd_ndr = msg['nTSecurityDescriptor'][0]
985 ds_sd = ndr_unpack(security.descriptor, ds_sd_ndr).as_sddl()
987 # Create a file system security descriptor
988 domain_sid = security.dom_sid(self.samdb.get_domain_sid())
989 sddl = dsacl2fsacl(ds_sd, domain_sid)
990 fs_sd = security.descriptor.from_sddl(sddl, domain_sid)
992 # Copy GPO directory
993 create_directory_hier(conn, sharepath)
995 # Set ACL
996 sio = ( security.SECINFO_OWNER |
997 security.SECINFO_GROUP |
998 security.SECINFO_DACL |
999 security.SECINFO_PROTECTED_DACL )
1000 conn.set_acl(sharepath, fs_sd, sio)
1002 # Copy GPO files over SMB
1003 copy_directory_local_to_remote(conn, gpodir, sharepath)
1005 m = ldb.Message()
1006 m.dn = gpo_dn
1007 m['a02'] = ldb.MessageElement(displayname, ldb.FLAG_MOD_REPLACE, "displayName")
1008 m['a03'] = ldb.MessageElement(unc_path, ldb.FLAG_MOD_REPLACE, "gPCFileSysPath")
1009 m['a05'] = ldb.MessageElement("0", ldb.FLAG_MOD_REPLACE, "versionNumber")
1010 m['a07'] = ldb.MessageElement("2", ldb.FLAG_MOD_REPLACE, "gpcFunctionalityVersion")
1011 m['a04'] = ldb.MessageElement("0", ldb.FLAG_MOD_REPLACE, "flags")
1012 controls=["permissive_modify:0"]
1013 self.samdb.modify(m, controls=controls)
1014 except Exception:
1015 self.samdb.transaction_cancel()
1016 raise
1017 else:
1018 self.samdb.transaction_commit()
1020 self.outf.write("GPO '%s' created as %s\n" % (displayname, gpo))
1023 class cmd_del(Command):
1024 """Delete a GPO."""
1026 synopsis = "%prog <gpo> [options]"
1028 takes_optiongroups = {
1029 "sambaopts": options.SambaOptions,
1030 "versionopts": options.VersionOptions,
1031 "credopts": options.CredentialsOptions,
1034 takes_args = ['gpo']
1036 takes_options = [
1037 Option("-H", help="LDB URL for database or target server", type=str),
1040 def run(self, gpo, H=None, sambaopts=None, credopts=None,
1041 versionopts=None):
1043 self.lp = sambaopts.get_loadparm()
1044 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
1046 # We need to know writable DC to setup SMB connection
1047 if H and H.startswith('ldap://'):
1048 dc_hostname = H[7:]
1049 self.url = H
1050 else:
1051 dc_hostname = netcmd_finddc(self.lp, self.creds)
1052 self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
1054 samdb_connect(self)
1056 # Check if valid GPO
1057 try:
1058 msg = get_gpo_info(self.samdb, gpo=gpo)[0]
1059 unc_path = msg['gPCFileSysPath'][0]
1060 except Exception:
1061 raise CommandError("GPO '%s' does not exist" % gpo)
1063 # Connect to DC over SMB
1064 [dom_name, service, sharepath] = parse_unc(unc_path)
1065 try:
1066 conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
1067 except Exception, e:
1068 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname, e)
1070 self.samdb.transaction_start()
1071 try:
1072 # Check for existing links
1073 msg = get_gpo_containers(self.samdb, gpo)
1075 if len(msg):
1076 self.outf.write("GPO %s is linked to containers\n" % gpo)
1077 for m in msg:
1078 del_gpo_link(self.samdb, m['dn'], gpo)
1079 self.outf.write(" Removed link from %s.\n" % m['dn'])
1081 # Remove LDAP entries
1082 gpo_dn = get_gpo_dn(self.samdb, gpo)
1083 self.samdb.delete(ldb.Dn(self.samdb, "CN=User,%s" % str(gpo_dn)))
1084 self.samdb.delete(ldb.Dn(self.samdb, "CN=Machine,%s" % str(gpo_dn)))
1085 self.samdb.delete(gpo_dn)
1087 # Remove GPO files
1088 conn.deltree(sharepath)
1090 except Exception:
1091 self.samdb.transaction_cancel()
1092 raise
1093 else:
1094 self.samdb.transaction_commit()
1096 self.outf.write("GPO %s deleted.\n" % gpo)
1099 class cmd_aclcheck(Command):
1100 """Check all GPOs have matching LDAP and DS ACLs."""
1102 synopsis = "%prog [options]"
1104 takes_optiongroups = {
1105 "sambaopts": options.SambaOptions,
1106 "versionopts": options.VersionOptions,
1107 "credopts": options.CredentialsOptions,
1110 takes_options = [
1111 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1112 metavar="URL", dest="H")
1115 def run(self, H=None, sambaopts=None, credopts=None, versionopts=None):
1117 self.lp = sambaopts.get_loadparm()
1118 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
1120 self.url = dc_url(self.lp, self.creds, H)
1122 # We need to know writable DC to setup SMB connection
1123 if H and H.startswith('ldap://'):
1124 dc_hostname = H[7:]
1125 self.url = H
1126 else:
1127 dc_hostname = netcmd_finddc(self.lp, self.creds)
1128 self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
1130 samdb_connect(self)
1132 msg = get_gpo_info(self.samdb, None)
1134 for m in msg:
1135 # verify UNC path
1136 unc = m['gPCFileSysPath'][0]
1137 try:
1138 [dom_name, service, sharepath] = parse_unc(unc)
1139 except ValueError:
1140 raise CommandError("Invalid GPO path (%s)" % unc)
1142 # SMB connect to DC
1143 try:
1144 conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
1145 except Exception:
1146 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname)
1148 fs_sd = conn.get_acl(sharepath, security.SECINFO_OWNER | security.SECINFO_GROUP | security.SECINFO_DACL, security.SEC_FLAG_MAXIMUM_ALLOWED)
1150 ds_sd_ndr = m['nTSecurityDescriptor'][0]
1151 ds_sd = ndr_unpack(security.descriptor, ds_sd_ndr).as_sddl()
1153 # Create a file system security descriptor
1154 domain_sid = security.dom_sid(self.samdb.get_domain_sid())
1155 expected_fs_sddl = dsacl2fsacl(ds_sd, domain_sid)
1157 if (fs_sd.as_sddl(domain_sid) != expected_fs_sddl):
1158 raise CommandError("Invalid GPO ACL %s on path (%s), should be %s" % (fs_sd.as_sddl(domain_sid), sharepath, expected_fs_sddl))
1161 class cmd_gpo(SuperCommand):
1162 """Group Policy Object (GPO) management."""
1164 subcommands = {}
1165 subcommands["listall"] = cmd_listall()
1166 subcommands["list"] = cmd_list()
1167 subcommands["show"] = cmd_show()
1168 subcommands["getlink"] = cmd_getlink()
1169 subcommands["setlink"] = cmd_setlink()
1170 subcommands["dellink"] = cmd_dellink()
1171 subcommands["listcontainers"] = cmd_listcontainers()
1172 subcommands["getinheritance"] = cmd_getinheritance()
1173 subcommands["setinheritance"] = cmd_setinheritance()
1174 subcommands["fetch"] = cmd_fetch()
1175 subcommands["create"] = cmd_create()
1176 subcommands["del"] = cmd_del()
1177 subcommands["aclcheck"] = cmd_aclcheck()