s4:samba-tool/gpo: set the same security.descriptor type as the Windows GUI
[metze/samba/wip.git] / python / samba / netcmd / gpo.py
index 6d5e79ac36eb8edec9a088561f32daaa7b248383..277c5725a1ed1c273c06465542a7eed91b44733d 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
-
+from __future__ import print_function
 import os
 import samba.getopt as options
 import ldb
 import re
 import xml.etree.ElementTree as ET
 import shutil
+import tempfile
 
 from samba.auth import system_session
 from samba.netcmd import (
@@ -32,7 +33,7 @@ from samba.netcmd import (
     CommandError,
     Option,
     SuperCommand,
-    )
+)
 from samba.samdb import SamDB
 from samba import dsdb
 from samba.dcerpc import security
@@ -42,7 +43,8 @@ import samba.auth
 from samba.auth import AUTH_SESSION_INFO_DEFAULT_GROUPS, AUTH_SESSION_INFO_AUTHENTICATED, AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
 from samba.netcmd.common import netcmd_finddc
 from samba import policy
-from samba import smb
+from samba.samba3 import param as s3param
+from samba.samba3 import libsmb_samba_internal as libsmb
 from samba import NTSTATUSError
 import uuid
 from samba.ntacls import dsacl2fsacl
@@ -61,16 +63,6 @@ from samba.gp_parse.gp_inf import GptTmplInfParser
 from samba.gp_parse.gp_aas import GPAasParser
 
 
-def samdb_connect(ctx):
-    '''make a ldap connection to the server'''
-    try:
-        ctx.samdb = SamDB(url=ctx.url,
-                          session_info=system_session(),
-                          credentials=ctx.creds, lp=ctx.lp)
-    except Exception as e:
-        raise CommandError("LDAP connection to %s failed " % ctx.url, e)
-
-
 def attr_default(msg, attrname, default):
     '''get an attribute from a ldap msg with a default'''
     if attrname in msg:
@@ -108,7 +100,7 @@ def parse_gplink(gplink):
         d = g.split(';')
         if len(d) != 2 or not d[0].startswith("[LDAP://"):
             raise RuntimeError("Badly formed gPLink '%s'" % g)
-        ret.append({ 'dn' : d[0][8:], 'options' : int(d[1])})
+        ret.append({'dn': d[0][8:], 'options': int(d[1])})
     return ret
 
 
@@ -144,7 +136,10 @@ def get_gpo_dn(samdb, gpo):
 
 
 def get_gpo_info(samdb, gpo=None, displayname=None, dn=None,
-                 sd_flags=security.SECINFO_OWNER|security.SECINFO_GROUP|security.SECINFO_DACL|security.SECINFO_SACL):
+                 sd_flags=(security.SECINFO_OWNER |
+                           security.SECINFO_GROUP |
+                           security.SECINFO_DACL |
+                           security.SECINFO_SACL)):
     '''Get GPO information using gpo, displayname or dn'''
 
     policies_dn = samdb.get_default_basedn()
@@ -166,14 +161,14 @@ def get_gpo_info(samdb, gpo=None, displayname=None, dn=None,
 
     try:
         msg = samdb.search(base=base_dn, scope=search_scope,
-                            expression=search_expr,
-                            attrs=['nTSecurityDescriptor',
-                                    'versionNumber',
-                                    'flags',
-                                    'name',
-                                    'displayName',
-                                    'gPCFileSysPath'],
-                            controls=['sd_flags:1:%d' % sd_flags])
+                           expression=search_expr,
+                           attrs=['nTSecurityDescriptor',
+                                  'versionNumber',
+                                  'flags',
+                                  'name',
+                                  'displayName',
+                                  'gPCFileSysPath'],
+                           controls=['sd_flags:1:%d' % sd_flags])
     except Exception as e:
         if gpo is not None:
             mesg = "Cannot get information for GPO %s" % gpo
@@ -201,15 +196,15 @@ def del_gpo_link(samdb, container_dn, gpo):
     # Check if valid Container DN and get existing GPlinks
     try:
         msg = samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
-                            expression="(objectClass=*)",
-                            attrs=['gPLink'])[0]
+                           expression="(objectClass=*)",
+                           attrs=['gPLink'])[0]
     except Exception as e:
         raise CommandError("Container '%s' does not exist" % container_dn, e)
 
     found = False
     gpo_dn = str(get_gpo_dn(samdb, gpo))
     if 'gPLink' in msg:
-        gplist = parse_gplink(msg['gPLink'][0])
+        gplist = parse_gplink(str(msg['gPLink'][0]))
         for g in gplist:
             if g['dn'].lower() == gpo_dn.lower():
                 gplist.remove(g)
