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 import xml
.etree
.ElementTree
as ET
29 from samba
.auth
import system_session
30 from samba
.netcmd
import (
36 from samba
.samdb
import SamDB
37 from samba
import dsdb
38 from samba
.dcerpc
import security
39 from samba
.ndr
import ndr_unpack
42 from samba
.auth
import AUTH_SESSION_INFO_DEFAULT_GROUPS
, AUTH_SESSION_INFO_AUTHENTICATED
, AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
43 from samba
.netcmd
.common
import netcmd_finddc
44 from samba
import policy
46 from samba
import NTSTATUSError
48 from samba
.ntacls
import dsacl2fsacl
49 from samba
.dcerpc
import nbt
50 from samba
.net
import Net
51 from samba
.gp_parse
import GPParser
, GPNoParserException
, GPGeneralizeException
52 from samba
.gp_parse
.gp_pol
import GPPolParser
53 from samba
.gp_parse
.gp_ini
import (
59 from samba
.gp_parse
.gp_csv
import GPAuditCsvParser
60 from samba
.gp_parse
.gp_inf
import GptTmplInfParser
61 from samba
.gp_parse
.gp_aas
import GPAasParser
64 def samdb_connect(ctx
):
65 '''make a ldap connection to the server'''
67 ctx
.samdb
= SamDB(url
=ctx
.url
,
68 session_info
=system_session(),
69 credentials
=ctx
.creds
, lp
=ctx
.lp
)
70 except Exception as e
:
71 raise CommandError("LDAP connection to %s failed " % ctx
.url
, e
)
74 def attr_default(msg
, attrname
, default
):
75 '''get an attribute from a ldap msg with a default'''
77 return msg
[attrname
][0]
81 def gpo_flags_string(value
):
82 '''return gpo flags string'''
83 flags
= policy
.get_gpo_flags(value
)
91 def gplink_options_string(value
):
92 '''return gplink options string'''
93 options
= policy
.get_gplink_options(value
)
97 ret
= ' '.join(options
)
101 def parse_gplink(gplink
):
102 '''parse a gPLink into an array of dn and options'''
104 a
= gplink
.split(']')
109 if len(d
) != 2 or not d
[0].startswith("[LDAP://"):
110 raise RuntimeError("Badly formed gPLink '%s'" % g
)
111 ret
.append({ 'dn' : d
[0][8:], 'options' : int(d
[1])})
115 def encode_gplink(gplist
):
116 '''Encode an array of dn and options into gPLink string'''
119 ret
+= "[LDAP://%s;%d]" % (g
['dn'], g
['options'])
123 def dc_url(lp
, creds
, url
=None, dc
=None):
124 '''If URL is not specified, return URL for writable DC.
125 If dc is provided, use that to construct ldap URL'''
130 dc
= netcmd_finddc(lp
, creds
)
131 except Exception as e
:
132 raise RuntimeError("Could not find a DC for domain", e
)
137 def get_gpo_dn(samdb
, gpo
):
138 '''Construct the DN for gpo'''
140 dn
= samdb
.get_default_basedn()
141 dn
.add_child(ldb
.Dn(samdb
, "CN=Policies,CN=System"))
142 dn
.add_child(ldb
.Dn(samdb
, "CN=%s" % gpo
))
146 def get_gpo_info(samdb
, gpo
=None, displayname
=None, dn
=None,
147 sd_flags
=security
.SECINFO_OWNER|security
.SECINFO_GROUP|security
.SECINFO_DACL|security
.SECINFO_SACL
):
148 '''Get GPO information using gpo, displayname or dn'''
150 policies_dn
= samdb
.get_default_basedn()
151 policies_dn
.add_child(ldb
.Dn(samdb
, "CN=Policies,CN=System"))
153 base_dn
= policies_dn
154 search_expr
= "(objectClass=groupPolicyContainer)"
155 search_scope
= ldb
.SCOPE_ONELEVEL
158 search_expr
= "(&(objectClass=groupPolicyContainer)(name=%s))" % ldb
.binary_encode(gpo
)
160 if displayname
is not None:
161 search_expr
= "(&(objectClass=groupPolicyContainer)(displayname=%s))" % ldb
.binary_encode(displayname
)
165 search_scope
= ldb
.SCOPE_BASE
168 msg
= samdb
.search(base
=base_dn
, scope
=search_scope
,
169 expression
=search_expr
,
170 attrs
=['nTSecurityDescriptor',
176 controls
=['sd_flags:1:%d' % sd_flags
])
177 except Exception as e
:
179 mesg
= "Cannot get information for GPO %s" % gpo
181 mesg
= "Cannot get information for GPOs"
182 raise CommandError(mesg
, e
)
187 def get_gpo_containers(samdb
, gpo
):
188 '''lists dn of containers for a GPO'''
190 search_expr
= "(&(objectClass=*)(gPLink=*%s*))" % gpo
192 msg
= samdb
.search(expression
=search_expr
, attrs
=['gPLink'])
193 except Exception as e
:
194 raise CommandError("Could not find container(s) with GPO %s" % gpo
, e
)
199 def del_gpo_link(samdb
, container_dn
, gpo
):
200 '''delete GPO link for the container'''
201 # Check if valid Container DN and get existing GPlinks
203 msg
= samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
204 expression
="(objectClass=*)",
206 except Exception as e
:
207 raise CommandError("Container '%s' does not exist" % container_dn
, e
)
210 gpo_dn
= str(get_gpo_dn(samdb
, gpo
))
212 gplist
= parse_gplink(msg
['gPLink'][0])
214 if g
['dn'].lower() == gpo_dn
.lower():
219 raise CommandError("No GPO(s) linked to this container")
222 raise CommandError("GPO '%s' not linked to this container" % gpo
)
227 gplink_str
= encode_gplink(gplist
)
228 m
['r0'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_REPLACE
, 'gPLink')
230 m
['d0'] = ldb
.MessageElement(msg
['gPLink'][0], ldb
.FLAG_MOD_DELETE
, 'gPLink')
233 except Exception as e
:
234 raise CommandError("Error removing GPO from container", e
)
238 '''Parse UNC string into a hostname, a service, and a filepath'''
239 if unc
.startswith('\\\\') and unc
.startswith('//'):
240 raise ValueError("UNC doesn't start with \\\\ or //")
241 tmp
= unc
[2:].split('/', 2)
244 tmp
= unc
[2:].split('\\', 2)
247 raise ValueError("Invalid UNC string: %s" % unc
)
250 def find_parser(name
, flags
=re
.IGNORECASE
):
251 if re
.match('fdeploy1\.ini$', name
, flags
=flags
):
252 return GPFDeploy1IniParser()
253 if re
.match('audit\.csv$', name
, flags
=flags
):
254 return GPAuditCsvParser()
255 if re
.match('GptTmpl\.inf$', name
, flags
=flags
):
256 return GptTmplInfParser()
257 if re
.match('GPT\.INI$', name
, flags
=flags
):
258 return GPTIniParser()
259 if re
.match('scripts.ini$', name
, flags
=flags
):
260 return GPScriptsIniParser()
261 if re
.match('psscripts.ini$', name
, flags
=flags
):
262 return GPScriptsIniParser()
263 if re
.match('.*\.ini$', name
, flags
=flags
):
265 if re
.match('.*\.pol$', name
, flags
=flags
):
267 if re
.match('.*\.aas$', name
, flags
=flags
):
273 def backup_directory_remote_to_local(conn
, remotedir
, localdir
):
274 SUFFIX
= '.SAMBABACKUP'
275 if not os
.path
.isdir(localdir
):
277 r_dirs
= [ remotedir
]
278 l_dirs
= [ localdir
]
283 dirlist
= conn
.list(r_dir
, attribs
=attr_flags
)
286 r_name
= r_dir
+ '\\' + e
['name']
287 l_name
= os
.path
.join(l_dir
, e
['name'])
289 if e
['attrib'] & smb
.FILE_ATTRIBUTE_DIRECTORY
:
290 r_dirs
.append(r_name
)
291 l_dirs
.append(l_name
)
294 data
= conn
.loadfile(r_name
)
295 with
file(l_name
+ SUFFIX
, 'w') as f
:
298 parser
= find_parser(e
['name'])
300 parser
.write_xml(l_name
+ '.xml')
303 attr_flags
= smb
.FILE_ATTRIBUTE_SYSTEM | \
304 smb
.FILE_ATTRIBUTE_DIRECTORY | \
305 smb
.FILE_ATTRIBUTE_ARCHIVE | \
306 smb
.FILE_ATTRIBUTE_HIDDEN
308 def copy_directory_remote_to_local(conn
, remotedir
, localdir
):
309 if not os
.path
.isdir(localdir
):
311 r_dirs
= [ remotedir
]
312 l_dirs
= [ localdir
]
317 dirlist
= conn
.list(r_dir
, attribs
=attr_flags
)
320 r_name
= r_dir
+ '\\' + e
['name']
321 l_name
= os
.path
.join(l_dir
, e
['name'])
323 if e
['attrib'] & smb
.FILE_ATTRIBUTE_DIRECTORY
:
324 r_dirs
.append(r_name
)
325 l_dirs
.append(l_name
)
328 data
= conn
.loadfile(r_name
)
329 open(l_name
, 'w').write(data
)
332 def copy_directory_local_to_remote(conn
, localdir
, remotedir
,
333 ignore_existing
=False):
334 if not conn
.chkpath(remotedir
):
335 conn
.mkdir(remotedir
)
336 l_dirs
= [ localdir
]
337 r_dirs
= [ remotedir
]
342 dirlist
= os
.listdir(l_dir
)
345 l_name
= os
.path
.join(l_dir
, e
)
346 r_name
= r_dir
+ '\\' + e
348 if os
.path
.isdir(l_name
):
349 l_dirs
.append(l_name
)
350 r_dirs
.append(r_name
)
353 except NTSTATUSError
:
354 if not ignore_existing
:
357 data
= open(l_name
, 'r').read()
358 conn
.savefile(r_name
, data
)
361 def create_directory_hier(conn
, remotedir
):
362 elems
= remotedir
.replace('/', '\\').split('\\')
365 path
= path
+ '\\' + e
366 if not conn
.chkpath(path
):
370 class cmd_listall(Command
):
373 synopsis
= "%prog [options]"
375 takes_optiongroups
= {
376 "sambaopts": options
.SambaOptions
,
377 "versionopts": options
.VersionOptions
,
378 "credopts": options
.CredentialsOptions
,
382 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
383 metavar
="URL", dest
="H")
386 def run(self
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
388 self
.lp
= sambaopts
.get_loadparm()
389 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
391 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
395 msg
= get_gpo_info(self
.samdb
, None)
398 self
.outf
.write("GPO : %s\n" % m
['name'][0])
399 self
.outf
.write("display name : %s\n" % m
['displayName'][0])
400 self
.outf
.write("path : %s\n" % m
['gPCFileSysPath'][0])
401 self
.outf
.write("dn : %s\n" % m
.dn
)
402 self
.outf
.write("version : %s\n" % attr_default(m
, 'versionNumber', '0'))
403 self
.outf
.write("flags : %s\n" % gpo_flags_string(int(attr_default(m
, 'flags', 0))))
404 self
.outf
.write("\n")
407 class cmd_list(Command
):
408 """List GPOs for an account."""
410 synopsis
= "%prog <username> [options]"
412 takes_args
= ['username']
413 takes_optiongroups
= {
414 "sambaopts": options
.SambaOptions
,
415 "versionopts": options
.VersionOptions
,
416 "credopts": options
.CredentialsOptions
,
420 Option("-H", "--URL", help="LDB URL for database or target server",
421 type=str, metavar
="URL", dest
="H")
424 def run(self
, username
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
426 self
.lp
= sambaopts
.get_loadparm()
427 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
429 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
434 msg
= self
.samdb
.search(expression
='(&(|(samAccountName=%s)(samAccountName=%s$))(objectClass=User))' %
435 (ldb
.binary_encode(username
),ldb
.binary_encode(username
)))
438 raise CommandError("Failed to find account %s" % username
)
440 # check if its a computer account
442 msg
= self
.samdb
.search(base
=user_dn
, scope
=ldb
.SCOPE_BASE
, attrs
=['objectClass'])[0]
443 is_computer
= 'computer' in msg
['objectClass']
445 raise CommandError("Failed to find objectClass for user %s" % username
)
447 session_info_flags
= ( AUTH_SESSION_INFO_DEFAULT_GROUPS |
448 AUTH_SESSION_INFO_AUTHENTICATED
)
450 # When connecting to a remote server, don't look up the local privilege DB
451 if self
.url
is not None and self
.url
.startswith('ldap'):
452 session_info_flags |
= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
454 session
= samba
.auth
.user_session(self
.samdb
, lp_ctx
=self
.lp
, dn
=user_dn
,
455 session_info_flags
=session_info_flags
)
457 token
= session
.security_token
462 dn
= ldb
.Dn(self
.samdb
, str(user_dn
)).parent()
464 msg
= self
.samdb
.search(base
=dn
, scope
=ldb
.SCOPE_BASE
, attrs
=['gPLink', 'gPOptions'])[0]
466 glist
= parse_gplink(msg
['gPLink'][0])
468 if not inherit
and not (g
['options'] & dsdb
.GPLINK_OPT_ENFORCE
):
470 if g
['options'] & dsdb
.GPLINK_OPT_DISABLE
:
474 sd_flags
=security
.SECINFO_OWNER|security
.SECINFO_GROUP|security
.SECINFO_DACL
475 gmsg
= self
.samdb
.search(base
=g
['dn'], scope
=ldb
.SCOPE_BASE
,
476 attrs
=['name', 'displayName', 'flags',
477 'nTSecurityDescriptor'],
478 controls
=['sd_flags:1:%d' % sd_flags
])
479 secdesc_ndr
= gmsg
[0]['nTSecurityDescriptor'][0]
480 secdesc
= ndr_unpack(security
.descriptor
, secdesc_ndr
)
482 self
.outf
.write("Failed to fetch gpo object with nTSecurityDescriptor %s\n" %
487 samba
.security
.access_check(secdesc
, token
,
488 security
.SEC_STD_READ_CONTROL |
489 security
.SEC_ADS_LIST |
490 security
.SEC_ADS_READ_PROP
)
492 self
.outf
.write("Failed access check on %s\n" % msg
.dn
)
495 # check the flags on the GPO
496 flags
= int(attr_default(gmsg
[0], 'flags', 0))
497 if is_computer
and (flags
& dsdb
.GPO_FLAG_MACHINE_DISABLE
):
499 if not is_computer
and (flags
& dsdb
.GPO_FLAG_USER_DISABLE
):
501 gpos
.append((gmsg
[0]['displayName'][0], gmsg
[0]['name'][0]))
503 # check if this blocks inheritance
504 gpoptions
= int(attr_default(msg
, 'gPOptions', 0))
505 if gpoptions
& dsdb
.GPO_BLOCK_INHERITANCE
:
508 if dn
== self
.samdb
.get_default_basedn():
517 self
.outf
.write("GPOs for %s %s\n" % (msg_str
, username
))
519 self
.outf
.write(" %s %s\n" % (g
[0], g
[1]))
522 class cmd_show(Command
):
523 """Show information for a GPO."""
525 synopsis
= "%prog <gpo> [options]"
527 takes_optiongroups
= {
528 "sambaopts": options
.SambaOptions
,
529 "versionopts": options
.VersionOptions
,
530 "credopts": options
.CredentialsOptions
,
536 Option("-H", help="LDB URL for database or target server", type=str)
539 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
541 self
.lp
= sambaopts
.get_loadparm()
542 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
544 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
549 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
551 raise CommandError("GPO '%s' does not exist" % gpo
)
554 secdesc_ndr
= msg
['nTSecurityDescriptor'][0]
555 secdesc
= ndr_unpack(security
.descriptor
, secdesc_ndr
)
556 secdesc_sddl
= secdesc
.as_sddl()
558 secdesc_sddl
= "<hidden>"
560 self
.outf
.write("GPO : %s\n" % msg
['name'][0])
561 self
.outf
.write("display name : %s\n" % msg
['displayName'][0])
562 self
.outf
.write("path : %s\n" % msg
['gPCFileSysPath'][0])
563 self
.outf
.write("dn : %s\n" % msg
.dn
)
564 self
.outf
.write("version : %s\n" % attr_default(msg
, 'versionNumber', '0'))
565 self
.outf
.write("flags : %s\n" % gpo_flags_string(int(attr_default(msg
, 'flags', 0))))
566 self
.outf
.write("ACL : %s\n" % secdesc_sddl
)
567 self
.outf
.write("\n")
570 class cmd_getlink(Command
):
571 """List GPO Links for a container."""
573 synopsis
= "%prog <container_dn> [options]"
575 takes_optiongroups
= {
576 "sambaopts": options
.SambaOptions
,
577 "versionopts": options
.VersionOptions
,
578 "credopts": options
.CredentialsOptions
,
581 takes_args
= ['container_dn']
584 Option("-H", help="LDB URL for database or target server", type=str)
587 def run(self
, container_dn
, H
=None, sambaopts
=None, credopts
=None,
590 self
.lp
= sambaopts
.get_loadparm()
591 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
593 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
598 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
599 expression
="(objectClass=*)",
602 raise CommandError("Container '%s' does not exist" % container_dn
)
605 self
.outf
.write("GPO(s) linked to DN %s\n" % container_dn
)
606 gplist
= parse_gplink(msg
['gPLink'][0])
608 msg
= get_gpo_info(self
.samdb
, dn
=g
['dn'])
609 self
.outf
.write(" GPO : %s\n" % msg
[0]['name'][0])
610 self
.outf
.write(" Name : %s\n" % msg
[0]['displayName'][0])
611 self
.outf
.write(" Options : %s\n" % gplink_options_string(g
['options']))
612 self
.outf
.write("\n")
614 self
.outf
.write("No GPO(s) linked to DN=%s\n" % container_dn
)
617 class cmd_setlink(Command
):
618 """Add or update a GPO link to a container."""
620 synopsis
= "%prog <container_dn> <gpo> [options]"
622 takes_optiongroups
= {
623 "sambaopts": options
.SambaOptions
,
624 "versionopts": options
.VersionOptions
,
625 "credopts": options
.CredentialsOptions
,
628 takes_args
= ['container_dn', 'gpo']
631 Option("-H", help="LDB URL for database or target server", type=str),
632 Option("--disable", dest
="disabled", default
=False, action
='store_true',
633 help="Disable policy"),
634 Option("--enforce", dest
="enforced", default
=False, action
='store_true',
635 help="Enforce policy")
638 def run(self
, container_dn
, gpo
, H
=None, disabled
=False, enforced
=False,
639 sambaopts
=None, credopts
=None, versionopts
=None):
641 self
.lp
= sambaopts
.get_loadparm()
642 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
644 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
650 gplink_options |
= dsdb
.GPLINK_OPT_DISABLE
652 gplink_options |
= dsdb
.GPLINK_OPT_ENFORCE
654 # Check if valid GPO DN
656 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
658 raise CommandError("GPO '%s' does not exist" % gpo
)
659 gpo_dn
= str(get_gpo_dn(self
.samdb
, gpo
))
661 # Check if valid Container DN
663 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
664 expression
="(objectClass=*)",
667 raise CommandError("Container '%s' does not exist" % container_dn
)
669 # Update existing GPlinks or Add new one
670 existing_gplink
= False
672 gplist
= parse_gplink(msg
['gPLink'][0])
673 existing_gplink
= True
676 if g
['dn'].lower() == gpo_dn
.lower():
677 g
['options'] = gplink_options
681 raise CommandError("GPO '%s' already linked to this container" % gpo
)
683 gplist
.insert(0, { 'dn' : gpo_dn
, 'options' : gplink_options
})
686 gplist
.append({ 'dn' : gpo_dn
, 'options' : gplink_options
})
688 gplink_str
= encode_gplink(gplist
)
691 m
.dn
= ldb
.Dn(self
.samdb
, container_dn
)
694 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_REPLACE
, 'gPLink')
696 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_ADD
, 'gPLink')
700 except Exception as e
:
701 raise CommandError("Error adding GPO Link", e
)
703 self
.outf
.write("Added/Updated GPO link\n")
704 cmd_getlink().run(container_dn
, H
, sambaopts
, credopts
, versionopts
)
707 class cmd_dellink(Command
):
708 """Delete GPO link from a container."""
710 synopsis
= "%prog <container_dn> <gpo> [options]"
712 takes_optiongroups
= {
713 "sambaopts": options
.SambaOptions
,
714 "versionopts": options
.VersionOptions
,
715 "credopts": options
.CredentialsOptions
,
718 takes_args
= ['container', 'gpo']
721 Option("-H", help="LDB URL for database or target server", type=str),
724 def run(self
, container
, gpo
, H
=None, sambaopts
=None, credopts
=None,
727 self
.lp
= sambaopts
.get_loadparm()
728 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
730 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
736 get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
738 raise CommandError("GPO '%s' does not exist" % gpo
)
740 container_dn
= ldb
.Dn(self
.samdb
, container
)
741 del_gpo_link(self
.samdb
, container_dn
, gpo
)
742 self
.outf
.write("Deleted GPO link.\n")
743 cmd_getlink().run(container_dn
, H
, sambaopts
, credopts
, versionopts
)
746 class cmd_listcontainers(Command
):
747 """List all linked containers for a GPO."""
749 synopsis
= "%prog <gpo> [options]"
751 takes_optiongroups
= {
752 "sambaopts": options
.SambaOptions
,
753 "versionopts": options
.VersionOptions
,
754 "credopts": options
.CredentialsOptions
,
760 Option("-H", help="LDB URL for database or target server", type=str)
763 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=None,
766 self
.lp
= sambaopts
.get_loadparm()
767 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
769 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
773 msg
= get_gpo_containers(self
.samdb
, gpo
)
775 self
.outf
.write("Container(s) using GPO %s\n" % gpo
)
777 self
.outf
.write(" DN: %s\n" % m
['dn'])
779 self
.outf
.write("No Containers using GPO %s\n" % gpo
)
782 class cmd_getinheritance(Command
):
783 """Get inheritance flag for a container."""
785 synopsis
= "%prog <container_dn> [options]"
787 takes_optiongroups
= {
788 "sambaopts": options
.SambaOptions
,
789 "versionopts": options
.VersionOptions
,
790 "credopts": options
.CredentialsOptions
,
793 takes_args
= ['container_dn']
796 Option("-H", help="LDB URL for database or target server", type=str)
799 def run(self
, container_dn
, H
=None, sambaopts
=None, credopts
=None,
802 self
.lp
= sambaopts
.get_loadparm()
803 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
805 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
810 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
811 expression
="(objectClass=*)",
812 attrs
=['gPOptions'])[0]
814 raise CommandError("Container '%s' does not exist" % container_dn
)
817 if 'gPOptions' in msg
:
818 inheritance
= int(msg
['gPOptions'][0])
820 if inheritance
== dsdb
.GPO_BLOCK_INHERITANCE
:
821 self
.outf
.write("Container has GPO_BLOCK_INHERITANCE\n")
823 self
.outf
.write("Container has GPO_INHERIT\n")
826 class cmd_setinheritance(Command
):
827 """Set inheritance flag on a container."""
829 synopsis
= "%prog <container_dn> <block|inherit> [options]"
831 takes_optiongroups
= {
832 "sambaopts": options
.SambaOptions
,
833 "versionopts": options
.VersionOptions
,
834 "credopts": options
.CredentialsOptions
,
837 takes_args
= [ 'container_dn', 'inherit_state' ]
840 Option("-H", help="LDB URL for database or target server", type=str)
843 def run(self
, container_dn
, inherit_state
, H
=None, sambaopts
=None, credopts
=None,
846 if inherit_state
.lower() == 'block':
847 inheritance
= dsdb
.GPO_BLOCK_INHERITANCE
848 elif inherit_state
.lower() == 'inherit':
849 inheritance
= dsdb
.GPO_INHERIT
851 raise CommandError("Unknown inheritance state (%s)" % inherit_state
)
853 self
.lp
= sambaopts
.get_loadparm()
854 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
856 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
860 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
861 expression
="(objectClass=*)",
862 attrs
=['gPOptions'])[0]
864 raise CommandError("Container '%s' does not exist" % container_dn
)
867 m
.dn
= ldb
.Dn(self
.samdb
, container_dn
)
869 if 'gPOptions' in msg
:
870 m
['new_value'] = ldb
.MessageElement(str(inheritance
), ldb
.FLAG_MOD_REPLACE
, 'gPOptions')
872 m
['new_value'] = ldb
.MessageElement(str(inheritance
), ldb
.FLAG_MOD_ADD
, 'gPOptions')
876 except Exception as e
:
877 raise CommandError("Error setting inheritance state %s" % inherit_state
, e
)
880 class cmd_fetch(Command
):
881 """Download a GPO."""
883 synopsis
= "%prog <gpo> [options]"
885 takes_optiongroups
= {
886 "sambaopts": options
.SambaOptions
,
887 "versionopts": options
.VersionOptions
,
888 "credopts": options
.CredentialsOptions
,
894 Option("-H", help="LDB URL for database or target server", type=str),
895 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
898 def run(self
, gpo
, H
=None, tmpdir
=None, sambaopts
=None, credopts
=None, versionopts
=None):
900 self
.lp
= sambaopts
.get_loadparm()
901 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
903 # We need to know writable DC to setup SMB connection
904 if H
and H
.startswith('ldap://'):
908 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
909 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
913 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
915 raise CommandError("GPO '%s' does not exist" % gpo
)
918 unc
= msg
['gPCFileSysPath'][0]
920 [dom_name
, service
, sharepath
] = parse_unc(unc
)
922 raise CommandError("Invalid GPO path (%s)" % unc
)
926 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
928 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
)
933 if not os
.path
.isdir(tmpdir
):
934 raise CommandError("Temoprary directory '%s' does not exist" % tmpdir
)
936 localdir
= os
.path
.join(tmpdir
, "policy")
937 if not os
.path
.isdir(localdir
):
940 gpodir
= os
.path
.join(localdir
, gpo
)
941 if os
.path
.isdir(gpodir
):
942 raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir
)
946 copy_directory_remote_to_local(conn
, sharepath
, gpodir
)
947 except Exception as e
:
948 # FIXME: Catch more specific exception
949 raise CommandError("Error copying GPO from DC", e
)
950 self
.outf
.write('GPO copied to %s\n' % gpodir
)
953 class cmd_backup(Command
):
956 synopsis
= "%prog <gpo> [options]"
958 takes_optiongroups
= {
959 "sambaopts": options
.SambaOptions
,
960 "versionopts": options
.VersionOptions
,
961 "credopts": options
.CredentialsOptions
,
967 Option("-H", help="LDB URL for database or target server", type=str),
968 Option("--tmpdir", help="Temporary directory for copying policy files", type=str),
969 Option("--generalize", help="Generalize XML entities to restore",
970 default
=False, action
='store_true'),
971 Option("--entities", help="File to export defining XML entities for the restore",
972 dest
='ent_file', type=str)
975 def run(self
, gpo
, H
=None, tmpdir
=None, generalize
=False, sambaopts
=None,
976 credopts
=None, versionopts
=None, ent_file
=None):
978 self
.lp
= sambaopts
.get_loadparm()
979 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
981 # We need to know writable DC to setup SMB connection
982 if H
and H
.startswith('ldap://'):
986 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
987 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
991 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
993 raise CommandError("GPO '%s' does not exist" % gpo
)
996 unc
= msg
['gPCFileSysPath'][0]
998 [dom_name
, service
, sharepath
] = parse_unc(unc
)
1000 raise CommandError("Invalid GPO path (%s)" % unc
)
1004 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
1006 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
)
1011 if not os
.path
.isdir(tmpdir
):
1012 raise CommandError("Temoprary directory '%s' does not exist" % tmpdir
)
1014 localdir
= os
.path
.join(tmpdir
, "policy")
1015 if not os
.path
.isdir(localdir
):
1018 gpodir
= os
.path
.join(localdir
, gpo
)
1019 if os
.path
.isdir(gpodir
):
1020 raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir
)
1024 backup_directory_remote_to_local(conn
, sharepath
, gpodir
)
1025 except Exception as e
:
1026 # FIXME: Catch more specific exception
1027 raise CommandError("Error copying GPO from DC", e
)
1029 self
.outf
.write('GPO copied to %s\n' % gpodir
)
1032 self
.outf
.write('\nAttempting to generalize XML entities:\n')
1033 entities
= cmd_backup
.generalize_xml_entities(self
.outf
, gpodir
,
1037 for ent
in sorted(entities
.items(), key
=operator
.itemgetter(1)):
1038 ents
+= '<!ENTITY {} "{}">\n'.format(ent
[1].strip('&;'), ent
[0])
1041 with
open(ent_file
, 'w') as f
:
1043 self
.outf
.write('Entities successfully written to %s\n' %
1046 self
.outf
.write('\nEntities:\n')
1047 self
.outf
.write(ents
)
1050 def generalize_xml_entities(outf
, sourcedir
, targetdir
):
1053 if not os
.path
.exists(targetdir
):
1056 l_dirs
= [ sourcedir
]
1057 r_dirs
= [ targetdir
]
1059 l_dir
= l_dirs
.pop()
1060 r_dir
= r_dirs
.pop()
1062 dirlist
= os
.listdir(l_dir
)
1065 l_name
= os
.path
.join(l_dir
, e
)
1066 r_name
= os
.path
.join(r_dir
, e
)
1068 if os
.path
.isdir(l_name
):
1069 l_dirs
.append(l_name
)
1070 r_dirs
.append(r_name
)
1071 if not os
.path
.exists(r_name
):
1074 if l_name
.endswith('.xml'):
1075 # Restore the xml file if possible
1077 # Get the filename to find the parser
1078 to_parse
= os
.path
.basename(l_name
)[:-4]
1080 parser
= find_parser(to_parse
)
1082 with
open(l_name
, 'r') as ltemp
:
1085 concrete_xml
= ET
.fromstring(data
)
1086 found_entities
= parser
.generalize_xml(concrete_xml
, r_name
, entities
)
1087 except GPGeneralizeException
:
1088 outf
.write('SKIPPING: Generalizing failed for %s\n' % to_parse
)
1091 # No need to generalize non-xml files.
1093 # TODO This could be improved with xml files stored in
1094 # the renamed backup file (with custom extension) by
1095 # inlining them into the exported backups.
1096 if not os
.path
.samefile(l_name
, r_name
):
1097 shutil
.copy2(l_name
, r_name
)
1102 class cmd_create(Command
):
1103 """Create an empty GPO."""
1105 synopsis
= "%prog <displayname> [options]"
1107 takes_optiongroups
= {
1108 "sambaopts": options
.SambaOptions
,
1109 "versionopts": options
.VersionOptions
,
1110 "credopts": options
.CredentialsOptions
,
1113 takes_args
= ['displayname']
1116 Option("-H", help="LDB URL for database or target server", type=str),
1117 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
1120 def run(self
, displayname
, H
=None, tmpdir
=None, sambaopts
=None, credopts
=None,
1123 self
.lp
= sambaopts
.get_loadparm()
1124 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
1126 net
= Net(creds
=self
.creds
, lp
=self
.lp
)
1128 # We need to know writable DC to setup SMB connection
1129 if H
and H
.startswith('ldap://'):
1132 flags
= (nbt
.NBT_SERVER_LDAP |
1134 nbt
.NBT_SERVER_WRITABLE
)
1135 cldap_ret
= net
.finddc(address
=dc_hostname
, flags
=flags
)
1137 flags
= (nbt
.NBT_SERVER_LDAP |
1139 nbt
.NBT_SERVER_WRITABLE
)
1140 cldap_ret
= net
.finddc(domain
=self
.lp
.get('realm'), flags
=flags
)
1141 dc_hostname
= cldap_ret
.pdc_dns_name
1142 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
1146 msg
= get_gpo_info(self
.samdb
, displayname
=displayname
)
1148 raise CommandError("A GPO already existing with name '%s'" % displayname
)
1151 guid
= str(uuid
.uuid4())
1152 gpo
= "{%s}" % guid
.upper()
1156 realm
= cldap_ret
.dns_domain
1157 unc_path
= "\\\\%s\\sysvol\\%s\\Policies\\%s" % (realm
, realm
, gpo
)
1162 if not os
.path
.isdir(tmpdir
):
1163 raise CommandError("Temporary directory '%s' does not exist" % tmpdir
)
1164 self
.tmpdir
= tmpdir
1166 localdir
= os
.path
.join(tmpdir
, "policy")
1167 if not os
.path
.isdir(localdir
):
1170 gpodir
= os
.path
.join(localdir
, gpo
)
1171 self
.gpodir
= gpodir
1172 if os
.path
.isdir(gpodir
):
1173 raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir
)
1177 os
.mkdir(os
.path
.join(gpodir
, "Machine"))
1178 os
.mkdir(os
.path
.join(gpodir
, "User"))
1179 gpt_contents
= "[General]\r\nVersion=0\r\n"
1180 open(os
.path
.join(gpodir
, "GPT.INI"), "w").write(gpt_contents
)
1181 except Exception as e
:
1182 raise CommandError("Error Creating GPO files", e
)
1184 # Connect to DC over SMB
1185 [dom_name
, service
, sharepath
] = parse_unc(unc_path
)
1186 self
.sharepath
= sharepath
1188 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
1189 except Exception as e
:
1190 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
, e
)
1194 self
.samdb
.transaction_start()
1197 gpo_dn
= get_gpo_dn(self
.samdb
, gpo
)
1201 m
['a01'] = ldb
.MessageElement("groupPolicyContainer", ldb
.FLAG_MOD_ADD
, "objectClass")
1204 # Add cn=User,cn=<guid>
1206 m
.dn
= ldb
.Dn(self
.samdb
, "CN=User,%s" % str(gpo_dn
))
1207 m
['a01'] = ldb
.MessageElement("container", ldb
.FLAG_MOD_ADD
, "objectClass")
1210 # Add cn=Machine,cn=<guid>
1212 m
.dn
= ldb
.Dn(self
.samdb
, "CN=Machine,%s" % str(gpo_dn
))
1213 m
['a01'] = ldb
.MessageElement("container", ldb
.FLAG_MOD_ADD
, "objectClass")
1216 # Get new security descriptor
1217 ds_sd_flags
= ( security
.SECINFO_OWNER |
1218 security
.SECINFO_GROUP |
1219 security
.SECINFO_DACL
)
1220 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
, sd_flags
=ds_sd_flags
)[0]
1221 ds_sd_ndr
= msg
['nTSecurityDescriptor'][0]
1222 ds_sd
= ndr_unpack(security
.descriptor
, ds_sd_ndr
).as_sddl()
1224 # Create a file system security descriptor
1225 domain_sid
= security
.dom_sid(self
.samdb
.get_domain_sid())
1226 sddl
= dsacl2fsacl(ds_sd
, domain_sid
)
1227 fs_sd
= security
.descriptor
.from_sddl(sddl
, domain_sid
)
1229 # Copy GPO directory
1230 create_directory_hier(conn
, sharepath
)
1233 sio
= ( security
.SECINFO_OWNER |
1234 security
.SECINFO_GROUP |
1235 security
.SECINFO_DACL |
1236 security
.SECINFO_PROTECTED_DACL
)
1237 conn
.set_acl(sharepath
, fs_sd
, sio
)
1239 # Copy GPO files over SMB
1240 copy_directory_local_to_remote(conn
, gpodir
, sharepath
)
1244 m
['a02'] = ldb
.MessageElement(displayname
, ldb
.FLAG_MOD_REPLACE
, "displayName")
1245 m
['a03'] = ldb
.MessageElement(unc_path
, ldb
.FLAG_MOD_REPLACE
, "gPCFileSysPath")
1246 m
['a05'] = ldb
.MessageElement("0", ldb
.FLAG_MOD_REPLACE
, "versionNumber")
1247 m
['a07'] = ldb
.MessageElement("2", ldb
.FLAG_MOD_REPLACE
, "gpcFunctionalityVersion")
1248 m
['a04'] = ldb
.MessageElement("0", ldb
.FLAG_MOD_REPLACE
, "flags")
1249 controls
=["permissive_modify:0"]
1250 self
.samdb
.modify(m
, controls
=controls
)
1252 self
.samdb
.transaction_cancel()
1255 self
.samdb
.transaction_commit()
1257 self
.outf
.write("GPO '%s' created as %s\n" % (displayname
, gpo
))
1260 class cmd_restore(cmd_create
):
1261 """Restore a GPO to a new container."""
1263 synopsis
= "%prog <displayname> <backup location> [options]"
1265 takes_optiongroups
= {
1266 "sambaopts": options
.SambaOptions
,
1267 "versionopts": options
.VersionOptions
,
1268 "credopts": options
.CredentialsOptions
,
1271 takes_args
= ['displayname', 'backup']
1274 Option("-H", help="LDB URL for database or target server", type=str),
1275 Option("--tmpdir", help="Temporary directory for copying policy files", type=str),
1276 Option("--entities", help="File defining XML entities to insert into DOCTYPE header", type=str)
1279 def restore_from_backup_to_local_dir(self
, sourcedir
, targetdir
, dtd_header
=''):
1280 SUFFIX
= '.SAMBABACKUP'
1282 if not os
.path
.exists(targetdir
):
1285 l_dirs
= [ sourcedir
]
1286 r_dirs
= [ targetdir
]
1288 l_dir
= l_dirs
.pop()
1289 r_dir
= r_dirs
.pop()
1291 dirlist
= os
.listdir(l_dir
)
1294 l_name
= os
.path
.join(l_dir
, e
)
1295 r_name
= os
.path
.join(r_dir
, e
)
1297 if os
.path
.isdir(l_name
):
1298 l_dirs
.append(l_name
)
1299 r_dirs
.append(r_name
)
1300 if not os
.path
.exists(r_name
):
1303 if l_name
.endswith('.xml'):
1304 # Restore the xml file if possible
1306 # Get the filename to find the parser
1307 to_parse
= os
.path
.basename(l_name
)[:-4]
1309 parser
= find_parser(to_parse
)
1311 with
open(l_name
, 'r') as ltemp
:
1313 # Load the XML file with the DTD (entity) header
1314 parser
.load_xml(ET
.fromstring(dtd_header
+ data
))
1316 # Write out the substituted files in the output
1317 # location, ready to copy over.
1318 parser
.write_binary(r_name
[:-4])
1320 except GPNoParserException
:
1321 # In the failure case, we fallback
1322 original_file
= l_name
[:-4] + SUFFIX
1323 shutil
.copy2(original_file
, r_name
[:-4])
1325 self
.outf
.write('WARNING: No such parser for %s\n' % to_parse
)
1326 self
.outf
.write('WARNING: Falling back to simple copy-restore.\n')
1329 traceback
.print_exc()
1331 # In the failure case, we fallback
1332 original_file
= l_name
[:-4] + SUFFIX
1333 shutil
.copy2(original_file
, r_name
[:-4])
1335 self
.outf
.write('WARNING: Error during parsing for %s\n' % l_name
)
1336 self
.outf
.write('WARNING: Falling back to simple copy-restore.\n')
1338 def run(self
, displayname
, backup
, H
=None, tmpdir
=None, entities
=None, sambaopts
=None, credopts
=None,
1343 if not os
.path
.exists(backup
):
1344 raise CommandError("Backup directory does not exist %s" % backup
)
1346 if entities
is not None:
1347 # DOCTYPE name is meant to match root element, but ElementTree does
1348 # not seem to care, so this seems to be enough.
1350 dtd_header
= '<!DOCTYPE foobar [\n'
1352 if not os
.path
.exists(entities
):
1353 raise CommandError("Entities file does not exist %s" %
1355 with
open(entities
, 'r') as entities_file
:
1356 entities_content
= entities_file
.read()
1358 # Do a basic regex test of the entities file format
1359 if re
.match('(\s*<!ENTITY\s*[a-zA-Z0-9_]+\s*.*?>)+\s*\Z',
1360 entities_content
, flags
=re
.MULTILINE
) is None:
1361 raise CommandError("Entities file does not appear to "
1362 "conform to format\n"
1363 'e.g. <!ENTITY entity "value">')
1364 dtd_header
+= entities_content
.strip()
1366 dtd_header
+= '\n]>\n'
1368 super(cmd_restore
, self
).run(displayname
, H
, tmpdir
, sambaopts
,
1369 credopts
, versionopts
)
1372 # Iterate over backup files and restore with DTD
1373 self
.restore_from_backup_to_local_dir(backup
, self
.gpodir
,
1376 # Copy GPO files over SMB
1377 copy_directory_local_to_remote(self
.conn
, self
.gpodir
,
1379 ignore_existing
=True)
1381 except Exception as e
:
1383 traceback
.print_exc()
1384 self
.outf
.write(str(e
) + '\n')
1386 self
.outf
.write("Failed to restore GPO -- deleting...\n")
1388 cmd
.run(self
.gpo_name
, H
, sambaopts
, credopts
, versionopts
)
1390 raise CommandError("Failed to restore: %s" % e
)
1393 class cmd_del(Command
):
1396 synopsis
= "%prog <gpo> [options]"
1398 takes_optiongroups
= {
1399 "sambaopts": options
.SambaOptions
,
1400 "versionopts": options
.VersionOptions
,
1401 "credopts": options
.CredentialsOptions
,
1404 takes_args
= ['gpo']
1407 Option("-H", help="LDB URL for database or target server", type=str),
1410 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=None,
1413 self
.lp
= sambaopts
.get_loadparm()
1414 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
1416 # We need to know writable DC to setup SMB connection
1417 if H
and H
.startswith('ldap://'):
1421 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
1422 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
1426 # Check if valid GPO
1428 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
1429 unc_path
= msg
['gPCFileSysPath'][0]
1431 raise CommandError("GPO '%s' does not exist" % gpo
)
1433 # Connect to DC over SMB
1434 [dom_name
, service
, sharepath
] = parse_unc(unc_path
)
1436 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
1437 except Exception as e
:
1438 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
, e
)
1440 self
.samdb
.transaction_start()
1442 # Check for existing links
1443 msg
= get_gpo_containers(self
.samdb
, gpo
)
1446 self
.outf
.write("GPO %s is linked to containers\n" % gpo
)
1448 del_gpo_link(self
.samdb
, m
['dn'], gpo
)
1449 self
.outf
.write(" Removed link from %s.\n" % m
['dn'])
1451 # Remove LDAP entries
1452 gpo_dn
= get_gpo_dn(self
.samdb
, gpo
)
1453 self
.samdb
.delete(ldb
.Dn(self
.samdb
, "CN=User,%s" % str(gpo_dn
)))
1454 self
.samdb
.delete(ldb
.Dn(self
.samdb
, "CN=Machine,%s" % str(gpo_dn
)))
1455 self
.samdb
.delete(gpo_dn
)
1458 conn
.deltree(sharepath
)
1461 self
.samdb
.transaction_cancel()
1464 self
.samdb
.transaction_commit()
1466 self
.outf
.write("GPO %s deleted.\n" % gpo
)
1469 class cmd_aclcheck(Command
):
1470 """Check all GPOs have matching LDAP and DS ACLs."""
1472 synopsis
= "%prog [options]"
1474 takes_optiongroups
= {
1475 "sambaopts": options
.SambaOptions
,
1476 "versionopts": options
.VersionOptions
,
1477 "credopts": options
.CredentialsOptions
,
1481 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
1482 metavar
="URL", dest
="H")
1485 def run(self
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
1487 self
.lp
= sambaopts
.get_loadparm()
1488 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
1490 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
1492 # We need to know writable DC to setup SMB connection
1493 if H
and H
.startswith('ldap://'):
1497 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
1498 self
.url
= dc_url(self
.lp
, self
.creds
, dc
=dc_hostname
)
1502 msg
= get_gpo_info(self
.samdb
, None)
1506 unc
= m
['gPCFileSysPath'][0]
1508 [dom_name
, service
, sharepath
] = parse_unc(unc
)
1510 raise CommandError("Invalid GPO path (%s)" % unc
)
1514 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
1516 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
)
1518 fs_sd
= conn
.get_acl(sharepath
, security
.SECINFO_OWNER | security
.SECINFO_GROUP | security
.SECINFO_DACL
, security
.SEC_FLAG_MAXIMUM_ALLOWED
)
1520 ds_sd_ndr
= m
['nTSecurityDescriptor'][0]
1521 ds_sd
= ndr_unpack(security
.descriptor
, ds_sd_ndr
).as_sddl()
1523 # Create a file system security descriptor
1524 domain_sid
= security
.dom_sid(self
.samdb
.get_domain_sid())
1525 expected_fs_sddl
= dsacl2fsacl(ds_sd
, domain_sid
)
1527 if (fs_sd
.as_sddl(domain_sid
) != expected_fs_sddl
):
1528 raise CommandError("Invalid GPO ACL %s on path (%s), should be %s" % (fs_sd
.as_sddl(domain_sid
), sharepath
, expected_fs_sddl
))
1531 class cmd_gpo(SuperCommand
):
1532 """Group Policy Object (GPO) management."""
1535 subcommands
["listall"] = cmd_listall()
1536 subcommands
["list"] = cmd_list()
1537 subcommands
["show"] = cmd_show()
1538 subcommands
["getlink"] = cmd_getlink()
1539 subcommands
["setlink"] = cmd_setlink()
1540 subcommands
["dellink"] = cmd_dellink()
1541 subcommands
["listcontainers"] = cmd_listcontainers()
1542 subcommands
["getinheritance"] = cmd_getinheritance()
1543 subcommands
["setinheritance"] = cmd_setinheritance()
1544 subcommands
["fetch"] = cmd_fetch()
1545 subcommands
["create"] = cmd_create()
1546 subcommands
["del"] = cmd_del()
1547 subcommands
["aclcheck"] = cmd_aclcheck()
1548 subcommands
["backup"] = cmd_backup()
1549 subcommands
["restore"] = cmd_restore()