3 # implement samba_tool gpo commands
5 # Copyright Andrew Tridgell 2010
6 # Copyright Giampaolo Lauria 2011 <lauria2@yahoo.com>
8 # based on C implementation by Guenther Deschner and Wilco Baan Hofman
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 3 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 import samba.getopt as options
27 from samba.auth import system_session
28 from samba.netcmd import (
34 from samba.samdb import SamDB
35 from samba import drs_utils, nttime2string, dsdb, dcerpc
36 from samba.dcerpc import misc
37 from samba.ndr import ndr_unpack
40 from samba.auth import AUTH_SESSION_INFO_DEFAULT_GROUPS, AUTH_SESSION_INFO_AUTHENTICATED, AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
42 def samdb_connect(ctx):
43 '''make a ldap connection to the server'''
45 ctx.samdb = SamDB(url=ctx.url,
46 session_info=system_session(),
47 credentials=ctx.creds, lp=ctx.lp)
49 raise CommandError("LDAP connection to %s failed " % ctx.url, e)
52 def attr_default(msg, attrname, default):
53 '''get an attribute from a ldap msg with a default'''
55 return msg[attrname][0]
59 def flags_string(flags, value):
60 '''return a set of flags as a string'''
64 for (str, val) in flags:
69 ret += '0x%08x' % value
73 def parse_gplink(gplink):
74 '''parse a gPLink into an array of dn and options'''
81 if len(d) != 2 or not d[0].startswith("[LDAP://"):
82 raise RuntimeError("Badly formed gPLink '%s'" % g)
83 ret.append({ 'dn' : d[0][8:], 'options' : int(d[1])})
87 def encode_gplink(gplist):
88 '''Encode an array of dn and options into gPLink string'''
91 ret += "[LDAP://%s;%d]" % (g['dn'], g['options'])
95 class cmd_listall(Command):
98 synopsis = "%prog gpo listall"
101 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
102 metavar="URL", dest="H")
105 def run(self, H=None, sambaopts=None, credopts=None, versionopts=None):
108 self.lp = sambaopts.get_loadparm()
110 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
114 policies_dn = self.samdb.get_default_basedn()
115 policies_dn.add_child(ldb.Dn(self.samdb, "CN=Policies,CN=System"))
118 ("GPO_FLAG_USER_DISABLE", dsdb.GPO_FLAG_USER_DISABLE ),
119 ( "GPO_FLAG_MACHINE_DISABLE", dsdb.GPO_FLAG_MACHINE_DISABLE ) ]
122 msg = self.samdb.search(base=policies_dn, scope=ldb.SCOPE_ONELEVEL,
123 expression="(objectClass=groupPolicyContainer)",
124 attrs=['nTSecurityDescriptor',
131 raise CommandError("Failed to list policies in %s" % policies_dn, e)
133 print("GPO : %s" % m['name'][0])
134 print("display name : %s" % m['displayName'][0])
135 print("path : %s" % m['gPCFileSysPath'][0])
136 print("dn : %s" % m.dn)
137 print("version : %s" % attr_default(m, 'versionNumber', '0'))
138 print("flags : %s" % flags_string(gpo_flags, int(attr_default(m, 'flags', 0))))
142 class cmd_list(Command):
143 """list GPOs for an account"""
145 synopsis = "%prog gpo list <username>"
147 takes_args = [ 'username' ]
150 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
151 metavar="URL", dest="H")
154 def run(self, username, H=None, sambaopts=None, credopts=None, versionopts=None):
157 self.lp = sambaopts.get_loadparm()
159 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
164 msg = self.samdb.search(expression='(&(|(samAccountName=%s)(samAccountName=%s$))(objectClass=User))' %
168 raise CommandError("Failed to find account %s" % username, e)
170 # check if its a computer account
172 msg = self.samdb.search(base=user_dn, scope=ldb.SCOPE_BASE, attrs=['objectClass'])[0]
173 is_computer = 'computer' in msg['objectClass']
175 raise CommandError("Failed to find objectClass for user %s" % username, e)
177 session_info_flags = ( AUTH_SESSION_INFO_DEFAULT_GROUPS |
178 AUTH_SESSION_INFO_AUTHENTICATED )
180 # When connecting to a remote server, don't look up the local privilege DB
181 if self.url is not None and self.url.startswith('ldap'):
182 session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
184 session = samba.auth.user_session(self.samdb, lp_ctx=self.lp, dn=user_dn,
185 session_info_flags=session_info_flags)
187 token = session.security_token
192 dn = ldb.Dn(self.samdb, str(user_dn)).parent()
194 msg = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['gPLink', 'gPOptions'])[0]
196 glist = parse_gplink(msg['gPLink'][0])
198 if not inherit and not (g['options'] & dsdb.GPLINK_OPT_ENFORCE):
200 if g['options'] & dsdb.GPLINK_OPT_DISABLE:
204 gmsg = self.samdb.search(base=g['dn'], scope=ldb.SCOPE_BASE,
205 attrs=['flags', 'ntSecurityDescriptor'])
207 print "Failed to fetch gpo object %s" % g['dn']
210 secdesc_ndr = gmsg[0]['ntSecurityDescriptor'][0]
211 secdesc = ndr_unpack(dcerpc.security.descriptor, secdesc_ndr)
214 samba.security.access_check(secdesc, token,
215 dcerpc.security.SEC_STD_READ_CONTROL |
216 dcerpc.security.SEC_ADS_LIST |
217 dcerpc.security.SEC_ADS_READ_PROP)
219 print "Failed access check on %s" % msg.dn
222 # check the flags on the GPO
223 flags = int(attr_default(gmsg[0], 'flags', 0))
224 if is_computer and (flags & dsdb.GPO_FLAG_MACHINE_DISABLE):
226 if not is_computer and (flags & dsdb.GPO_FLAG_USER_DISABLE):
230 # check if this blocks inheritance
231 gpoptions = int(attr_default(msg, 'gPOptions', 0))
232 if gpoptions & dsdb.GPO_BLOCK_INHERITANCE:
235 if dn == self.samdb.get_default_basedn():
244 print "GPOs for %s %s" % (msg_str, username)
246 print "\t%s" % g['dn']
249 class cmd_show(Command):
250 """Show information for a GPO"""
252 synopsis = "%prog gpo show <dn>"
254 takes_optiongroups = {
255 "sambaopts": options.SambaOptions,
256 "versionopts": options.VersionOptions,
257 "credopts": options.CredentialsOptions,
260 takes_args = [ 'dn' ]
263 Option("-H", help="LDB URL for database or target server", type=str)
266 def run(self, dn, H=None, sambaopts=None, credopts=None, versionopts=None):
269 self.lp = sambaopts.get_loadparm()
271 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
276 ("GPO_FLAG_USER_DISABLE", dsdb.GPO_FLAG_USER_DISABLE ),
277 ( "GPO_FLAG_MACHINE_DISABLE", dsdb.GPO_FLAG_MACHINE_DISABLE ) ]
280 msg = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE,
281 expression="(objectClass=groupPolicyContainer)",
282 attrs=['nTSecurityDescriptor',
287 'gPCFileSysPath'])[0]
289 raise CommandError("Could not find GPC with DN %s (%s)" % dn, e)
291 secdesc_ndr = msg['ntSecurityDescriptor'][0]
292 secdesc = ndr_unpack(dcerpc.security.descriptor, secdesc_ndr)
294 print("GPO : %s" % msg['name'][0])
295 print("display name : %s" % msg['displayName'][0])
296 print("path : %s" % msg['gPCFileSysPath'][0])
297 print("dn : %s" % msg.dn)
298 print("version : %s" % attr_default(msg, 'versionNumber', '0'))
299 print("flags : %s" % flags_string(gpo_flags, int(attr_default(msg, 'flags', 0))))
300 print("ACL : %s" % secdesc.as_sddl())
304 class cmd_getlink(Command):
305 """List GPO Links for a container"""
307 synopsis = "%prog gpo getlink <container_dn>"
309 takes_optiongroups = {
310 "sambaopts": options.SambaOptions,
311 "versionopts": options.VersionOptions,
312 "credopts": options.CredentialsOptions,
315 takes_args = [ 'container_dn' ]
318 Option("-H", help="LDB URL for database or target server", type=str)
321 def run(self, container_dn, H=None, sambaopts=None, credopts=None,
325 self.lp = sambaopts.get_loadparm()
327 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
332 ("GPLINK_OPT_DISABLE", dsdb.GPLINK_OPT_DISABLE),
333 ("GPLINK_OPT_ENFORCE", dsdb.GPLINK_OPT_ENFORCE),
337 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
338 expression="(objectClass=*)",
341 raise CommandError("Could not find Container DN %s (%s)" % container_dn, e)
344 print "GPO(s) linked to DN=%s" % container_dn
345 gplist = parse_gplink(msg['gPLink'][0])
347 print("GPO DN : %s" % g['dn'])
348 print("Options : %s" % flags_string(gplink_options, g['options']))
351 print "No GPO(s) linked to DN=%s" % container_dn
354 class cmd_setlink(Command):
355 """Add or Update a GPO link to a container"""
357 synopsis = "%prog gpo setlink <container_dn> <gpo_dn>"
359 takes_optiongroups = {
360 "sambaopts": options.SambaOptions,
361 "versionopts": options.VersionOptions,
362 "credopts": options.CredentialsOptions,
365 takes_args = [ 'container_dn', 'gpo_dn' ]
368 Option("-H", help="LDB URL for database or target server", type=str),
369 Option("--disable", dest="disabled", default=False, action='store_true',
370 help="Disable policy"),
371 Option("--enforce", dest="enforced", default=False, action='store_true',
372 help="Enforce policy")
375 def run(self, container_dn, gpo_dn, H=None, disabled=False, enforced=False,
376 sambaopts=None, credopts=None, versionopts=None):
379 self.lp = sambaopts.get_loadparm()
381 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
387 gplink_options |= dsdb.GPLINK_OPT_DISABLE
389 gplink_options |= dsdb.GPLINK_OPT_ENFORCE
391 # Check if valid GPO DN
393 msg = self.samdb.search(base=gpo_dn, scope=ldb.SCOPE_BASE,
394 expression="(objectClass=groupPolicyContainer)",
397 raise CommandError("DN (%s) is not a GPO" % gpo_dn)
399 # Check if valid Container DN
401 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
402 expression="(objectClass=*)",
405 raise CommandError("Could not find container DN %s (%s)" % dn, e)
407 # Update existing GPlinks or Add new one
408 existing_gplink = False
410 gplist = parse_gplink(msg['gPLink'][0])
411 existing_gplink = True
414 if g['dn'].lower() == gpo_dn.lower():
415 g['options'] = gplink_options
419 gplist.insert(0, { 'dn' : gpo_dn, 'options' : gplink_options })
422 gplist.append({ 'dn' : gpo_dn, 'options' : gplink_options })
424 gplink_str = encode_gplink(gplist)
427 m.dn = ldb.Dn(self.samdb, container_dn)
430 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_REPLACE, 'gPLink')
432 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_ADD, 'gPLink')
437 raise CommandError("Error adding GPO Link (%s)" % e)
439 print "Added/Updated GPO link"
440 cmd_getlink().run(container_dn, H, sambaopts, credopts, versionopts)
443 class cmd_dellink(Command):
444 """Delete GPO link from a container"""
446 synopsis = "%prog gpo dellink <container_dn> <gpo_dn>"
448 takes_optiongroups = {
449 "sambaopts": options.SambaOptions,
450 "versionopts": options.VersionOptions,
451 "credopts": options.CredentialsOptions,
454 takes_args = [ 'container_dn', 'gpo_dn' ]
457 Option("-H", help="LDB URL for database or target server", type=str),
460 def run(self, container_dn, gpo_dn, H=None, sambaopts=None, credopts=None,
464 self.lp = sambaopts.get_loadparm()
466 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
470 # Check if valid GPO DN
472 msg = self.samdb.search(base=gpo_dn, scope=ldb.SCOPE_BASE,
473 expression="(objectClass=groupPolicyContainer)",
476 raise CommandError("DN (%s) is not a GPO" % gpo_dn)
478 # Check if valid Container DN and get existing GPlinks
480 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
481 expression="(objectClass=*)",
484 raise CommandError("Could not find container DN %s (%s)" % dn, e)
487 gplist = parse_gplink(msg['gPLink'][0])
489 if g['dn'].lower() == gpo_dn.lower():
493 raise CommandError("Specified GPO is not linked to this container");
497 m.dn = ldb.Dn(self.samdb, container_dn)
500 gplink_str = encode_gplink(gplist)
501 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_REPLACE, 'gPLink')
503 m['new_value'] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, 'gPLink')
508 raise CommandError("Error Removing GPO Link (%s)" % e)
510 print "Deleted GPO link."
511 cmd_getlink().run(container_dn, H, sambaopts, credopts, versionopts)
514 class cmd_getinheritance(Command):
515 """Get inheritance flag for a container"""
517 synopsis = "%prog gpo getinheritance <container_dn>"
519 takes_optiongroups = {
520 "sambaopts": options.SambaOptions,
521 "versionopts": options.VersionOptions,
522 "credopts": options.CredentialsOptions,
525 takes_args = [ 'container_dn' ]
528 Option("-H", help="LDB URL for database or target server", type=str)
531 def run(self, container_dn, H=None, sambaopts=None, credopts=None,
535 self.lp = sambaopts.get_loadparm()
537 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
542 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
543 expression="(objectClass=*)",
544 attrs=['gPOptions'])[0]
546 raise CommandError("Could not find Container DN %s (%s)" % (container_dn, e))
549 if 'gPOptions' in msg:
550 inheritance = int(msg['gPOptions'][0]);
552 if inheritance == dsdb.GPO_BLOCK_INHERITANCE:
553 print "Container has GPO_BLOCK_INHERITANCE"
555 print "Container has GPO_INHERIT"
558 class cmd_setinheritance(Command):
559 """Set inheritance flag on a container"""
561 synopsis = "%prog gpo setinheritance <container_dn> <block|inherit>"
563 takes_optiongroups = {
564 "sambaopts": options.SambaOptions,
565 "versionopts": options.VersionOptions,
566 "credopts": options.CredentialsOptions,
569 takes_args = [ 'container_dn', 'inherit_state' ]
572 Option("-H", help="LDB URL for database or target server", type=str)
575 def run(self, container_dn, inherit_state, H=None, sambaopts=None, credopts=None,
578 if inherit_state.lower() == 'block':
579 inheritance = dsdb.GPO_BLOCK_INHERITANCE
580 elif inherit_state.lower() == 'inherit':
581 inheritance = dsdb.GPO_INHERIT
583 raise CommandError("Unknown inheritance state (%s)" % inherit_state)
586 self.lp = sambaopts.get_loadparm()
588 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
593 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
594 expression="(objectClass=*)",
595 attrs=['gPOptions'])[0]
597 raise CommandError("Could not find Container DN %s (%s)" % (container_dn, e))
600 m.dn = ldb.Dn(self.samdb, container_dn)
602 if 'gPOptions' in msg:
603 m['new_value'] = ldb.MessageElement(str(inheritance), ldb.FLAG_MOD_REPLACE, 'gPOptions')
605 m['new_value'] = ldb.MessageElement(str(inheritance), ldb.FLAG_MOD_ADD, 'gPOptions');
610 raise CommandError("Error setting inheritance state %s (%s)" % (inherit_state, e))
613 class cmd_fetch(Command):
616 class cmd_create(Command):
619 class cmd_setacl(Command):
620 """Set ACL on a GPO"""
623 class cmd_gpo(SuperCommand):
624 """Group Policy Object (GPO) commands"""
627 subcommands["listall"] = cmd_listall()
628 subcommands["list"] = cmd_list()
629 subcommands["show"] = cmd_show()
630 subcommands["getlink"] = cmd_getlink()
631 subcommands["setlink"] = cmd_setlink()
632 subcommands["dellink"] = cmd_dellink()
633 subcommands["getinheritance"] = cmd_getinheritance()
634 subcommands["setinheritance"] = cmd_setinheritance()
635 subcommands["fetch"] = cmd_fetch()
636 subcommands["create"] = cmd_create()
637 subcommands["setacl"] = cmd_setacl()