@@ -281,18 +276,18 @@ def backup_directory_remote_to_local(conn, remotedir, localdir):
         l_dir = l_dirs.pop()
 
         dirlist = conn.list(r_dir, attribs=attr_flags)
-        dirlist.sort()
+        dirlist.sort(key=lambda x : x['name'])
         for e in dirlist:
             r_name = r_dir + '\\' + e['name']
             l_name = os.path.join(l_dir, e['name'])
 
-            if e['attrib'] & smb.FILE_ATTRIBUTE_DIRECTORY:
+            if e['attrib'] & libsmb.FILE_ATTRIBUTE_DIRECTORY:
                 r_dirs.append(r_name)
                 l_dirs.append(l_name)
                 os.mkdir(l_name)
             else:
                 data = conn.loadfile(r_name)
-                with file(l_name + SUFFIX, 'w') as f:
+                with open(l_name + SUFFIX, 'wb') as f:
                     f.write(data)
 
                 parser = find_parser(e['name'])
@@ -300,41 +295,42 @@ def backup_directory_remote_to_local(conn, remotedir, localdir):
                 parser.write_xml(l_name + '.xml')
 
 
-attr_flags = smb.FILE_ATTRIBUTE_SYSTEM | \
-             smb.FILE_ATTRIBUTE_DIRECTORY | \
-             smb.FILE_ATTRIBUTE_ARCHIVE | \
-             smb.FILE_ATTRIBUTE_HIDDEN
+attr_flags = libsmb.FILE_ATTRIBUTE_SYSTEM | \
+             libsmb.FILE_ATTRIBUTE_DIRECTORY | \
+             libsmb.FILE_ATTRIBUTE_ARCHIVE | \
+             libsmb.FILE_ATTRIBUTE_HIDDEN
+
 
 def copy_directory_remote_to_local(conn, remotedir, localdir):
     if not os.path.isdir(localdir):
         os.mkdir(localdir)
-    r_dirs = [ remotedir ]
-    l_dirs = [ localdir ]
+    r_dirs = [remotedir]
+    l_dirs = [localdir]
     while r_dirs:
         r_dir = r_dirs.pop()
         l_dir = l_dirs.pop()
 
         dirlist = conn.list(r_dir, attribs=attr_flags)
-        dirlist.sort()
+        dirlist.sort(key=lambda x : x['name'])
         for e in dirlist:
             r_name = r_dir + '\\' + e['name']
             l_name = os.path.join(l_dir, e['name'])
 
-            if e['attrib'] & smb.FILE_ATTRIBUTE_DIRECTORY:
+            if e['attrib'] & libsmb.FILE_ATTRIBUTE_DIRECTORY:
                 r_dirs.append(r_name)
                 l_dirs.append(l_name)
                 os.mkdir(l_name)
             else:
                 data = conn.loadfile(r_name)
-                open(l_name, 'w').write(data)
+                open(l_name, 'wb').write(data)
 
 
 def copy_directory_local_to_remote(conn, localdir, remotedir,
                                    ignore_existing=False):
     if not conn.chkpath(remotedir):
         conn.mkdir(remotedir)
-    l_dirs = [ localdir ]
-    r_dirs = [ remotedir ]
+    l_dirs = [localdir]
+    r_dirs = [remotedir]
     while l_dirs:
         l_dir = l_dirs.pop()
         r_dir = r_dirs.pop()
