1 # implement samba_tool gpo commands
3 # Copyright Andrew Tridgell 2010
4 # Copyright Amitay Isaacs 2011 <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
)
169 '''Parse UNC string into a hostname, a service, and a filepath'''
170 if unc
.startswith('\\\\') and unc
.startswith('//'):
171 raise ValueError("UNC doesn't start with \\\\ or //")
172 tmp
= unc
[2:].split('/', 2)
175 tmp
= unc
[2:].split('\\', 2)
178 raise ValueError("Invalid UNC string: %s" % unc
)
181 def copy_directory_remote_to_local(conn
, remotedir
, localdir
):
182 if not os
.path
.isdir(localdir
):
184 r_dirs
= [ remotedir
]
185 l_dirs
= [ localdir
]
190 dirlist
= conn
.list(r_dir
)
192 r_name
= r_dir
+ '\\' + e
['name']
193 l_name
= os
.path
.join(l_dir
, e
['name'])
195 if e
['attrib'] & smb
.FILE_ATTRIBUTE_DIRECTORY
:
196 r_dirs
.append(r_name
)
197 l_dirs
.append(l_name
)
200 data
= conn
.loadfile(r_name
)
201 file(l_name
, 'w').write(data
)
204 def copy_directory_local_to_remote(conn
, localdir
, remotedir
):
205 if not conn
.chkpath(remotedir
):
206 conn
.mkdir(remotedir
)
207 l_dirs
= [ localdir
]
208 r_dirs
= [ remotedir
]
213 dirlist
= os
.listdir(l_dir
)
215 l_name
= os
.path
.join(l_dir
, e
)
216 r_name
= r_dir
+ '\\' + e
218 if os
.path
.isdir(l_name
):
219 l_dirs
.append(l_name
)
220 r_dirs
.append(r_name
)
223 data
= file(l_name
, 'r').read()
224 conn
.savefile(r_name
, data
)
227 def create_directory_hier(conn
, remotedir
):
228 elems
= remotedir
.replace('/', '\\').split('\\')
231 path
= path
+ '\\' + e
232 if not conn
.chkpath(path
):
236 class cmd_listall(Command
):
239 synopsis
= "%prog [options]"
241 takes_optiongroups
= {
242 "sambaopts": options
.SambaOptions
,
243 "versionopts": options
.VersionOptions
,
244 "credopts": options
.CredentialsOptions
,
248 Option("-H", "--URL", help="LDB URL for database or target server", type=str,
249 metavar
="URL", dest
="H")
252 def run(self
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
254 self
.lp
= sambaopts
.get_loadparm()
255 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
257 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
261 msg
= get_gpo_info(self
.samdb
, None)
264 self
.outf
.write("GPO : %s\n" % m
['name'][0])
265 self
.outf
.write("display name : %s\n" % m
['displayName'][0])
266 self
.outf
.write("path : %s\n" % m
['gPCFileSysPath'][0])
267 self
.outf
.write("dn : %s\n" % m
.dn
)
268 self
.outf
.write("version : %s\n" % attr_default(m
, 'versionNumber', '0'))
269 self
.outf
.write("flags : %s\n" % gpo_flags_string(int(attr_default(m
, 'flags', 0))))
270 self
.outf
.write("\n")
273 class cmd_list(Command
):
274 """list GPOs for an account"""
276 synopsis
= "%prog <username> [options]"
278 takes_args
= ['username']
279 takes_optiongroups
= {
280 "sambaopts": options
.SambaOptions
,
281 "versionopts": options
.VersionOptions
,
282 "credopts": options
.CredentialsOptions
,
286 Option("-H", "--URL", help="LDB URL for database or target server",
287 type=str, metavar
="URL", dest
="H")
290 def run(self
, username
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
292 self
.lp
= sambaopts
.get_loadparm()
293 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
295 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
300 msg
= self
.samdb
.search(expression
='(&(|(samAccountName=%s)(samAccountName=%s$))(objectClass=User))' %
301 (ldb
.binary_encode(username
),ldb
.binary_encode(username
)))
304 raise CommandError("Failed to find account %s" % username
, e
)
306 # check if its a computer account
308 msg
= self
.samdb
.search(base
=user_dn
, scope
=ldb
.SCOPE_BASE
, attrs
=['objectClass'])[0]
309 is_computer
= 'computer' in msg
['objectClass']
311 raise CommandError("Failed to find objectClass for user %s" % username
, e
)
313 session_info_flags
= ( AUTH_SESSION_INFO_DEFAULT_GROUPS |
314 AUTH_SESSION_INFO_AUTHENTICATED
)
316 # When connecting to a remote server, don't look up the local privilege DB
317 if self
.url
is not None and self
.url
.startswith('ldap'):
318 session_info_flags |
= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
320 session
= samba
.auth
.user_session(self
.samdb
, lp_ctx
=self
.lp
, dn
=user_dn
,
321 session_info_flags
=session_info_flags
)
323 token
= session
.security_token
328 dn
= ldb
.Dn(self
.samdb
, str(user_dn
)).parent()
330 msg
= self
.samdb
.search(base
=dn
, scope
=ldb
.SCOPE_BASE
, attrs
=['gPLink', 'gPOptions'])[0]
332 glist
= parse_gplink(msg
['gPLink'][0])
334 if not inherit
and not (g
['options'] & dsdb
.GPLINK_OPT_ENFORCE
):
336 if g
['options'] & dsdb
.GPLINK_OPT_DISABLE
:
340 gmsg
= self
.samdb
.search(base
=g
['dn'], scope
=ldb
.SCOPE_BASE
,
341 attrs
=['name', 'displayName', 'flags',
342 'ntSecurityDescriptor'])
344 self
.outf
.write("Failed to fetch gpo object %s\n" %
348 secdesc_ndr
= gmsg
[0]['ntSecurityDescriptor'][0]
349 secdesc
= ndr_unpack(security
.descriptor
, secdesc_ndr
)
352 samba
.security
.access_check(secdesc
, token
,
353 security
.SEC_STD_READ_CONTROL |
354 security
.SEC_ADS_LIST |
355 security
.SEC_ADS_READ_PROP
)
357 self
.outf
.write("Failed access check on %s\n" % msg
.dn
)
360 # check the flags on the GPO
361 flags
= int(attr_default(gmsg
[0], 'flags', 0))
362 if is_computer
and (flags
& dsdb
.GPO_FLAG_MACHINE_DISABLE
):
364 if not is_computer
and (flags
& dsdb
.GPO_FLAG_USER_DISABLE
):
366 gpos
.append((gmsg
[0]['displayName'][0], gmsg
[0]['name'][0]))
368 # check if this blocks inheritance
369 gpoptions
= int(attr_default(msg
, 'gPOptions', 0))
370 if gpoptions
& dsdb
.GPO_BLOCK_INHERITANCE
:
373 if dn
== self
.samdb
.get_default_basedn():
382 self
.outf
.write("GPOs for %s %s\n" % (msg_str
, username
))
384 self
.outf
.write(" %s %s\n" % (g
[0], g
[1]))
387 class cmd_show(Command
):
388 """Show information for a GPO"""
390 synopsis
= "%prog <gpo> [options]"
392 takes_optiongroups
= {
393 "sambaopts": options
.SambaOptions
,
394 "versionopts": options
.VersionOptions
,
395 "credopts": options
.CredentialsOptions
,
401 Option("-H", help="LDB URL for database or target server", type=str)
404 def run(self
, gpo
, H
=None, sambaopts
=None, credopts
=None, versionopts
=None):
406 self
.lp
= sambaopts
.get_loadparm()
407 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
409 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
414 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
416 raise CommandError("GPO %s does not exist" % gpo
, e
)
418 secdesc_ndr
= msg
['ntSecurityDescriptor'][0]
419 secdesc
= ndr_unpack(security
.descriptor
, secdesc_ndr
)
421 self
.outf
.write("GPO : %s\n" % msg
['name'][0])
422 self
.outf
.write("display name : %s\n" % msg
['displayName'][0])
423 self
.outf
.write("path : %s\n" % msg
['gPCFileSysPath'][0])
424 self
.outf
.write("dn : %s\n" % msg
.dn
)
425 self
.outf
.write("version : %s\n" % attr_default(msg
, 'versionNumber', '0'))
426 self
.outf
.write("flags : %s\n" % gpo_flags_string(int(attr_default(msg
, 'flags', 0))))
427 self
.outf
.write("ACL : %s\n" % secdesc
.as_sddl())
428 self
.outf
.write("\n")
431 class cmd_getlink(Command
):
432 """List GPO Links for a container"""
434 synopsis
= "%prog <container_dn> [options]"
436 takes_optiongroups
= {
437 "sambaopts": options
.SambaOptions
,
438 "versionopts": options
.VersionOptions
,
439 "credopts": options
.CredentialsOptions
,
442 takes_args
= ['container_dn']
445 Option("-H", help="LDB URL for database or target server", type=str)
448 def run(self
, container_dn
, H
=None, sambaopts
=None, credopts
=None,
451 self
.lp
= sambaopts
.get_loadparm()
452 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
454 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
459 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
460 expression
="(objectClass=*)",
463 raise CommandError("Could not find Container DN %s (%s)" % container_dn
, e
)
466 self
.outf
.write("GPO(s) linked to DN %s\n" % container_dn
)
467 gplist
= parse_gplink(msg
['gPLink'][0])
469 msg
= get_gpo_info(self
.samdb
, dn
=g
['dn'])
470 self
.outf
.write(" GPO : %s\n" % msg
[0]['name'][0])
471 self
.outf
.write(" Name : %s\n" % msg
[0]['displayName'][0])
472 self
.outf
.write(" Options : %s\n" % gplink_options_string(g
['options']))
473 self
.outf
.write("\n")
475 self
.outf
.write("No GPO(s) linked to DN=%s\n" % container_dn
)
478 class cmd_setlink(Command
):
479 """Add or Update a GPO link to a container"""
481 synopsis
= "%prog <container_dn> <gpo> [options]"
483 takes_optiongroups
= {
484 "sambaopts": options
.SambaOptions
,
485 "versionopts": options
.VersionOptions
,
486 "credopts": options
.CredentialsOptions
,
489 takes_args
= ['container_dn', 'gpo']
492 Option("-H", help="LDB URL for database or target server", type=str),
493 Option("--disable", dest
="disabled", default
=False, action
='store_true',
494 help="Disable policy"),
495 Option("--enforce", dest
="enforced", default
=False, action
='store_true',
496 help="Enforce policy")
499 def run(self
, container_dn
, gpo
, H
=None, disabled
=False, enforced
=False,
500 sambaopts
=None, credopts
=None, versionopts
=None):
502 self
.lp
= sambaopts
.get_loadparm()
503 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
505 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
511 gplink_options |
= dsdb
.GPLINK_OPT_DISABLE
513 gplink_options |
= dsdb
.GPLINK_OPT_ENFORCE
515 # Check if valid GPO DN
517 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
519 raise CommandError("GPO %s does not exist" % gpo_dn
, e
)
520 gpo_dn
= get_gpo_dn(self
.samdb
, gpo
)
522 # Check if valid Container DN
524 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
525 expression
="(objectClass=*)",
528 raise CommandError("Could not find container DN %s" % container_dn
, e
)
530 # Update existing GPlinks or Add new one
531 existing_gplink
= False
533 gplist
= parse_gplink(msg
['gPLink'][0])
534 existing_gplink
= True
537 if g
['dn'].lower() == gpo_dn
.lower():
538 g
['options'] = gplink_options
542 gplist
.insert(0, { 'dn' : gpo_dn
, 'options' : gplink_options
})
545 gplist
.append({ 'dn' : gpo_dn
, 'options' : gplink_options
})
547 gplink_str
= encode_gplink(gplist
)
550 m
.dn
= ldb
.Dn(self
.samdb
, container_dn
)
553 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_REPLACE
, 'gPLink')
555 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_ADD
, 'gPLink')
560 raise CommandError("Error adding GPO Link", e
)
562 self
.outf
.write("Added/Updated GPO link\n")
563 cmd_getlink().run(container_dn
, H
, sambaopts
, credopts
, versionopts
)
566 class cmd_dellink(Command
):
567 """Delete GPO link from a container"""
569 synopsis
= "%prog <container_dn> <gpo> [options]"
571 takes_optiongroups
= {
572 "sambaopts": options
.SambaOptions
,
573 "versionopts": options
.VersionOptions
,
574 "credopts": options
.CredentialsOptions
,
577 takes_args
= ['container_dn', 'gpo']
580 Option("-H", help="LDB URL for database or target server", type=str),
583 def run(self
, container_dn
, gpo_dn
, H
=None, sambaopts
=None, credopts
=None,
586 self
.lp
= sambaopts
.get_loadparm()
587 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
589 self
.url
= dc_url(self
.lp
, self
.creds
, H
)
595 msg
= get_gpo_info(self
.sambdb
, gpo
=gpo
)[0]
597 raise CommandError("GPO %s does not exist" % gpo
, e
)
598 gpo_dn
= get_gpo_dn(self
.samdb
, gpo
)
600 # Check if valid Container DN and get existing GPlinks
602 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
603 expression
="(objectClass=*)",
606 raise CommandError("Could not find container DN %s" % dn
, e
)
609 gplist
= parse_gplink(msg
['gPLink'][0])
611 if g
['dn'].lower() == gpo_dn
.lower():
615 raise CommandError("Specified GPO is not linked to this container")
618 m
.dn
= ldb
.Dn(self
.samdb
, container_dn
)
621 gplink_str
= encode_gplink(gplist
)
622 m
['new_value'] = ldb
.MessageElement(gplink_str
, ldb
.FLAG_MOD_REPLACE
, 'gPLink')
624 m
['new_value'] = ldb
.MessageElement('', ldb
.FLAG_MOD_DELETE
, 'gPLink')
629 raise CommandError("Error Removing GPO Link (%s)" % e
)
631 self
.outf
.write("Deleted GPO link.\n")
632 cmd_getlink().run(container_dn
, H
, sambaopts
, credopts
, versionopts
)
635 class cmd_getinheritance(Command
):
636 """Get inheritance flag for a container"""
638 synopsis
= "%prog <container_dn> [options]"
640 takes_optiongroups
= {
641 "sambaopts": options
.SambaOptions
,
642 "versionopts": options
.VersionOptions
,
643 "credopts": options
.CredentialsOptions
,
646 takes_args
= ['container_dn']
649 Option("-H", help="LDB URL for database or target server", type=str)
652 def run(self
, container_dn
, H
=None, sambaopts
=None, credopts
=None,
656 self
.lp
= sambaopts
.get_loadparm()
658 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
663 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
664 expression
="(objectClass=*)",
665 attrs
=['gPOptions'])[0]
667 raise CommandError("Could not find Container DN %s" % container_dn
, e
)
670 if 'gPOptions' in msg
:
671 inheritance
= int(msg
['gPOptions'][0])
673 if inheritance
== dsdb
.GPO_BLOCK_INHERITANCE
:
674 self
.outf
.write("Container has GPO_BLOCK_INHERITANCE\n")
676 self
.outf
.write("Container has GPO_INHERIT\n")
679 class cmd_setinheritance(Command
):
680 """Set inheritance flag on a container"""
682 synopsis
= "%prog <container_dn> <block|inherit> [options]"
684 takes_optiongroups
= {
685 "sambaopts": options
.SambaOptions
,
686 "versionopts": options
.VersionOptions
,
687 "credopts": options
.CredentialsOptions
,
690 takes_args
= [ 'container_dn', 'inherit_state' ]
693 Option("-H", help="LDB URL for database or target server", type=str)
696 def run(self
, container_dn
, inherit_state
, H
=None, sambaopts
=None, credopts
=None,
699 if inherit_state
.lower() == 'block':
700 inheritance
= dsdb
.GPO_BLOCK_INHERITANCE
701 elif inherit_state
.lower() == 'inherit':
702 inheritance
= dsdb
.GPO_INHERIT
704 raise CommandError("Unknown inheritance state (%s)" % inherit_state
)
707 self
.lp
= sambaopts
.get_loadparm()
709 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
714 msg
= self
.samdb
.search(base
=container_dn
, scope
=ldb
.SCOPE_BASE
,
715 expression
="(objectClass=*)",
716 attrs
=['gPOptions'])[0]
718 raise CommandError("Could not find Container DN %s" % container_dn
, e
)
721 m
.dn
= ldb
.Dn(self
.samdb
, container_dn
)
723 if 'gPOptions' in msg
:
724 m
['new_value'] = ldb
.MessageElement(str(inheritance
), ldb
.FLAG_MOD_REPLACE
, 'gPOptions')
726 m
['new_value'] = ldb
.MessageElement(str(inheritance
), ldb
.FLAG_MOD_ADD
, 'gPOptions')
731 raise CommandError("Error setting inheritance state %s" % inherit_state
, e
)
734 class cmd_fetch(Command
):
737 synopsis
= "%prog <gpo> [options]"
739 takes_optiongroups
= {
740 "sambaopts": options
.SambaOptions
,
741 "versionopts": options
.VersionOptions
,
742 "credopts": options
.CredentialsOptions
,
748 Option("-H", help="LDB URL for database or target server", type=str),
749 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
752 def run(self
, gpo
, H
=None, tmpdir
=None, sambaopts
=None, credopts
=None, versionopts
=None):
754 self
.lp
= sambaopts
.get_loadparm()
755 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
757 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
758 self
.url
= dc_url(self
.lp
, self
.creds
, H
, dc
=dc_hostname
)
762 msg
= get_gpo_info(self
.samdb
, gpo
)[0]
764 raise CommandError("GPO %s does not exist" % gpo
)
767 unc
= msg
['gPCFileSysPath'][0]
769 [dom_name
, service
, sharepath
] = parse_unc(unc
)
771 raise CommandError("Invalid GPO path (%s)" % unc
)
775 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
777 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
, e
)
782 if not os
.path
.isdir(tmpdir
):
783 raise CommandError("Temoprary directory '%s' does not exist" % tmpdir
)
785 localdir
= os
.path
.join(tmpdir
, "policy")
786 if not os
.path
.isdir(localdir
):
789 gpodir
= os
.path
.join(localdir
, gpo
)
790 if os
.path
.isdir(gpodir
):
791 raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir
)
795 copy_directory_remote_to_local(conn
, sharepath
, gpodir
)
797 # FIXME: Catch more specific exception
798 raise CommandError("Error copying GPO from DC", e
)
799 self
.outf
.write('GPO copied to %s\n' % gpodir
)
802 class cmd_create(Command
):
803 """Create an empty GPO"""
805 synopsis
= "%prog <displayname> [options]"
807 takes_optiongroups
= {
808 "sambaopts": options
.SambaOptions
,
809 "versionopts": options
.VersionOptions
,
810 "credopts": options
.CredentialsOptions
,
813 takes_args
= ['displayname']
816 Option("-H", help="LDB URL for database or target server", type=str),
817 Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
820 def run(self
, displayname
, H
=None, tmpdir
=None, sambaopts
=None, credopts
=None,
823 self
.lp
= sambaopts
.get_loadparm()
824 self
.creds
= credopts
.get_credentials(self
.lp
, fallback_machine
=True)
826 self
.url
= dc_url(self
.lp
, self
.creds
, url
=H
)
828 dc_hostname
= netcmd_finddc(self
.lp
, self
.creds
)
831 msg
= get_gpo_info(self
.samdb
, displayname
=displayname
)
833 raise CommandError("A GPO already existing with name '%s'" % displayname
)
836 guid
= str(uuid
.uuid4())
837 gpo
= "{%s}" % guid
.upper()
838 realm
= self
.lp
.get('realm')
839 unc_path
= "\\\\%s\\sysvol\\%s\\Policies\\%s" % (realm
, realm
, gpo
)
844 if not os
.path
.isdir(tmpdir
):
845 raise CommandError("Temporary 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 os
.mkdir(os
.path
.join(gpodir
, "Machine"))
858 os
.mkdir(os
.path
.join(gpodir
, "User"))
859 gpt_contents
= "[General]\r\nVersion=0\r\n"
860 file(os
.path
.join(gpodir
, "GPT.INI"), "w").write(gpt_contents
)
862 raise CommandError("Error Creating GPO files", e
)
864 # Connect to DC over SMB
865 [dom_name
, service
, sharepath
] = parse_unc(unc_path
)
867 conn
= smb
.SMB(dc_hostname
, service
, lp
=self
.lp
, creds
=self
.creds
)
869 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname
, e
)
871 self
.samdb
.transaction_start()
874 gpo_dn
= self
.samdb
.get_default_basedn()
875 gpo_dn
.add_child(ldb
.Dn(self
.samdb
, "CN=Policies,CN=System"))
876 gpo_dn
.add_child(ldb
.Dn(self
.samdb
, "CN=%s" % gpo
))
879 m
.dn
= ldb
.Dn(self
.samdb
, gpo_dn
.get_linearized())
880 m
['a01'] = ldb
.MessageElement("groupPolicyContainer", ldb
.FLAG_MOD_ADD
, "objectClass")
881 m
['a02'] = ldb
.MessageElement(displayname
, ldb
.FLAG_MOD_ADD
, "displayName")
882 m
['a03'] = ldb
.MessageElement(unc_path
, ldb
.FLAG_MOD_ADD
, "gPCFileSysPath")
883 m
['a04'] = ldb
.MessageElement("0", ldb
.FLAG_MOD_ADD
, "flags")
884 m
['a05'] = ldb
.MessageElement("0", ldb
.FLAG_MOD_ADD
, "versionNumber")
885 m
['a06'] = ldb
.MessageElement("TRUE", ldb
.FLAG_MOD_ADD
, "showInAdvancedViewOnly")
886 m
['a07'] = ldb
.MessageElement("2", ldb
.FLAG_MOD_ADD
, "gpcFunctionalityVersion")
889 # Add cn=User,cn=<guid>
891 m
.dn
= ldb
.Dn(self
.samdb
, "CN=User,%s" % str(gpo_dn
))
892 m
['a01'] = ldb
.MessageElement("container", ldb
.FLAG_MOD_ADD
, "objectClass")
893 m
['a02'] = ldb
.MessageElement("TRUE", ldb
.FLAG_MOD_ADD
, "showInAdvancedViewOnly")
896 # Add cn=Machine,cn=<guid>
898 m
.dn
= ldb
.Dn(self
.samdb
, "CN=Machine,%s" % str(gpo_dn
))
899 m
['a01'] = ldb
.MessageElement("container", ldb
.FLAG_MOD_ADD
, "objectClass")
900 m
['a02'] = ldb
.MessageElement("TRUE", ldb
.FLAG_MOD_ADD
, "showInAdvancedViewOnly")
903 # Copy GPO files over SMB
904 create_directory_hier(conn
, sharepath
)
905 copy_directory_local_to_remote(conn
, gpodir
, sharepath
)
907 # Get new security descriptor
908 msg
= get_gpo_info(self
.samdb
, gpo
=gpo
)[0]
909 ds_sd_ndr
= msg
['ntSecurityDescriptor'][0]
910 ds_sd
= ndr_unpack(security
.descriptor
, ds_sd_ndr
).as_sddl()
912 # Create a file system security descriptor
913 domain_sid
= self
.samdb
.get_domain_sid()
914 sddl
= dsacl2fsacl(ds_sd
, domain_sid
)
915 fs_sd
= security
.descriptor
.from_sddl(sddl
, security
.dom_sid(domain_sid
))
918 sio
= ( security
.SECINFO_OWNER |
919 security
.SECINFO_GROUP |
920 security
.SECINFO_DACL |
921 security
.SECINFO_PROTECTED_DACL
)
922 conn
.set_acl(sharepath
, fs_sd
, sio
)
924 self
.samdb
.transaction_cancel()
927 self
.samdb
.transaction_commit()
929 self
.outf
.write("GPO '%s' created as %s\n" % (displayname
, gpo
))
932 class cmd_gpo(SuperCommand
):
933 """Group Policy Object (GPO) management"""
936 subcommands
["listall"] = cmd_listall()
937 subcommands
["list"] = cmd_list()
938 subcommands
["show"] = cmd_show()
939 subcommands
["getlink"] = cmd_getlink()
940 subcommands
["setlink"] = cmd_setlink()
941 subcommands
["dellink"] = cmd_dellink()
942 subcommands
["getinheritance"] = cmd_getinheritance()
943 subcommands
["setinheritance"] = cmd_setinheritance()
944 subcommands
["fetch"] = cmd_fetch()
945 subcommands
["create"] = cmd_create()