rdiff-backup-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Rdiff-backup-commits] Changes to rdiff-backup/rdiff_backup/Security.py


From: Ben Escoto
Subject: [Rdiff-backup-commits] Changes to rdiff-backup/rdiff_backup/Security.py [r1-0]
Date: Fri, 21 Oct 2005 22:43:57 -0400

Index: rdiff-backup/rdiff_backup/Security.py
diff -u /dev/null rdiff-backup/rdiff_backup/Security.py:1.25.2.1
--- /dev/null   Sat Oct 22 02:43:57 2005
+++ rdiff-backup/rdiff_backup/Security.py       Sat Oct 22 02:43:56 2005
@@ -0,0 +1,231 @@
+# Copyright 2002 Ben Escoto
+#
+# This file is part of rdiff-backup.
+#
+# rdiff-backup is free software; you can redistribute it and/or modify
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# rdiff-backup is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with rdiff-backup; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+
+"""Functions to make sure remote requests are kosher"""
+
+import sys, tempfile, types
+import Globals, Main, rpath, log
+
+class Violation(Exception):
+       """Exception that indicates an improper request has been received"""
+       pass
+
+
+# This will store the list of functions that will be honored from
+# remote connections.
+allowed_requests = None
+
+# This stores the list of global variables that the client can not
+# set on the server.
+disallowed_server_globals = ["server", "security_level", "restrict_path"]
+
+# Some common file commands we may want to check to make sure they are
+# in the right directory.  Any commands accessing files that could be
+# added to allowed_requests must be here.
+#
+# The keys are files request, the value is the index of the argument
+# taking a file.
+file_requests = {'os.listdir':0, 'C.make_file_dict':0, 'os.chmod':0,
+                                'os.chown':0, 'os.remove':0, 'os.removedirs':0,
+                                'os.rename':0, 'os.renames':0, 'os.rmdir':0, 
'os.unlink':0,
+                                'os.utime':0, 'os.lchown':0, 'os.link':1, 
'os.symlink':1,
+                                'os.mkdir':0, 'os.makedirs':0}
+                                
+
+def initialize(action, cmdpairs):
+       """Initialize allowable request list and chroot"""
+       global allowed_requests
+       set_security_level(action, cmdpairs)
+       set_allowed_requests(Globals.security_level)
+
+def set_security_level(action, cmdpairs):
+       """If running client, set security level and restrict_path
+
+       To find these settings, we must look at the action to see what is
+       supposed to happen, and then look at the cmdpairs to see what end
+       the client is on.
+
+       """
+       def islocal(cmdpair): return not cmdpair[0]
+       def bothlocal(cp1, cp2): return islocal(cp1) and islocal(cp2)
+       def bothremote(cp1, cp2): return not islocal(cp1) and not islocal(cp2)
+       def getpath(cmdpair): return cmdpair[1]
+
+       if Globals.server: return
+       cp1 = cmdpairs[0]
+       if len(cmdpairs) > 1: cp2 = cmdpairs[1]
+       else: cp2 = cp1
+
+       if action == "backup" or action == "check-destination-dir":
+               if bothlocal(cp1, cp2) or bothremote(cp1, cp2):
+                       sec_level = "minimal"
+                       rdir = tempfile.gettempdir()
+               elif islocal(cp1):
+                       sec_level = "read-only"
+                       rdir = getpath(cp1)
+               else:
+                       assert islocal(cp2)
+                       sec_level = "update-only"
+                       rdir = getpath(cp2)
+       elif action == "restore" or action == "restore-as-of":
+               if len(cmdpairs) == 1 or bothlocal(cp1, cp2) or bothremote(cp1, 
cp2):
+                       sec_level = "minimal"
+                       rdir = tempfile.gettempdir()
+               elif islocal(cp1):
+                       sec_level = "read-only"
+                       
Main.restore_set_root(rpath.RPath(Globals.local_connection,
+                                                                               
          getpath(cp1)))