@@ -354,7 +350,7 @@ def copy_directory_local_to_remote(conn, localdir, remotedir,
                     if not ignore_existing:
                         raise
             else:
-                data = open(l_name, 'r').read()
+                data = open(l_name, 'rb').read()
                 conn.savefile(r_name, data)
 
 
@@ -366,8 +362,65 @@ def create_directory_hier(conn, remotedir):
         if not conn.chkpath(path):
             conn.mkdir(path)
 
+def smb_connection(dc_hostname, service, lp, creds, sign=False):
+    # SMB connect to DC
+    try:
+        # the SMB bindings rely on having a s3 loadparm
+        s3_lp = s3param.get_context()
+        s3_lp.load(lp.configfile)
+        conn = libsmb.Conn(dc_hostname, service, lp=s3_lp, creds=creds, sign=sign)
+    except Exception:
+        raise CommandError("Error connecting to '%s' using SMB" % dc_hostname)
+    return conn
+
+
+class GPOCommand(Command):
+    def construct_tmpdir(self, tmpdir, gpo):
+        """Ensure that the temporary directory structure used in fetch,
+        backup, create, and restore is consistent.
+
+        If --tmpdir is used the named directory must be present, which may
+        contain a 'policy' subdirectory, but 'policy' must not itself have
+        a subdirectory with the gpo name. The policy and gpo directories
+        will be created.
+
+        If --tmpdir is not used, a temporary directory is securely created.
+        """
+        if tmpdir is None:
+            tmpdir = tempfile.mkdtemp()
+            print("Using temporary directory %s (use --tmpdir to change)" % tmpdir,
+                  file=self.outf)
+
+        if not os.path.isdir(tmpdir):
+            raise CommandError("Temporary directory '%s' does not exist" % tmpdir)
 
-class cmd_listall(Command):
+        localdir = os.path.join(tmpdir, "policy")
+        if not os.path.isdir(localdir):
+            os.mkdir(localdir)
+
+        gpodir = os.path.join(localdir, gpo)
+        if os.path.isdir(gpodir):
+            raise CommandError(
+                "GPO directory '%s' already exists, refusing to overwrite" % gpodir)
+
+        try:
+            os.mkdir(gpodir)
+        except (IOError, OSError) as e:
+            raise CommandError("Error creating teporary GPO directory", e)
+
+        return tmpdir, gpodir
+
+    def samdb_connect(self):
+        '''make a ldap connection to the server'''
+        try:
+            self.samdb = SamDB(url=self.url,
+                               session_info=system_session(),
+                               credentials=self.creds, lp=self.lp)
+        except Exception as e:
+            raise CommandError("LDAP connection to %s failed " % self.url, e)
+
+
+class cmd_listall(GPOCommand):
     """List all GPOs."""
 
     synopsis = "%prog [options]"
@@ -381,7 +434,7 @@ class cmd_listall(Command):
     takes_options = [
         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
                metavar="URL", dest="H")
-        ]
+    ]
 
     def run(self, H=None, sambaopts=None, credopts=None, versionopts=None):
 
@@ -390,7 +443,7 @@ class cmd_listall(Command):
 
         self.url = dc_url(self.lp, self.creds, H)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         msg = get_gpo_info(self.samdb, None)
 
@@ -404,7 +457,7 @@ class cmd_listall(Command):
             self.outf.write("\n")
 
 
-class cmd_list(Command):
+class cmd_list(GPOCommand):
     """List GPOs for an account."""
 
     synopsis = "%prog <username> [options]"
@@ -418,8 +471,8 @@ class cmd_list(Command):
 
     takes_options = [
         Option("-H", "--URL", help="LDB URL for database or target server",
-            type=str, metavar="URL", dest="H")
-        ]
+               type=str, metavar="URL", dest="H")
+    ]
 
     def run(self, username, H=None, sambaopts=None, credopts=None, versionopts=None):
 
@@ -428,11 +481,11 @@ class cmd_list(Command):
 
         self.url = dc_url(self.lp, self.creds, H)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         try:
             msg = self.samdb.search(expression='(&(|(samAccountName=%s)(samAccountName=%s$))(objectClass=User))' %
-                                                (ldb.binary_encode(username),ldb.binary_encode(username)))
+                                    (ldb.binary_encode(username), ldb.binary_encode(username)))
             user_dn = msg[0].dn
         except Exception:
             raise CommandError("Failed to find account %s" % username)
@@ -444,8 +497,8 @@ class cmd_list(Command):
         except Exception:
             raise CommandError("Failed to find objectClass for user %s" % username)
 
-        session_info_flags = ( AUTH_SESSION_INFO_DEFAULT_GROUPS |
-                               AUTH_SESSION_INFO_AUTHENTICATED )
+        session_info_flags = (AUTH_SESSION_INFO_DEFAULT_GROUPS |
+                              AUTH_SESSION_INFO_AUTHENTICATED)
 
         # When connecting to a remote server, don't look up the local privilege DB
         if self.url is not None and self.url.startswith('ldap'):
@@ -463,7 +516,7 @@ class cmd_list(Command):
         while True:
             msg = self.samdb.search(base=dn, scope=ldb.SCOPE_BASE, attrs=['gPLink', 'gPOptions'])[0]
             if 'gPLink' in msg:
-                glist = parse_gplink(msg['gPLink'][0])
+                glist = parse_gplink(str(msg['gPLink'][0]))
                 for g in glist:
                     if not inherit and not (g['options'] & dsdb.GPLINK_OPT_ENFORCE):
                         continue
@@ -471,7 +524,9 @@ class cmd_list(Command):
                         continue
 
                     try:
