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 gmsg
= self
.samdb
.search(base
=g
['dn'], scope
=ldb
.SCOPE_BASE
,
391 attrs
=['name', 'displayName', 'flags',
392 'nTSecurityDescriptor'])
394 self
.outf
.write("Failed to fetch gpo object %s\n" %
398 secdesc_ndr
= gmsg
[0]['nTSecurityDescriptor'][0]
399 secdesc
= ndr_unpack(security
.descriptor
, secdesc_ndr
)
402 samba
.security
.access_check(secdesc
, token
,
403 security
.SEC_STD_READ_CONTROL |
404 security
.SEC_ADS_LIST |
405 security
.SEC_ADS_READ_PROP
)
407 self
.outf
.write("Failed access check on %s\n" % msg
.dn
)
410 # check the flags on the GPO
411 flags
= int(attr_default(gmsg
[0], 'flags', 0))
412 if is_computer
and (flags
& dsdb
.GPO_FLAG_MACHINE_DISABLE
):
414 if not is_computer
and (flags
& dsdb
.GPO_FLAG_USER_DISABLE
):
416 gpos
.append((gmsg
[0]['displayName'][0], gmsg
[0]['name'][0]))
418 # check if this blocks inheritance
419 gpoptions
= int(attr_default(msg
, 'gPOptions', 0))
420 if gpoptions
& dsdb
.GPO_BLOCK_INHERITANCE
:
423 if dn
== self
.samdb
.get_default_basedn():
432 self
.outf
.write("GPOs for %s %s\n" % (msg_str
, username
))
434 self
.outf
.write(" %s %s\n" % (g
[0], g
[1]))
437 class cmd_show(Command
):
438 """Show information for a GPO."""
440 synopsis
= "%prog <gpo> [options]"
442 takes_optiongroups
= {
443 "sambaopts": options
.SambaOptions
,
444 "versionopts": options
.VersionOptions
,
445 "credopts": options
.CredentialsOptions
,
451 Option("-H", help="LDB URL for database or target server", type=str)
454 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
456 self
.lp
= sambaopts
.get_loadparm()
457 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
459 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
464 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
466 raise CommandError("GPO '%s' does not exist" % gpo
)
468 secdesc_ndr
= msg
['nTSecurityDescriptor'][0]
469 secdesc
= ndr_unpack(security
.descriptor
, secdesc_ndr
)
471 self
.outf
.write("GPO : %s\n" % msg
['name'][0])
472 self
.outf
.write("display name : %s\n" % msg
['displayName'][0])
473 self
.outf
.write("path : %s\n" % msg
['gPCFileSysPath'][0])
474 self
.outf
.write("dn : %s\n" % msg
.dn
)
475 self
.outf
.write("version : %s\n" % attr_default(msg
, 'versionNumber', '0'))
476 self
.outf
.write("flags : %s\n" % gpo_flags_string(int(attr_default(msg
, 'flags', 0))))
477 self
.outf
.write("ACL : %s\n" % secdesc
.as_sddl())
478 self
.outf
.write("\n")
481 class cmd_getlink(Command
):
482 """List GPO Links for a container."""
484 synopsis
= "%prog <container_dn> [options]"
486 takes_optiongroups
= {
487 "sambaopts": options
.SambaOptions
,
488 "versionopts": options
.VersionOptions
,
489 "credopts": options
.CredentialsOptions
,
492 takes_args
= ['container_dn']
495 Option("-H", help="LDB URL for database or target server", type=str)
498 def run(self
, container_dn
, H
=None, sambaopts
=None, credopts
=None,
501 self
.lp
= sambaopts
.get_loadparm()
502 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
504 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
509 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
510 expression
="(objectClass=*)",
513 raise CommandError("Container '%s' does not exist" % container_dn
)
516 self
.outf
.write("GPO(s) linked to DN %s\n" % container_dn
)
517 gplist
= parse_gplink(msg
['gPLink'][0])
519 msg
= get_gpo_info(self
.samdb
, dn
=g
['dn'])
520 self
.outf
.write(" GPO : %s\n" % msg
[0]['name'][0])
521 self
.outf
.write(" Name : %s\n" % msg
[0]['displayName'][0])
522 self
.outf
.write(" Options : %s\n" % gplink_options_string(g
['options']))
523 self
.outf
.write("\n")
525 self
.outf
.write("No GPO(s) linked to DN=%s\n" % container_dn
)
528 class cmd_setlink(Command
):
529 """Add or update a GPO link to a container."""
531 synopsis
= "%prog <container_dn> <gpo> [options]"
533 takes_optiongroups
= {
534 "sambaopts": options
.SambaOptions
,
535 "versionopts": options
.VersionOptions
,
536 "credopts": options
.CredentialsOptions
,
539 takes_args
= ['container_dn', 'gpo']
542 Option("-H", help="LDB URL for database or target server", type=str),
543 Option("--disable", dest
="disabled", default
=False, action
='store_true',
544 help="Disable policy"),
545 Option("--enforce", dest
="enforced", default
=False, action
='store_true',
546 help="Enforce policy")
549 def run(self
, container_dn
, gpo
, H
=None, disabled
=False, enforced
=False,
550 sambaopts
=None, credopts
=None, versionopts
=None):
552 self
.lp
= sambaopts
.get_loadparm()
553 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
555 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
561 gplink_options |
= dsdb
.GPLINK_OPT_DISABLE
563 gplink_options |
= dsdb
.GPLINK_OPT_ENFORCE
565 # Check if valid GPO DN
567 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
569 raise CommandError("GPO '%s' does not exist" % gpo
)
570 gpo_dn
= str(get_gpo_dn(self
.samdb
, gpo
))
572 # Check if valid Container DN
574 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
575 expression
="(objectClass=*)",
578 raise CommandError("Container '%s' does not exist" % container_dn
)
580 # Update existing GPlinks or Add new one
581 existing_gplink
= False
583 gplist
= parse_gplink(msg
['gPLink'][0])
584 existing_gplink
= True
587 if g
['dn'].lower() == gpo_dn
.lower():
588 g
['options'] = gplink_options
592 raise CommandError("GPO '%s' already linked to this container" % gpo
)
594 gplist
.insert(0, { 'dn' : gpo_dn
, 'options' : gplink_options
})
597 gplist
.append({ 'dn' : gpo_dn
, 'options' : gplink_options
})
599 gplink_str
= encode_gplink(gplist
)
602 m
.dn
= ldb
.Dn(self
.samdb
, container_dn
)
605 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_REPLACE
, 'gPLink')
607 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_ADD
, 'gPLink')
612 raise CommandError("Error adding GPO Link", e
)
614 self
.outf
.write("Added/Updated GPO link\n")
615 cmd_getlink().run(container_dn
, H
, sambaopts
, credopts
, versionopts
)
618 class cmd_dellink(Command
):
619 """Delete GPO link from a container."""
621 synopsis
= "%prog <container_dn> <gpo> [options]"
623 takes_optiongroups
= {
624 "sambaopts": options
.SambaOptions
,
625 "versionopts": options
.VersionOptions
,
626 "credopts": options
.CredentialsOptions
,
629 takes_args
= ['container', 'gpo']
632 Option("-H", help="LDB URL for database or target server", type=str),
635 def run(self
, container
, gpo
, H
=None, sambaopts
=None, credopts
=None,
638 self
.lp
= sambaopts
.get_loadparm()
639 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
641 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
647 get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
649 raise CommandError("GPO '%s' does not exist" % gpo
)
651 container_dn
= ldb
.Dn(self
.samdb
, container
)
652 del_gpo_link(self
.samdb
, container_dn
, gpo
)
653 self
.outf
.write("Deleted GPO link.\n")
654 cmd_getlink().run(container_dn
, H
, sambaopts
, credopts
, versionopts
)
657 class cmd_listcontainers(Command
):
658 """List all linked containers for a GPO."""
660 synopsis
= "%prog <gpo> [options]"
662 takes_optiongroups
= {
663 "sambaopts": options
.SambaOptions
,
664 "versionopts": options
.VersionOptions
,
665 "credopts": options
.CredentialsOptions
,
671 Option("-H", help="LDB URL for database or target server", type=str)
674 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=None,
677 self
.lp
= sambaopts
.get_loadparm()
678 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
680 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
684 msg
= get_gpo_containers(self
.samdb
, gpo
)
686 self
.outf
.write("Container(s) using GPO %s\n" % gpo
)
688 self
.outf
.write(" DN: %s\n" % m
['dn'])
690 self
.outf
.write("No Containers using GPO %s\n" % gpo
)
693 class cmd_getinheritance(Command
):
694 """Get inheritance flag for a container."""
696 synopsis
= "%prog <container_dn> [options]"
698 takes_optiongroups
= {
699 "sambaopts": options
.SambaOptions
,
700 "versionopts": options
.VersionOptions
,
701 "credopts": options
.CredentialsOptions
,
704 takes_args
= ['container_dn']
707 Option("-H", help="LDB URL for database or target server", type=str)
710 def run(self
, container_dn
, H
=None, sambaopts
=None, credopts
=None,
713 self
.lp
= sambaopts
.get_loadparm()
714 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
716 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
721 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
722 expression
="(objectClass=*)",
723 attrs
=['gPOptions'])[0]
725 raise CommandError("Container '%s' does not exist" % container_dn
)
728 if 'gPOptions' in msg
:
729 inheritance
= int(msg
['gPOptions'][0])
731 if inheritance
== dsdb
.GPO_BLOCK_INHERITANCE
:
732 self
.outf
.write("Container has GPO_BLOCK_INHERITANCE\n")
734 self
.outf
.write("Container has GPO_INHERIT\n")
737 class cmd_setinheritance(Command
):
738 """Set inheritance flag on a container."""
740 synopsis
= "%prog <container_dn> <block|inherit> [options]"
742 takes_optiongroups
= {
743 "sambaopts": options
.SambaOptions
,
744 "versionopts": options
.VersionOptions
,
745 "credopts": options
.CredentialsOptions
,
748 takes_args
= [ 'container_dn', 'inherit_state' ]
751 Option("-H", help="LDB URL for database or target server", type=str)
754 def run(self
, container_dn
, inherit_state
, H
=None, sambaopts
=None, credopts
=None,
757 if inherit_state
.lower() == 'block':
758 inheritance
= dsdb
.GPO_BLOCK_INHERITANCE
759 elif inherit_state
.lower() == 'inherit':
760 inheritance
= dsdb
.GPO_INHERIT
762 raise CommandError("Unknown inheritance state (%s)" % inherit_state
)
764 self
.lp
= sambaopts
.get_loadparm()
765 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
767 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
771 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
772 expression
="(objectClass=*)",
773 attrs
=['gPOptions'])[0]
775 raise CommandError("Container '%s' does not exist" % container_dn
)
778 m
.dn
= ldb
.Dn(self
.samdb
, container_dn
)
780 if 'gPOptions' in msg
:
781 m
['new_value'] = ldb
.MessageElement(str(inheritance
), ldb
.FLAG_MOD_REPLACE
, 'gPOptions')
783 m
['new_value'] = ldb
.MessageElement(str(inheritance
), ldb
.FLAG_MOD_ADD
, 'gPOptions')
788 raise CommandError("Error setting inheritance state %s" % inherit_state
, e
)
791 class cmd_fetch(Command
):
792 """Download a GPO."""
794 synopsis
= "%prog <gpo> [options]"
796 takes_optiongroups
= {
797 "sambaopts": options
.SambaOptions
,
798 "versionopts": options
.VersionOptions
,
799 "credopts": options
.CredentialsOptions
,
805 Option("-H", help="LDB URL for database or target server", type=str),
806 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
809 def run(self
, gpo
, H
=None, tmpdir
=None, sambaopts
=None, credopts
=None, versionopts
=None):
811 self
.lp
= sambaopts
.get_loadparm()
812 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
814 # We need to know writable DC to setup SMB connection
815 if H
and H
.startswith('ldap://'):
819 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
820 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
824 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
826 raise CommandError("GPO '%s' does not exist" % gpo
)
829 unc
= msg
['gPCFileSysPath'][0]
831 [dom_name
, service
, sharepath
] = parse_unc(unc
)
833 raise CommandError("Invalid GPO path (%s)" % unc
)
837 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
839 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
)
844 if not os
.path
.isdir(tmpdir
):
845 raise CommandError("Temoprary directory '%s' does not exist" % tmpdir
)
847 localdir
= os
.path
.join(tmpdir
, "policy")
848 if not os
.path
.isdir(localdir
):
851 gpodir
= os
.path
.join(localdir
, gpo
)
852 if os
.path
.isdir(gpodir
):
853 raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir
)
857 copy_directory_remote_to_local(conn
, sharepath
, gpodir
)
859 # FIXME: Catch more specific exception
860 raise CommandError("Error copying GPO from DC", e
)
861 self
.outf
.write('GPO copied to %s\n' % gpodir
)
864 class cmd_create(Command
):
865 """Create an empty GPO."""
867 synopsis
= "%prog <displayname> [options]"
869 takes_optiongroups
= {
870 "sambaopts": options
.SambaOptions
,
871 "versionopts": options
.VersionOptions
,
872 "credopts": options
.CredentialsOptions
,
875 takes_args
= ['displayname']
878 Option("-H", help="LDB URL for database or target server", type=str),
879 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
882 def run(self
, displayname
, H
=None, tmpdir
=None, sambaopts
=None, credopts
=None,
885 self
.lp
= sambaopts
.get_loadparm()
886 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
888 # We need to know writable DC to setup SMB connection
889 if H
and H
.startswith('ldap://'):
893 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
894 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
898 msg
= get_gpo_info(self
.samdb
, displayname
=displayname
)
900 raise CommandError("A GPO already existing with name '%s'" % displayname
)
903 guid
= str(uuid
.uuid4())
904 gpo
= "{%s}" % guid
.upper()
905 realm
= self
.lp
.get('realm')
906 unc_path
= "\\\\%s\\sysvol\\%s\\Policies\\%s" % (realm
, realm
, gpo
)
911 if not os
.path
.isdir(tmpdir
):
912 raise CommandError("Temporary directory '%s' does not exist" % tmpdir
)
914 localdir
= os
.path
.join(tmpdir
, "policy")
915 if not os
.path
.isdir(localdir
):
918 gpodir
= os
.path
.join(localdir
, gpo
)
919 if os
.path
.isdir(gpodir
):
920 raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir
)
924 os
.mkdir(os
.path
.join(gpodir
, "Machine"))
925 os
.mkdir(os
.path
.join(gpodir
, "User"))
926 gpt_contents
= "[General]\r\nVersion=0\r\n"
927 file(os
.path
.join(gpodir
, "GPT.INI"), "w").write(gpt_contents
)
929 raise CommandError("Error Creating GPO files", e
)
931 # Connect to DC over SMB
932 [dom_name
, service
, sharepath
] = parse_unc(unc_path
)
934 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
936 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
, e
)
938 self
.samdb
.transaction_start()
941 gpo_dn
= get_gpo_dn(self
.samdb
, gpo
)
945 m
['a01'] = ldb
.MessageElement("groupPolicyContainer", ldb
.FLAG_MOD_ADD
, "objectClass")
946 m
['a02'] = ldb
.MessageElement(displayname
, ldb
.FLAG_MOD_ADD
, "displayName")
947 m
['a03'] = ldb
.MessageElement(unc_path
, ldb
.FLAG_MOD_ADD
, "gPCFileSysPath")
948 m
['a04'] = ldb
.MessageElement("0", ldb
.FLAG_MOD_ADD
, "flags")
949 m
['a05'] = ldb
.MessageElement("0", ldb
.FLAG_MOD_ADD
, "versionNumber")
950 m
['a06'] = ldb
.MessageElement("TRUE", ldb
.FLAG_MOD_ADD
, "showInAdvancedViewOnly")
951 m
['a07'] = ldb
.MessageElement("2", ldb
.FLAG_MOD_ADD
, "gpcFunctionalityVersion")
954 # Add cn=User,cn=<guid>
956 m
.dn
= ldb
.Dn(self
.samdb
, "CN=User,%s" % str(gpo_dn
))
957 m
['a01'] = ldb
.MessageElement("container", ldb
.FLAG_MOD_ADD
, "objectClass")
958 m
['a02'] = ldb
.MessageElement("TRUE", ldb
.FLAG_MOD_ADD
, "showInAdvancedViewOnly")
961 # Add cn=Machine,cn=<guid>
963 m
.dn
= ldb
.Dn(self
.samdb
, "CN=Machine,%s" % str(gpo_dn
))
964 m
['a01'] = ldb
.MessageElement("container", ldb
.FLAG_MOD_ADD
, "objectClass")
965 m
['a02'] = ldb
.MessageElement("TRUE", ldb
.FLAG_MOD_ADD
, "showInAdvancedViewOnly")
968 # Copy GPO files over SMB
969 create_directory_hier(conn
, sharepath
)
970 copy_directory_local_to_remote(conn
, gpodir
, sharepath
)
972 # Get new security descriptor
973 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
974 ds_sd_ndr
= msg
['nTSecurityDescriptor'][0]
975 ds_sd
= ndr_unpack(security
.descriptor
, ds_sd_ndr
).as_sddl()
977 # Create a file system security descriptor
978 domain_sid
= security
.dom_sid(self
.samdb
.get_domain_sid())
979 sddl
= dsacl2fsacl(ds_sd
, domain_sid
)
980 fs_sd
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
983 sio
= ( security
.SECINFO_OWNER |
984 security
.SECINFO_GROUP |
985 security
.SECINFO_DACL |
986 security
.SECINFO_PROTECTED_DACL
)
987 conn
.set_acl(sharepath
, fs_sd
, sio
)
989 self
.samdb
.transaction_cancel()
992 self
.samdb
.transaction_commit()
994 self
.outf
.write("GPO '%s' created as %s\n" % (displayname
, gpo
))
997 class cmd_del(Command
):
1000 synopsis
= "%prog <gpo> [options]"
1002 takes_optiongroups
= {
1003 "sambaopts": options
.SambaOptions
,
1004 "versionopts": options
.VersionOptions
,
1005 "credopts": options
.CredentialsOptions
,
1008 takes_args
= ['gpo']
1011 Option("-H", help="LDB URL for database or target server", type=str),
1014 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=None,
1017 self
.lp
= sambaopts
.get_loadparm()
1018 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
1020 # We need to know writable DC to setup SMB connection
1021 if H
and H
.startswith('ldap://'):
1025 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
1026 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
1030 # Check if valid GPO
1032 get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
1034 raise CommandError("GPO '%s' does not exist" % gpo
)
1036 realm
= self
.lp
.get('realm')
1037 unc_path
= "\\\\%s\\sysvol\\%s\\Policies\\%s" % (realm
, realm
, gpo
)
1039 # Connect to DC over SMB
1040 [dom_name
, service
, sharepath
] = parse_unc(unc_path
)
1042 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
1043 except Exception, e
:
1044 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
, e
)
1046 self
.samdb
.transaction_start()
1048 # Check for existing links
1049 msg
= get_gpo_containers(self
.samdb
, gpo
)
1052 self
.outf
.write("GPO %s is linked to containers\n" % gpo
)
1054 del_gpo_link(self
.samdb
, m
['dn'], gpo
)
1055 self
.outf
.write(" Removed link from %s.\n" % m
['dn'])
1057 # Remove LDAP entries
1058 gpo_dn
= get_gpo_dn(self
.samdb
, gpo
)
1059 self
.samdb
.delete(ldb
.Dn(self
.samdb
, "CN=User,%s" % str(gpo_dn
)))
1060 self
.samdb
.delete(ldb
.Dn(self
.samdb
, "CN=Machine,%s" % str(gpo_dn
)))
1061 self
.samdb
.delete(gpo_dn
)
1064 conn
.deltree(sharepath
)
1067 self
.samdb
.transaction_cancel()
1070 self
.samdb
.transaction_commit()
1072 self
.outf
.write("GPO %s deleted.\n" % gpo
)
1075 class cmd_aclcheck(Command
):
1076 """Check all GPOs have matching LDAP and DS ACLs."""
1078 synopsis
= "%prog [options]"
1080 takes_optiongroups
= {
1081 "sambaopts": options
.SambaOptions
,
1082 "versionopts": options
.VersionOptions
,
1083 "credopts": options
.CredentialsOptions
,
1087 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1088 metavar
="URL", dest
="H")
1091 def run(self
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
1093 self
.lp
= sambaopts
.get_loadparm()
1094 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
1096 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
1098 # We need to know writable DC to setup SMB connection
1099 if H
and H
.startswith('ldap://'):
1103 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
1104 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
1108 msg
= get_gpo_info(self
.samdb
, None)
1112 unc
= m
['gPCFileSysPath'][0]
1114 [dom_name
, service
, sharepath
] = parse_unc(unc
)
1116 raise CommandError("Invalid GPO path (%s)" % unc
)
1120 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
1122 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
)
1124 fs_sd
= conn
.get_acl(sharepath
, security
.SECINFO_OWNER | security
.SECINFO_GROUP | security
.SECINFO_DACL
, security
.SEC_FLAG_MAXIMUM_ALLOWED
)
1126 ds_sd_ndr
= m
['nTSecurityDescriptor'][0]
1127 ds_sd
= ndr_unpack(security
.descriptor
, ds_sd_ndr
).as_sddl()
1129 # Create a file system security descriptor
1130 domain_sid
= security
.dom_sid(self
.samdb
.get_domain_sid())
1131 expected_fs_sddl
= dsacl2fsacl(ds_sd
, domain_sid
)
1133 if (fs_sd
.as_sddl(domain_sid
) != expected_fs_sddl
):
1134 raise CommandError("Invalid GPO ACL %s on path (%s), should be %s" % (fs_sd
.as_sddl(domain_sid
), sharepath
, expected_fs_sddl
))
1137 class cmd_gpo(SuperCommand
):
1138 """Group Policy Object (GPO) management."""
1141 subcommands
["listall"] = cmd_listall()
1142 subcommands
["list"] = cmd_list()
1143 subcommands
["show"] = cmd_show()
1144 subcommands
["getlink"] = cmd_getlink()
1145 subcommands
["setlink"] = cmd_setlink()
1146 subcommands
["dellink"] = cmd_dellink()
1147 subcommands
["listcontainers"] = cmd_listcontainers()
1148 subcommands
["getinheritance"] = cmd_getinheritance()
1149 subcommands
["setinheritance"] = cmd_setinheritance()
1150 subcommands
["fetch"] = cmd_fetch()
1151 subcommands
["create"] = cmd_create()
1152 subcommands
["del"] = cmd_del()
1153 subcommands
["aclcheck"] = cmd_aclcheck()