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
47 def samdb_connect(ctx
):
48 '''make a ldap connection to the server'''
50 ctx
.samdb
= SamDB(url
=ctx
.url
,
51 session_info
=system_session(),
52 credentials
=ctx
.creds
, lp
=ctx
.lp
)
54 raise CommandError("LDAP connection to %s failed " % ctx
.url
, e
)
57 def attr_default(msg
, attrname
, default
):
58 '''get an attribute from a ldap msg with a default'''
60 return msg
[attrname
][0]
64 def gpo_flags_string(value
):
65 '''return gpo flags string'''
66 flags
= policy
.get_gpo_flags(value
)
74 def gplink_options_string(value
):
75 '''return gplink options string'''
76 options
= policy
.get_gplink_options(value
)
80 ret
= ' '.join(options
)
84 def parse_gplink(gplink
):
85 '''parse a gPLink into an array of dn and options'''
92 if len(d
) != 2 or not d
[0].startswith("[LDAP://"):
93 raise RuntimeError("Badly formed gPLink '%s'" % g
)
94 ret
.append({ 'dn' : d
[0][8:], 'options' : int(d
[1])})
98 def encode_gplink(gplist
):
99 '''Encode an array of dn and options into gPLink string'''
102 ret
+= "[LDAP://%s;%d]" % (g
['dn'], g
['options'])
106 def dc_url(lp
, creds
, url
=None, dc
=None):
107 '''If URL is not specified, return URL for writable DC.
108 If dc is provided, use that to construct ldap URL'''
113 dc
= netcmd_finddc(lp
, creds
)
115 raise RuntimeError("Could not find a DC for domain", e
)
120 def get_gpo_dn(samdb
, gpo
):
121 '''Construct the DN for gpo'''
123 dn
= samdb
.get_default_basedn()
124 dn
.add_child(ldb
.Dn(samdb
, "CN=Policies,CN=System"))
125 dn
.add_child(ldb
.Dn(samdb
, "CN=%s" % gpo
))
129 def get_gpo_info(samdb
, gpo
=None, displayname
=None, dn
=None):
130 '''Get GPO information using gpo, displayname or dn'''
132 policies_dn
= samdb
.get_default_basedn()
133 policies_dn
.add_child(ldb
.Dn(samdb
, "CN=Policies,CN=System"))
135 base_dn
= policies_dn
136 search_expr
= "(objectClass=groupPolicyContainer)"
137 search_scope
= ldb
.SCOPE_ONELEVEL
140 search_expr
= "(&(objectClass=groupPolicyContainer)(name=%s))" % ldb
.binary_encode(gpo
)
142 if displayname
is not None:
143 search_expr
= "(&(objectClass=groupPolicyContainer)(displayname=%s))" % ldb
.binary_encode(displayname
)
147 search_scope
= ldb
.SCOPE_BASE
150 msg
= samdb
.search(base
=base_dn
, scope
=search_scope
,
151 expression
=search_expr
,
152 attrs
=['nTSecurityDescriptor',
160 mesg
= "Cannot get information for GPO %s" % gpo
162 mesg
= "Cannot get information for GPOs"
163 raise CommandError(mesg
, e
)
168 def get_gpo_containers(samdb
, gpo
):
169 '''lists dn of containers for a GPO'''
171 search_expr
= "(&(objectClass=*)(gPLink=*%s*))" % gpo
173 msg
= samdb
.search(expression
=search_expr
, attrs
=['gPLink'])
175 raise CommandError("Could not find container(s) with GPO %s" % gpo
, e
)
180 def del_gpo_link(samdb
, container_dn
, gpo
):
181 '''delete GPO link for the container'''
182 # Check if valid Container DN and get existing GPlinks
184 msg
= samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
185 expression
="(objectClass=*)",
188 raise CommandError("Container '%s' does not exist" % container_dn
, e
)
191 gpo_dn
= str(get_gpo_dn(samdb
, gpo
))
193 gplist
= parse_gplink(msg
['gPLink'][0])
195 if g
['dn'].lower() == gpo_dn
.lower():
200 raise CommandError("No GPO(s) linked to this container")
203 raise CommandError("GPO '%s' not linked to this container" % gpo
)
208 gplink_str
= encode_gplink(gplist
)
209 m
['r0'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_REPLACE
, 'gPLink')
211 m
['d0'] = ldb
.MessageElement(msg
['gPLink'][0], ldb
.FLAG_MOD_DELETE
, 'gPLink')
215 raise CommandError("Error removing GPO from container", e
)
219 '''Parse UNC string into a hostname, a service, and a filepath'''
220 if unc
.startswith('\\\\') and unc
.startswith('//'):
221 raise ValueError("UNC doesn't start with \\\\ or //")
222 tmp
= unc
[2:].split('/', 2)
225 tmp
= unc
[2:].split('\\', 2)
228 raise ValueError("Invalid UNC string: %s" % unc
)
231 def copy_directory_remote_to_local(conn
, remotedir
, localdir
):
232 if not os
.path
.isdir(localdir
):
234 r_dirs
= [ remotedir
]
235 l_dirs
= [ localdir
]
240 dirlist
= conn
.list(r_dir
)
242 r_name
= r_dir
+ '\\' + e
['name']
243 l_name
= os
.path
.join(l_dir
, e
['name'])
245 if e
['attrib'] & smb
.FILE_ATTRIBUTE_DIRECTORY
:
246 r_dirs
.append(r_name
)
247 l_dirs
.append(l_name
)
250 data
= conn
.loadfile(r_name
)
251 file(l_name
, 'w').write(data
)
254 def copy_directory_local_to_remote(conn
, localdir
, remotedir
):
255 if not conn
.chkpath(remotedir
):
256 conn
.mkdir(remotedir
)
257 l_dirs
= [ localdir
]
258 r_dirs
= [ remotedir
]
263 dirlist
= os
.listdir(l_dir
)
265 l_name
= os
.path
.join(l_dir
, e
)
266 r_name
= r_dir
+ '\\' + e
268 if os
.path
.isdir(l_name
):
269 l_dirs
.append(l_name
)
270 r_dirs
.append(r_name
)
273 data
= file(l_name
, 'r').read()
274 conn
.savefile(r_name
, data
)
277 def create_directory_hier(conn
, remotedir
):
278 elems
= remotedir
.replace('/', '\\').split('\\')
281 path
= path
+ '\\' + e
282 if not conn
.chkpath(path
):
286 class cmd_listall(Command
):
289 synopsis
= "%prog [options]"
291 takes_optiongroups
= {
292 "sambaopts": options
.SambaOptions
,
293 "versionopts": options
.VersionOptions
,
294 "credopts": options
.CredentialsOptions
,
298 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
299 metavar
="URL", dest
="H")
302 def run(self
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
304 self
.lp
= sambaopts
.get_loadparm()
305 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
307 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
311 msg
= get_gpo_info(self
.samdb
, None)
314 self
.outf
.write("GPO : %s\n" % m
['name'][0])
315 self
.outf
.write("display name : %s\n" % m
['displayName'][0])
316 self
.outf
.write("path : %s\n" % m
['gPCFileSysPath'][0])
317 self
.outf
.write("dn : %s\n" % m
.dn
)
318 self
.outf
.write("version : %s\n" % attr_default(m
, 'versionNumber', '0'))
319 self
.outf
.write("flags : %s\n" % gpo_flags_string(int(attr_default(m
, 'flags', 0))))
320 self
.outf
.write("\n")
323 class cmd_list(Command
):
324 """List GPOs for an account."""
326 synopsis
= "%prog <username> [options]"
328 takes_args
= ['username']
329 takes_optiongroups
= {
330 "sambaopts": options
.SambaOptions
,
331 "versionopts": options
.VersionOptions
,
332 "credopts": options
.CredentialsOptions
,
336 Option("-H", "--URL", help="LDB URL for database or target server",
337 type=str, metavar
="URL", dest
="H")
340 def run(self
, username
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
342 self
.lp
= sambaopts
.get_loadparm()
343 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
345 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
350 msg
= self
.samdb
.search(expression
='(&(|(samAccountName=%s)(samAccountName=%s$))(objectClass=User))' %
351 (ldb
.binary_encode(username
),ldb
.binary_encode(username
)))
354 raise CommandError("Failed to find account %s" % username
)
356 # check if its a computer account
358 msg
= self
.samdb
.search(base
=user_dn
, scope
=ldb
.SCOPE_BASE
, attrs
=['objectClass'])[0]
359 is_computer
= 'computer' in msg
['objectClass']
361 raise CommandError("Failed to find objectClass for user %s" % username
)
363 session_info_flags
= ( AUTH_SESSION_INFO_DEFAULT_GROUPS |
364 AUTH_SESSION_INFO_AUTHENTICATED
)
366 # When connecting to a remote server, don't look up the local privilege DB
367 if self
.url
is not None and self
.url
.startswith('ldap'):
368 session_info_flags |
= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
370 session
= samba
.auth
.user_session(self
.samdb
, lp_ctx
=self
.lp
, dn
=user_dn
,
371 session_info_flags
=session_info_flags
)
373 token
= session
.security_token
378 dn
= ldb
.Dn(self
.samdb
, str(user_dn
)).parent()
380 msg
= self
.samdb
.search(base
=dn
, scope
=ldb
.SCOPE_BASE
, attrs
=['gPLink', 'gPOptions'])[0]
382 glist
= parse_gplink(msg
['gPLink'][0])
384 if not inherit
and not (g
['options'] & dsdb
.GPLINK_OPT_ENFORCE
):
386 if g
['options'] & dsdb
.GPLINK_OPT_DISABLE
:
390 sd_flags
=security
.SECINFO_OWNER|security
.SECINFO_GROUP|security
.SECINFO_DACL
391 gmsg
= self
.samdb
.search(base
=g
['dn'], scope
=ldb
.SCOPE_BASE
,
392 attrs
=['name', 'displayName', 'flags',
393 'nTSecurityDescriptor'],
394 controls
=['sd_flags:1:%d' % sd_flags
])
395 secdesc_ndr
= gmsg
[0]['nTSecurityDescriptor'][0]
396 secdesc
= ndr_unpack(security
.descriptor
, secdesc_ndr
)
398 self
.outf
.write("Failed to fetch gpo object with nTSecurityDescriptor %s\n" %
403 samba
.security
.access_check(secdesc
, token
,
404 security
.SEC_STD_READ_CONTROL |
405 security
.SEC_ADS_LIST |
406 security
.SEC_ADS_READ_PROP
)
408 self
.outf
.write("Failed access check on %s\n" % msg
.dn
)
411 # check the flags on the GPO
412 flags
= int(attr_default(gmsg
[0], 'flags', 0))
413 if is_computer
and (flags
& dsdb
.GPO_FLAG_MACHINE_DISABLE
):
415 if not is_computer
and (flags
& dsdb
.GPO_FLAG_USER_DISABLE
):
417 gpos
.append((gmsg
[0]['displayName'][0], gmsg
[0]['name'][0]))
419 # check if this blocks inheritance
420 gpoptions
= int(attr_default(msg
, 'gPOptions', 0))
421 if gpoptions
& dsdb
.GPO_BLOCK_INHERITANCE
:
424 if dn
== self
.samdb
.get_default_basedn():
433 self
.outf
.write("GPOs for %s %s\n" % (msg_str
, username
))
435 self
.outf
.write(" %s %s\n" % (g
[0], g
[1]))
438 class cmd_show(Command
):
439 """Show information for a GPO."""
441 synopsis
= "%prog <gpo> [options]"
443 takes_optiongroups
= {
444 "sambaopts": options
.SambaOptions
,
445 "versionopts": options
.VersionOptions
,
446 "credopts": options
.CredentialsOptions
,
452 Option("-H", help="LDB URL for database or target server", type=str)
455 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
457 self
.lp
= sambaopts
.get_loadparm()
458 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
460 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
465 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
467 raise CommandError("GPO '%s' does not exist" % gpo
)
470 secdesc_ndr
= msg
['nTSecurityDescriptor'][0]
471 secdesc
= ndr_unpack(security
.descriptor
, secdesc_ndr
)
472 secdesc_sddl
= secdesc
.as_sddl()
474 secdesc_sddl
= "<hidden>"
476 self
.outf
.write("GPO : %s\n" % msg
['name'][0])
477 self
.outf
.write("display name : %s\n" % msg
['displayName'][0])
478 self
.outf
.write("path : %s\n" % msg
['gPCFileSysPath'][0])
479 self
.outf
.write("dn : %s\n" % msg
.dn
)
480 self
.outf
.write("version : %s\n" % attr_default(msg
, 'versionNumber', '0'))
481 self
.outf
.write("flags : %s\n" % gpo_flags_string(int(attr_default(msg
, 'flags', 0))))
482 self
.outf
.write("ACL : %s\n" % secdesc_sddl
)
483 self
.outf
.write("\n")
486 class cmd_getlink(Command
):
487 """List GPO Links for a container."""
489 synopsis
= "%prog <container_dn> [options]"
491 takes_optiongroups
= {
492 "sambaopts": options
.SambaOptions
,
493 "versionopts": options
.VersionOptions
,
494 "credopts": options
.CredentialsOptions
,
497 takes_args
= ['container_dn']
500 Option("-H", help="LDB URL for database or target server", type=str)
503 def run(self
, container_dn
, H
=None, sambaopts
=None, credopts
=None,
506 self
.lp
= sambaopts
.get_loadparm()
507 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
509 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
514 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
515 expression
="(objectClass=*)",
518 raise CommandError("Container '%s' does not exist" % container_dn
)
521 self
.outf
.write("GPO(s) linked to DN %s\n" % container_dn
)
522 gplist
= parse_gplink(msg
['gPLink'][0])
524 msg
= get_gpo_info(self
.samdb
, dn
=g
['dn'])
525 self
.outf
.write(" GPO : %s\n" % msg
[0]['name'][0])
526 self
.outf
.write(" Name : %s\n" % msg
[0]['displayName'][0])
527 self
.outf
.write(" Options : %s\n" % gplink_options_string(g
['options']))
528 self
.outf
.write("\n")
530 self
.outf
.write("No GPO(s) linked to DN=%s\n" % container_dn
)
533 class cmd_setlink(Command
):
534 """Add or update a GPO link to a container."""
536 synopsis
= "%prog <container_dn> <gpo> [options]"
538 takes_optiongroups
= {
539 "sambaopts": options
.SambaOptions
,
540 "versionopts": options
.VersionOptions
,
541 "credopts": options
.CredentialsOptions
,
544 takes_args
= ['container_dn', 'gpo']
547 Option("-H", help="LDB URL for database or target server", type=str),
548 Option("--disable", dest
="disabled", default
=False, action
='store_true',
549 help="Disable policy"),
550 Option("--enforce", dest
="enforced", default
=False, action
='store_true',
551 help="Enforce policy")
554 def run(self
, container_dn
, gpo
, H
=None, disabled
=False, enforced
=False,
555 sambaopts
=None, credopts
=None, versionopts
=None):
557 self
.lp
= sambaopts
.get_loadparm()
558 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
560 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
566 gplink_options |
= dsdb
.GPLINK_OPT_DISABLE
568 gplink_options |
= dsdb
.GPLINK_OPT_ENFORCE
570 # Check if valid GPO DN
572 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
574 raise CommandError("GPO '%s' does not exist" % gpo
)
575 gpo_dn
= str(get_gpo_dn(self
.samdb
, gpo
))
577 # Check if valid Container DN
579 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
580 expression
="(objectClass=*)",
583 raise CommandError("Container '%s' does not exist" % container_dn
)
585 # Update existing GPlinks or Add new one
586 existing_gplink
= False
588 gplist
= parse_gplink(msg
['gPLink'][0])
589 existing_gplink
= True
592 if g
['dn'].lower() == gpo_dn
.lower():
593 g
['options'] = gplink_options
597 raise CommandError("GPO '%s' already linked to this container" % gpo
)
599 gplist
.insert(0, { 'dn' : gpo_dn
, 'options' : gplink_options
})
602 gplist
.append({ 'dn' : gpo_dn
, 'options' : gplink_options
})
604 gplink_str
= encode_gplink(gplist
)
607 m
.dn
= ldb
.Dn(self
.samdb
, container_dn
)
610 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_REPLACE
, 'gPLink')
612 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_ADD
, 'gPLink')
617 raise CommandError("Error adding GPO Link", e
)
619 self
.outf
.write("Added/Updated GPO link\n")
620 cmd_getlink().run(container_dn
, H
, sambaopts
, credopts
, versionopts
)
623 class cmd_dellink(Command
):
624 """Delete GPO link from a container."""
626 synopsis
= "%prog <container_dn> <gpo> [options]"
628 takes_optiongroups
= {
629 "sambaopts": options
.SambaOptions
,
630 "versionopts": options
.VersionOptions
,
631 "credopts": options
.CredentialsOptions
,
634 takes_args
= ['container', 'gpo']
637 Option("-H", help="LDB URL for database or target server", type=str),
640 def run(self
, container
, gpo
, H
=None, sambaopts
=None, credopts
=None,
643 self
.lp
= sambaopts
.get_loadparm()
644 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
646 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
652 get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
654 raise CommandError("GPO '%s' does not exist" % gpo
)
656 container_dn
= ldb
.Dn(self
.samdb
, container
)
657 del_gpo_link(self
.samdb
, container_dn
, gpo
)
658 self
.outf
.write("Deleted GPO link.\n")
659 cmd_getlink().run(container_dn
, H
, sambaopts
, credopts
, versionopts
)
662 class cmd_listcontainers(Command
):
663 """List all linked containers for a GPO."""
665 synopsis
= "%prog <gpo> [options]"
667 takes_optiongroups
= {
668 "sambaopts": options
.SambaOptions
,
669 "versionopts": options
.VersionOptions
,
670 "credopts": options
.CredentialsOptions
,
676 Option("-H", help="LDB URL for database or target server", type=str)
679 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=None,
682 self
.lp
= sambaopts
.get_loadparm()
683 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
685 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
689 msg
= get_gpo_containers(self
.samdb
, gpo
)
691 self
.outf
.write("Container(s) using GPO %s\n" % gpo
)
693 self
.outf
.write(" DN: %s\n" % m
['dn'])
695 self
.outf
.write("No Containers using GPO %s\n" % gpo
)
698 class cmd_getinheritance(Command
):
699 """Get inheritance flag for a container."""
701 synopsis
= "%prog <container_dn> [options]"
703 takes_optiongroups
= {
704 "sambaopts": options
.SambaOptions
,
705 "versionopts": options
.VersionOptions
,
706 "credopts": options
.CredentialsOptions
,
709 takes_args
= ['container_dn']
712 Option("-H", help="LDB URL for database or target server", type=str)
715 def run(self
, container_dn
, H
=None, sambaopts
=None, credopts
=None,
718 self
.lp
= sambaopts
.get_loadparm()
719 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
721 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
726 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
727 expression
="(objectClass=*)",
728 attrs
=['gPOptions'])[0]
730 raise CommandError("Container '%s' does not exist" % container_dn
)
733 if 'gPOptions' in msg
:
734 inheritance
= int(msg
['gPOptions'][0])
736 if inheritance
== dsdb
.GPO_BLOCK_INHERITANCE
:
737 self
.outf
.write("Container has GPO_BLOCK_INHERITANCE\n")
739 self
.outf
.write("Container has GPO_INHERIT\n")
742 class cmd_setinheritance(Command
):
743 """Set inheritance flag on a container."""
745 synopsis
= "%prog <container_dn> <block|inherit> [options]"
747 takes_optiongroups
= {
748 "sambaopts": options
.SambaOptions
,
749 "versionopts": options
.VersionOptions
,
750 "credopts": options
.CredentialsOptions
,
753 takes_args
= [ 'container_dn', 'inherit_state' ]
756 Option("-H", help="LDB URL for database or target server", type=str)
759 def run(self
, container_dn
, inherit_state
, H
=None, sambaopts
=None, credopts
=None,
762 if inherit_state
.lower() == 'block':
763 inheritance
= dsdb
.GPO_BLOCK_INHERITANCE
764 elif inherit_state
.lower() == 'inherit':
765 inheritance
= dsdb
.GPO_INHERIT
767 raise CommandError("Unknown inheritance state (%s)" % inherit_state
)
769 self
.lp
= sambaopts
.get_loadparm()
770 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
772 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
776 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
777 expression
="(objectClass=*)",
778 attrs
=['gPOptions'])[0]
780 raise CommandError("Container '%s' does not exist" % container_dn
)
783 m
.dn
= ldb
.Dn(self
.samdb
, container_dn
)
785 if 'gPOptions' in msg
:
786 m
['new_value'] = ldb
.MessageElement(str(inheritance
), ldb
.FLAG_MOD_REPLACE
, 'gPOptions')
788 m
['new_value'] = ldb
.MessageElement(str(inheritance
), ldb
.FLAG_MOD_ADD
, 'gPOptions')
793 raise CommandError("Error setting inheritance state %s" % inherit_state
, e
)
796 class cmd_fetch(Command
):
797 """Download a GPO."""
799 synopsis
= "%prog <gpo> [options]"
801 takes_optiongroups
= {
802 "sambaopts": options
.SambaOptions
,
803 "versionopts": options
.VersionOptions
,
804 "credopts": options
.CredentialsOptions
,
810 Option("-H", help="LDB URL for database or target server", type=str),
811 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
814 def run(self
, gpo
, H
=None, tmpdir
=None, sambaopts
=None, credopts
=None, versionopts
=None):
816 self
.lp
= sambaopts
.get_loadparm()
817 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
819 # We need to know writable DC to setup SMB connection
820 if H
and H
.startswith('ldap://'):
824 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
825 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
829 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
831 raise CommandError("GPO '%s' does not exist" % gpo
)
834 unc
= msg
['gPCFileSysPath'][0]
836 [dom_name
, service
, sharepath
] = parse_unc(unc
)
838 raise CommandError("Invalid GPO path (%s)" % unc
)
842 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
844 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
)
849 if not os
.path
.isdir(tmpdir
):
850 raise CommandError("Temoprary directory '%s' does not exist" % tmpdir
)
852 localdir
= os
.path
.join(tmpdir
, "policy")
853 if not os
.path
.isdir(localdir
):
856 gpodir
= os
.path
.join(localdir
, gpo
)
857 if os
.path
.isdir(gpodir
):
858 raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir
)
862 copy_directory_remote_to_local(conn
, sharepath
, gpodir
)
864 # FIXME: Catch more specific exception
865 raise CommandError("Error copying GPO from DC", e
)
866 self
.outf
.write('GPO copied to %s\n' % gpodir
)
869 class cmd_create(Command
):
870 """Create an empty GPO."""
872 synopsis
= "%prog <displayname> [options]"
874 takes_optiongroups
= {
875 "sambaopts": options
.SambaOptions
,
876 "versionopts": options
.VersionOptions
,
877 "credopts": options
.CredentialsOptions
,
880 takes_args
= ['displayname']
883 Option("-H", help="LDB URL for database or target server", type=str),
884 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
887 def run(self
, displayname
, H
=None, tmpdir
=None, sambaopts
=None, credopts
=None,
890 self
.lp
= sambaopts
.get_loadparm()
891 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
893 # We need to know writable DC to setup SMB connection
894 if H
and H
.startswith('ldap://'):
898 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
899 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
903 msg
= get_gpo_info(self
.samdb
, displayname
=displayname
)
905 raise CommandError("A GPO already existing with name '%s'" % displayname
)
908 guid
= str(uuid
.uuid4())
909 gpo
= "{%s}" % guid
.upper()
910 realm
= self
.lp
.get('realm')
911 unc_path
= "\\\\%s\\sysvol\\%s\\Policies\\%s" % (realm
, realm
, gpo
)
916 if not os
.path
.isdir(tmpdir
):
917 raise CommandError("Temporary directory '%s' does not exist" % tmpdir
)
919 localdir
= os
.path
.join(tmpdir
, "policy")
920 if not os
.path
.isdir(localdir
):
923 gpodir
= os
.path
.join(localdir
, gpo
)
924 if os
.path
.isdir(gpodir
):
925 raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir
)
929 os
.mkdir(os
.path
.join(gpodir
, "Machine"))
930 os
.mkdir(os
.path
.join(gpodir
, "User"))
931 gpt_contents
= "[General]\r\nVersion=0\r\n"
932 file(os
.path
.join(gpodir
, "GPT.INI"), "w").write(gpt_contents
)
934 raise CommandError("Error Creating GPO files", e
)
936 # Connect to DC over SMB
937 [dom_name
, service
, sharepath
] = parse_unc(unc_path
)
939 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
941 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
, e
)
943 self
.samdb
.transaction_start()
946 gpo_dn
= get_gpo_dn(self
.samdb
, gpo
)
950 m
['a01'] = ldb
.MessageElement("groupPolicyContainer", ldb
.FLAG_MOD_ADD
, "objectClass")
951 m
['a02'] = ldb
.MessageElement(displayname
, ldb
.FLAG_MOD_ADD
, "displayName")
952 m
['a03'] = ldb
.MessageElement(unc_path
, ldb
.FLAG_MOD_ADD
, "gPCFileSysPath")
953 m
['a04'] = ldb
.MessageElement("0", ldb
.FLAG_MOD_ADD
, "flags")
954 m
['a05'] = ldb
.MessageElement("0", ldb
.FLAG_MOD_ADD
, "versionNumber")
955 m
['a06'] = ldb
.MessageElement("TRUE", ldb
.FLAG_MOD_ADD
, "showInAdvancedViewOnly")
956 m
['a07'] = ldb
.MessageElement("2", ldb
.FLAG_MOD_ADD
, "gpcFunctionalityVersion")
959 # Add cn=User,cn=<guid>
961 m
.dn
= ldb
.Dn(self
.samdb
, "CN=User,%s" % str(gpo_dn
))
962 m
['a01'] = ldb
.MessageElement("container", ldb
.FLAG_MOD_ADD
, "objectClass")
963 m
['a02'] = ldb
.MessageElement("TRUE", ldb
.FLAG_MOD_ADD
, "showInAdvancedViewOnly")
966 # Add cn=Machine,cn=<guid>
968 m
.dn
= ldb
.Dn(self
.samdb
, "CN=Machine,%s" % str(gpo_dn
))
969 m
['a01'] = ldb
.MessageElement("container", ldb
.FLAG_MOD_ADD
, "objectClass")
970 m
['a02'] = ldb
.MessageElement("TRUE", ldb
.FLAG_MOD_ADD
, "showInAdvancedViewOnly")
973 # Copy GPO files over SMB
974 create_directory_hier(conn
, sharepath
)
975 copy_directory_local_to_remote(conn
, gpodir
, sharepath
)
977 # Get new security descriptor
978 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
979 ds_sd_ndr
= msg
['nTSecurityDescriptor'][0]
980 ds_sd
= ndr_unpack(security
.descriptor
, ds_sd_ndr
).as_sddl()
982 # Create a file system security descriptor
983 domain_sid
= security
.dom_sid(self
.samdb
.get_domain_sid())
984 sddl
= dsacl2fsacl(ds_sd
, domain_sid
)
985 fs_sd
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
988 sio
= ( security
.SECINFO_OWNER |
989 security
.SECINFO_GROUP |
990 security
.SECINFO_DACL |
991 security
.SECINFO_PROTECTED_DACL
)
992 conn
.set_acl(sharepath
, fs_sd
, sio
)
994 self
.samdb
.transaction_cancel()
997 self
.samdb
.transaction_commit()
999 self
.outf
.write("GPO '%s' created as %s\n" % (displayname
, gpo
))
1002 class cmd_del(Command
):
1005 synopsis
= "%prog <gpo> [options]"
1007 takes_optiongroups
= {
1008 "sambaopts": options
.SambaOptions
,
1009 "versionopts": options
.VersionOptions
,
1010 "credopts": options
.CredentialsOptions
,
1013 takes_args
= ['gpo']
1016 Option("-H", help="LDB URL for database or target server", type=str),
1019 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=None,
1022 self
.lp
= sambaopts
.get_loadparm()
1023 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
1025 # We need to know writable DC to setup SMB connection
1026 if H
and H
.startswith('ldap://'):
1030 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
1031 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
1035 # Check if valid GPO
1037 get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
1039 raise CommandError("GPO '%s' does not exist" % gpo
)
1041 realm
= self
.lp
.get('realm')
1042 unc_path
= "\\\\%s\\sysvol\\%s\\Policies\\%s" % (realm
, realm
, gpo
)
1044 # Connect to DC over SMB
1045 [dom_name
, service
, sharepath
] = parse_unc(unc_path
)
1047 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
1048 except Exception, e
:
1049 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
, e
)
1051 self
.samdb
.transaction_start()
1053 # Check for existing links
1054 msg
= get_gpo_containers(self
.samdb
, gpo
)
1057 self
.outf
.write("GPO %s is linked to containers\n" % gpo
)
1059 del_gpo_link(self
.samdb
, m
['dn'], gpo
)
1060 self
.outf
.write(" Removed link from %s.\n" % m
['dn'])
1062 # Remove LDAP entries
1063 gpo_dn
= get_gpo_dn(self
.samdb
, gpo
)
1064 self
.samdb
.delete(ldb
.Dn(self
.samdb
, "CN=User,%s" % str(gpo_dn
)))
1065 self
.samdb
.delete(ldb
.Dn(self
.samdb
, "CN=Machine,%s" % str(gpo_dn
)))
1066 self
.samdb
.delete(gpo_dn
)
1069 conn
.deltree(sharepath
)
1072 self
.samdb
.transaction_cancel()
1075 self
.samdb
.transaction_commit()
1077 self
.outf
.write("GPO %s deleted.\n" % gpo
)
1080 class cmd_aclcheck(Command
):
1081 """Check all GPOs have matching LDAP and DS ACLs."""
1083 synopsis
= "%prog [options]"
1085 takes_optiongroups
= {
1086 "sambaopts": options
.SambaOptions
,
1087 "versionopts": options
.VersionOptions
,
1088 "credopts": options
.CredentialsOptions
,
1092 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1093 metavar
="URL", dest
="H")
1096 def run(self
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
1098 self
.lp
= sambaopts
.get_loadparm()
1099 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
1101 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
1103 # We need to know writable DC to setup SMB connection
1104 if H
and H
.startswith('ldap://'):
1108 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
1109 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
1113 msg
= get_gpo_info(self
.samdb
, None)
1117 unc
= m
['gPCFileSysPath'][0]
1119 [dom_name
, service
, sharepath
] = parse_unc(unc
)
1121 raise CommandError("Invalid GPO path (%s)" % unc
)
1125 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
1127 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
)
1129 fs_sd
= conn
.get_acl(sharepath
, security
.SECINFO_OWNER | security
.SECINFO_GROUP | security
.SECINFO_DACL
, security
.SEC_FLAG_MAXIMUM_ALLOWED
)
1131 ds_sd_ndr
= m
['nTSecurityDescriptor'][0]
1132 ds_sd
= ndr_unpack(security
.descriptor
, ds_sd_ndr
).as_sddl()
1134 # Create a file system security descriptor
1135 domain_sid
= security
.dom_sid(self
.samdb
.get_domain_sid())
1136 expected_fs_sddl
= dsacl2fsacl(ds_sd
, domain_sid
)
1138 if (fs_sd
.as_sddl(domain_sid
) != expected_fs_sddl
):
1139 raise CommandError("Invalid GPO ACL %s on path (%s), should be %s" % (fs_sd
.as_sddl(domain_sid
), sharepath
, expected_fs_sddl
))
1142 class cmd_gpo(SuperCommand
):
1143 """Group Policy Object (GPO) management."""
1146 subcommands
["listall"] = cmd_listall()
1147 subcommands
["list"] = cmd_list()
1148 subcommands
["show"] = cmd_show()
1149 subcommands
["getlink"] = cmd_getlink()
1150 subcommands
["setlink"] = cmd_setlink()
1151 subcommands
["dellink"] = cmd_dellink()
1152 subcommands
["listcontainers"] = cmd_listcontainers()
1153 subcommands
["getinheritance"] = cmd_getinheritance()
1154 subcommands
["setinheritance"] = cmd_setinheritance()
1155 subcommands
["fetch"] = cmd_fetch()
1156 subcommands
["create"] = cmd_create()
1157 subcommands
["del"] = cmd_del()
1158 subcommands
["aclcheck"] = cmd_aclcheck()