-                        sd_flags=security.SECINFO_OWNER|security.SECINFO_GROUP|security.SECINFO_DACL
+                        sd_flags = (security.SECINFO_OWNER |
+                                    security.SECINFO_GROUP |
+                                    security.SECINFO_DACL)
                         gmsg = self.samdb.search(base=g['dn'], scope=ldb.SCOPE_BASE,
                                                  attrs=['name', 'displayName', 'flags',
                                                         'nTSecurityDescriptor'],
@@ -480,7 +535,7 @@ class cmd_list(Command):
                         secdesc = ndr_unpack(security.descriptor, secdesc_ndr)
                     except Exception:
                         self.outf.write("Failed to fetch gpo object with nTSecurityDescriptor %s\n" %
-                            g['dn'])
+                                        g['dn'])
                         continue
 
                     try:
@@ -519,7 +574,7 @@ class cmd_list(Command):
             self.outf.write("    %s %s\n" % (g[0], g[1]))
 
 
-class cmd_show(Command):
+class cmd_show(GPOCommand):
     """Show information for a GPO."""
 
     synopsis = "%prog <gpo> [options]"
@@ -534,7 +589,7 @@ class cmd_show(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str)
-        ]
+    ]
 
     def run(self, gpo, H=None, sambaopts=None, credopts=None, versionopts=None):
 
@@ -543,7 +598,7 @@ class cmd_show(Command):
 
         self.url = dc_url(self.lp, self.creds, H)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         try:
             msg = get_gpo_info(self.samdb, gpo)[0]
@@ -567,7 +622,7 @@ class cmd_show(Command):
         self.outf.write("\n")
 
 
-class cmd_getlink(Command):
+class cmd_getlink(GPOCommand):
     """List GPO Links for a container."""
 
     synopsis = "%prog <container_dn> [options]"
@@ -582,17 +637,17 @@ class cmd_getlink(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str)
-        ]
+    ]
 
     def run(self, container_dn, H=None, sambaopts=None, credopts=None,
-                versionopts=None):
+            versionopts=None):
 
         self.lp = sambaopts.get_loadparm()
         self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
 
         self.url = dc_url(self.lp, self.creds, H)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         try:
             msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
@@ -603,7 +658,7 @@ class cmd_getlink(Command):
 
         if msg['gPLink']:
             self.outf.write("GPO(s) linked to DN %s\n" % container_dn)
-            gplist = parse_gplink(msg['gPLink'][0])
+            gplist = parse_gplink(str(msg['gPLink'][0]))
             for g in gplist:
                 msg = get_gpo_info(self.samdb, dn=g['dn'])
                 self.outf.write("    GPO     : %s\n" % msg[0]['name'][0])
@@ -614,7 +669,7 @@ class cmd_getlink(Command):
             self.outf.write("No GPO(s) linked to DN=%s\n" % container_dn)
 
 
-class cmd_setlink(Command):
+class cmd_setlink(GPOCommand):
     """Add or update a GPO link to a container."""
 
     synopsis = "%prog <container_dn> <gpo> [options]"
@@ -630,20 +685,20 @@ class cmd_setlink(Command):
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str),
         Option("--disable", dest="disabled", default=False, action='store_true',
-            help="Disable policy"),
+               help="Disable policy"),
         Option("--enforce", dest="enforced", default=False, action='store_true',
-            help="Enforce policy")
-        ]
+               help="Enforce policy")
+    ]
 
     def run(self, container_dn, gpo, H=None, disabled=False, enforced=False,
-                sambaopts=None, credopts=None, versionopts=None):
+            sambaopts=None, credopts=None, versionopts=None):
 
         self.lp = sambaopts.get_loadparm()
         self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
 
         self.url = dc_url(self.lp, self.creds, H)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         gplink_options = 0
         if disabled:
@@ -669,7 +724,7 @@ class cmd_setlink(Command):
         # Update existing GPlinks or Add new one
         existing_gplink = False
         if 'gPLink' in msg:
-            gplist = parse_gplink(msg['gPLink'][0])
+            gplist = parse_gplink(str(msg['gPLink'][0]))
             existing_gplink = True
             found = False
             for g in gplist:
@@ -680,10 +735,10 @@ class cmd_setlink(Command):
             if found:
                 raise CommandError("GPO '%s' already linked to this container" % gpo)
             else:
-                gplist.insert(0, { 'dn' : gpo_dn, 'options' : gplink_options })
+                gplist.insert(0, {'dn': gpo_dn, 'options': gplink_options})
         else:
             gplist = []
-            gplist.append({ 'dn' : gpo_dn, 'options' : gplink_options })
+            gplist.append({'dn': gpo_dn, 'options': gplink_options})
 
         gplink_str = encode_gplink(gplist)
 
