3 # implement samba_tool gpo commands
5 # Copyright Andrew Tridgell 2010
6 # Copyright Giampaolo Lauria 2011 <lauria2@yahoo.com>
7 # Copyright Amitay Isaacs 2011 <amitay@gmail.com>
9 # based on C implementation by Guenther Deschner and Wilco Baan Hofman
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import samba.getopt as options
28 from samba.auth import system_session
29 from samba.netcmd import (
35 from samba.samdb import SamDB
36 from samba import drs_utils, nttime2string, dsdb, dcerpc
37 from samba.dcerpc import misc
38 from samba.ndr import ndr_unpack
41 from samba.auth import AUTH_SESSION_INFO_DEFAULT_GROUPS, AUTH_SESSION_INFO_AUTHENTICATED, AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
42 from samba.netcmd.common import netcmd_finddc
45 def samdb_connect(ctx):
46 '''make a ldap connection to the server'''
48 ctx.samdb = SamDB(url=ctx.url,
49 session_info=system_session(),
50 credentials=ctx.creds, lp=ctx.lp)
52 raise CommandError("LDAP connection to %s failed " % ctx.url, e)
55 def attr_default(msg, attrname, default):
56 '''get an attribute from a ldap msg with a default'''
58 return msg[attrname][0]
62 def flags_string(flags, value):
63 '''return a set of flags as a string'''
67 for (str, val) in flags:
72 ret += '0x%08x' % value
76 def parse_gplink(gplink):
77 '''parse a gPLink into an array of dn and options'''
84 if len(d) != 2 or not d[0].startswith("[LDAP://"):
85 raise RuntimeError("Badly formed gPLink '%s'" % g)
86 ret.append({ 'dn' : d[0][8:], 'options' : int(d[1])})
90 def encode_gplink(gplist):
91 '''Encode an array of dn and options into gPLink string'''
94 ret += "[LDAP://%s;%d]" % (g['dn'], g['options'])
98 def dc_url(lp, creds, url=None, dc=None):
99 '''If URL is not specified, return URL for writable DC.
100 If dc is provided, use that to construct ldap URL'''
105 dc = netcmd_finddc(lp, creds)
107 raise RunTimeError("Could not find a DC for domain", e)
112 def get_gpo_dn(samdb, gpo):
113 '''Construct the DN for gpo'''
115 dn = samdb.get_default_basedn()
116 dn.add_child(ldb.Dn(samdb, "CN=Policies,DC=System"))
117 dn.add_child(ldb.Dn(samdb, "CN=%s" % gpo))
121 def get_gpo_info(samdb, gpo=None, displayname=None, dn=None):
122 '''Get GPO information using gpo, displayname or dn'''
124 policies_dn = samdb.get_default_basedn()
125 policies_dn.add_child(ldb.Dn(samdb, "CN=Policies,CN=System"))
127 base_dn = policies_dn
128 search_expr = "(objectClass=groupPolicyContainer)"
129 search_scope = ldb.SCOPE_ONELEVEL
132 search_expr = "(&(objectClass=groupPolicyContainer)(name=%s))" % gpo
134 if displayname is not None:
135 search_expr = "(&(objectClass=groupPolicyContainer)(displayname=%s))" % displayname
139 search_scope = ldb.SCOPE_BASE
142 msg = samdb.search(base=base_dn, scope=search_scope,
143 expression=search_expr,
144 attrs=['nTSecurityDescriptor',
152 mesg = "Cannot get information for GPO %s" % gpo
154 mesg = "Cannot get information for GPOs"
155 raise CommandError(mesg, e)
160 class cmd_listall(Command):
163 synopsis = "%prog gpo listall [options]"
166 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
167 metavar="URL", dest="H")
170 def run(self, H=None, sambaopts=None, credopts=None, versionopts=None):
172 self.lp = sambaopts.get_loadparm()
173 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
175 self.url = dc_url(self.lp, self.creds, H)
180 ("GPO_FLAG_USER_DISABLE", dsdb.GPO_FLAG_USER_DISABLE ),
181 ( "GPO_FLAG_MACHINE_DISABLE", dsdb.GPO_FLAG_MACHINE_DISABLE ) ]
183 msg = get_gpo_info(self.samdb, None)
186 print("GPO : %s" % m['name'][0])
187 print("display name : %s" % m['displayName'][0])
188 print("path : %s" % m['gPCFileSysPath'][0])
189 print("dn : %s" % m.dn)
190 print("version : %s" % attr_default(m, 'versionNumber', '0'))
191 print("flags : %s" % flags_string(gpo_flags, int(attr_default(m, 'flags', 0))))
195 class cmd_list(Command):
196 """list GPOs for an account"""
198 synopsis = "%prog gpo list <username> [options]"
200 takes_args = [ 'username' ]
203 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
204 metavar="URL", dest="H")
207 def run(self, username, H=None, sambaopts=None, credopts=None, versionopts=None):
209 self.lp = sambaopts.get_loadparm()
210 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
212 self.url = dc_url(self.lp, self.creds, H)
217 msg = self.samdb.search(expression='(&(|(samAccountName=%s)(samAccountName=%s$))(objectClass=User))' %
221 raise CommandError("Failed to find account %s" % username, e)
223 # check if its a computer account
225 msg = self.samdb.search(base=user_dn, scope=ldb.SCOPE_BASE, attrs=['objectClass'])[0]
226 is_computer = 'computer' in msg['objectClass']
228 raise CommandError("Failed to find objectClass for user %s" % username, e)
230 session_info_flags = ( AUTH_SESSION_INFO_DEFAULT_GROUPS |
231 AUTH_SESSION_INFO_AUTHENTICATED )
233 # When connecting to a remote server, don't look up the local privilege DB
234 if self.url is not None and self.url.startswith('ldap'):
235 session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
237 session = samba.auth.user_session(self.samdb, lp_ctx=self.lp, dn=user_dn,
238 session_info_flags=session_info_flags)
240 token = session.security_token
245 dn = ldb.Dn(self.samdb, str(user_dn)).parent()
247 msg = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['gPLink', 'gPOptions'])[0]
249 glist = parse_gplink(msg['gPLink'][0])
251 if not inherit and not (g['options'] & dsdb.GPLINK_OPT_ENFORCE):
253 if g['options'] & dsdb.GPLINK_OPT_DISABLE:
257 gmsg = self.samdb.search(base=g['dn'], scope=ldb.SCOPE_BASE,
258 attrs=['name', 'displayName', 'flags',
259 'ntSecurityDescriptor'])
261 print("Failed to fetch gpo object %s" % g['dn'])
264 secdesc_ndr = gmsg[0]['ntSecurityDescriptor'][0]
265 secdesc = ndr_unpack(dcerpc.security.descriptor, secdesc_ndr)
268 samba.security.access_check(secdesc, token,
269 dcerpc.security.SEC_STD_READ_CONTROL |
270 dcerpc.security.SEC_ADS_LIST |
271 dcerpc.security.SEC_ADS_READ_PROP)
273 print("Failed access check on %s" % msg.dn)
276 # check the flags on the GPO
277 flags = int(attr_default(gmsg[0], 'flags', 0))
278 if is_computer and (flags & dsdb.GPO_FLAG_MACHINE_DISABLE):
280 if not is_computer and (flags & dsdb.GPO_FLAG_USER_DISABLE):
282 gpos.append((gmsg[0]['displayName'][0], gmsg[0]['name'][0]))
284 # check if this blocks inheritance
285 gpoptions = int(attr_default(msg, 'gPOptions', 0))
286 if gpoptions & dsdb.GPO_BLOCK_INHERITANCE:
289 if dn == self.samdb.get_default_basedn():
298 print("GPOs for %s %s" % (msg_str, username))
300 print(" %s %s" % (g[0], g[1]))
303 class cmd_show(Command):
304 """Show information for a GPO"""
306 synopsis = "%prog gpo show <gpo> [options]"
308 takes_optiongroups = {
309 "sambaopts": options.SambaOptions,
310 "versionopts": options.VersionOptions,
311 "credopts": options.CredentialsOptions,
314 takes_args = [ 'gpo' ]
317 Option("-H", help="LDB URL for database or target server", type=str)
320 def run(self, gpo, H=None, sambaopts=None, credopts=None, versionopts=None):
322 self.lp = sambaopts.get_loadparm()
323 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
325 self.url = dc_url(self.lp, self.creds, H)
330 ("GPO_FLAG_USER_DISABLE", dsdb.GPO_FLAG_USER_DISABLE ),
331 ( "GPO_FLAG_MACHINE_DISABLE", dsdb.GPO_FLAG_MACHINE_DISABLE ) ]
334 msg = get_gpo_info(self.samdb, gpo)[0]
336 raise CommandError("GPO %s does not exist" % gpo, e)
338 secdesc_ndr = msg['ntSecurityDescriptor'][0]
339 secdesc = ndr_unpack(dcerpc.security.descriptor, secdesc_ndr)
341 print("GPO : %s" % msg['name'][0])
342 print("display name : %s" % msg['displayName'][0])
343 print("path : %s" % msg['gPCFileSysPath'][0])
344 print("dn : %s" % msg.dn)
345 print("version : %s" % attr_default(msg, 'versionNumber', '0'))
346 print("flags : %s" % flags_string(gpo_flags, int(attr_default(msg, 'flags', 0))))
347 print("ACL : %s" % secdesc.as_sddl())
351 class cmd_getlink(Command):
352 """List GPO Links for a container"""
354 synopsis = "%prog gpo getlink <container_dn> [options]"
356 takes_optiongroups = {
357 "sambaopts": options.SambaOptions,
358 "versionopts": options.VersionOptions,
359 "credopts": options.CredentialsOptions,
362 takes_args = [ 'container_dn' ]
365 Option("-H", help="LDB URL for database or target server", type=str)
368 def run(self, container_dn, H=None, sambaopts=None, credopts=None,
371 self.lp = sambaopts.get_loadparm()
372 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
374 self.url = dc_url(self.lp, self.creds, H)
379 ("GPLINK_OPT_DISABLE", dsdb.GPLINK_OPT_DISABLE),
380 ("GPLINK_OPT_ENFORCE", dsdb.GPLINK_OPT_ENFORCE),
384 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
385 expression="(objectClass=*)",
388 raise CommandError("Could not find Container DN %s (%s)" % container_dn, e)
391 print("GPO(s) linked to DN %s" % container_dn)
392 gplist = parse_gplink(msg['gPLink'][0])
394 msg = get_gpo_info(self.samdb, dn=g['dn'])
395 print(" GPO : %s" % msg[0]['name'][0])
396 print(" Name : %s" % msg[0]['displayName'][0])
397 print(" Options : %s" % flags_string(gplink_options, g['options']))
400 print("No GPO(s) linked to DN=%s" % container_dn)
403 class cmd_setlink(Command):
404 """Add or Update a GPO link to a container"""
406 synopsis = "%prog gpo setlink <container_dn> <gpo> [options]"
408 takes_optiongroups = {
409 "sambaopts": options.SambaOptions,
410 "versionopts": options.VersionOptions,
411 "credopts": options.CredentialsOptions,
414 takes_args = [ 'container_dn', 'gpo' ]
417 Option("-H", help="LDB URL for database or target server", type=str),
418 Option("--disable", dest="disabled", default=False, action='store_true',
419 help="Disable policy"),
420 Option("--enforce", dest="enforced", default=False, action='store_true',
421 help="Enforce policy")
424 def run(self, container_dn, gpo, H=None, disabled=False, enforced=False,
425 sambaopts=None, credopts=None, versionopts=None):
427 self.lp = sambaopts.get_loadparm()
428 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
430 self.url = dc_url(self.lp, self.creds, H)
436 gplink_options |= dsdb.GPLINK_OPT_DISABLE
438 gplink_options |= dsdb.GPLINK_OPT_ENFORCE
440 # Check if valid GPO DN
442 msg = get_gpo_info(self.samdb, gpo=gpo)[0]
444 raise CommandError("GPO %s does not exist" % gpo_dn, e)
445 gpo_dn = get_gpo_dn(self.samdb, gpo)
447 # Check if valid Container DN
449 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
450 expression="(objectClass=*)",
453 raise CommandError("Could not find container DN %s" % container_dn, e)
455 # Update existing GPlinks or Add new one
456 existing_gplink = False
458 gplist = parse_gplink(msg['gPLink'][0])
459 existing_gplink = True
462 if g['dn'].lower() == gpo_dn.lower():
463 g['options'] = gplink_options
467 gplist.insert(0, { 'dn' : gpo_dn, 'options' : gplink_options })
470 gplist.append({ 'dn' : gpo_dn, 'options' : gplink_options })
472 gplink_str = encode_gplink(gplist)
475 m.dn = ldb.Dn(self.samdb, container_dn)
478 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_REPLACE, 'gPLink')
480 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_ADD, 'gPLink')
485 raise CommandError("Error adding GPO Link", e)
487 print("Added/Updated GPO link")
488 cmd_getlink().run(container_dn, H, sambaopts, credopts, versionopts)
491 class cmd_dellink(Command):
492 """Delete GPO link from a container"""
494 synopsis = "%prog gpo dellink <container_dn> <gpo> [options]"
496 takes_optiongroups = {
497 "sambaopts": options.SambaOptions,
498 "versionopts": options.VersionOptions,
499 "credopts": options.CredentialsOptions,
502 takes_args = [ 'container_dn', 'gpo' ]
505 Option("-H", help="LDB URL for database or target server", type=str),
508 def run(self, container_dn, gpo_dn, H=None, sambaopts=None, credopts=None,
511 self.lp = sambaopts.get_loadparm()
512 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
514 self.url = dc_url(self.lp, self.creds, H)
520 msg = get_gpo_info(self.sambdb, gpo=gpo)[0]
522 raise CommandError("GPO %s does not exist" % gpo, e)
523 gpo_dn = get_gpo_dn(self.samdb, gpo)
525 # Check if valid Container DN and get existing GPlinks
527 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
528 expression="(objectClass=*)",
531 raise CommandError("Could not find container DN %s" % dn, e)
534 gplist = parse_gplink(msg['gPLink'][0])
536 if g['dn'].lower() == gpo_dn.lower():
540 raise CommandError("Specified GPO is not linked to this container");
543 m.dn = ldb.Dn(self.samdb, container_dn)
546 gplink_str = encode_gplink(gplist)
547 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_REPLACE, 'gPLink')
549 m['new_value'] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, 'gPLink')
554 raise CommandError("Error Removing GPO Link (%s)" % e)
556 print("Deleted GPO link.")
557 cmd_getlink().run(container_dn, H, sambaopts, credopts, versionopts)
560 class cmd_getinheritance(Command):
561 """Get inheritance flag for a container"""
563 synopsis = "%prog gpo getinheritance <container_dn> [options]"
565 takes_optiongroups = {
566 "sambaopts": options.SambaOptions,
567 "versionopts": options.VersionOptions,
568 "credopts": options.CredentialsOptions,
571 takes_args = [ 'container_dn' ]
574 Option("-H", help="LDB URL for database or target server", type=str)
577 def run(self, container_dn, H=None, sambaopts=None, credopts=None,
581 self.lp = sambaopts.get_loadparm()
583 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
588 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
589 expression="(objectClass=*)",
590 attrs=['gPOptions'])[0]
592 raise CommandError("Could not find Container DN %s" % container_dn, e)
595 if 'gPOptions' in msg:
596 inheritance = int(msg['gPOptions'][0]);
598 if inheritance == dsdb.GPO_BLOCK_INHERITANCE:
599 print("Container has GPO_BLOCK_INHERITANCE")
601 print("Container has GPO_INHERIT")
604 class cmd_setinheritance(Command):
605 """Set inheritance flag on a container"""
607 synopsis = "%prog gpo setinheritance <container_dn> <block|inherit> [options]"
609 takes_optiongroups = {
610 "sambaopts": options.SambaOptions,
611 "versionopts": options.VersionOptions,
612 "credopts": options.CredentialsOptions,
615 takes_args = [ 'container_dn', 'inherit_state' ]
618 Option("-H", help="LDB URL for database or target server", type=str)
621 def run(self, container_dn, inherit_state, H=None, sambaopts=None, credopts=None,
624 if inherit_state.lower() == 'block':
625 inheritance = dsdb.GPO_BLOCK_INHERITANCE
626 elif inherit_state.lower() == 'inherit':
627 inheritance = dsdb.GPO_INHERIT
629 raise CommandError("Unknown inheritance state (%s)" % inherit_state)
632 self.lp = sambaopts.get_loadparm()
634 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
639 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
640 expression="(objectClass=*)",
641 attrs=['gPOptions'])[0]
643 raise CommandError("Could not find Container DN %s" % container_dn, e)
646 m.dn = ldb.Dn(self.samdb, container_dn)
648 if 'gPOptions' in msg:
649 m['new_value'] = ldb.MessageElement(str(inheritance), ldb.FLAG_MOD_REPLACE, 'gPOptions')
651 m['new_value'] = ldb.MessageElement(str(inheritance), ldb.FLAG_MOD_ADD, 'gPOptions');
656 raise CommandError("Error setting inheritance state %s" % inherit_state, e)
659 class cmd_fetch(Command):
662 class cmd_create(Command):
665 class cmd_setacl(Command):
666 """Set ACL on a GPO"""
669 class cmd_gpo(SuperCommand):
670 """Group Policy Object (GPO) commands"""
673 subcommands["listall"] = cmd_listall()
674 subcommands["list"] = cmd_list()
675 subcommands["show"] = cmd_show()
676 subcommands["getlink"] = cmd_getlink()
677 subcommands["setlink"] = cmd_setlink()
678 subcommands["dellink"] = cmd_dellink()
679 subcommands["getinheritance"] = cmd_getinheritance()
680 subcommands["setinheritance"] = cmd_setinheritance()
681 subcommands["fetch"] = cmd_fetch()
682 subcommands["create"] = cmd_create()
683 subcommands["setacl"] = cmd_setacl()