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/>.
26 import samba.getopt as options
29 from samba.auth import system_session
30 from samba.netcmd import (
36 from samba.samdb import SamDB
37 from samba import drs_utils, nttime2string, dsdb, dcerpc
38 from samba.dcerpc import misc
39 from samba.ndr import ndr_unpack
42 from samba.auth import AUTH_SESSION_INFO_DEFAULT_GROUPS, AUTH_SESSION_INFO_AUTHENTICATED, AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
43 from samba.netcmd.common import netcmd_finddc
44 from samba import policy
48 def samdb_connect(ctx):
49 '''make a ldap connection to the server'''
51 ctx.samdb = SamDB(url=ctx.url,
52 session_info=system_session(),
53 credentials=ctx.creds, lp=ctx.lp)
55 raise CommandError("LDAP connection to %s failed " % ctx.url, e)
58 def attr_default(msg, attrname, default):
59 '''get an attribute from a ldap msg with a default'''
61 return msg[attrname][0]
65 def gpo_flags_string(value):
66 '''return gpo flags string'''
67 flags = policy.get_gpo_flags(value)
75 def gplink_options_string(value):
76 '''return gplink options string'''
77 options = policy.get_gplink_options(value)
81 ret = ' '.join(options)
85 def parse_gplink(gplink):
86 '''parse a gPLink into an array of dn and options'''
93 if len(d) != 2 or not d[0].startswith("[LDAP://"):
94 raise RuntimeError("Badly formed gPLink '%s'" % g)
95 ret.append({ 'dn' : d[0][8:], 'options' : int(d[1])})
99 def encode_gplink(gplist):
100 '''Encode an array of dn and options into gPLink string'''
103 ret += "[LDAP://%s;%d]" % (g['dn'], g['options'])
107 def dc_url(lp, creds, url=None, dc=None):
108 '''If URL is not specified, return URL for writable DC.
109 If dc is provided, use that to construct ldap URL'''
114 dc = netcmd_finddc(lp, creds)
116 raise RunTimeError("Could not find a DC for domain", e)
121 def get_gpo_dn(samdb, gpo):
122 '''Construct the DN for gpo'''
124 dn = samdb.get_default_basedn()
125 dn.add_child(ldb.Dn(samdb, "CN=Policies,DC=System"))
126 dn.add_child(ldb.Dn(samdb, "CN=%s" % gpo))
130 def get_gpo_info(samdb, gpo=None, displayname=None, dn=None):
131 '''Get GPO information using gpo, displayname or dn'''
133 policies_dn = samdb.get_default_basedn()
134 policies_dn.add_child(ldb.Dn(samdb, "CN=Policies,CN=System"))
136 base_dn = policies_dn
137 search_expr = "(objectClass=groupPolicyContainer)"
138 search_scope = ldb.SCOPE_ONELEVEL
141 search_expr = "(&(objectClass=groupPolicyContainer)(name=%s))" % ldb.binary_encode(gpo)
143 if displayname is not None:
144 search_expr = "(&(objectClass=groupPolicyContainer)(displayname=%s))" % ldb.binary_encode(displayname)
148 search_scope = ldb.SCOPE_BASE
151 msg = samdb.search(base=base_dn, scope=search_scope,
152 expression=search_expr,
153 attrs=['nTSecurityDescriptor',
161 mesg = "Cannot get information for GPO %s" % gpo
163 mesg = "Cannot get information for GPOs"
164 raise CommandError(mesg, e)
170 '''Parse UNC string into a hostname, a service, and a filepath'''
171 if unc.startswith('\\\\') and unc.startswith('//'):
173 tmp = unc[2:].split('/', 2)
176 tmp = unc[2:].split('\\', 2)
182 def copy_directory_recurse(conn, remotedir, localdir):
183 if not os.path.isdir(localdir):
185 r_dirs = [ remotedir ]
186 l_dirs = [ localdir ]
191 dirlist = conn.list(r_dir)
193 r_name = r_dir + '\\' + e['name']
194 l_name = l_dir + os.path.sep + e['name']
196 if e['attrib'] & smb.FILE_ATTRIBUTE_DIRECTORY:
197 r_dirs.append(r_name)
198 l_dirs.append(l_name)
200 elif e['attrib'] & smb.FILE_ATTRIBUTE_ARCHIVE:
201 data = conn.loadfile(r_name)
202 file(l_name, 'w').write(data)
205 class cmd_listall(Command):
208 synopsis = "%prog gpo listall [options]"
211 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
212 metavar="URL", dest="H")
215 def run(self, H=None, sambaopts=None, credopts=None, versionopts=None):
217 self.lp = sambaopts.get_loadparm()
218 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
220 self.url = dc_url(self.lp, self.creds, H)
224 msg = get_gpo_info(self.samdb, None)
227 print("GPO : %s" % m['name'][0])
228 print("display name : %s" % m['displayName'][0])
229 print("path : %s" % m['gPCFileSysPath'][0])
230 print("dn : %s" % m.dn)
231 print("version : %s" % attr_default(m, 'versionNumber', '0'))
232 print("flags : %s" % gpo_flags_string(int(attr_default(m, 'flags', 0))))
236 class cmd_list(Command):
237 """list GPOs for an account"""
239 synopsis = "%prog gpo list <username> [options]"
241 takes_args = [ 'username' ]
244 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
245 metavar="URL", dest="H")
248 def run(self, username, H=None, sambaopts=None, credopts=None, versionopts=None):
250 self.lp = sambaopts.get_loadparm()
251 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
253 self.url = dc_url(self.lp, self.creds, H)
258 msg = self.samdb.search(expression='(&(|(samAccountName=%s)(samAccountName=%s$))(objectClass=User))' %
259 (ldb.binary_encode(username),ldb.binary_encode(username)))
262 raise CommandError("Failed to find account %s" % username, e)
264 # check if its a computer account
266 msg = self.samdb.search(base=user_dn, scope=ldb.SCOPE_BASE, attrs=['objectClass'])[0]
267 is_computer = 'computer' in msg['objectClass']
269 raise CommandError("Failed to find objectClass for user %s" % username, e)
271 session_info_flags = ( AUTH_SESSION_INFO_DEFAULT_GROUPS |
272 AUTH_SESSION_INFO_AUTHENTICATED )
274 # When connecting to a remote server, don't look up the local privilege DB
275 if self.url is not None and self.url.startswith('ldap'):
276 session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
278 session = samba.auth.user_session(self.samdb, lp_ctx=self.lp, dn=user_dn,
279 session_info_flags=session_info_flags)
281 token = session.security_token
286 dn = ldb.Dn(self.samdb, str(user_dn)).parent()
288 msg = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['gPLink', 'gPOptions'])[0]
290 glist = parse_gplink(msg['gPLink'][0])
292 if not inherit and not (g['options'] & dsdb.GPLINK_OPT_ENFORCE):
294 if g['options'] & dsdb.GPLINK_OPT_DISABLE:
298 gmsg = self.samdb.search(base=g['dn'], scope=ldb.SCOPE_BASE,
299 attrs=['name', 'displayName', 'flags',
300 'ntSecurityDescriptor'])
302 print("Failed to fetch gpo object %s" % g['dn'])
305 secdesc_ndr = gmsg[0]['ntSecurityDescriptor'][0]
306 secdesc = ndr_unpack(dcerpc.security.descriptor, secdesc_ndr)
309 samba.security.access_check(secdesc, token,
310 dcerpc.security.SEC_STD_READ_CONTROL |
311 dcerpc.security.SEC_ADS_LIST |
312 dcerpc.security.SEC_ADS_READ_PROP)
314 print("Failed access check on %s" % msg.dn)
317 # check the flags on the GPO
318 flags = int(attr_default(gmsg[0], 'flags', 0))
319 if is_computer and (flags & dsdb.GPO_FLAG_MACHINE_DISABLE):
321 if not is_computer and (flags & dsdb.GPO_FLAG_USER_DISABLE):
323 gpos.append((gmsg[0]['displayName'][0], gmsg[0]['name'][0]))
325 # check if this blocks inheritance
326 gpoptions = int(attr_default(msg, 'gPOptions', 0))
327 if gpoptions & dsdb.GPO_BLOCK_INHERITANCE:
330 if dn == self.samdb.get_default_basedn():
339 print("GPOs for %s %s" % (msg_str, username))
341 print(" %s %s" % (g[0], g[1]))
344 class cmd_show(Command):
345 """Show information for a GPO"""
347 synopsis = "%prog gpo show <gpo> [options]"
349 takes_optiongroups = {
350 "sambaopts": options.SambaOptions,
351 "versionopts": options.VersionOptions,
352 "credopts": options.CredentialsOptions,
355 takes_args = [ 'gpo' ]
358 Option("-H", help="LDB URL for database or target server", type=str)
361 def run(self, gpo, H=None, sambaopts=None, credopts=None, versionopts=None):
363 self.lp = sambaopts.get_loadparm()
364 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
366 self.url = dc_url(self.lp, self.creds, H)
371 msg = get_gpo_info(self.samdb, gpo)[0]
373 raise CommandError("GPO %s does not exist" % gpo, e)
375 secdesc_ndr = msg['ntSecurityDescriptor'][0]
376 secdesc = ndr_unpack(dcerpc.security.descriptor, secdesc_ndr)
378 print("GPO : %s" % msg['name'][0])
379 print("display name : %s" % msg['displayName'][0])
380 print("path : %s" % msg['gPCFileSysPath'][0])
381 print("dn : %s" % msg.dn)
382 print("version : %s" % attr_default(msg, 'versionNumber', '0'))
383 print("flags : %s" % gpo_flags_string(int(attr_default(msg, 'flags', 0))))
384 print("ACL : %s" % secdesc.as_sddl())
388 class cmd_getlink(Command):
389 """List GPO Links for a container"""
391 synopsis = "%prog gpo getlink <container_dn> [options]"
393 takes_optiongroups = {
394 "sambaopts": options.SambaOptions,
395 "versionopts": options.VersionOptions,
396 "credopts": options.CredentialsOptions,
399 takes_args = [ 'container_dn' ]
402 Option("-H", help="LDB URL for database or target server", type=str)
405 def run(self, container_dn, H=None, sambaopts=None, credopts=None,
408 self.lp = sambaopts.get_loadparm()
409 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
411 self.url = dc_url(self.lp, self.creds, H)
416 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
417 expression="(objectClass=*)",
420 raise CommandError("Could not find Container DN %s (%s)" % container_dn, e)
423 print("GPO(s) linked to DN %s" % container_dn)
424 gplist = parse_gplink(msg['gPLink'][0])
426 msg = get_gpo_info(self.samdb, dn=g['dn'])
427 print(" GPO : %s" % msg[0]['name'][0])
428 print(" Name : %s" % msg[0]['displayName'][0])
429 print(" Options : %s" % gplink_options_string(g['options']))
432 print("No GPO(s) linked to DN=%s" % container_dn)
435 class cmd_setlink(Command):
436 """Add or Update a GPO link to a container"""
438 synopsis = "%prog gpo setlink <container_dn> <gpo> [options]"
440 takes_optiongroups = {
441 "sambaopts": options.SambaOptions,
442 "versionopts": options.VersionOptions,
443 "credopts": options.CredentialsOptions,
446 takes_args = [ 'container_dn', 'gpo' ]
449 Option("-H", help="LDB URL for database or target server", type=str),
450 Option("--disable", dest="disabled", default=False, action='store_true',
451 help="Disable policy"),
452 Option("--enforce", dest="enforced", default=False, action='store_true',
453 help="Enforce policy")
456 def run(self, container_dn, gpo, H=None, disabled=False, enforced=False,
457 sambaopts=None, credopts=None, versionopts=None):
459 self.lp = sambaopts.get_loadparm()
460 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
462 self.url = dc_url(self.lp, self.creds, H)
468 gplink_options |= dsdb.GPLINK_OPT_DISABLE
470 gplink_options |= dsdb.GPLINK_OPT_ENFORCE
472 # Check if valid GPO DN
474 msg = get_gpo_info(self.samdb, gpo=gpo)[0]
476 raise CommandError("GPO %s does not exist" % gpo_dn, e)
477 gpo_dn = get_gpo_dn(self.samdb, gpo)
479 # Check if valid Container DN
481 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
482 expression="(objectClass=*)",
485 raise CommandError("Could not find container DN %s" % container_dn, e)
487 # Update existing GPlinks or Add new one
488 existing_gplink = False
490 gplist = parse_gplink(msg['gPLink'][0])
491 existing_gplink = True
494 if g['dn'].lower() == gpo_dn.lower():
495 g['options'] = gplink_options
499 gplist.insert(0, { 'dn' : gpo_dn, 'options' : gplink_options })
502 gplist.append({ 'dn' : gpo_dn, 'options' : gplink_options })
504 gplink_str = encode_gplink(gplist)
507 m.dn = ldb.Dn(self.samdb, container_dn)
510 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_REPLACE, 'gPLink')
512 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_ADD, 'gPLink')
517 raise CommandError("Error adding GPO Link", e)
519 print("Added/Updated GPO link")
520 cmd_getlink().run(container_dn, H, sambaopts, credopts, versionopts)
523 class cmd_dellink(Command):
524 """Delete GPO link from a container"""
526 synopsis = "%prog gpo dellink <container_dn> <gpo> [options]"
528 takes_optiongroups = {
529 "sambaopts": options.SambaOptions,
530 "versionopts": options.VersionOptions,
531 "credopts": options.CredentialsOptions,
534 takes_args = [ 'container_dn', 'gpo' ]
537 Option("-H", help="LDB URL for database or target server", type=str),
540 def run(self, container_dn, gpo_dn, H=None, sambaopts=None, credopts=None,
543 self.lp = sambaopts.get_loadparm()
544 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
546 self.url = dc_url(self.lp, self.creds, H)
552 msg = get_gpo_info(self.sambdb, gpo=gpo)[0]
554 raise CommandError("GPO %s does not exist" % gpo, e)
555 gpo_dn = get_gpo_dn(self.samdb, gpo)
557 # Check if valid Container DN and get existing GPlinks
559 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
560 expression="(objectClass=*)",
563 raise CommandError("Could not find container DN %s" % dn, e)
566 gplist = parse_gplink(msg['gPLink'][0])
568 if g['dn'].lower() == gpo_dn.lower():
572 raise CommandError("Specified GPO is not linked to this container");
575 m.dn = ldb.Dn(self.samdb, container_dn)
578 gplink_str = encode_gplink(gplist)
579 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_REPLACE, 'gPLink')
581 m['new_value'] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, 'gPLink')
586 raise CommandError("Error Removing GPO Link (%s)" % e)
588 print("Deleted GPO link.")
589 cmd_getlink().run(container_dn, H, sambaopts, credopts, versionopts)
592 class cmd_getinheritance(Command):
593 """Get inheritance flag for a container"""
595 synopsis = "%prog gpo getinheritance <container_dn> [options]"
597 takes_optiongroups = {
598 "sambaopts": options.SambaOptions,
599 "versionopts": options.VersionOptions,
600 "credopts": options.CredentialsOptions,
603 takes_args = [ 'container_dn' ]
606 Option("-H", help="LDB URL for database or target server", type=str)
609 def run(self, container_dn, H=None, sambaopts=None, credopts=None,
613 self.lp = sambaopts.get_loadparm()
615 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
620 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
621 expression="(objectClass=*)",
622 attrs=['gPOptions'])[0]
624 raise CommandError("Could not find Container DN %s" % container_dn, e)
627 if 'gPOptions' in msg:
628 inheritance = int(msg['gPOptions'][0]);
630 if inheritance == dsdb.GPO_BLOCK_INHERITANCE:
631 print("Container has GPO_BLOCK_INHERITANCE")
633 print("Container has GPO_INHERIT")
636 class cmd_setinheritance(Command):
637 """Set inheritance flag on a container"""
639 synopsis = "%prog gpo setinheritance <container_dn> <block|inherit> [options]"
641 takes_optiongroups = {
642 "sambaopts": options.SambaOptions,
643 "versionopts": options.VersionOptions,
644 "credopts": options.CredentialsOptions,
647 takes_args = [ 'container_dn', 'inherit_state' ]
650 Option("-H", help="LDB URL for database or target server", type=str)
653 def run(self, container_dn, inherit_state, H=None, sambaopts=None, credopts=None,
656 if inherit_state.lower() == 'block':
657 inheritance = dsdb.GPO_BLOCK_INHERITANCE
658 elif inherit_state.lower() == 'inherit':
659 inheritance = dsdb.GPO_INHERIT
661 raise CommandError("Unknown inheritance state (%s)" % inherit_state)
664 self.lp = sambaopts.get_loadparm()
666 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
671 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
672 expression="(objectClass=*)",
673 attrs=['gPOptions'])[0]
675 raise CommandError("Could not find Container DN %s" % container_dn, e)
678 m.dn = ldb.Dn(self.samdb, container_dn)
680 if 'gPOptions' in msg:
681 m['new_value'] = ldb.MessageElement(str(inheritance), ldb.FLAG_MOD_REPLACE, 'gPOptions')
683 m['new_value'] = ldb.MessageElement(str(inheritance), ldb.FLAG_MOD_ADD, 'gPOptions');
688 raise CommandError("Error setting inheritance state %s" % inherit_state, e)
691 class cmd_fetch(Command):
694 synopsis = "%prog gpo fetch <gpo> [options]"
696 takes_optiongroups = {
697 "sambaopts": options.SambaOptions,
698 "versionopts": options.VersionOptions,
699 "credopts": options.CredentialsOptions,
702 takes_args = [ 'gpo' ]
705 Option("-H", help="LDB URL for database or target server", type=str),
706 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
709 def run(self, gpo, H=None, tmpdir=None, sambaopts=None, credopts=None, versionopts=None):
711 self.lp = sambaopts.get_loadparm()
712 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
714 dc_hostname = netcmd_finddc(self.lp, self.creds)
715 self.url = dc_url(self.lp, self.creds, H, dc=dc_hostname)
719 msg = get_gpo_info(self.samdb, gpo)[0]
721 raise CommandError("GPO %s does not exist" % gpo)
723 unc = msg['gPCFileSysPath'][0]
725 [dom_name, service, sharepath] = parse_unc(unc)
727 raise CommandError("Invalid GPO path (%s)" % unc)
730 conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
732 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname, e)
738 localdir = tmpdir + os.path.sep + "policy"
739 if not os.path.isdir(localdir):
741 gpodir = localdir + os.path.sep + gpo
742 if not os.path.isdir(gpodir):
744 copy_directory_recurse(conn, sharepath, gpodir)
746 raise CommandError("Error copying GPO", e)
747 print('GPO copied to %s' % gpodir)
750 class cmd_create(Command):
753 class cmd_setacl(Command):
754 """Set ACL on a GPO"""
757 class cmd_gpo(SuperCommand):
758 """Group Policy Object (GPO) commands"""
761 subcommands["listall"] = cmd_listall()
762 subcommands["list"] = cmd_list()
763 subcommands["show"] = cmd_show()
764 subcommands["getlink"] = cmd_getlink()
765 subcommands["setlink"] = cmd_setlink()
766 subcommands["dellink"] = cmd_dellink()
767 subcommands["getinheritance"] = cmd_getinheritance()
768 subcommands["setinheritance"] = cmd_setinheritance()
769 subcommands["fetch"] = cmd_fetch()
770 subcommands["create"] = cmd_create()
771 subcommands["setacl"] = cmd_setacl()