@@ -704,7 +759,7 @@ class cmd_setlink(Command):
         cmd_getlink().run(container_dn, H, sambaopts, credopts, versionopts)
 
 
-class cmd_dellink(Command):
+class cmd_dellink(GPOCommand):
     """Delete GPO link from a container."""
 
     synopsis = "%prog <container_dn> <gpo> [options]"
@@ -719,17 +774,17 @@ class cmd_dellink(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str),
-        ]
+    ]
 
     def run(self, container, gpo, H=None, sambaopts=None, credopts=None,
-                versionopts=None):
+            versionopts=None):
 
         self.lp = sambaopts.get_loadparm()
         self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
 
         self.url = dc_url(self.lp, self.creds, H)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         # Check if valid GPO
         try:
@@ -743,7 +798,7 @@ class cmd_dellink(Command):
         cmd_getlink().run(container_dn, H, sambaopts, credopts, versionopts)
 
 
-class cmd_listcontainers(Command):
+class cmd_listcontainers(GPOCommand):
     """List all linked containers for a GPO."""
 
     synopsis = "%prog <gpo> [options]"
@@ -758,17 +813,17 @@ class cmd_listcontainers(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str)
-        ]
+    ]
 
     def run(self, gpo, H=None, sambaopts=None, credopts=None,
-                versionopts=None):
+            versionopts=None):
 
         self.lp = sambaopts.get_loadparm()
         self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
 
         self.url = dc_url(self.lp, self.creds, H)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         msg = get_gpo_containers(self.samdb, gpo)
         if len(msg):
@@ -779,7 +834,7 @@ class cmd_listcontainers(Command):
             self.outf.write("No Containers using GPO %s\n" % gpo)
 
 
-class cmd_getinheritance(Command):
+class cmd_getinheritance(GPOCommand):
     """Get inheritance flag for a container."""
 
     synopsis = "%prog <container_dn> [options]"
@@ -794,17 +849,17 @@ class cmd_getinheritance(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str)
-        ]
+    ]
 
     def run(self, container_dn, H=None, sambaopts=None, credopts=None,
-                versionopts=None):
+            versionopts=None):
 
         self.lp = sambaopts.get_loadparm()
         self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
 
         self.url = dc_url(self.lp, self.creds, H)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         try:
             msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
@@ -823,7 +878,7 @@ class cmd_getinheritance(Command):
             self.outf.write("Container has GPO_INHERIT\n")
 
 
-class cmd_setinheritance(Command):
+class cmd_setinheritance(GPOCommand):
     """Set inheritance flag on a container."""
 
     synopsis = "%prog <container_dn> <block|inherit> [options]"
@@ -834,14 +889,14 @@ class cmd_setinheritance(Command):
         "credopts": options.CredentialsOptions,
     }
 
-    takes_args = [ 'container_dn', 'inherit_state' ]
+    takes_args = ['container_dn', 'inherit_state']
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str)
-        ]
+    ]
 
     def run(self, container_dn, inherit_state, H=None, sambaopts=None, credopts=None,
-                versionopts=None):
+            versionopts=None):
 
         if inherit_state.lower() == 'block':
             inheritance = dsdb.GPO_BLOCK_INHERITANCE
@@ -855,7 +910,7 @@ class cmd_setinheritance(Command):
 
         self.url = dc_url(self.lp, self.creds, H)
 
-        samdb_connect(self)
+        self.samdb_connect()
         try:
             msg = self.samdb.search(base=container_dn, scope=ldb.SCOPE_BASE,
                                     expression="(objectClass=*)",
@@ -877,7 +932,7 @@ class cmd_setinheritance(Command):
             raise CommandError("Error setting inheritance state %s" % inherit_state, e)
 
 
-class cmd_fetch(Command):
+class cmd_fetch(GPOCommand):
     """Download a GPO."""
 
     synopsis = "%prog <gpo> [options]"
@@ -893,7 +948,7 @@ class cmd_fetch(Command):
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str),
         Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
-        ]
+    ]
 
     def run(self, gpo, H=None, tmpdir=None, sambaopts=None, credopts=None, versionopts=None):
 
@@ -908,45 +963,26 @@ class cmd_fetch(Command):
             dc_hostname = netcmd_finddc(self.lp, self.creds)
             self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
 
-        samdb_connect(self)
+        self.samdb_connect()
         try:
             msg = get_gpo_info(self.samdb, gpo)[0]
         except Exception:
             raise CommandError("GPO '%s' does not exist" % gpo)
 
         # verify UNC path
