#!/usr/bin/env python # $Id: validatejob,v 1.11 2008/02/20 19:40:20 gveldman Exp $ ### # Load any modules we'll need ### import sys import string import os import pwd ### # Set some variables we'll use later ### job_perm_file = "/usr/local/etc/job_perm" fail_closed = 1 # fail open or closed. default is closed. sanity_check = 1 # do some regex-based sanity checks/fixes on inputs invalid_users = ["root", "daemon", "globus"] # special system/globus users if sanity_check: import re ### # Handle failures and determine our exit status ### def do_return(retval): if fail_closed: sys.exit(retval) else: sys.exit(0) ### # Determine if a given uid is the owner of a given path ### def is_owner(path, uid): # Only check ownership on files we can stat. try: stat_array = os.stat(path) stat_array_l = os.lstat(path) except OSError, e: return 0 if stat_array[4] == uid or stat_array_l[4] == uid: return 1 else: return 0 ### # This script is designed to be run from globus-job-manager-script.pl, # which means we should always have at least one arguement (the name # of the program the user is trying to run) to check. However, make # sure that's the case, since we'll reference sys.argv[1] later. ### if len(sys.argv) < 2: do_return(2) ### # Determine who we are and make sure we're not a special user. ### process_uid = os.getuid() try: process_username = pwd.getpwuid(process_uid)[0] except KeyError, e: do_return(5) if process_username in invalid_users: do_return(6) ### # Read in and process the list of community accounts and allowed paths ### try: FH = open(job_perm_file, "r") except IOError, e: do_return(3) Line = FH.readline() while Line: Line = string.strip(Line) if len(Line) == 0: # Ignore empty lines Line = FH.readline() continue if Line[0] == "#": # Ignore comment lines Line = FH.readline() continue Line_array = string.split(Line, None, 1) ### # The job_perm file should always be of the form # "account colon:seperated:path", which splits into an array of # two elements. Make sure this is the case. ### if len(Line_array) != 2: FH.close() do_return(4) account_name = string.strip(Line_array[0], "\"\'" + string.whitespace) account_path = string.strip(Line_array[1], "\"\':" + string.whitespace) if account_name == process_username: if sanity_check: account_path = re.sub(":{2,}", ":", account_path) account_path = re.sub("/{2,}", "/", account_path) account_path = re.sub("/($|:)", "\\1", account_path) account_path = re.sub("(^|:)([^/])", "\\1/\\2", account_path) if account_path != "": ### # Get the requested program and find the path. Note that we # intentionally don't sanitize the user input here because it # is only used for a string comparison and not passed to a # command. If someone malicious is trying to hide something # in the path, we want it to stay in so the string comparison # below fails and they get denied. ### job_prog_fullpath = sys.argv[1] if sanity_check: job_prog_fullpath = re.sub("/{2,}", "/", job_prog_fullpath) job_prog_fullpath = re.sub("/$", "", job_prog_fullpath) job_prog_array = string.split(job_prog_fullpath, "/") job_prog_dir = string.join(job_prog_array[0:(len(job_prog_array) - 1)], "/") ### # Validate the path against what's allowed in the list. ### command_forbidden = 1 for test_dir in string.split(account_path, ":"): if job_prog_dir == test_dir: command_forbidden = 0 ### # Check the requested executable and parent directories # for write perms or ownership. ### for i in xrange(len(job_prog_array)): cur_dir = string.join(job_prog_array[0:(i + 1)], "/") if os.access(cur_dir, os.W_OK) \ or is_owner(cur_dir, process_uid): command_forbidden = 1 # Return thumbs up or thumbs down FH.close() sys.exit(command_forbidden) else: # No commands are allowed FH.close() sys.exit(1) Line = FH.readline() FH.close() ### # If we reach this, we've made it through the whole file without finding # our username, so we're not a community account. Allow all commands. ### sys.exit(0)