samba-tool: gpo: Fix policy DN
[Samba.git] / source4 / scripting / python / samba / netcmd / gpo.py
blob8482e73fbd90f6acc288511cb482dbcaf945c2f5
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/>.
22 import os
23 import samba.getopt as options
24 import ldb
26 from samba.auth import system_session
27 from samba.netcmd import (
28 Command,
29 CommandError,
30 Option,
31 SuperCommand,
33 from samba.samdb import SamDB
34 from samba import dsdb
35 from samba.dcerpc import security
36 from samba.ndr import ndr_unpack
37 import samba.security
38 import samba.auth
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
42 from samba import smb
43 import uuid
44 from samba.ntacls import dsacl2fsacl
47 def samdb_connect(ctx):
48 '''make a ldap connection to the server'''
49 try:
50 ctx.samdb = SamDB(url=ctx.url,
51 session_info=system_session(),
52 credentials=ctx.creds, lp=ctx.lp)
53 except Exception, e:
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'''
59 if attrname in msg:
60 return msg[attrname][0]
61 return default
64 def gpo_flags_string(value):
65 '''return gpo flags string'''
66 flags = policy.get_gpo_flags(value)
67 if not flags:
68 ret = 'NONE'
69 else:
70 ret = ' '.join(flags)
71 return ret
74 def gplink_options_string(value):
75 '''return gplink options string'''
76 options = policy.get_gplink_options(value)
77 if not options:
78 ret = 'NONE'
79 else:
80 ret = ' '.join(options)
81 return ret
84 def parse_gplink(gplink):
85 '''parse a gPLink into an array of dn and options'''
86 ret = []
87 a = gplink.split(']')
88 for g in a:
89 if not g:
90 continue
91 d = g.split(';')
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])})
95 return ret
98 def encode_gplink(gplist):
99 '''Encode an array of dn and options into gPLink string'''
100 ret = ''
101 for g in gplist:
102 ret += "[LDAP://%s;%d]" % (g['dn'], g['options'])
103 return ret
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'''
110 if url is None:
111 if dc is None:
112 try:
113 dc = netcmd_finddc(lp, creds)
114 except Exception, e:
115 raise RuntimeError("Could not find a DC for domain", e)
116 url = 'ldap://' + dc
117 return url
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))
126 return dn
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
139 if gpo is not None:
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)
145 if dn is not None:
146 base_dn = dn
147 search_scope = ldb.SCOPE_BASE
149 try:
150 msg = samdb.search(base=base_dn, scope=search_scope,
151 expression=search_expr,
152 attrs=['nTSecurityDescriptor',
153 'versionNumber',
154 'flags',
155 'name',
156 'displayName',
157 'gPCFileSysPath'])
158 except Exception, e:
159 if gpo is not None:
160 mesg = "Cannot get information for GPO %s" % gpo
161 else:
162 mesg = "Cannot get information for GPOs"
163 raise CommandError(mesg, e)
165 return msg
168 def parse_unc(unc):
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)
173 if len(tmp) == 3:
174 return tmp
175 tmp = unc[2:].split('\\', 2)
176 if len(tmp) == 3:
177 return tmp
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):
183 os.mkdir(localdir)
184 r_dirs = [ remotedir ]
185 l_dirs = [ localdir ]
186 while r_dirs:
187 r_dir = r_dirs.pop()
188 l_dir = l_dirs.pop()
190 dirlist = conn.list(r_dir)
191 for e in dirlist:
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)
198 os.mkdir(l_name)
199 else:
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 ]
209 while l_dirs:
210 l_dir = l_dirs.pop()
211 r_dir = r_dirs.pop()
213 dirlist = os.listdir(l_dir)
214 for e in dirlist:
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)
221 conn.mkdir(r_name)
222 else:
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('\\')
229 path = ""
230 for e in elems:
231 path = path + '\\' + e
232 if not conn.chkpath(path):
233 conn.mkdir(path)
236 class cmd_listall(Command):
237 """list all GPOs"""
239 synopsis = "%prog [options]"
241 takes_optiongroups = {
242 "sambaopts": options.SambaOptions,
243 "versionopts": options.VersionOptions,
244 "credopts": options.CredentialsOptions,
247 takes_options = [
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)
259 samdb_connect(self)
261 msg = get_gpo_info(self.samdb, None)
263 for m in msg:
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,
285 takes_options = [
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)
297 samdb_connect(self)
299 try:
300 msg = self.samdb.search(expression='(&(|(samAccountName=%s)(samAccountName=%s$))(objectClass=User))' %
301 (ldb.binary_encode(username),ldb.binary_encode(username)))
302 user_dn = msg[0].dn
303 except Exception, e:
304 raise CommandError("Failed to find account %s" % username, e)
306 # check if its a computer account
307 try:
308 msg = self.samdb.search(base=user_dn, scope=ldb.SCOPE_BASE, attrs=['objectClass'])[0]
309 is_computer = 'computer' in msg['objectClass']
310 except Exception, e:
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
325 gpos = []
327 inherit = True
328 dn = ldb.Dn(self.samdb, str(user_dn)).parent()
329 while True:
330 msg = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['gPLink', 'gPOptions'])[0]
331 if 'gPLink' in msg:
332 glist = parse_gplink(msg['gPLink'][0])
333 for g in glist:
334 if not inherit and not (g['options'] & dsdb.GPLINK_OPT_ENFORCE):
335 continue
336 if g['options'] & dsdb.GPLINK_OPT_DISABLE:
337 continue
339 try:
340 gmsg = self.samdb.search(base=g['dn'], scope=ldb.SCOPE_BASE,
341 attrs=['name', 'displayName', 'flags',
342 'ntSecurityDescriptor'])
343 except Exception:
344 self.outf.write("Failed to fetch gpo object %s\n" %
345 g['dn'])
346 continue
348 secdesc_ndr = gmsg[0]['ntSecurityDescriptor'][0]
349 secdesc = ndr_unpack(security.descriptor, secdesc_ndr)
351 try:
352 samba.security.access_check(secdesc, token,
353 security.SEC_STD_READ_CONTROL |
354 security.SEC_ADS_LIST |
355 security.SEC_ADS_READ_PROP)
356 except RuntimeError:
357 self.outf.write("Failed access check on %s\n" % msg.dn)
358 continue
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):
363 continue
364 if not is_computer and (flags & dsdb.GPO_FLAG_USER_DISABLE):
365 continue
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:
371 inherit = False
373 if dn == self.samdb.get_default_basedn():
374 break
375 dn = dn.parent()
377 if is_computer:
378 msg_str = 'computer'
379 else:
380 msg_str = 'user'
382 self.outf.write("GPOs for %s %s\n" % (msg_str, username))
383 for g in gpos:
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,
398 takes_args = ['gpo']
400 takes_options = [
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)
411 samdb_connect(self)
413 try:
414 msg = get_gpo_info(self.samdb, gpo)[0]
415 except Exception, e:
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']
444 takes_options = [
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,
449 versionopts=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)
456 samdb_connect(self)
458 try:
459 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
460 expression="(objectClass=*)",
461 attrs=['gPlink'])[0]
462 except Exception, e:
463 raise CommandError("Could not find Container DN %s (%s)" % container_dn, e)
465 if 'gPLink' in msg:
466 self.outf.write("GPO(s) linked to DN %s\n" % container_dn)
467 gplist = parse_gplink(msg['gPLink'][0])
468 for g in gplist:
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")
474 else:
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']
491 takes_options = [
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)
507 samdb_connect(self)
509 gplink_options = 0
510 if disabled:
511 gplink_options |= dsdb.GPLINK_OPT_DISABLE
512 if enforced:
513 gplink_options |= dsdb.GPLINK_OPT_ENFORCE
515 # Check if valid GPO DN
516 try:
517 msg = get_gpo_info(self.samdb, gpo=gpo)[0]
518 except Exception, e:
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
523 try:
524 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
525 expression="(objectClass=*)",
526 attrs=['gPlink'])[0]
527 except Exception, e:
528 raise CommandError("Could not find container DN %s" % container_dn, e)
530 # Update existing GPlinks or Add new one
531 existing_gplink = False
532 if 'gPLink' in msg:
533 gplist = parse_gplink(msg['gPLink'][0])
534 existing_gplink = True
535 found = False
536 for g in gplist:
537 if g['dn'].lower() == gpo_dn.lower():
538 g['options'] = gplink_options
539 found = True
540 break
541 if not found:
542 gplist.insert(0, { 'dn' : gpo_dn, 'options' : gplink_options })
543 else:
544 gplist = []
545 gplist.append({ 'dn' : gpo_dn, 'options' : gplink_options })
547 gplink_str = encode_gplink(gplist)
549 m = ldb.Message()
550 m.dn = ldb.Dn(self.samdb, container_dn)
552 if existing_gplink:
553 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_REPLACE, 'gPLink')
554 else:
555 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_ADD, 'gPLink')
557 try:
558 self.samdb.modify(m)
559 except Exception, e:
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']
579 takes_options = [
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,
584 versionopts=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)
591 samdb_connect(self)
593 # Check if valid GPO
594 try:
595 msg = get_gpo_info(self.sambdb, gpo=gpo)[0]
596 except Exception, e:
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
601 try:
602 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
603 expression="(objectClass=*)",
604 attrs=['gPlink'])[0]
605 except Exception, e:
606 raise CommandError("Could not find container DN %s" % dn, e)
608 if 'gPLink' in msg:
609 gplist = parse_gplink(msg['gPLink'][0])
610 for g in gplist:
611 if g['dn'].lower() == gpo_dn.lower():
612 gplist.remove(g)
613 break
614 else:
615 raise CommandError("Specified GPO is not linked to this container")
617 m = ldb.Message()
618 m.dn = ldb.Dn(self.samdb, container_dn)
620 if gplist:
621 gplink_str = encode_gplink(gplist)
622 m['new_value'] = ldb.MessageElement(gplink_str, ldb.FLAG_MOD_REPLACE, 'gPLink')
623 else:
624 m['new_value'] = ldb.MessageElement('', ldb.FLAG_MOD_DELETE, 'gPLink')
626 try:
627 self.samdb.modify(m)
628 except Exception, e:
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']
648 takes_options = [
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,
653 versionopts=None):
655 self.url = H
656 self.lp = sambaopts.get_loadparm()
658 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
660 samdb_connect(self)
662 try:
663 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
664 expression="(objectClass=*)",
665 attrs=['gPOptions'])[0]
666 except Exception, e:
667 raise CommandError("Could not find Container DN %s" % container_dn, e)
669 inheritance = 0
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")
675 else:
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' ]
692 takes_options = [
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,
697 versionopts=None):
699 if inherit_state.lower() == 'block':
700 inheritance = dsdb.GPO_BLOCK_INHERITANCE
701 elif inherit_state.lower() == 'inherit':
702 inheritance = dsdb.GPO_INHERIT
703 else:
704 raise CommandError("Unknown inheritance state (%s)" % inherit_state)
706 self.url = H
707 self.lp = sambaopts.get_loadparm()
709 self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
711 samdb_connect(self)
713 try:
714 msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
715 expression="(objectClass=*)",
716 attrs=['gPOptions'])[0]
717 except Exception, e:
718 raise CommandError("Could not find Container DN %s" % container_dn, e)
720 m = ldb.Message()
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')
725 else:
726 m['new_value'] = ldb.MessageElement(str(inheritance), ldb.FLAG_MOD_ADD, 'gPOptions')
728 try:
729 self.samdb.modify(m)
730 except Exception, e:
731 raise CommandError("Error setting inheritance state %s" % inherit_state, e)
734 class cmd_fetch(Command):
735 """Download a GPO"""
737 synopsis = "%prog <gpo> [options]"
739 takes_optiongroups = {
740 "sambaopts": options.SambaOptions,
741 "versionopts": options.VersionOptions,
742 "credopts": options.CredentialsOptions,
745 takes_args = ['gpo']
747 takes_options = [
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)
760 samdb_connect(self)
761 try:
762 msg = get_gpo_info(self.samdb, gpo)[0]
763 except Exception, e:
764 raise CommandError("GPO %s does not exist" % gpo)
766 # verify UNC path
767 unc = msg['gPCFileSysPath'][0]
768 try:
769 [dom_name, service, sharepath] = parse_unc(unc)
770 except ValueError:
771 raise CommandError("Invalid GPO path (%s)" % unc)
773 # SMB connect to DC
774 try:
775 conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
776 except Exception, e:
777 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname, e)
779 # Copy GPT
780 if tmpdir is None:
781 tmpdir = "/tmp"
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):
787 os.mkdir(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)
793 try:
794 os.mkdir(gpodir)
795 copy_directory_remote_to_local(conn, sharepath, gpodir)
796 except Exception, e:
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']
815 takes_options = [
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,
821 versionopts=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)
829 samdb_connect(self)
831 msg = get_gpo_info(self.samdb, displayname=displayname)
832 if msg.count > 0:
833 raise CommandError("A GPO already existing with name '%s'" % displayname)
835 # Create new GUID
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)
841 # Create GPT
842 if tmpdir is None:
843 tmpdir = "/tmp"
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):
849 os.mkdir(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)
855 try:
856 os.mkdir(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)
861 except Exception, e:
862 raise CommandError("Error Creating GPO files", e)
864 # Connect to DC over SMB
865 [dom_name, service, sharepath] = parse_unc(unc_path)
866 try:
867 conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
868 except Exception, e:
869 raise CommandError("Error connecting to '%s' using SMB" % dc_hostname, e)
871 self.samdb.transaction_start()
872 try:
873 # Add cn=<guid>
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))
878 m = ldb.Message()
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")
887 self.samdb.add(m)
889 # Add cn=User,cn=<guid>
890 m = ldb.Message()
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")
894 self.samdb.add(m)
896 # Add cn=Machine,cn=<guid>
897 m = ldb.Message()
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")
901 self.samdb.add(m)
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))
917 # Set ACL
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)
923 except:
924 self.samdb.transaction_cancel()
925 raise
926 else:
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"""
935 subcommands = {}
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()