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/>.
23 import samba
.getopt
as options
26 from samba
.auth
import system_session
27 from samba
.netcmd
import (
33 from samba
.samdb
import SamDB
34 from samba
import dsdb
35 from samba
.dcerpc
import security
36 from samba
.ndr
import ndr_unpack
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
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'''
52 ctx
.samdb
= SamDB(url
=ctx
.url
,
53 session_info
=system_session(),
54 credentials
=ctx
.creds
, lp
=ctx
.lp
)
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'''
62 return msg
[attrname
][0]
66 def gpo_flags_string(value
):
67 '''return gpo flags string'''
68 flags
= policy
.get_gpo_flags(value
)
76 def gplink_options_string(value
):
77 '''return gplink options string'''
78 options
= policy
.get_gplink_options(value
)
82 ret
= ' '.join(options
)
86 def parse_gplink(gplink
):
87 '''parse a gPLink into an array of dn and options'''
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])})
100 def encode_gplink(gplist
):
101 '''Encode an array of dn and options into gPLink string'''
104 ret
+= "[LDAP://%s;%d]" % (g
['dn'], g
['options'])
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'''
115 dc
= netcmd_finddc(lp
, creds
)
117 raise RuntimeError("Could not find a DC for domain", e
)
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
))
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
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
)
150 search_scope
= ldb
.SCOPE_BASE
153 msg
= samdb
.search(base
=base_dn
, scope
=search_scope
,
154 expression
=search_expr
,
155 attrs
=['nTSecurityDescriptor',
161 controls
=['sd_flags:1:%d' % sd_flags
])
164 mesg
= "Cannot get information for GPO %s" % gpo
166 mesg
= "Cannot get information for GPOs"
167 raise CommandError(mesg
, e
)
172 def get_gpo_containers(samdb
, gpo
):
173 '''lists dn of containers for a GPO'''
175 search_expr
= "(&(objectClass=*)(gPLink=*%s*))" % gpo
177 msg
= samdb
.search(expression
=search_expr
, attrs
=['gPLink'])
179 raise CommandError("Could not find container(s) with GPO %s" % gpo
, e
)
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
188 msg
= samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
189 expression
="(objectClass=*)",
192 raise CommandError("Container '%s' does not exist" % container_dn
, e
)
195 gpo_dn
= str(get_gpo_dn(samdb
, gpo
))
197 gplist
= parse_gplink(msg
['gPLink'][0])
199 if g
['dn'].lower() == gpo_dn
.lower():
204 raise CommandError("No GPO(s) linked to this container")
207 raise CommandError("GPO '%s' not linked to this container" % gpo
)
212 gplink_str
= encode_gplink(gplist
)
213 m
['r0'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_REPLACE
, 'gPLink')
215 m
['d0'] = ldb
.MessageElement(msg
['gPLink'][0], ldb
.FLAG_MOD_DELETE
, 'gPLink')
219 raise CommandError("Error removing GPO from container", e
)
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)
229 tmp
= unc
[2:].split('\\', 2)
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
):
238 r_dirs
= [ remotedir
]
239 l_dirs
= [ localdir
]
244 dirlist
= conn
.list(r_dir
)
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
)
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
]
267 dirlist
= os
.listdir(l_dir
)
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
)
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('\\')
285 path
= path
+ '\\' + e
286 if not conn
.chkpath(path
):
290 class cmd_listall(Command
):
293 synopsis
= "%prog [options]"
295 takes_optiongroups
= {
296 "sambaopts": options
.SambaOptions
,
297 "versionopts": options
.VersionOptions
,
298 "credopts": options
.CredentialsOptions
,
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
)
315 msg
= get_gpo_info(self
.samdb
, None)
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
,
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
)
354 msg
= self
.samdb
.search(expression
='(&(|(samAccountName=%s)(samAccountName=%s$))(objectClass=User))' %
355 (ldb
.binary_encode(username
),ldb
.binary_encode(username
)))
358 raise CommandError("Failed to find account %s" % username
)
360 # check if its a computer account
362 msg
= self
.samdb
.search(base
=user_dn
, scope
=ldb
.SCOPE_BASE
, attrs
=['objectClass'])[0]
363 is_computer
= 'computer' in msg
['objectClass']
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
382 dn
= ldb
.Dn(self
.samdb
, str(user_dn
)).parent()
384 msg
= self
.samdb
.search(base
=dn
, scope
=ldb
.SCOPE_BASE
, attrs
=['gPLink', 'gPOptions'])[0]
386 glist
= parse_gplink(msg
['gPLink'][0])
388 if not inherit
and not (g
['options'] & dsdb
.GPLINK_OPT_ENFORCE
):
390 if g
['options'] & dsdb
.GPLINK_OPT_DISABLE
:
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
)
402 self
.outf
.write("Failed to fetch gpo object with nTSecurityDescriptor %s\n" %
407 samba
.security
.access_check(secdesc
, token
,
408 security
.SEC_STD_READ_CONTROL |
409 security
.SEC_ADS_LIST |
410 security
.SEC_ADS_READ_PROP
)
412 self
.outf
.write("Failed access check on %s\n" % msg
.dn
)
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
):
419 if not is_computer
and (flags
& dsdb
.GPO_FLAG_USER_DISABLE
):
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
:
428 if dn
== self
.samdb
.get_default_basedn():
437 self
.outf
.write("GPOs for %s %s\n" % (msg_str
, username
))
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
,
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
)
469 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
471 raise CommandError("GPO '%s' does not exist" % gpo
)
474 secdesc_ndr
= msg
['nTSecurityDescriptor'][0]
475 secdesc
= ndr_unpack(security
.descriptor
, secdesc_ndr
)
476 secdesc_sddl
= secdesc
.as_sddl()
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']
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,
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
)
518 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
519 expression
="(objectClass=*)",
522 raise CommandError("Container '%s' does not exist" % container_dn
)
525 self
.outf
.write("GPO(s) linked to DN %s\n" % container_dn
)
526 gplist
= parse_gplink(msg
['gPLink'][0])
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")
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']
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
)
570 gplink_options |
= dsdb
.GPLINK_OPT_DISABLE
572 gplink_options |
= dsdb
.GPLINK_OPT_ENFORCE
574 # Check if valid GPO DN
576 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
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
583 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
584 expression
="(objectClass=*)",
587 raise CommandError("Container '%s' does not exist" % container_dn
)
589 # Update existing GPlinks or Add new one
590 existing_gplink
= False
592 gplist
= parse_gplink(msg
['gPLink'][0])
593 existing_gplink
= True
596 if g
['dn'].lower() == gpo_dn
.lower():
597 g
['options'] = gplink_options
601 raise CommandError("GPO '%s' already linked to this container" % gpo
)
603 gplist
.insert(0, { 'dn' : gpo_dn
, 'options' : gplink_options
})
606 gplist
.append({ 'dn' : gpo_dn
, 'options' : gplink_options
})
608 gplink_str
= encode_gplink(gplist
)
611 m
.dn
= ldb
.Dn(self
.samdb
, container_dn
)
614 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_REPLACE
, 'gPLink')
616 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_ADD
, 'gPLink')
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']
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,
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
)
656 get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
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
,
680 Option("-H", help="LDB URL for database or target server", type=str)
683 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=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
)
693 msg
= get_gpo_containers(self
.samdb
, gpo
)
695 self
.outf
.write("Container(s) using GPO %s\n" % gpo
)
697 self
.outf
.write(" DN: %s\n" % m
['dn'])
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']
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,
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
)
730 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
731 expression
="(objectClass=*)",
732 attrs
=['gPOptions'])[0]
734 raise CommandError("Container '%s' does not exist" % container_dn
)
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")
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' ]
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,
766 if inherit_state
.lower() == 'block':
767 inheritance
= dsdb
.GPO_BLOCK_INHERITANCE
768 elif inherit_state
.lower() == 'inherit':
769 inheritance
= dsdb
.GPO_INHERIT
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
)
780 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
781 expression
="(objectClass=*)",
782 attrs
=['gPOptions'])[0]
784 raise CommandError("Container '%s' does not exist" % container_dn
)
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')
792 m
['new_value'] = ldb
.MessageElement(str(inheritance
), ldb
.FLAG_MOD_ADD
, 'gPOptions')
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
,
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://'):
828 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
829 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
833 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
835 raise CommandError("GPO '%s' does not exist" % gpo
)
838 unc
= msg
['gPCFileSysPath'][0]
840 [dom_name
, service
, sharepath
] = parse_unc(unc
)
842 raise CommandError("Invalid GPO path (%s)" % unc
)
846 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
848 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
)
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
):
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
)
866 copy_directory_remote_to_local(conn
, sharepath
, gpodir
)
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']
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,
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://'):
903 flags
= (nbt
.NBT_SERVER_LDAP |
905 nbt
.NBT_SERVER_WRITABLE
)
906 cldap_ret
= net
.finddc(address
=dc_hostname
, flags
=flags
)
908 flags
= (nbt
.NBT_SERVER_LDAP |
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
)
917 msg
= get_gpo_info(self
.samdb
, displayname
=displayname
)
919 raise CommandError("A GPO already existing with name '%s'" % displayname
)
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
)
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
):
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
)
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
)
948 raise CommandError("Error Creating GPO files", e
)
950 # Connect to DC over SMB
951 [dom_name
, service
, sharepath
] = parse_unc(unc_path
)
953 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
955 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
, e
)
957 self
.samdb
.transaction_start()
960 gpo_dn
= get_gpo_dn(self
.samdb
, gpo
)
964 m
['a01'] = ldb
.MessageElement("groupPolicyContainer", ldb
.FLAG_MOD_ADD
, "objectClass")
967 # Add cn=User,cn=<guid>
969 m
.dn
= ldb
.Dn(self
.samdb
, "CN=User,%s" % str(gpo_dn
))
970 m
['a01'] = ldb
.MessageElement("container", ldb
.FLAG_MOD_ADD
, "objectClass")
973 # Add cn=Machine,cn=<guid>
975 m
.dn
= ldb
.Dn(self
.samdb
, "CN=Machine,%s" % str(gpo_dn
))
976 m
['a01'] = ldb
.MessageElement("container", ldb
.FLAG_MOD_ADD
, "objectClass")
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
)
993 create_directory_hier(conn
, sharepath
)
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
)
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
)
1015 self
.samdb
.transaction_cancel()
1018 self
.samdb
.transaction_commit()
1020 self
.outf
.write("GPO '%s' created as %s\n" % (displayname
, gpo
))
1023 class cmd_del(Command
):
1026 synopsis
= "%prog <gpo> [options]"
1028 takes_optiongroups
= {
1029 "sambaopts": options
.SambaOptions
,
1030 "versionopts": options
.VersionOptions
,
1031 "credopts": options
.CredentialsOptions
,
1034 takes_args
= ['gpo']
1037 Option("-H", help="LDB URL for database or target server", type=str),
1040 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=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://'):
1051 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
1052 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
1056 # Check if valid GPO
1058 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
1059 unc_path
= msg
['gPCFileSysPath'][0]
1061 raise CommandError("GPO '%s' does not exist" % gpo
)
1063 # Connect to DC over SMB
1064 [dom_name
, service
, sharepath
] = parse_unc(unc_path
)
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()
1072 # Check for existing links
1073 msg
= get_gpo_containers(self
.samdb
, gpo
)
1076 self
.outf
.write("GPO %s is linked to containers\n" % gpo
)
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
)
1088 conn
.deltree(sharepath
)
1091 self
.samdb
.transaction_cancel()
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
,
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://'):
1127 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
1128 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
1132 msg
= get_gpo_info(self
.samdb
, None)
1136 unc
= m
['gPCFileSysPath'][0]
1138 [dom_name
, service
, sharepath
] = parse_unc(unc
)
1140 raise CommandError("Invalid GPO path (%s)" % unc
)
1144 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
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."""
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()