self.outf.write("GPO : %s\n" % msg['name'][0])
self.outf.write("display name : %s\n" % msg['displayName'][0])
self.outf.write("path : %s\n" % msg['gPCFileSysPath'][0])
+ if 'gPCMachineExtensionNames' in msg:
+ self.outf.write("Machine Exts : %s\n" % msg['gPCMachineExtensionNames'][0])
+ if 'gPCUserExtensionNames' in msg:
+ self.outf.write("User Exts : %s\n" % msg['gPCUserExtensionNames'][0])
self.outf.write("dn : %s\n" % msg.dn)
self.outf.write("version : %s\n" % attr_default(msg, 'versionNumber', '0'))
self.outf.write("flags : %s\n" % gpo_flags_string(int(attr_default(msg, 'flags', 0))))
Valid class attributes: MACHINE|USER|BOTH
Data arrays are interpreted as bytes.
+
+ The --machine-ext-name and --user-ext-name options are multi-value inputs
+ which respectively set the gPCMachineExtensionNames and gPCUserExtensionNames
+ ldap attributes on the GPO. These attributes must be set to the correct GUID
+ names for Windows Group Policy to work correctly. These GUIDs represent
+ the client side extensions to apply on the machine. Linux Group Policy does
+ not enforce this constraint.
"""
synopsis = "%prog <gpo> [options]"
takes_options = [
Option("-H", help="LDB URL for database or target server", type=str),
- Option("--content", help="JSON file of policy inputs", type=str)
+ Option("--content", help="JSON file of policy inputs", type=str),
+ Option("--machine-ext-name",
+ action="append", default=[], dest="machine_exts",
+ help="A machine extension name to add to gPCMachineExtensionNames"),
+ Option("--user-ext-name",
+ action="append", default=[], dest="user_exts",
+ help="A user extension name to add to gPCUserExtensionNames")
]
- def run(self, gpo, H=None, content=None, sambaopts=None, credopts=None,
- versionopts=None):
+ def run(self, gpo, H=None, content=None, machine_exts=[], user_exts=[],
+ sambaopts=None, credopts=None, versionopts=None):
if content is None:
policy_defs = json.loads(sys.stdin.read())
elif os.path.exists(content):
self.url = dc_url(self.lp, self.creds, H)
self.samdb_connect()
reg = RegistryGroupPolicies(gpo, self.lp, self.creds, self.samdb, H)
+ for ext_name in machine_exts:
+ reg.register_extension_name(ext_name, 'gPCMachineExtensionNames')
+ for ext_name in user_exts:
+ reg.register_extension_name(ext_name, 'gPCUserExtensionNames')
try:
reg.merge_s(policy_defs)
except NTSTATUSError as e:
takes_options = [
Option("-H", help="LDB URL for database or target server", type=str),
- Option("--content", help="JSON file of policy inputs", type=str)
+ Option("--content", help="JSON file of policy inputs", type=str),
+ Option("--machine-ext-name",
+ action="append", default=[], dest="machine_exts",
+ help="A machine extension name to remove from gPCMachineExtensionNames"),
+ Option("--user-ext-name",
+ action="append", default=[], dest="user_exts",
+ help="A user extension name to remove from gPCUserExtensionNames")
]
- def run(self, gpo, H=None, content=None, sambaopts=None, credopts=None,
- versionopts=None):
+ def run(self, gpo, H=None, content=None, machine_exts=[], user_exts=[],
+ sambaopts=None, credopts=None, versionopts=None):
if content is None:
policy_defs = json.loads(sys.stdin.read())
elif os.path.exists(content):
self.url = dc_url(self.lp, self.creds, H)
self.samdb_connect()
reg = RegistryGroupPolicies(gpo, self.lp, self.creds, self.samdb, H)
+ for ext_name in machine_exts:
+ reg.unregister_extension_name(ext_name, 'gPCMachineExtensionNames')
+ for ext_name in user_exts:
+ reg.unregister_extension_name(ext_name, 'gPCUserExtensionNames')
try:
reg.remove_s(policy_defs)
except NTSTATUSError as e:
from samba.ntstatus import (
NT_STATUS_OBJECT_NAME_INVALID,
NT_STATUS_OBJECT_NAME_NOT_FOUND,
- NT_STATUS_OBJECT_PATH_NOT_FOUND
+ NT_STATUS_OBJECT_PATH_NOT_FOUND,
+ NT_STATUS_INVALID_PARAMETER
)
from samba.gp_parse.gp_ini import GPTIniParser
+from samba.common import get_string
+from samba.dcerpc.misc import GUID
GPT_EMPTY = \
"""
realm = self.lp.get('realm')
self.pol_dir = '\\'.join([realm.lower(), 'Policies', gpo, '%s'])
self.pol_file = '\\'.join([self.pol_dir, 'Registry.pol'])
-
+ self.policy_dn = get_gpo_dn(self.samdb, self.gpo)
if host and host.startswith('ldap://'):
dc_hostname = host[7:]
self.conn.savefile(GPT_INI, out_data.read().encode('utf-8'))
# Set the new versionNumber on the ldap object
- policy_dn = get_gpo_dn(self.samdb, self.gpo)
m = ldb.Message()
- m.dn = policy_dn
+ m.dn = self.policy_dn
m['new_value'] = ldb.MessageElement(str(version), ldb.FLAG_MOD_REPLACE,
'versionNumber')
self.samdb.modify(m)
+ def __validate_extension_registration(self, ext_name, ext_attr):
+ try:
+ ext_name_guid = GUID(ext_name)
+ except samba.NTSTATUSError as e:
+ if e.args[0] == NT_STATUS_INVALID_PARAMETER:
+ raise SyntaxError('Extension name not formated correctly')
+ raise
+ if ext_attr not in ['gPCMachineExtensionNames',
+ 'gPCUserExtensionNames']:
+ raise SyntaxError('Extension attribute incorrect')
+ return '{%s}' % ext_name_guid
+
+ def register_extension_name(self, ext_name, ext_attr):
+ ext_name = self.__validate_extension_registration(ext_name, ext_attr)
+ res = self.samdb.search(base=self.policy_dn, scope=ldb.SCOPE_BASE,
+ attrs=[ext_attr])
+ if len(res) == 0 or ext_attr not in res[0]:
+ ext_names = '[]'
+ else:
+ ext_names = get_string(res[0][ext_attr][-1])
+ if ext_name not in ext_names:
+ ext_names = '[' + ext_names.strip('[]') + ext_name + ']'
+ else:
+ return
+
+ m = ldb.Message()
+ m.dn = self.policy_dn
+ m['new_value'] = ldb.MessageElement(ext_names, ldb.FLAG_MOD_REPLACE,
+ ext_attr)
+ self.samdb.modify(m)
+
+ def unregister_extension_name(self, ext_name, ext_attr):
+ ext_name = self.__validate_extension_registration(ext_name, ext_attr)
+ res = self.samdb.search(base=self.policy_dn, scope=ldb.SCOPE_BASE,
+ attrs=[ext_attr])
+ if len(res) == 0 or ext_attr not in res[0]:
+ return
+ else:
+ ext_names = get_string(res[0][ext_attr][-1])
+ if ext_name in ext_names:
+ ext_names = ext_names.replace(ext_name, '')
+ else:
+ return
+
+ m = ldb.Message()
+ m.dn = self.policy_dn
+ m['new_value'] = ldb.MessageElement(ext_names, ldb.FLAG_MOD_REPLACE,
+ ext_attr)
+ self.samdb.modify(m)
+
def remove_s(self, json_input):
'''remove_s
json_input: JSON list of entries to remove from GPO
]
"""
+# These are new GUIDs, not used elsewhere, made up for the use of testing the
+# adding of extension GUIDs in `samba-tool gpo load`.
+ext_guids = ['{123d2b56-7b14-4516-bbc4-763d29d57654}',
+ '{d000e91b-e70f-481b-9549-58de7929bcee}']
+
source_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../.."))
def has_difference(path1, path2, binary=True, xml=True, sortlines=False):
(result, out, err) = self.runsubcmd("gpo", "load",
self.gpo_guid,
"--content=%s" % f.name,
+ "--machine-ext-name=%s" %
+ ext_guids[0],
+ "--user-ext-name=%s" %
+ ext_guids[1],
"-H", "ldap://%s" %
os.environ["SERVER"],
"-U%s%%%s" %
self.assertCmdSuccess(result, out, err, 'Failed to fetch gpos')
self.assertIn('homepage', out, 'Homepage policy not loaded')
self.assertIn('samba.org', out, 'Homepage policy not loaded')
+ self.assertIn(ext_guids[0], out, 'Machine extension not loaded')
+ self.assertIn(ext_guids[1], out, 'User extension not loaded')
toolbar_data = '"valuename": "IEToolbar",\n "class": "USER",' + \
'\n "type": "REG_BINARY",' + \
'\n "data": [\n 0\n ]'
(result, out, err) = self.runsubcmd("gpo", "remove",
self.gpo_guid,
"--content=%s" % f.name,
+ "--machine-ext-name=%s" %
+ ext_guids[0],
+ "--user-ext-name=%s" %
+ ext_guids[1],
"-H", "ldap://%s" %
os.environ["SERVER"],
"-U%s%%%s" %
"ldap://%s" % os.environ["SERVER"])
self.assertCmdSuccess(result, out, err, 'Failed to fetch gpos')
self.assertNotIn('samba.org', out, 'Homepage policy not removed')
+ self.assertNotIn(ext_guids[0], out, 'Machine extension not unloaded')
+ self.assertNotIn(ext_guids[1], out, 'User extension not unloaded')
def setUp(self):
"""set up a temporary GPO to work with"""