+                       if Main.restore_root: rdir = Main.restore_root.path
+                       else: log.Log.FatalError("Invalid restore directory")
+               else:
+                       assert islocal(cp2)
+                       sec_level = "all"
+                       rdir = getpath(cp2)
+       elif action == "mirror":
+               if bothlocal(cp1, cp2) or bothremote(cp1, cp2):
+                       sec_level = "minimal"
+                       rdir = tempfile.gettempdir()
+               elif islocal(cp1):
+                       sec_level = "read-only"
+                       rdir = getpath(cp1)
+               else:
+                       assert islocal(cp2)
+                       sec_level = "all"
+                       rdir = getpath(cp2)
+       elif action in ["test-server", "list-increments", 
'list-increment-sizes',
+                                        "list-at-time", "list-changed-since",
+                                        "calculate-average", 
"remove-older-than", "compare"]:
+               sec_level = "minimal"
+               rdir = tempfile.gettempdir()
+       else: assert 0, "Unknown action %s" % action
+
+       Globals.security_level = sec_level
+       Globals.restrict_path = rpath.RPath(Globals.local_connection,
+                                                                               
rdir).normalize().path
+
+def set_allowed_requests(sec_level):
+       """Set the allowed requests list using the security level"""
+       global allowed_requests
+       l = ["VirtualFile.readfromid", "VirtualFile.closebyid",
+                "Globals.get", "Globals.is_not_None", "Globals.get_dict_val",
+                "log.Log.open_logfile_allconn", 
"log.Log.close_logfile_allconn",
+                "Log.log_to_file", "FilenameMapping.set_init_quote_vals_local",
+                "SetConnections.add_redirected_conn", "RedirectedRun",
+                "sys.stdout.write", "robust.install_signal_handlers"]
+       if (sec_level == "read-only" or sec_level == "update-only" or
+               sec_level == "all"):
+               l.extend(["C.make_file_dict", "os.listdir", "rpath.ea_get",
+                                 "rpath.acl_get", "rpath.setdata_local",
+                                 "log.Log.log_to_file", "os.getuid", 
"Time.setcurtime_local",
+                                 "rpath.gzip_open_local_read", 
"rpath.open_local_read",
+                                 "Hardlink.initialize_dictionaries", 
"user_group.uid2uname",
+                                 "user_group.gid2gname"])
+       if sec_level == "read-only" or sec_level == "all":
+               l.extend(["fs_abilities.get_fsabilities_readonly",
+                                 "fs_abilities.get_fsabilities_restoresource",
+                                 
"restore.MirrorStruct.set_mirror_and_rest_times",
+                                 "restore.MirrorStruct.set_mirror_select",
+                                 "restore.MirrorStruct.initialize_rf_cache",
+                                 "restore.MirrorStruct.close_rf_cache",
+                                 "restore.MirrorStruct.get_diffs",
+                                 "restore.ListChangedSince",
+                                 "restore.ListAtTime",
+                                 "backup.SourceStruct.get_source_select",
+                                 "backup.SourceStruct.set_source_select",
+                                 "backup.SourceStruct.get_diffs"])
+       if sec_level == "update-only" or sec_level == "all":
+               l.extend(["log.Log.open_logfile_local", 
"log.Log.close_logfile_local",
+                                 "log.ErrorLog.open", "log.ErrorLog.isopen",
+                                 "log.ErrorLog.close",
+                                 "backup.DestinationStruct.set_rorp_cache",
+                                 "backup.DestinationStruct.get_sigs",          
                 
+                                 
"backup.DestinationStruct.patch_and_increment",
+                                 "Main.backup_touch_curmirror_local",
+                                 "Main.backup_remove_curmirror_local",
+                                 "Globals.ITRB.increment_stat",
+                                 "statistics.record_error",
+                                 "log.ErrorLog.write_if_open",
+                                 "fs_abilities.get_fsabilities_readwrite"])
+       if sec_level == "all":
+               l.extend(["os.mkdir", "os.chown", "os.lchown", "os.rename",
+                                 "os.unlink", "os.remove", "os.chmod",
+                                 "backup.DestinationStruct.patch",
+                                 "restore.TargetStruct.get_initial_iter",
+                                 "restore.TargetStruct.patch",
+                                 "restore.TargetStruct.set_target_select",
+                                 "regress.Regress", 
"manage.delete_earlier_than_local"])
+       if Globals.server:
+               l.extend(["SetConnections.init_connection_remote",
+                                 "log.Log.setverbosity", 
"log.Log.setterm_verbosity",
+                                 "Time.setprevtime_local", 
"Globals.postset_regexp_local",
+                                 "Globals.set_select", 
"backup.SourceStruct.set_session_info",
+                                 "backup.DestinationStruct.set_session_info",
+                                 "user_group.init_user_mapping",
+                                 "user_group.init_group_mapping"])
+       allowed_requests = {}
+       for req in l: allowed_requests[req] = None
+
+def raise_violation(request, arglist):
+       """Raise a security violation about given request"""
+       raise Violation("\nWarning Security Violation!\n"
+                                       "Bad request for function: %s\n"
+                                       "with arguments: %s\n" % 
(request.function_string,
+                                                                               
          arglist))
+
+def vet_request(request, arglist):
+       """Examine request for security violations"""
+       #if Globals.server: sys.stderr.write(str(request) + "\n")
+       security_level = Globals.security_level
+       if Globals.restrict_path:
+               for arg in arglist:
+                       if isinstance(arg, rpath.RPath): vet_rpath(arg)
+               if request.function_string in file_requests:
+                       vet_filename(request, arglist)
+       if security_level == "override": return
+       if request.function_string in allowed_requests: return
+       if request.function_string in ("Globals.set", "Globals.set_local"):
+               if Globals.server and arglist[0] not in 
disallowed_server_globals:
+                       return
+       raise_violation(request, arglist)
+
+def vet_rpath(rpath):
+       """Require rpath not to step outside retricted directory"""
+       if Globals.restrict_path and rpath.conn is Globals.local_connection:
+               normalized, restrict = rpath.normalize().path, 
Globals.restrict_path
+               if restrict == "/": return
+               components = normalized.split("/")
+               # 3 cases for restricted dir /usr/foo:  /var, /usr/foobar, 
/usr/foo/..
+               if (not normalized.startswith(restrict) or
+                       (len(normalized) > len(restrict) and
+                        normalized[len(restrict)] != "/") or
+                       ".." in components):
+                       raise Violation("\nWarning Security Violation!\n"
+                                                       "Request to handle path 
%s\n"
+                                                       "which doesn't appear 
to be within "
+                                                       "restrict path %s.\n" % 
(normalized, restrict))
+
+def vet_filename(request, arglist):
+       """Check to see if file operation is within the restrict_path"""
+       i = file_requests[request.function_string]
+       if len(arglist) <= i: raise_violation(request, arglist)
+       filename = arglist[i]
+       if type(filename) is not types.StringType:
+               raise_violation(request, arglist)
+       vet_rpath(rpath.RPath(Globals.local_connection, filename))
+




reply via email to

[Prev in Thread] Current Thread [Next in Thread]