-        unc = msg['gPCFileSysPath'][0]
+        unc = str(msg['gPCFileSysPath'][0])
         try:
             [dom_name, service, sharepath] = parse_unc(unc)
         except ValueError:
             raise CommandError("Invalid GPO path (%s)" % unc)
 
         # SMB connect to DC
-        try:
-            conn = smb.SMB(dc_hostname,
-                           service,
-                           lp=self.lp,
-                           creds=self.creds,
-                           sign=True)
-        except Exception:
-            raise CommandError("Error connecting to '%s' using SMB" % dc_hostname)
+        conn = smb_connection(dc_hostname, service, lp=self.lp,
+                              creds=self.creds, sign=True)
 
         # Copy GPT
-        if tmpdir is None:
-            tmpdir = "/tmp"
-        if not os.path.isdir(tmpdir):
-            raise CommandError("Temoprary directory '%s' does not exist" % tmpdir)
-
-        localdir = os.path.join(tmpdir, "policy")
-        if not os.path.isdir(localdir):
-            os.mkdir(localdir)
-
-        gpodir = os.path.join(localdir, gpo)
-        if os.path.isdir(gpodir):
-            raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir)
-
+        tmpdir, gpodir = self.construct_tmpdir(tmpdir, gpo)
         try:
-            os.mkdir(gpodir)
             copy_directory_remote_to_local(conn, sharepath, gpodir)
         except Exception as e:
             # FIXME: Catch more specific exception
@@ -954,7 +990,7 @@ class cmd_fetch(Command):
         self.outf.write('GPO copied to %s\n' % gpodir)
 
 
-class cmd_backup(Command):
+class cmd_backup(GPOCommand):
     """Backup a GPO."""
 
     synopsis = "%prog <gpo> [options]"
@@ -974,7 +1010,7 @@ class cmd_backup(Command):
                default=False, action='store_true'),
         Option("--entities", help="File to export defining XML entities for the restore",
                dest='ent_file', type=str)
-        ]
+    ]
 
     def run(self, gpo, H=None, tmpdir=None, generalize=False, sambaopts=None,
             credopts=None, versionopts=None, ent_file=None):
@@ -990,41 +1026,27 @@ class cmd_backup(Command):
             dc_hostname = netcmd_finddc(self.lp, self.creds)
             self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
 
-        samdb_connect(self)
+        self.samdb_connect()
         try:
             msg = get_gpo_info(self.samdb, gpo)[0]
         except Exception:
             raise CommandError("GPO '%s' does not exist" % gpo)
 
         # verify UNC path
-        unc = msg['gPCFileSysPath'][0]
+        unc = str(msg['gPCFileSysPath'][0])
         try:
             [dom_name, service, sharepath] = parse_unc(unc)
         except ValueError:
             raise CommandError("Invalid GPO path (%s)" % unc)
 
         # SMB connect to DC
-        try:
-            conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
-        except Exception:
-            raise CommandError("Error connecting to '%s' using SMB" % dc_hostname)
+        conn = smb_connection(dc_hostname, service, lp=self.lp,
+                              creds=self.creds)
 
         # Copy GPT
-        if tmpdir is None:
-            tmpdir = "/tmp"
-        if not os.path.isdir(tmpdir):
-            raise CommandError("Temoprary directory '%s' does not exist" % tmpdir)
-
-        localdir = os.path.join(tmpdir, "policy")
-        if not os.path.isdir(localdir):
-            os.mkdir(localdir)
-
-        gpodir = os.path.join(localdir, gpo)
-        if os.path.isdir(gpodir):
-            raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir)
+        tmpdir, gpodir = self.construct_tmpdir(tmpdir, gpo)
 
         try:
-            os.mkdir(gpodir)
             backup_directory_remote_to_local(conn, sharepath, gpodir)
         except Exception as e:
             # FIXME: Catch more specific exception
@@ -1103,7 +1125,7 @@ class cmd_backup(Command):
         return entities
 
 
-class cmd_create(Command):
+class cmd_create(GPOCommand):
     """Create an empty GPO."""
 
     synopsis = "%prog <displayname> [options]"
@@ -1119,7 +1141,7 @@ class cmd_create(Command):
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str),
         Option("--tmpdir", help="Temporary directory for copying policy files", type=str)
-        ]
+    ]
 
     def run(self, displayname, H=None, tmpdir=None, sambaopts=None, credopts=None,
             versionopts=None):
@@ -1145,7 +1167,7 @@ class cmd_create(Command):
             dc_hostname = cldap_ret.pdc_dns_name
             self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         msg = get_gpo_info(self.samdb, displayname=displayname)
         if msg.count > 0:
