1# Unix SMB/CIFS implementation.
2# Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010
3#
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17#
18
19from __future__ import print_function
20"""NT Acls."""
21
22
23import os
24import tarfile
25import tempfile
26import shutil
27
28import samba.xattr_native
29import samba.xattr_tdb
30import samba.posix_eadb
31from samba.samba3 import param as s3param
32from samba.dcerpc import security, xattr, idmap
33from samba.ndr import ndr_pack, ndr_unpack
34from samba.samba3 import smbd
35from samba.samba3 import libsmb_samba_internal as libsmb
36from samba.logger import get_samba_logger
37from samba import NTSTATUSError
38from samba.auth_util import system_session_unix
39
40# don't include volumes
41SMB_FILE_ATTRIBUTE_FLAGS = libsmb.FILE_ATTRIBUTE_SYSTEM | \
42                           libsmb.FILE_ATTRIBUTE_DIRECTORY | \
43                           libsmb.FILE_ATTRIBUTE_ARCHIVE | \
44                           libsmb.FILE_ATTRIBUTE_HIDDEN
45
46
47SECURITY_SECINFO_FLAGS = security.SECINFO_OWNER | \
48                         security.SECINFO_GROUP | \
49                         security.SECINFO_DACL  | \
50                         security.SECINFO_SACL
51
52
53# SEC_FLAG_SYSTEM_SECURITY is required otherwise get Access Denied
54SECURITY_SEC_FLAGS = security.SEC_FLAG_SYSTEM_SECURITY | \
55                     security.SEC_STD_READ_CONTROL
56
57
58class XattrBackendError(Exception):
59    """A generic xattr backend error."""
60
61
62def checkset_backend(lp, backend, eadbfile):
63    '''return the path to the eadb, or None'''
64    if backend is None:
65        xattr_tdb = lp.get("xattr_tdb:file")
66        if xattr_tdb is not None:
67            return (samba.xattr_tdb, lp.get("xattr_tdb:file"))
68        posix_eadb = lp.get("posix:eadb")
69        if posix_eadb is not None:
70            return (samba.posix_eadb, lp.get("posix:eadb"))
71        return (None, None)
72    elif backend == "native":
73        return (None, None)
74    elif backend == "eadb":
75        if eadbfile is not None:
76            return (samba.posix_eadb, eadbfile)
77        else:
78            return (samba.posix_eadb, os.path.abspath(os.path.join(lp.get("private dir"), "eadb.tdb")))
79    elif backend == "tdb":
80        if eadbfile is not None:
81            return (samba.xattr_tdb, eadbfile)
82        else:
83            state_dir = lp.get("state directory")
84            db_path = os.path.abspath(os.path.join(state_dir, "xattr.tdb"))
85            return (samba.xattr_tdb, db_path)
86    else:
87        raise XattrBackendError("Invalid xattr backend choice %s" % backend)
88
89
90def getdosinfo(lp, file):
91    try:
92        attribute = samba.xattr_native.wrap_getxattr(file,
93                                                     xattr.XATTR_DOSATTRIB_NAME_S3)
94    except Exception:
95        return
96
97    return ndr_unpack(xattr.DOSATTRIB, attribute)
98
99
100def getntacl(lp,
101             file,
102             session_info,
103             backend=None,
104             eadbfile=None,
105             direct_db_access=True,
106             service=None):
107    if direct_db_access:
108        (backend_obj, dbname) = checkset_backend(lp, backend, eadbfile)
109        if dbname is not None:
110            try:
111                attribute = backend_obj.wrap_getxattr(dbname, file,
112                                                      xattr.XATTR_NTACL_NAME)
113            except Exception:
114                # FIXME: Don't catch all exceptions, just those related to opening
115                # xattrdb
116                print("Fail to open %s" % dbname)
117                attribute = samba.xattr_native.wrap_getxattr(file,
118                                                             xattr.XATTR_NTACL_NAME)
119        else:
120            attribute = samba.xattr_native.wrap_getxattr(file,
121                                                         xattr.XATTR_NTACL_NAME)
122        ntacl = ndr_unpack(xattr.NTACL, attribute)
123        if ntacl.version == 1:
124            return ntacl.info
125        elif ntacl.version == 2:
126            return ntacl.info.sd
127        elif ntacl.version == 3:
128            return ntacl.info.sd
129        elif ntacl.version == 4:
130            return ntacl.info.sd
131    else:
132        return smbd.get_nt_acl(file,
133                               SECURITY_SECINFO_FLAGS,
134                               session_info,
135                               service=service)
136
137
138def setntacl(lp, file, sddl, domsid, session_info,
139             backend=None, eadbfile=None,
140             use_ntvfs=True, skip_invalid_chown=False,
141             passdb=None, service=None):
142    """
143    A wrapper for smbd set_nt_acl api.
144
145    Args:
146        lp (LoadParam): load param from conf
147        file (str): a path to file or dir
148        sddl (str): ntacl sddl string
149        service (str): name of share service, e.g.: sysvol
150        session_info (auth_session_info): session info for authentication
151
152    Note:
153        Get `session_info` with `samba.auth.user_session`, do not use the
154        `admin_session` api.
155
156    Returns:
157        None
158    """
159
160    assert(isinstance(domsid, str) or isinstance(domsid, security.dom_sid))
161    if isinstance(domsid, str):
162        sid = security.dom_sid(domsid)
163    elif isinstance(domsid, security.dom_sid):
164        sid = domsid
165        domsid = str(sid)
166
167    assert(isinstance(sddl, str) or isinstance(sddl, security.descriptor))
168    if isinstance(sddl, str):
169        sd = security.descriptor.from_sddl(sddl, sid)
170    elif isinstance(sddl, security.descriptor):
171        sd = sddl
172        sddl = sd.as_sddl(sid)
173
174    if not use_ntvfs and skip_invalid_chown:
175        # Check if the owner can be resolved as a UID
176        (owner_id, owner_type) = passdb.sid_to_id(sd.owner_sid)
177        if ((owner_type != idmap.ID_TYPE_UID) and (owner_type != idmap.ID_TYPE_BOTH)):
178            # Check if this particular owner SID was domain admins,
179            # because we special-case this as mapping to
180            # 'administrator' instead.
181            if sd.owner_sid == security.dom_sid("%s-%d" % (domsid, security.DOMAIN_RID_ADMINS)):
182                administrator = security.dom_sid("%s-%d" % (domsid, security.DOMAIN_RID_ADMINISTRATOR))
183                (admin_id, admin_type) = passdb.sid_to_id(administrator)
184
185                # Confirm we have a UID for administrator
186                if ((admin_type == idmap.ID_TYPE_UID) or (admin_type == idmap.ID_TYPE_BOTH)):
187
188                    # Set it, changing the owner to 'administrator' rather than domain admins
189                    sd2 = sd
190                    sd2.owner_sid = administrator
191
192                    smbd.set_nt_acl(
193                        file, SECURITY_SECINFO_FLAGS, sd2,
194                        session_info,
195                        service=service)
196
197                    # and then set an NTVFS ACL (which does not set the posix ACL) to pretend the owner really was set
198                    use_ntvfs = True
199                else:
200                    raise XattrBackendError("Unable to find UID for domain administrator %s, got id %d of type %d" % (administrator, admin_id, admin_type))
201            else:
202                # For all other owning users, reset the owner to root
203                # and then set the ACL without changing the owner
204                #
205                # This won't work in test environments, as it tries a real (rather than xattr-based fake) chown
206
207                os.chown(file, 0, 0)
208                smbd.set_nt_acl(
209                    file,
210                    security.SECINFO_GROUP |
211                    security.SECINFO_DACL |
212                    security.SECINFO_SACL,
213                    sd,
214                    session_info,
215                    service=service)
216
217    if use_ntvfs:
218        (backend_obj, dbname) = checkset_backend(lp, backend, eadbfile)
219        ntacl = xattr.NTACL()
220        ntacl.version = 1
221        ntacl.info = sd
222        if dbname is not None:
223            try:
224                backend_obj.wrap_setxattr(dbname,
225                                          file, xattr.XATTR_NTACL_NAME, ndr_pack(ntacl))
226            except Exception:
227                # FIXME: Don't catch all exceptions, just those related to opening
228                # xattrdb
229                print("Fail to open %s" % dbname)
230                samba.xattr_native.wrap_setxattr(file, xattr.XATTR_NTACL_NAME,
231                                                 ndr_pack(ntacl))
232        else:
233            samba.xattr_native.wrap_setxattr(file, xattr.XATTR_NTACL_NAME,
234                                             ndr_pack(ntacl))
235    else:
236        smbd.set_nt_acl(
237            file, SECURITY_SECINFO_FLAGS, sd,
238            service=service, session_info=session_info)
239
240
241def ldapmask2filemask(ldm):
242    """Takes the access mask of a DS ACE and transform them in a File ACE mask.
243    """
244    RIGHT_DS_CREATE_CHILD     = 0x00000001
245    RIGHT_DS_DELETE_CHILD     = 0x00000002
246    RIGHT_DS_LIST_CONTENTS    = 0x00000004
247    ACTRL_DS_SELF             = 0x00000008
248    RIGHT_DS_READ_PROPERTY    = 0x00000010
249    RIGHT_DS_WRITE_PROPERTY   = 0x00000020
250    RIGHT_DS_DELETE_TREE      = 0x00000040
251    RIGHT_DS_LIST_OBJECT      = 0x00000080
252    RIGHT_DS_CONTROL_ACCESS   = 0x00000100
253    FILE_READ_DATA            = 0x0001
254    FILE_LIST_DIRECTORY       = 0x0001
255    FILE_WRITE_DATA           = 0x0002
256    FILE_ADD_FILE             = 0x0002
257    FILE_APPEND_DATA          = 0x0004
258    FILE_ADD_SUBDIRECTORY     = 0x0004
259    FILE_CREATE_PIPE_INSTANCE = 0x0004
260    FILE_READ_EA              = 0x0008
261    FILE_WRITE_EA             = 0x0010
262    FILE_EXECUTE              = 0x0020
263    FILE_TRAVERSE             = 0x0020
264    FILE_DELETE_CHILD         = 0x0040
265    FILE_READ_ATTRIBUTES      = 0x0080
266    FILE_WRITE_ATTRIBUTES     = 0x0100
267    DELETE                    = 0x00010000
268    READ_CONTROL              = 0x00020000
269    WRITE_DAC                 = 0x00040000
270    WRITE_OWNER               = 0x00080000
271    SYNCHRONIZE               = 0x00100000
272    STANDARD_RIGHTS_ALL       = 0x001F0000
273
274    filemask = ldm & STANDARD_RIGHTS_ALL
275
276    if (ldm & RIGHT_DS_READ_PROPERTY) and (ldm & RIGHT_DS_LIST_CONTENTS):
277        filemask = filemask | (SYNCHRONIZE | FILE_LIST_DIRECTORY |
278                               FILE_READ_ATTRIBUTES | FILE_READ_EA |
279                               FILE_READ_DATA | FILE_EXECUTE)
280
281    if ldm & RIGHT_DS_WRITE_PROPERTY:
282        filemask = filemask | (SYNCHRONIZE | FILE_WRITE_DATA |
283                               FILE_APPEND_DATA | FILE_WRITE_EA |
284                               FILE_WRITE_ATTRIBUTES | FILE_ADD_FILE |
285                               FILE_ADD_SUBDIRECTORY)
286
287    if ldm & RIGHT_DS_CREATE_CHILD:
288        filemask = filemask | (FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE)
289
290    if ldm & RIGHT_DS_DELETE_CHILD:
291        filemask = filemask | FILE_DELETE_CHILD
292
293    return filemask
294
295
296def dsacl2fsacl(dssddl, sid, as_sddl=True):
297    """
298
299    This function takes an the SDDL representation of a DS
300    ACL and return the SDDL representation of this ACL adapted
301    for files. It's used for Policy object provision
302    """
303    ref = security.descriptor.from_sddl(dssddl, sid)
304    fdescr = security.descriptor()
305    fdescr.owner_sid = ref.owner_sid
306    fdescr.group_sid = ref.group_sid
307    fdescr.type = ref.type
308    fdescr.revision = ref.revision
309    aces = ref.dacl.aces
310    for i in range(0, len(aces)):
311        ace = aces[i]
312        if not ace.type & security.SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT and str(ace.trustee) != security.SID_BUILTIN_PREW2K:
313           #    if fdescr.type & security.SEC_DESC_DACL_AUTO_INHERITED:
314            ace.flags = ace.flags | security.SEC_ACE_FLAG_OBJECT_INHERIT | security.SEC_ACE_FLAG_CONTAINER_INHERIT
315            if str(ace.trustee) == security.SID_CREATOR_OWNER:
316                # For Creator/Owner the IO flag is set as this ACE has only a sense for child objects
317                ace.flags = ace.flags | security.SEC_ACE_FLAG_INHERIT_ONLY
318            ace.access_mask = ldapmask2filemask(ace.access_mask)
319            fdescr.dacl_add(ace)
320
321    if not as_sddl:
322        return fdescr
323
324    return fdescr.as_sddl(sid)
325
326
327class SMBHelper:
328    """
329    A wrapper class for SMB connection
330
331    smb_path: path with separator "\\" other than "/"
332    """
333
334    def __init__(self, smb_conn, dom_sid):
335        self.smb_conn = smb_conn
336        self.dom_sid = dom_sid
337
338    def get_acl(self, smb_path, as_sddl=False):
339        assert '/' not in smb_path
340
341        ntacl_sd = self.smb_conn.get_acl(
342            smb_path, SECURITY_SECINFO_FLAGS, SECURITY_SEC_FLAGS)
343
344        return ntacl_sd.as_sddl(self.dom_sid) if as_sddl else ntacl_sd
345
346    def list(self, smb_path=''):
347        """
348        List file and dir base names in smb_path without recursive.
349        """
350        assert '/' not in smb_path
351        return self.smb_conn.list(smb_path, attribs=SMB_FILE_ATTRIBUTE_FLAGS)
352
353    def is_dir(self, attrib):
354        """
355        Check whether the attrib value is a directory.
356
357        attrib is from list method.
358        """
359        return bool(attrib & libsmb.FILE_ATTRIBUTE_DIRECTORY)
360
361    def join(self, root, name):
362        """
363        Join path with '\\'
364        """
365        return root + '\\' + name if root else name
366
367    def loadfile(self, smb_path):
368        assert '/' not in smb_path
369        return self.smb_conn.loadfile(smb_path)
370
371    def create_tree(self, tree, smb_path=''):
372        """
373        Create files as defined in tree
374        """
375        for name, content in tree.items():
376            fullname = self.join(smb_path, name)
377            if isinstance(content, dict):  # a dir
378                if not self.smb_conn.chkpath(fullname):
379                    self.smb_conn.mkdir(fullname)
380                self.create_tree(content, smb_path=fullname)
381            else:  # a file
382                self.smb_conn.savefile(fullname, content)
383
384    def get_tree(self, smb_path=''):
385        """
386        Get the tree structure via smb conn
387
388        self.smb_conn.list example:
389
390        [
391          {
392            'attrib': 16,
393            'mtime': 1528848309,
394            'name': 'dir1',
395            'short_name': 'dir1',
396            'size': 0L
397          }, {
398            'attrib': 32,
399            'mtime': 1528848309,
400            'name': 'file0.txt',
401            'short_name': 'file0.txt',
402            'size': 10L
403          }
404        ]
405        """
406        tree = {}
407        for item in self.list(smb_path):
408            name = item['name']
409            fullname = self.join(smb_path, name)
410            if self.is_dir(item['attrib']):
411                tree[name] = self.get_tree(smb_path=fullname)
412            else:
413                tree[name] = self.loadfile(fullname)
414        return tree
415
416    def get_ntacls(self, smb_path=''):
417        """
418        Get ntacl for each file and dir via smb conn
419        """
420        ntacls = {}
421        for item in self.list(smb_path):
422            name = item['name']
423            fullname = self.join(smb_path, name)
424            if self.is_dir(item['attrib']):
425                ntacls.update(self.get_ntacls(smb_path=fullname))
426            else:
427                ntacl_sd = self.get_acl(fullname)
428                ntacls[fullname] = ntacl_sd.as_sddl(self.dom_sid)
429        return ntacls
430
431    def delete_tree(self):
432        for item in self.list():
433            name = item['name']
434            if self.is_dir(item['attrib']):
435                self.smb_conn.deltree(name)
436            else:
437                self.smb_conn.unlink(name)
438
439
440class NtaclsHelper:
441
442    def __init__(self, service, smb_conf_path, dom_sid):
443        self.service = service
444        self.dom_sid = dom_sid
445
446        # this is important to help smbd find services.
447        self.lp = s3param.get_context()
448        self.lp.load(smb_conf_path)
449
450        self.use_ntvfs = "smb" in self.lp.get("server services")
451
452    def getntacl(self, path, session_info, as_sddl=False, direct_db_access=None):
453        if direct_db_access is None:
454            direct_db_access = self.use_ntvfs
455
456        ntacl_sd = getntacl(
457            self.lp, path, session_info,
458            direct_db_access=direct_db_access,
459            service=self.service)
460
461        return ntacl_sd.as_sddl(self.dom_sid) if as_sddl else ntacl_sd
462
463    def setntacl(self, path, ntacl_sd, session_info):
464        # ntacl_sd can be obj or str
465        return setntacl(self.lp, path, ntacl_sd, self.dom_sid, session_info,
466                        use_ntvfs=self.use_ntvfs)
467
468
469def _create_ntacl_file(dst, ntacl_sddl_str):
470    with open(dst + '.NTACL', 'w') as f:
471        f.write(ntacl_sddl_str)
472
473
474def _read_ntacl_file(src):
475    ntacl_file = src + '.NTACL'
476
477    if not os.path.exists(ntacl_file):
478        return None
479
480    with open(ntacl_file, 'r') as f:
481        return f.read()
482
483
484def backup_online(smb_conn, dest_tarfile_path, dom_sid):
485    """
486    Backup all files and dirs with ntacl for the serive behind smb_conn.
487
488    1. Create a temp dir as container dir
489    2. Backup all files with dir structure into container dir
490    3. Generate file.NTACL files for each file and dir in contianer dir
491    4. Create a tar file from container dir(without top level folder)
492    5. Delete contianer dir
493    """
494
495    logger = get_samba_logger()
496
497    if isinstance(dom_sid, str):
498        dom_sid = security.dom_sid(dom_sid)
499
500    smb_helper = SMBHelper(smb_conn, dom_sid)
501
502    remotedir = ''  # root dir
503
504    localdir = tempfile.mkdtemp()
505
506    r_dirs = [remotedir]
507    l_dirs = [localdir]
508
509    while r_dirs:
510        r_dir = r_dirs.pop()
511        l_dir = l_dirs.pop()
512
513        for e in smb_helper.list(smb_path=r_dir):
514            r_name = smb_helper.join(r_dir, e['name'])
515            l_name = os.path.join(l_dir, e['name'])
516
517            if smb_helper.is_dir(e['attrib']):
518                r_dirs.append(r_name)
519                l_dirs.append(l_name)
520                os.mkdir(l_name)
521            else:
522                data = smb_helper.loadfile(r_name)
523                with open(l_name, 'wb') as f:
524                    f.write(data)
525
526            # get ntacl for this entry and save alongside
527            try:
528                ntacl_sddl_str = smb_helper.get_acl(r_name, as_sddl=True)
529                _create_ntacl_file(l_name, ntacl_sddl_str)
530            except NTSTATUSError as e:
531                logger.error('Failed to get the ntacl for %s: %s' % \
532                             (r_name, e.args[1]))
533                logger.warning('The permissions for %s may not be' % r_name +
534                               ' restored correctly')
535
536    with tarfile.open(name=dest_tarfile_path, mode='w:gz') as tar:
537        for name in os.listdir(localdir):
538            path = os.path.join(localdir, name)
539            tar.add(path, arcname=name)
540
541    shutil.rmtree(localdir)
542
543
544def backup_offline(src_service_path, dest_tarfile_path, samdb_conn, smb_conf_path):
545    """
546    Backup files and ntacls to a tarfile for a service
547    """
548    service = src_service_path.rstrip('/').rsplit('/', 1)[-1]
549    tempdir = tempfile.mkdtemp()
550    session_info = system_session_unix()
551
552    dom_sid_str = samdb_conn.get_domain_sid()
553    dom_sid = security.dom_sid(dom_sid_str)
554
555    ntacls_helper = NtaclsHelper(service, smb_conf_path, dom_sid)
556
557    for dirpath, dirnames, filenames in os.walk(src_service_path):
558        # each dir only cares about its direct children
559        rel_dirpath = os.path.relpath(dirpath, start=src_service_path)
560        dst_dirpath = os.path.join(tempdir, rel_dirpath)
561
562        # create sub dirs and NTACL file
563        for dirname in dirnames:
564            src = os.path.join(dirpath, dirname)
565            dst = os.path.join(dst_dirpath, dirname)
566            # mkdir with metadata
567            smbd.mkdir(dst, session_info, service)
568            ntacl_sddl_str = ntacls_helper.getntacl(src, session_info, as_sddl=True)
569            _create_ntacl_file(dst, ntacl_sddl_str)
570
571        # create files and NTACL file, then copy data
572        for filename in filenames:
573            src = os.path.join(dirpath, filename)
574            dst = os.path.join(dst_dirpath, filename)
575            # create an empty file with metadata
576            smbd.create_file(dst, session_info, service)
577            ntacl_sddl_str = ntacls_helper.getntacl(src, session_info, as_sddl=True)
578            _create_ntacl_file(dst, ntacl_sddl_str)
579
580            # now put data in
581            with open(src, 'rb') as src_file:
582                data = src_file.read()
583                with open(dst, 'wb') as dst_file:
584                    dst_file.write(data)
585
586    # add all files in tempdir to tarfile without a top folder
587    with tarfile.open(name=dest_tarfile_path, mode='w:gz') as tar:
588        for name in os.listdir(tempdir):
589            path = os.path.join(tempdir, name)
590            tar.add(path, arcname=name)
591
592    shutil.rmtree(tempdir)
593
594
595def backup_restore(src_tarfile_path, dst_service_path, samdb_conn, smb_conf_path):
596    """
597    Restore files and ntacls from a tarfile to a service
598    """
599    logger = get_samba_logger()
600    service = dst_service_path.rstrip('/').rsplit('/', 1)[-1]
601    tempdir = tempfile.mkdtemp()  # src files
602
603    dom_sid_str = samdb_conn.get_domain_sid()
604    dom_sid = security.dom_sid(dom_sid_str)
605
606    ntacls_helper = NtaclsHelper(service, smb_conf_path, dom_sid)
607    session_info = system_session_unix()
608
609    with tarfile.open(src_tarfile_path) as f:
610        f.extractall(path=tempdir)
611        # e.g.: /tmp/tmpRNystY/{dir1,dir1.NTACL,...file1,file1.NTACL}
612
613    for dirpath, dirnames, filenames in os.walk(tempdir):
614        rel_dirpath = os.path.relpath(dirpath, start=tempdir)
615        dst_dirpath = os.path.normpath(
616            os.path.join(dst_service_path, rel_dirpath))
617
618        for dirname in dirnames:
619            if not dirname.endswith('.NTACL'):
620                src = os.path.join(dirpath, dirname)
621                dst = os.path.join(dst_dirpath, dirname)
622                if not os.path.isdir(dst):
623                    # dst must be absolute path for smbd API
624                    smbd.mkdir(dst, session_info, service)
625
626                ntacl_sddl_str = _read_ntacl_file(src)
627                if ntacl_sddl_str:
628                    ntacls_helper.setntacl(dst, ntacl_sddl_str, session_info)
629                else:
630                    logger.warning(
631                        'Failed to restore ntacl for directory %s.' % dst
632                        + ' Please check the permissions are correct')
633
634        for filename in filenames:
635            if not filename.endswith('.NTACL'):
636                src = os.path.join(dirpath, filename)
637                dst = os.path.join(dst_dirpath, filename)
638                if not os.path.isfile(dst):
639                    # dst must be absolute path for smbd API
640                    smbd.create_file(dst, session_info, service)
641
642                ntacl_sddl_str = _read_ntacl_file(src)
643                if ntacl_sddl_str:
644                    ntacls_helper.setntacl(dst, ntacl_sddl_str, session_info)
645                else:
646                    logger.warning('Failed to restore ntacl for file %s.' % dst
647                                 + ' Please check the permissions are correct')
648
649                # now put data in
650                with open(src, 'rb') as src_file:
651                    data = src_file.read()
652                    with open(dst, 'wb') as dst_file:
653                        dst_file.write(data)
654
655    shutil.rmtree(tempdir)
656