@@ -1161,23 +1183,10 @@ class cmd_create(Command):
         unc_path = "\\\\%s\\sysvol\\%s\\Policies\\%s" % (realm, realm, gpo)
 
         # Create GPT
-        if tmpdir is None:
-            tmpdir = "/tmp"
-        if not os.path.isdir(tmpdir):
-            raise CommandError("Temporary directory '%s' does not exist" % tmpdir)
-        self.tmpdir = tmpdir
-
-        localdir = os.path.join(tmpdir, "policy")
-        if not os.path.isdir(localdir):
-            os.mkdir(localdir)
-
-        gpodir = os.path.join(localdir, gpo)
+        self.tmpdir, gpodir = self.construct_tmpdir(tmpdir, gpo)
         self.gpodir = gpodir
-        if os.path.isdir(gpodir):
-            raise CommandError("GPO directory '%s' already exists, refusing to overwrite" % gpodir)
 
         try:
-            os.mkdir(gpodir)
             os.mkdir(os.path.join(gpodir, "Machine"))
             os.mkdir(os.path.join(gpodir, "User"))
             gpt_contents = "[General]\r\nVersion=0\r\n"
@@ -1188,10 +1197,8 @@ class cmd_create(Command):
         # Connect to DC over SMB
         [dom_name, service, sharepath] = parse_unc(unc_path)
         self.sharepath = sharepath
-        try:
-            conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
-        except Exception as e:
-            raise CommandError("Error connecting to '%s' using SMB" % dc_hostname, e)
+        conn = smb_connection(dc_hostname, service, lp=self.lp,
+                              creds=self.creds)
 
         self.conn = conn
 
@@ -1218,26 +1225,30 @@ class cmd_create(Command):
             self.samdb.add(m)
 
             # Get new security descriptor
-            ds_sd_flags = ( security.SECINFO_OWNER |
-                            security.SECINFO_GROUP |
-                            security.SECINFO_DACL )
+            ds_sd_flags = (security.SECINFO_OWNER |
+                           security.SECINFO_GROUP |
+                           security.SECINFO_DACL)
             msg = get_gpo_info(self.samdb, gpo=gpo, sd_flags=ds_sd_flags)[0]
             ds_sd_ndr = msg['nTSecurityDescriptor'][0]
             ds_sd = ndr_unpack(security.descriptor, ds_sd_ndr).as_sddl()
 
             # Create a file system security descriptor
             domain_sid = security.dom_sid(self.samdb.get_domain_sid())
-            sddl = dsacl2fsacl(ds_sd, domain_sid)
-            fs_sd = security.descriptor.from_sddl(sddl, domain_sid)
+            fs_sd = dsacl2fsacl(ds_sd, domain_sid, as_sddl=False)
+            fs_sd.type = security.SEC_DESC_SELF_RELATIVE
+            fs_sd.type |= security.SEC_DESC_DACL_PROTECTED
+            fs_sd.type |= security.SEC_DESC_DACL_AUTO_INHERITED
+            fs_sd.type |= security.SEC_DESC_DACL_AUTO_INHERIT_REQ
+            fs_sd.type |= security.SEC_DESC_SACL_AUTO_INHERITED
 
             # Copy GPO directory
             create_directory_hier(conn, sharepath)
 
             # Set ACL
-            sio = ( security.SECINFO_OWNER |
-                    security.SECINFO_GROUP |
-                    security.SECINFO_DACL |
-                    security.SECINFO_PROTECTED_DACL )
+            sio = (security.SECINFO_OWNER |
+                   security.SECINFO_GROUP |
+                   security.SECINFO_DACL |
+                   security.SECINFO_PROTECTED_DACL)
             conn.set_acl(sharepath, fs_sd, sio)
 
             # Copy GPO files over SMB
@@ -1250,7 +1261,7 @@ class cmd_create(Command):
             m['a05'] = ldb.MessageElement("0", ldb.FLAG_MOD_REPLACE, "versionNumber")
             m['a07'] = ldb.MessageElement("2", ldb.FLAG_MOD_REPLACE, "gpcFunctionalityVersion")
             m['a04'] = ldb.MessageElement("0", ldb.FLAG_MOD_REPLACE, "flags")
-            controls=["permissive_modify:0"]
+            controls = ["permissive_modify:0"]
             self.samdb.modify(m, controls=controls)
         except Exception:
             self.samdb.transaction_cancel()
@@ -1278,7 +1289,7 @@ class cmd_restore(cmd_create):
         Option("-H", help="LDB URL for database or target server", type=str),
         Option("--tmpdir", help="Temporary directory for copying policy files", type=str),
         Option("--entities", help="File defining XML entities to insert into DOCTYPE header", type=str)
-        ]
+    ]
 
     def restore_from_backup_to_local_dir(self, sourcedir, targetdir, dtd_header=''):
         SUFFIX = '.SAMBABACKUP'
@@ -1379,7 +1390,7 @@ class cmd_restore(cmd_create):
             dtd_header += '\n]>\n'
 
         super(cmd_restore, self).run(displayname, H, tmpdir, sambaopts,
-                                    credopts, versionopts)
+                                     credopts, versionopts)
 
         try:
             # Iterate over backup files and restore with DTD
@@ -1403,7 +1414,7 @@ class cmd_restore(cmd_create):
             raise CommandError("Failed to restore: %s" % e)
 
 
-class cmd_del(Command):
+class cmd_del(GPOCommand):
     """Delete a GPO."""
 
     synopsis = "%prog <gpo> [options]"
@@ -1418,10 +1429,10 @@ class cmd_del(Command):
 
     takes_options = [
         Option("-H", help="LDB URL for database or target server", type=str),
-        ]
+    ]
 
     def run(self, gpo, H=None, sambaopts=None, credopts=None,
-                versionopts=None):
+            versionopts=None):
 
         self.lp = sambaopts.get_loadparm()
         self.creds = credopts.get_credentials(self.lp, fallback_machine=True)
@@ -1434,21 +1445,19 @@ class cmd_del(Command):
             dc_hostname = netcmd_finddc(self.lp, self.creds)
             self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         # Check if valid GPO
         try:
             msg = get_gpo_info(self.samdb, gpo=gpo)[0]
-            unc_path = msg['gPCFileSysPath'][0]
+            unc_path = str(msg['gPCFileSysPath'][0])
         except Exception:
             raise CommandError("GPO '%s' does not exist" % gpo)
 
         # Connect to DC over SMB
         [dom_name, service, sharepath] = parse_unc(unc_path)
-        try:
-            conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
-        except Exception as e:
-            raise CommandError("Error connecting to '%s' using SMB" % dc_hostname, e)
+        conn = smb_connection(dc_hostname, service, lp=self.lp,
+                              creds=self.creds)
 
         self.samdb.transaction_start()
         try:
@@ -1479,7 +1488,7 @@ class cmd_del(Command):
         self.outf.write("GPO %s deleted.\n" % gpo)
 
 
-class cmd_aclcheck(Command):
+class cmd_aclcheck(GPOCommand):
     """Check all GPOs have matching LDAP and DS ACLs."""
 
     synopsis = "%prog [options]"
@@ -1493,7 +1502,7 @@ class cmd_aclcheck(Command):
     takes_options = [
         Option("-H", "--URL", help="LDB URL for database or target server", type=str,
                metavar="URL", dest="H")
-        ]
+    ]
 
     def run(self, H=None, sambaopts=None, credopts=None, versionopts=None):
 
@@ -1510,26 +1519,28 @@ class cmd_aclcheck(Command):
             dc_hostname = netcmd_finddc(self.lp, self.creds)
             self.url = dc_url(self.lp, self.creds, dc=dc_hostname)
 
-        samdb_connect(self)
+        self.samdb_connect()
 
         msg = get_gpo_info(self.samdb, None)
 
         for m in msg:
             # verify UNC path
-            unc = m['gPCFileSysPath'][0]
+            unc = str(m['gPCFileSysPath'][0])
             try:
                 [dom_name, service, sharepath] = parse_unc(unc)
             except ValueError:
                 raise CommandError("Invalid GPO path (%s)" % unc)
 
             # SMB connect to DC
-            try:
-                conn = smb.SMB(dc_hostname, service, lp=self.lp, creds=self.creds)
-            except Exception:
-                raise CommandError("Error connecting to '%s' using SMB" % dc_hostname)
+            conn = smb_connection(dc_hostname, service, lp=self.lp,
+                                  creds=self.creds)
 
             fs_sd = conn.get_acl(sharepath, security.SECINFO_OWNER | security.SECINFO_GROUP | security.SECINFO_DACL, security.SEC_FLAG_MAXIMUM_ALLOWED)
 
+            if 'nTSecurityDescriptor' not in m:
+                raise CommandError("Could not read nTSecurityDescriptor. "
+                                   "This requires an Administrator account")
+
             ds_sd_ndr = m['nTSecurityDescriptor'][0]
             ds_sd = ndr_unpack(security.descriptor, ds_sd_ndr).as_sddl()