void VMUniverseMgr::killVM(const char *matchstring) { if ( !matchstring ) { return; } if( !m_vm_type.Length() || !m_vmgahp_server.Length() ) { return; } // vmgahp is daemonCore, so we need to add -f -t options of daemonCore. // Then, try to execute vmgahp with // vmtype <vmtype> match <string>" ArgList systemcmd; systemcmd.AppendArg(m_vmgahp_server); systemcmd.AppendArg("-f"); char *gahp_log_file = param("VM_GAHP_LOG"); if( gahp_log_file ) { free(gahp_log_file); }else { systemcmd.AppendArg("-t"); } systemcmd.AppendArg("-M"); systemcmd.AppendArg(VMGAHP_KILL_MODE); systemcmd.AppendArg("vmtype"); systemcmd.AppendArg(m_vm_type); systemcmd.AppendArg("match"); systemcmd.AppendArg(matchstring); #if !defined(WIN32) if( can_switch_ids() ) { MyString tmp_str; tmp_str.formatstr("%d", (int)get_condor_uid()); SetEnv("VMGAHP_USER_UID", tmp_str.Value()); } #endif // execute vmgahp with root privilege priv_state priv = set_root_priv(); int ret = my_system(systemcmd); // restore privilege set_priv(priv); if( ret == 0 ) { dprintf( D_ALWAYS, "VMUniverseMgr::killVM() is called with " "'%s'\n", matchstring ); }else { dprintf( D_ALWAYS, "VMUniverseMgr::killVM() failed!\n"); } return; }
bool SharedPortEndpoint::ChownSocket(priv_state priv) { #ifndef HAVE_SHARED_PORT return false; #elif WIN32 return false; #elif HAVE_SCM_RIGHTS_PASSFD if( !can_switch_ids() ) { return true; } switch( priv ) { case PRIV_ROOT: case PRIV_CONDOR: case PRIV_CONDOR_FINAL: case PRIV_UNKNOWN: // Nothing needs to be done in this case, because the named // socket was created with condor ownership (we assume). return true; case PRIV_FILE_OWNER: case _priv_state_threshold: // these don't really make sense, but include them so // the compiler can warn about priv states not covered return true; case PRIV_USER: case PRIV_USER_FINAL: { priv_state orig_priv = set_root_priv(); int rc = fchown( m_listener_sock.get_file_desc(),get_user_uid(),get_user_gid() ); if( rc != 0 ) { dprintf(D_ALWAYS,"SharedPortEndpoint: failed to chown %s to %d:%d: %s.\n", m_full_name.Value(), get_user_uid(), get_user_gid(), strerror(errno)); } set_priv( orig_priv ); return rc == 0; } } EXCEPT("Unexpected priv state in SharedPortEndpoint(%d)",(int)priv); return false; #else #error HAVE_SHARED_PORT is defined, but no method for passing fds is enabled. #endif }
static int set_user_ids_implementation( uid_t uid, gid_t gid, const char *username, int is_quiet ) { if( uid == 0 || gid == 0 ) { // NOTE: we want this dprintf() even if we're in quiet // mode, since we should *never* be allowing this. dprintf( D_ALWAYS, "ERROR: Attempt to initialize user_priv " "with root privileges rejected\n" ); return FALSE; } // So if we are not root, trying to use any user id is bogus // since the OS will disallow it. So if we are not running as // root, may as well just set the user id to be the real id. // For setuid-root // -jaeyoung 05/22/07 //if ( get_my_uid() != ROOT ) { if ( !can_switch_ids() ) { uid = get_my_uid(); gid = get_my_gid(); } if( UserIdsInited && UserUid != uid && !is_quiet ) { dprintf( D_ALWAYS, "warning: setting UserUid to %d, was %d previously\n", uid, UserUid ); } UserUid = uid; UserGid = gid; UserIdsInited = TRUE; // find the user login name for this uid. note we should not // EXCEPT or log an error if we do not find it; it is OK for the // user not to be in the passwd file for a so-called SOFT_UID_DOMAIN. if( UserName ) { free( UserName ); } if ( !username ) { if ( !(pcache()->get_user_name( UserUid, UserName )) ) { UserName = NULL; } } else { UserName = strdup( username ); } return TRUE; }
void display_priv_log(void) { int i, idx; if (can_switch_ids()) { dprintf(D_ALWAYS, "running as root; privilege switching in effect\n"); } else { dprintf(D_ALWAYS, "running as non-root; no privilege switching\n"); } for (i=0; i < ph_count && i < HISTORY_LENGTH; i++) { idx = (ph_head-i-1+HISTORY_LENGTH)%HISTORY_LENGTH; dprintf(D_ALWAYS, "--> %s at %s:%d %s", priv_state_name[priv_history[idx].priv], priv_history[idx].file, priv_history[idx].line, ctime(&priv_history[idx].timestamp)); } }
int init_user_ids_implementation( const char username[], int is_quiet ) { int scm; uid_t usr_uid; gid_t usr_gid; // So if we are not root, trying to use any user id is bogus // since the OS will disallow it. So if we are not running as // root, may as well just set the user id to be the real id. // For setuid-root // -jaeyoung 05/22/07 //if ( get_my_uid() != ROOT ) { if ( !can_switch_ids() ) { return set_user_ids_implementation( get_my_uid(), get_my_gid(), NULL, is_quiet ); } /* ** N.B. if we are using the yellow pages, system calls which are ** not supported by either remote system calls or file descriptor ** mapping will occur. Thus we must be in LOCAL/UNRECORDED mode here. */ scm = SetSyscalls( SYS_LOCAL | SYS_UNRECORDED ); if( ! strcasecmp(username, "nobody") ) { // There's so much special logic for user nobody that it's // all in a seperate function now. return init_nobody_ids( is_quiet ); } if( !(pcache()->get_user_uid(username, usr_uid)) || !(pcache()->get_user_gid(username, usr_gid)) ) { if( ! is_quiet ) { dprintf( D_ALWAYS, "%s not in passwd file\n", username ); } (void)endpwent(); (void)SetSyscalls( scm ); return FALSE; } (void)endpwent(); (void)SetSyscalls( scm ); return set_user_ids_implementation( usr_uid, usr_gid, username, is_quiet ); }
bool SharedPortEndpoint::UseSharedPort(MyString *why_not,bool already_open) { #ifndef HAVE_SHARED_PORT if( why_not ) { *why_not = "shared ports not supported on this platform"; } return false; #else // The shared port server itself should not try to operate as // a shared point endpoint, since it needs to be the one // daemon with its own port. // This subsys check is appropriate for when we are inside of // the daemon in question, not when we are the master trying // to decide whether to create a shared port for our child. // In the latter case, other methods are used to determine // that a shared port should not be used. bool never_use_shared_port = get_mySubSystem()->isType(SUBSYSTEM_TYPE_SHARED_PORT); if( never_use_shared_port ) { if( why_not ) { *why_not = "this daemon requires its own port"; } return false; } if( !param_boolean("USE_SHARED_PORT",false) ) { if( why_not ) { *why_not = "USE_SHARED_PORT=false"; } return false; } if( already_open ) { // skip following tests of writability of daemon socket dir, // since we already have a socket (perhaps created for us by // our parent) return true; } #ifdef WIN32 return true; #endif if( can_switch_ids() ) { // If we are running as root, assume that we will be able to // write to the daemon socket dir (as condor). If we can't, // it is better to try and fail so that the admin will see // that something is broken. return true; } // If we can write to the daemon socket directory, we can use // the shared port service. Cache this result briefly so we // don't check access too often when spawning lots of children. // Invalidate the cache both forwards and backwards in time in // case of system clock jumps. static bool cached_result = false; static time_t cached_time = 0; time_t now = time(NULL); if( abs((int)now-(int)cached_time) > 10 || cached_time==0 || why_not ) { cached_time = now; std::string socket_dir; bool is_file_socket = true; #ifdef USE_ABSTRACT_DOMAIN_SOCKET is_file_socket = false; #endif if (!GetDaemonSocketDir(socket_dir)) { is_file_socket = true; if (!GetAltDaemonSocketDir(socket_dir)) { why_not->formatstr("No DAEMON_SOCKET_DIR is available.\n"); cached_result = false; return cached_result; } } if (!is_file_socket) { cached_result = true; return cached_result; } cached_result = access(socket_dir.c_str(),W_OK)==0; if( !cached_result && errno == ENOENT ) { // if socket_dir doesn't exist, see if we are allowed to // create it char *parent_dir = condor_dirname( socket_dir.c_str() ); if( parent_dir ) { cached_result = access(parent_dir,W_OK)==0; free( parent_dir ); } } if( !cached_result && why_not ) { why_not->formatstr("cannot write to %s: %s", socket_dir.c_str(), strerror(errno)); } } return cached_result; #endif }
bool canSwitchUid(void) { return can_switch_ids(); }
bool JobInfoCommunicator::initUserPrivWindows( void ) { // Win32 // taken origionally from OsProc::StartJob. Here we create the // user and initialize user_priv. // By default, assume execute login may be shared by other processes. setExecuteAccountIsDedicated( NULL ); // we support running the job as other users if the user // is specifed in the config file, and the account's password // is properly stored in our credential stash. char *name = NULL; char *domain = NULL; bool init_priv_succeeded = true; bool run_as_owner = allowRunAsOwner( false, false ); // TODO.. // Currently vmgahp for VMware VM universe can't run as user on Windows. // It seems like a bug of VMware. VMware command line tool such as "vmrun" // requires Administrator privilege. // So here we set name and domain with my_username and my_domainname // -jaeyoung 06/15/07 if( job_universe == CONDOR_UNIVERSE_VM ) { #if 0 // If "VM_UNIV_NOBODY_USER" is defined in Condor configuration file, // wee will use it. char *vm_jobs_as = param("VM_UNIV_NOBODY_USER"); if (vm_jobs_as) { getDomainAndName(vm_jobs_as, domain, name); /* * name and domain are now just pointers into vm_jobs_as * buffer. copy these values into their own buffer so we * deallocate below. */ if ( name ) { name = strdup(name); } if ( domain ) { domain = strdup(domain); } free(vm_jobs_as); } #endif MyString vm_type; job_ad->LookupString(ATTR_JOB_VM_TYPE, vm_type); if( strcasecmp(vm_type.Value(), CONDOR_VM_UNIVERSE_VMWARE) == MATCH ) { name = my_username(); domain = my_domainname(); } } if( !name ) { if ( run_as_owner ) { job_ad->LookupString(ATTR_OWNER,&name); job_ad->LookupString(ATTR_NT_DOMAIN,&domain); } } if ( !name ) { char slot_user[255]; MyString slotName = ""; slotName = Starter->getMySlotName(); slotName.upper_case(); sprintf(slot_user, "%s_USER", slotName); char *run_jobs_as = param(slot_user); if (run_jobs_as) { getDomainAndName(run_jobs_as, domain, name); /* * name and domain are now just pointers into run_jobs_as * buffer. copy these values into their own buffer so we * deallocate below. */ if ( name ) { name = strdup(name); } if ( domain ) { domain = strdup(domain); } free(run_jobs_as); } } if ( name ) { if (!init_user_ids(name, domain)) { dprintf(D_ALWAYS, "Could not initialize user_priv as \"%s\\%s\".\n" "\tMake sure this account's password is securely stored " "with condor_store_cred.\n", domain, name ); init_priv_succeeded = false; } else { MyString login_name; joinDomainAndName(name, domain, login_name); if( checkDedicatedExecuteAccounts( login_name.Value() ) ) { setExecuteAccountIsDedicated( login_name.Value() ); } } } else if ( !can_switch_ids() ) { char *u = my_username(); char *d = my_domainname(); if ( !init_user_ids(u, d) ) { // shouldn't happen - we always can get our own token dprintf(D_ALWAYS, "Could not initialize user_priv with our own token!\n"); init_priv_succeeded = false; } free(u); free(d); } else if( init_user_ids("nobody", ".") ) { // just init a new nobody user; dynuser handles the rest. // the "." means Local Machine to LogonUser setExecuteAccountIsDedicated( get_user_loginname() ); } else { dprintf( D_ALWAYS, "ERROR: Could not initialize user_priv " "as \"nobody\"\n" ); init_priv_succeeded = false; } if ( name ) free(name); if ( domain ) free(domain); user_priv_is_initialized = init_priv_succeeded; return init_priv_succeeded; }
bool VMUniverseMgr::testVMGahp(const char* gahppath, const char* vmtype) { m_needCheck = false; if( !m_starter_has_vmcode ) { return false; } if( !gahppath || !vmtype ) { return false; } #if defined(WIN32) // On Windows machine, the option that Starter log file includes // logs from vmgahp causes deadlock even if the option works well // on Linux machine. I guess that is due to Windows Pipes but // I don't know the exact reason. // Until the problem is solved, // this option will be disabled on Windows machine. char *need_log_file = param("VM_GAHP_LOG"); if( need_log_file ) { free(need_log_file); }else { dprintf( D_ALWAYS, "To support vm universe, '%s' must be defined " "in condor config file, which is a log file for vmgahp.\n", "VM_GAHP_LOG"); return false; } #endif // vmgahp is daemonCore, so we need to add -f -t options of daemonCore. // Then, try to execute vmgahp with // vmtype <vmtype>" // and grab the output as a ClassAd ArgList systemcmd; systemcmd.AppendArg(gahppath); systemcmd.AppendArg("-f"); char *gahp_log_file = param("VM_GAHP_LOG"); if( gahp_log_file ) { free(gahp_log_file); }else { systemcmd.AppendArg("-t"); } systemcmd.AppendArg("-M"); systemcmd.AppendArg(VMGAHP_TEST_MODE); systemcmd.AppendArg("vmtype"); systemcmd.AppendArg(vmtype); #if !defined(WIN32) if( can_switch_ids() ) { MyString tmp_str; tmp_str.formatstr("%d", (int)get_condor_uid()); SetEnv("VMGAHP_USER_UID", tmp_str.Value()); } #endif priv_state prev_priv; if( (strcasecmp(vmtype, CONDOR_VM_UNIVERSE_XEN) == MATCH) || (strcasecmp(vmtype, CONDOR_VM_UNIVERSE_KVM) == MATCH) ) { // Xen requires root privilege prev_priv = set_root_priv(); }else { prev_priv = set_condor_priv(); } FILE* fp = NULL; fp = my_popen(systemcmd, "r", FALSE ); set_priv(prev_priv); if( !fp ) { dprintf( D_ALWAYS, "Failed to execute %s, ignoring\n", gahppath ); return false; } bool read_something = false; char buf[2048]; m_vmgahp_info.Clear(); while( fgets(buf, 2048, fp) ) { if( !m_vmgahp_info.Insert(buf) ) { dprintf( D_ALWAYS, "Failed to insert \"%s\" into VMInfo, " "ignoring invalid parameter\n", buf ); continue; } read_something = true; } my_pclose( fp ); if( !read_something ) { MyString args_string; systemcmd.GetArgsStringForDisplay(&args_string,0); dprintf( D_ALWAYS, "Warning: '%s' did not produce any valid output.\n", args_string.Value()); if( (strcasecmp(vmtype, CONDOR_VM_UNIVERSE_XEN) == 0) ) { MyString err_msg; err_msg += "\n#######################################################\n"; err_msg += "##### Make sure the followings "; err_msg += "to use VM universe for Xen\n"; err_msg += "### - The owner of script progrm like "; err_msg += "'condor_vm_xen.sh' must be root\n"; err_msg += "### - The script program must be executable\n"; err_msg += "### - Other writable bit for the above files is "; err_msg += "not allowed.\n"; err_msg += "#########################################################\n"; dprintf( D_ALWAYS, "%s", err_msg.Value()); } else if( (strcasecmp(vmtype, CONDOR_VM_UNIVERSE_KVM) == 0)) { MyString err_msg; err_msg += "\n#######################################################\n"; err_msg += "##### Make sure the followings "; err_msg += "to use VM universe for KVM\n"; err_msg += "### - The owner of script progrm like "; err_msg += "'condor_vm_xen.sh' must be root\n"; err_msg += "### - The script program must be executable\n"; err_msg += "### - Other writable bit for the above files is "; err_msg += "not allowed.\n"; err_msg += "#########################################################\n"; dprintf( D_ALWAYS, "%s", err_msg.Value()); }else if( strcasecmp(vmtype, CONDOR_VM_UNIVERSE_VMWARE ) == 0 ) { MyString err_msg; MyString err_msg2; err_msg += "\n#######################################################\n"; err_msg += "##### Make sure the followings "; err_msg += "to use VM universe for VMware\n"; if( can_switch_ids() ) { // Condor runs as root err_msg += "### - The script program like 'condor_vm_vmware'"; err_msg += " must be readable for anybody.\n"; } err_msg += "### - Check the path of vmware-cmd, vmrun, and mkisofs "; err_msg += "in 'condor_vm_vmware\n'"; err_msg += "#########################################################\n"; dprintf( D_ALWAYS, "%s", err_msg.Value()); } return false; } // For debug printVMGahpInfo(D_ALWAYS); // Read vm_type MyString tmp_vmtype; if( m_vmgahp_info.LookupString( ATTR_VM_TYPE, tmp_vmtype) != 1 ) { dprintf( D_ALWAYS, "There is no %s in the output of vmgahp. " "So VM Universe will be disabled\n", ATTR_VM_TYPE); return false; } if( strcasecmp(tmp_vmtype.Value(), vmtype) != 0 ) { dprintf( D_ALWAYS, "The vmgahp(%s) doesn't support this vmtype(%s)\n", gahppath, vmtype); return false; } dprintf( D_ALWAYS, "VMType('%s') is supported\n", vmtype); // Read vm_memory if( m_vmgahp_info.LookupInteger(ATTR_VM_MEMORY, m_vm_max_memory) != 1 ) { dprintf( D_ALWAYS, "There is no %s in the output of vmgahp\n",ATTR_VM_MEMORY); return false; } if( m_vm_max_memory == 0 ) { dprintf( D_ALWAYS, "There is no sufficient memory for virtual machines\n"); return false; } dprintf( D_ALWAYS, "The maximum available memory for vm universe is " "set to %d MB\n", m_vm_max_memory); // Read vm_networking bool tmp_networking = false; MyString tmp_networking_types; m_vmgahp_info.LookupBool(ATTR_VM_NETWORKING, tmp_networking); if( tmp_networking ) { if( m_vmgahp_info.LookupString( ATTR_VM_NETWORKING_TYPES, tmp_networking_types) != 1 ) { tmp_networking = false; m_vmgahp_info.Assign(ATTR_VM_NETWORKING, false); } } m_vm_networking = param_boolean("VM_NETWORKING",false); if( m_vm_networking ) { if( !tmp_networking ) { dprintf( D_ALWAYS, "Even if VM_NETWORKING is TRUE in condor config," " VM_NETWORKING is disabled because vmgahp doesn't " "support VM_NETWORKING\n"); m_vm_networking = false; } } if( m_vm_networking == false ) { dprintf( D_ALWAYS, "VM networking is disabled\n"); }else { dprintf( D_ALWAYS, "VM networking is enabled\n"); dprintf( D_ALWAYS, "Supported networking types are %s\n", tmp_networking_types.Value()); } // Now, we received correct information from vmgahp m_vm_type = tmp_vmtype; m_vmgahp_server = gahppath; return true; }
static void check_execute_dir_perms( char const *exec_path ) { struct stat st; if (stat(exec_path, &st) < 0) { EXCEPT( "stat exec path (%s), errno: %d (%s)", exec_path, errno, strerror( errno ) ); } // in PrivSep mode, the EXECUTE directory must be trusted by // the PrivSep kernel. we can't determine this ourselves in general // (since the PrivSep Switchboard can be recompiled to trust // non-root users), so we'll have to be satisfied for now that we // could stat its path // if (privsep_enabled()) { return; } // the following logic sets up the new_mode variable, depending // on the execute dir's current perms. if new_mode is set non-zero, // it means we need to do a chmod // mode_t new_mode = 0; #if defined(WIN32) mode_t desired_mode = _S_IREAD | _S_IWRITE; if ((st.st_mode & desired_mode) != desired_mode) { new_mode = st.st_mode | desired_mode; } #else // we want to avoid having our execute directory world-writable // if possible. it's possible if the execute directory is owned // by condor and either: // - we're not switching UIDs. in this case, job sandbox dirs // will just be owned by the condor UID, so we just need // owner-writability // - there's no root squash on the execute dir (since then we // can do a mkdir as the condor UID then a chown to the job // owner UID) // // additionally, the GLEXEC_JOB feature requires world-writability // on the execute dir // bool glexec_job = param_boolean("GLEXEC_JOB", false); if ((st.st_uid == get_condor_uid()) && (!can_switch_ids() || not_root_squashed(exec_path)) && !glexec_job) { // do the chown unless the current mode is exactly 755 // if ((st.st_mode & 07777) != 0755) { new_mode = 0755; } } else { // do the chown if the mode doesn't already include 1777 // if ((st.st_mode & 01777) != 01777) { new_mode = 01777; } if (!glexec_job) { dprintf(D_ALWAYS, "WARNING: %s root-squashed or not condor-owned: " "requiring world-writability\n", exec_path); } } #endif // now do a chmod if needed // if (new_mode != 0) { dprintf(D_FULLDEBUG, "Changing permission on %s\n", exec_path); if (chmod(exec_path, new_mode) < 0) { EXCEPT( "chmod exec path (%s), errno: %d (%s)", exec_path, errno, strerror( errno ) ); } } }
bool VMGahpServer::startUp(Env *job_env, const char *workingdir, int nice_inc, FamilyInfo *family_info) { //check if we already have spawned a vmgahp server if( m_vmgahp_pid > 0 ) { //vmgahp is already running return true; } if( !m_job_ad ) { start_err_msg = "No JobAd in VMGahpServer::startUp()"; dprintf(D_ALWAYS,"%s\n", start_err_msg.Value()); return false; } MyString JobName; if( m_vmgahp_server.IsEmpty() ) { start_err_msg = "No path for vmgahp in VMGahpServer::startUp()"; dprintf(D_ALWAYS,"%s\n", start_err_msg.Value()); return false; } JobName = m_vmgahp_server; // Create two pairs of pipes which we will use to int stdin_pipefds[2]; int stdout_pipefds[2]; int stderr_pipefds[2]; if(!daemonCore->Create_Pipe(stdin_pipefds, true, // read end registerable false, // write end not registerable false, // read end blocking false // write end blocking )) { start_err_msg = "unable to create pipe to stdin of VM gahp"; dprintf(D_ALWAYS,"%s\n", start_err_msg.Value()); return false; } if(!daemonCore->Create_Pipe(stdout_pipefds, true, //read end registerable false, // write end not registerable false, // read end blocking false // write end blocking )) { // blocking read start_err_msg = "unable to create pipe to stdout of VM gahp"; dprintf(D_ALWAYS,"%s\n", start_err_msg.Value()); return false; } if( m_include_gahp_log ) { if(!daemonCore->Create_Pipe(stderr_pipefds, true, // read end registerable false, // write end not registerable true, // read end non-blocking true // write end non-blocking )) { // nonblocking read start_err_msg = "unable to create pipe to stderr of VM gahp"; dprintf(D_ALWAYS,"%s\n", start_err_msg.Value()); return false; } } int io_redirect[3]; io_redirect[0] = stdin_pipefds[0]; //stdin gets read side of in pipe io_redirect[1] = stdout_pipefds[1]; //stdout gets write side of out pipe if( m_include_gahp_log ) { io_redirect[2] = stderr_pipefds[1]; //stderr gets write side of err pipe } else { int null_fd = safe_open_wrapper_follow(NULL_FILE, O_WRONLY | O_APPEND, 0666); if( null_fd < 0 ) { start_err_msg = "unable to open null file for stderr of VM gahp"; dprintf(D_ALWAYS,"Failed to open '%s':%s (errno %d)\n", NULL_FILE, strerror(errno), errno); return false; } io_redirect[2] = null_fd; } // Set Arguments ArgList vmgahp_args; vmgahp_args.SetArgV1SyntaxToCurrentPlatform(); vmgahp_args.AppendArg(m_vmgahp_server.Value()); // Add daemonCore options vmgahp_args.AppendArg("-f"); if( m_include_gahp_log ) { vmgahp_args.AppendArg("-t"); } vmgahp_args.AppendArg("-M"); vmgahp_args.AppendArg(VMGAHP_STANDALONE_MODE); MyString args_string; vmgahp_args.GetArgsStringForDisplay(&args_string, 1); dprintf( D_ALWAYS, "About to exec %s %s\n", JobName.Value(), args_string.Value() ); #if !defined(WIN32) uid_t vmgahp_user_uid = (uid_t) -1; gid_t vmgahp_user_gid = (gid_t) -1; if( can_switch_ids() ) { // Condor runs as root vmgahp_user_uid = get_user_uid(); vmgahp_user_gid = get_user_gid(); } else if (Starter->condorPrivSepHelper() != NULL) { vmgahp_user_uid = Starter->condorPrivSepHelper()->get_uid(); char* user_name; if (!pcache()->get_user_name(vmgahp_user_uid, user_name)) { EXCEPT("unable to get user name for UID %u", vmgahp_user_uid); } if (!pcache()->get_user_ids(user_name, vmgahp_user_uid, vmgahp_user_gid)) { EXCEPT("unable to get GID for UID %u", vmgahp_user_uid); } free(user_name); } else { // vmgahp may have setuid-root (e.g. vmgahp for Xen) vmgahp_user_uid = get_condor_uid(); vmgahp_user_gid = get_condor_gid(); } // Setup vmgahp user uid/gid if( vmgahp_user_uid > 0 ) { if( vmgahp_user_gid <= 0 ) { vmgahp_user_gid = vmgahp_user_uid; } MyString tmp_str; tmp_str.sprintf("%d", (int)vmgahp_user_uid); job_env->SetEnv("VMGAHP_USER_UID", tmp_str.Value()); tmp_str.sprintf("%d", (int)vmgahp_user_gid); job_env->SetEnv("VMGAHP_USER_GID", tmp_str.Value()); } #endif job_env->SetEnv("VMGAHP_VMTYPE", m_vm_type.Value()); job_env->SetEnv("VMGAHP_WORKING_DIR", workingdir); // Grab the full environment back out of the Env object if(IsFulldebug(D_FULLDEBUG)) { MyString env_str; job_env->getDelimitedStringForDisplay(&env_str); dprintf(D_FULLDEBUG, "Env = %s\n", env_str.Value()); } priv_state vmgahp_priv = PRIV_ROOT; #if defined(WIN32) // TODO.. // Currently vmgahp for VMware VM universe can't run as user on Windows. // It seems like a bug of VMware. VMware command line tool such as "vmrun" // requires Administrator privilege. // -jaeyoung 06/15/07 if( strcasecmp(m_vm_type.Value(), CONDOR_VM_UNIVERSE_VMWARE ) == MATCH ) { vmgahp_priv = PRIV_UNKNOWN; } #endif m_vmgahp_pid = daemonCore->Create_Process( JobName.Value(), //Name of executable vmgahp_args, //Args vmgahp_priv, //Priv state 1, //id for our registered reaper FALSE, //do not want a command port job_env, //env workingdir, //cwd family_info, //family_info NULL, //network sockets to inherit io_redirect, //redirect stdin/out/err NULL, nice_inc ); //NOTE: Create_Process() saves the errno for us if it is an //"interesting" error. char const *create_process_error = NULL; if(m_vmgahp_pid == FALSE && errno) create_process_error = strerror(errno); // Now that the VMGAHP server is running, close the sides of // the pipes we gave away to the server, and stash the ones // we want to keep in an object data member. daemonCore->Close_Pipe(io_redirect[0]); daemonCore->Close_Pipe(io_redirect[1]); if( m_include_gahp_log ) { daemonCore->Close_Pipe(io_redirect[2]); } else { close(io_redirect[2]); } if ( m_vmgahp_pid == FALSE ) { m_vmgahp_pid = -1; start_err_msg = "Failed to start vm-gahp server"; dprintf(D_ALWAYS, "%s (%s)\n", start_err_msg.Value(), m_vmgahp_server.Value()); if(create_process_error) { MyString err_msg = "Failed to execute '"; err_msg += m_vmgahp_server.Value(), err_msg += "'"; if(!args_string.IsEmpty()) { err_msg += " with arguments "; err_msg += args_string.Value(); } err_msg += ": "; err_msg += create_process_error; dprintf(D_ALWAYS, "Failed to start vmgahp server (%s)\n", err_msg.Value()); } return false; } dprintf(D_ALWAYS, "VMGAHP server pid=%d\n", m_vmgahp_pid); m_vmgahp_writefd = stdin_pipefds[1]; m_vmgahp_readfd = stdout_pipefds[0]; if( m_include_gahp_log ) { m_vmgahp_errorfd = stderr_pipefds[0]; } // Now initialization is done m_is_initialized = true; // print initial stderr messages from vmgahp printSystemErrorMsg(); // Read the initial greeting from the vm-gahp, which is the version if( command_version() == false ) { start_err_msg = "Internal vmgahp server error"; dprintf(D_ALWAYS,"Failed to read vmgahp server version\n"); printSystemErrorMsg(); cleanup(); return false; } dprintf(D_FULLDEBUG,"VMGAHP server version: %s\n", m_vmgahp_version.Value()); // Now see what commands this server supports. if( command_commands() == false ) { start_err_msg = "Internal vmgahp server error"; dprintf(D_ALWAYS,"Failed to read supported commands from vmgahp server\n"); printSystemErrorMsg(); cleanup(); return false; } // Now see what virtual machine types this server supports if( command_support_vms() == false ) { start_err_msg = "Internal vmgahp server error"; dprintf(D_ALWAYS,"Failed to read supported vm types from vmgahp server\n"); printSystemErrorMsg(); cleanup(); return false; } int result = -1; if( m_include_gahp_log ) { result = daemonCore->Register_Pipe(m_vmgahp_errorfd, "m_vmgahp_errorfd", static_cast<PipeHandlercpp>(&VMGahpServer::err_pipe_ready), "VMGahpServer::err_pipe_ready",this); if( result == -1 ) { dprintf(D_ALWAYS,"Failed to register vmgahp stderr pipe\n"); if(m_stderr_tid != -1) { daemonCore->Cancel_Timer(m_stderr_tid); m_stderr_tid = -1; } m_stderr_tid = daemonCore->Register_Timer(2, 2, (TimerHandlercpp)&VMGahpServer::err_pipe_ready, "VMGahpServer::err_pipe_ready",this); if( m_stderr_tid == -1 ) { start_err_msg = "Internal vmgahp server error"; dprintf(D_ALWAYS,"Failed to register stderr timer\n"); printSystemErrorMsg(); cleanup(); return false; } } } // try to turn on vmgahp async notification mode if ( !command_async_mode_on() ) { // not supported, set a poll interval m_is_async_mode = false; setPollInterval(m_pollInterval); } else { // command worked... register the pipe and stop polling result = daemonCore->Register_Pipe(m_vmgahp_readfd, "m_vmgahp_readfd", static_cast<PipeHandlercpp>(&VMGahpServer::pipe_ready), "VMGahpServer::pipe_ready",this); if( result == -1 ) { // failed to register the pipe for some reason; fall // back on polling (yuck). dprintf(D_ALWAYS,"Failed to register vmgahp Read pipe\n"); m_is_async_mode = false; setPollInterval(m_pollInterval); } else { // pipe is registered. stop polling. setPollInterval(0); m_is_async_mode = true; } } return true; }
const char* priv_identifier( priv_state s ) { static char id[256]; int id_sz = 256; // for use w/ snprintf() switch( s ) { case PRIV_UNKNOWN: snprintf( id, id_sz, "unknown user" ); break; case PRIV_FILE_OWNER: if( ! OwnerIdsInited ) { if( !can_switch_ids() ) { return priv_identifier( PRIV_CONDOR ); } EXCEPT( "Programmer Error: priv_identifier() called for " "PRIV_FILE_OWNER, but owner ids are not initialized" ); } #ifdef WIN32 EXCEPT( "Programmer Error: priv_identifier() called for " "PRIV_FILE_OWNER, on WIN32" ); #else snprintf( id, id_sz, "file owner '%s' (%d.%d)", OwnerName ? OwnerName : "unknown", OwnerUid, OwnerGid ); #endif break; case PRIV_USER: case PRIV_USER_FINAL: if( ! UserIdsInited ) { if( !can_switch_ids() ) { return priv_identifier( PRIV_CONDOR ); } EXCEPT( "Programmer Error: priv_identifier() called for " "%s, but user ids are not initialized", priv_to_string(s) ); } #ifdef WIN32 snprintf( id, id_sz, "%s@%s", UserLoginName, UserDomainName ); #else snprintf( id, id_sz, "User '%s' (%d.%d)", UserName ? UserName : "******", UserUid, UserGid ); #endif break; #ifdef WIN32 case PRIV_ROOT: case PRIV_CONDOR: snprintf( id, id_sz, "SuperUser (system)" ); break; #else /* UNIX */ case PRIV_ROOT: snprintf( id, id_sz, "SuperUser (root)" ); break; case PRIV_CONDOR: snprintf( id, id_sz, "Condor daemon user '%s' (%d.%d)", CondorUserName ? CondorUserName : "******", CondorUid, CondorGid ); break; #endif /* WIN32 vs. UNIX */ default: EXCEPT( "Programmer error: unknown state (%d) in priv_identifier", (int)s ); } /* end of switch */ return (const char*) id; }
priv_state _set_priv(priv_state s, const char *file, int line, int dologging) { priv_state PrevPrivState = CurrentPrivState; if (s == CurrentPrivState) return s; if (CurrentPrivState == PRIV_USER_FINAL) { dprintf(D_ALWAYS, "warning: attempted switch out of PRIV_USER_FINAL\n"); return PRIV_USER_FINAL; } if (CurrentPrivState == PRIV_CONDOR_FINAL) { dprintf(D_ALWAYS, "warning: attempted switch out of PRIV_CONDOR_FINAL\n"); return PRIV_CONDOR_FINAL; } CurrentPrivState = s; if (can_switch_ids()) { switch (s) { case PRIV_ROOT: set_root_euid(); set_root_egid(); break; case PRIV_CONDOR: set_root_euid(); /* must be root to switch */ set_condor_egid(); set_condor_euid(); break; case PRIV_USER: set_root_euid(); /* must be root to switch */ set_user_egid(); set_user_euid(); break; case PRIV_FILE_OWNER: set_root_euid(); /* must be root to switch */ set_owner_egid(); set_owner_euid(); break; case PRIV_USER_FINAL: set_root_euid(); /* must be root to switch */ set_user_rgid(); set_user_ruid(); break; case PRIV_CONDOR_FINAL: set_root_euid(); /* must be root to switch */ set_condor_rgid(); set_condor_ruid(); case PRIV_UNKNOWN: /* silently ignore */ break; default: dprintf( D_ALWAYS, "set_priv: Unknown priv state %d\n", (int)s); } } if( dologging == NO_PRIV_MEMORY_CHANGES ) { // This is a special case for the call to set_priv from // within a child process, just before exec(), where the // child may be sharing memory with the parent, and // therefore doesn't want to have set_priv() mess up the // parent's memory of what priv state the parent is in. // It is ok that we temporarily changed the // CurrentPrivState variable above in a non-thread-safe // way, because the parent is suspended, but before // returning, we must set it back to what it was. For the // rest of the life of the child, CurrentPrivState is a // lie. CurrentPrivState = PrevPrivState; } else if( dologging ) { log_priv(PrevPrivState, CurrentPrivState, file, line); } return PrevPrivState; }
static bool createJobSpoolDirectory(ClassAd const *job_ad,priv_state desired_priv_state,char const *spool_path) { int cluster=-1,proc=-1; job_ad->LookupInteger(ATTR_CLUSTER_ID,cluster); job_ad->LookupInteger(ATTR_PROC_ID,proc); #ifndef WIN32 uid_t spool_path_uid; #endif StatInfo si( spool_path ); if( si.Error() == SINoFile ) { if(!mkdir_and_parents_if_needed(spool_path,0755,PRIV_CONDOR) ) { dprintf( D_ALWAYS, "Failed to create spool directory for job %d.%d: " "mkdir(%s): %s (errno %d)\n", cluster, proc, spool_path, strerror(errno), errno ); return false; } #ifndef WIN32 spool_path_uid = get_condor_uid(); #endif } else { #ifndef WIN32 // spool_path already exists, check owner spool_path_uid = si.GetOwner(); #endif } if( !can_switch_ids() || desired_priv_state == PRIV_UNKNOWN || desired_priv_state == PRIV_CONDOR ) { return true; // no need/desire for chowning } ASSERT( desired_priv_state == PRIV_USER ); #ifndef WIN32 MyString owner; job_ad->LookupString( ATTR_OWNER, owner ); uid_t src_uid = get_condor_uid(); uid_t dst_uid; gid_t dst_gid; passwd_cache* p_cache = pcache(); if( !p_cache->get_user_ids(owner.Value(), dst_uid, dst_gid) ) { dprintf( D_ALWAYS, "(%d.%d) Failed to find UID and GID for " "user %s. Cannot chown %s to user.\n", cluster, proc, owner.Value(), spool_path ); return false; } if( (spool_path_uid != dst_uid) && !recursive_chown(spool_path,src_uid,dst_uid,dst_gid,true) ) { dprintf( D_ALWAYS, "(%d.%d) Failed to chown %s from %d to %d.%d.\n", cluster, proc, spool_path, src_uid, dst_uid, dst_gid ); return false; } #else /* WIN32 */ MyString owner; job_ad->LookupString(ATTR_OWNER, owner); MyString nt_domain; job_ad->LookupString(ATTR_NT_DOMAIN, nt_domain); if(!recursive_chown(spool_path, owner.Value(), nt_domain.Value())) { dprintf( D_ALWAYS, "(%d.%d) Failed to chown %s from to %d\\%d.\n", cluster, proc, spool_path, nt_domain.Value(), owner.Value() ); return false; } #endif return true; // All happy paths lead here }
bool ProcFamilyProxy::start_procd() { // we'll only start one ProcD // ASSERT(m_procd_pid == -1); // now, we build up an ArgList for the procd // MyString exe; ArgList args; // path to the executable // char* path = param("PROCD"); if (path == NULL) { dprintf(D_ALWAYS, "start_procd: PROCD not defined in configuration\n"); return false; } exe = path; args.AppendArg(condor_basename(path)); free(path); // the procd's address // args.AppendArg("-A"); args.AppendArg(m_procd_addr); // the (optional) procd log file // if (m_procd_log.Length() > 0) { args.AppendArg("-L"); args.AppendArg(m_procd_log); } // the (optional) procd log file size // char *procd_log_size = param("MAX_PROCD_LOG"); if (procd_log_size != NULL) { args.AppendArg("-R"); args.AppendArg(procd_log_size); free(procd_log_size); } Env env; // The procd can't param, so pass this via the environment if (param_boolean("USE_PSS", false)) { env.SetEnv("_condor_USE_PSS=TRUE"); } // the (optional) maximum snapshot interval // (the procd will default to every minute) // char* max_snapshot_interval = param("PROCD_MAX_SNAPSHOT_INTERVAL"); if (max_snapshot_interval != NULL) { args.AppendArg("-S"); args.AppendArg(max_snapshot_interval); free(max_snapshot_interval); } // (optional) make the procd sleep on startup so a // debugger can attach // bool debug = param_boolean("PROCD_DEBUG", false); if (debug) { args.AppendArg("-D"); } #if !defined(WIN32) // On UNIX, we need to tell the procd to allow connections from the // condor user // args.AppendArg("-C"); args.AppendArg(get_condor_uid()); #endif #if defined(WIN32) // on Windows, we need to tell the procd what program to use to send // softkills // char* softkill_path = param("WINDOWS_SOFTKILL"); if ( softkill_path == NULL ) { dprintf(D_ALWAYS, "WINDOWS_SOFTKILL undefined; " "ProcD won't be able to send WM_CLOSE to jobs\n"); } else { args.AppendArg("-K"); args.AppendArg(softkill_path); free(softkill_path); } #endif #if defined(LINUX) // enable group-based tracking if a group ID range is given in the // config file // if (param_boolean("USE_GID_PROCESS_TRACKING", false)) { if (!can_switch_ids() && !privsep_enabled()) { EXCEPT("USE_GID_PROCESS_TRACKING enabled, but can't modify " "the group list of our children unless running as " "root or using PrivSep"); } int min_tracking_gid = param_integer("MIN_TRACKING_GID", 0); if (min_tracking_gid == 0) { EXCEPT("USE_GID_PROCESS_TRACKING enabled, " "but MIN_TRACKING_GID is %d\n", min_tracking_gid); } int max_tracking_gid = param_integer("MAX_TRACKING_GID", 0); if (max_tracking_gid == 0) { EXCEPT("USE_GID_PROCESS_TRACKING enabled, " "but MAX_TRACKING_GID is %d\n", max_tracking_gid); } if (min_tracking_gid > max_tracking_gid) { EXCEPT("invalid tracking gid range: %d - %d\n", min_tracking_gid, max_tracking_gid); } args.AppendArg("-G"); args.AppendArg(min_tracking_gid); args.AppendArg(max_tracking_gid); } #endif // for the GLEXEC_JOB feature, we'll need to pass the ProcD paths // to glexec and the condor_glexec_kill script // if (param_boolean("GLEXEC_JOB", false)) { args.AppendArg("-I"); char* libexec = param("LIBEXEC"); if (libexec == NULL) { EXCEPT("GLEXEC_JOB is defined, but LIBEXEC not configured"); } MyString glexec_kill; glexec_kill.formatstr("%s/condor_glexec_kill", libexec); free(libexec); args.AppendArg(glexec_kill.Value()); char* glexec = param("GLEXEC"); if (glexec == NULL) { EXCEPT("GLEXEC_JOB is defined, but GLEXEC not configured"); } args.AppendArg(glexec); free(glexec); int glexec_retries = param_integer("GLEXEC_RETRIES",3,0); int glexec_retry_delay = param_integer("GLEXEC_RETRY_DELAY",5,0); args.AppendArg(glexec_retries); args.AppendArg(glexec_retry_delay); } // done constructing the argument list; now register a reaper for // notification when the procd exits // if (m_reaper_id == FALSE) { m_reaper_id = daemonCore->Register_Reaper( "condor_procd reaper", (ReaperHandlercpp)&ProcFamilyProxyReaperHelper::procd_reaper, "condor_procd reaper", m_reaper_helper ); } if (m_reaper_id == FALSE) { dprintf(D_ALWAYS, "start_procd: unable to register a reaper for the procd\n"); return false; } // we start the procd with a pipe coming back to us on its stderr. // the procd will close this pipe after it starts listening for // commands. // int pipe_ends[2]; if (daemonCore->Create_Pipe(pipe_ends) == FALSE) { dprintf(D_ALWAYS, "start_procd: error creating pipe for the procd\n"); return false; } int std_io[3]; std_io[0] = -1; std_io[1] = -1; std_io[2] = pipe_ends[1]; // use Create_Process to start the procd // if (privsep_enabled()) { m_procd_pid = privsep_spawn_procd(exe.Value(), args, std_io, m_reaper_id); } else { m_procd_pid = daemonCore->Create_Process(exe.Value(), args, PRIV_ROOT, m_reaper_id, FALSE, &env, NULL, NULL, NULL, std_io); } if (m_procd_pid == FALSE) { dprintf(D_ALWAYS, "start_procd: unable to execute the procd\n"); daemonCore->Close_Pipe(pipe_ends[0]); daemonCore->Close_Pipe(pipe_ends[1]); m_procd_pid = -1; return false; } // now close the pipe end we handed to the child and then block on the // pipe until it closes (which tells us the procd is listening for // commands) // if (daemonCore->Close_Pipe(pipe_ends[1]) == FALSE) { dprintf(D_ALWAYS, "error closing procd's pipe end\n"); daemonCore->Shutdown_Graceful(m_procd_pid); daemonCore->Close_Pipe(pipe_ends[0]); m_procd_pid = -1; return false; } const int MAX_PROCD_ERR_LEN = 80; char err_msg[MAX_PROCD_ERR_LEN + 1]; int ret = daemonCore->Read_Pipe(pipe_ends[0], err_msg, MAX_PROCD_ERR_LEN); if (ret != 0) { daemonCore->Shutdown_Graceful(m_procd_pid); daemonCore->Close_Pipe(pipe_ends[0]); m_procd_pid = -1; if (ret == -1) { dprintf(D_ALWAYS, "start_procd: error reading pipe from procd\n"); return false; } err_msg[ret] = '\0'; dprintf(D_ALWAYS, "start_procd: error received from procd: %s\n", err_msg); return false; } if (daemonCore->Close_Pipe(pipe_ends[0]) == FALSE) { dprintf(D_ALWAYS, "start_procd: error closing pipe to procd\n"); daemonCore->Shutdown_Graceful(m_procd_pid); m_procd_pid = -1; return false; } // OK, the ProcD's up and running! // return true; }
int init_user_ids(const char username[], const char domain[]) { int retval = 0; if (!username || !domain) { dprintf(D_ALWAYS, "WARNING: init_user_ids() called with" " NULL arguments!\n"); return 0; } // we have to be very careful calling dprintf in this function, because dprintf // will try and _set_priv to root and then back to what it was. so we need to // make sure that our global variables are always in a state where that doesn't // except. // TJ:2010: Can't do this here, UserIdsInited must never be true // while CurrUserHandle is NULL or dprintf will EXCEPT. // UserIdsInited = true; // see if we already have a user handle for the requested user. // if so, just return 1. // TODO: cache multiple user handles to save time. dprintf(D_FULLDEBUG, "init_user_ids: want user '%s@%s', " "current is '%s@%s'\n", username, domain, UserLoginName, UserDomainName); if ( CurrUserHandle = cached_tokens.getToken(username, domain)) { UserIdsInited = true; // do this before we call dprintf // when we uninit_user_ids we can end up CurrUserHandle in the cache // but UserLoginName and UserDomainName not set, if that happens // we need to set them here. if ( ! UserLoginName) UserLoginName = strdup(username); if ( ! UserDomainName) UserDomainName = strdup(domain); dprintf(D_FULLDEBUG, "init_user_ids: Already have handle for %s@%s," " so returning.\n", username, domain); return 1; } else { char* myusr = my_username(); char* mydom = my_domainname(); // we cleared CurrUserHandle, so we aren't inited. UserIdsInited = false; // see if our calling thread matches the user and domain // we want a token for. This happens if we're submit for example. if ( strcmp( myusr, username ) == 0 && strcasecmp( mydom, domain ) == 0 ) { // domain is case insensitive dprintf(D_FULLDEBUG, "init_user_ids: Calling thread has token " "we want, so returning.\n"); CurrUserHandle = my_usertoken(); if (CurrUserHandle) { UserIdsInited = true; } else { dprintf(D_FULLDEBUG, "init_user_ids: handle is null!\n"); } if (UserLoginName) { free(UserLoginName); UserLoginName = NULL; } if (UserDomainName) { free(UserDomainName); UserDomainName = NULL; } // these are strdup'ed, so we can just stash their pointers UserLoginName = myusr; UserDomainName = mydom; // don't forget to drop it in the cache too cached_tokens.storeToken(UserLoginName, UserDomainName, CurrUserHandle); return 1; } } // at this point UserIdsInited should be false. ASSERT( ! UserIdsInited); // anything beyond this requires user switching if (!can_switch_ids()) { dprintf(D_ALWAYS, "init_user_ids: failed because user switching is disabled\n"); return 0; } if ( strcmp(username,"nobody") != 0 ) { // here we call routines to deal with passwords. Hopefully we're // running as LocalSystem here, otherwise we can't get at the // password stash. lsa_mgr lsaMan; char pw[255]; char user[255]; char dom[255]; wchar_t w_fullname[255]; wchar_t *w_pw; bool got_password = false; bool got_password_from_credd = false; // these should probably be snprintfs swprintf(w_fullname, L"%S@%S", username, domain); sprintf(user, "%s", username); sprintf(dom, "%s", domain); // make sure we're SYSTEM when we do this w_pw = lsaMan.query(w_fullname); if ( w_pw ) { // copy password into a char buffer sprintf(pw, "%S", w_pw); // we don't need the wide char pw anymore, so clean it up SecureZeroMemory(w_pw, wcslen(w_pw)*sizeof(wchar_t)); delete[](w_pw); w_pw = NULL; // now that we got a password, see if it is good. retval = LogonUser( user, // user name dom, // domain or server - local for now pw, // password LOGON32_LOGON_INTERACTIVE, // type of logon operation. // LOGON_BATCH doesn't seem to work right here. LOGON32_PROVIDER_DEFAULT, // logon provider &CurrUserHandle // receive tokens handle ); if ( !retval ) { dprintf(D_FULLDEBUG,"Locally stored credential for %s@%s is stale\n", user,dom); // Set handle to NULL to make certain we recall LogonUser again below CurrUserHandle = NULL; } else { got_password = true; // so we don't bother going to a credd } } // if we don't have the password from our local stash, try to fetch // it from a credd. this tiny function is in a separate file so // that we don't pull in all of daemon core when we link to the utils library. char *credd_host = param("CREDD_HOST"); if (credd_host && got_password==false) { #if 1 got_password = get_password_from_credd( credd_host, username, domain, pw, sizeof(pw)); got_password_from_credd = got_password; #else dprintf(D_FULLDEBUG, "trying to fetch password from credd: %s\n", credd_host); Daemon credd(DT_CREDD); Sock * credd_sock = credd.startCommand(CREDD_GET_PASSWD,Stream::reli_sock,10); if ( credd_sock ) { credd_sock->put((char*)username); // send user credd_sock->put((char*)domain); // send domain credd_sock->end_of_message(); credd_sock->decode(); pw[0] = '\0'; int my_stupid_sizeof_int_for_damn_cedar = sizeof(pw); char *my_buffer = pw; if ( credd_sock->code(my_buffer,my_stupid_sizeof_int_for_damn_cedar) && pw[0] ) { got_password = true; got_password_from_credd = true; } else { dprintf(D_FULLDEBUG, "credd (%s) did not have info for %s@%s\n", credd_host, username,domain); } delete credd_sock; credd_sock = NULL; } else { dprintf(D_FULLDEBUG,"Failed to contact credd %s: %s\n", credd_host,credd.error() ? credd.error() : ""); } #endif } if (credd_host) free(credd_host); if ( ! got_password ) { dprintf(D_ALWAYS, "ERROR: Could not locate valid credential for user " "'%s@%s'\n", username, domain); return 0; } else { dprintf(D_FULLDEBUG, "Found credential for user '%s'\n", username); // If we have not yet called LogonUser, then CurrUserHandle is NULL, // and we need to call it here. if ( CurrUserHandle == NULL ) { retval = LogonUser( user, // user name dom, // domain or server - local for now pw, // password LOGON32_LOGON_INTERACTIVE, // type of logon operation. // LOGON_BATCH doesn't seem to work right here. LOGON32_PROVIDER_DEFAULT, // logon provider &CurrUserHandle // receive tokens handle ); } else { // we already have a good user handle from calling LogonUser to check to // see if our stashed credential was stale or not, so set retval to success retval = 1; // LogonUser returns nonzero value on success } dprintf(D_FULLDEBUG, "LogonUser completed.\n"); if (UserLoginName) { free(UserLoginName); UserDomainName = NULL; } if (UserDomainName) { free(UserDomainName); UserDomainName = NULL; } UserLoginName = strdup(username); UserDomainName = strdup(domain); if ( !retval ) { dprintf(D_ALWAYS, "init_user_ids: LogonUser failed with NT Status %ld\n", GetLastError()); retval = 0; // return of 0 means FAILURE } else { // stash the new token in our cache cached_tokens.storeToken(UserLoginName, UserDomainName, CurrUserHandle); UserIdsInited = true; // if we got the password from the credd, and the admin wants passwords stashed // locally on this machine, then do it. if ( got_password_from_credd && param_boolean("CREDD_CACHE_LOCALLY",false) ) { cache_credd_locally(username, domain, pw); } retval = 1; // return of 1 means SUCCESS } // clear pw from memory SecureZeroMemory(pw, 255); return retval; } } else { /// // Here's where we use a nobody account // dprintf(D_FULLDEBUG, "Using dynamic user account.\n"); myDynuser->reset(); // at this point, we know we want a user nobody, so // generate a dynamic user and stash the handle if ( !myDynuser->init_user() ) { // This is indicative of a serious problem. EXCEPT("Failed to create a user nobody"); } if (UserLoginName) { free(UserLoginName); UserDomainName = NULL; } if (UserDomainName) { free(UserDomainName); UserDomainName = NULL; } UserLoginName = strdup( myDynuser->get_accountname() ); UserDomainName = strdup( "." ); // we created a new user, now just stash the token CurrUserHandle = myDynuser->get_token(); // drop the handle in our cache too cached_tokens.storeToken(UserLoginName, UserDomainName, CurrUserHandle); UserIdsInited = true; return 1; } }
void VMGahpServer::killVM(void) { if( m_vm_type.IsEmpty() || m_vmgahp_server.IsEmpty() ) { return; } if( m_workingdir.IsEmpty() ) { dprintf(D_ALWAYS, "VMGahpServer::killVM() : no workingdir\n"); return; } MyString matchstring; if( (strcasecmp(m_vm_type.Value(), CONDOR_VM_UNIVERSE_XEN ) == MATCH) || (strcasecmp(m_vm_type.Value(), CONDOR_VM_UNIVERSE_KVM ) == MATCH) ) { if( create_name_for_VM(m_job_ad, matchstring) == false ) { dprintf(D_ALWAYS, "VMGahpServer::killVM() : " "cannot make the name of VM\n"); return; } } else { // Except Xen, we need the path of working directory of Starter // in order to destroy a VM. matchstring = m_workingdir; } if( matchstring.IsEmpty() ) { dprintf(D_ALWAYS, "VMGahpServer::killVM() : empty matchstring\n"); return; } // vmgahp is daemonCore, so we need to add -f -t options of daemonCore. // Then, try to execute vmgahp with // vmtype <vmtype> match <string>" ArgList systemcmd; systemcmd.AppendArg(m_vmgahp_server); systemcmd.AppendArg("-f"); if( m_include_gahp_log ) { systemcmd.AppendArg("-t"); } systemcmd.AppendArg("-M"); systemcmd.AppendArg(VMGAHP_KILL_MODE); systemcmd.AppendArg("vmtype"); systemcmd.AppendArg(m_vm_type); systemcmd.AppendArg("match"); systemcmd.AppendArg(matchstring); #if !defined(WIN32) if( can_switch_ids() ) { MyString tmp_str; tmp_str.sprintf("%d", (int)get_condor_uid()); SetEnv("VMGAHP_USER_UID", tmp_str.Value()); } else if (Starter->condorPrivSepHelper() != NULL) { MyString tmp_str; tmp_str.sprintf("%d", (int)Starter->condorPrivSepHelper()->get_uid()); SetEnv("VMGAHP_USER_UID", tmp_str.Value()); } #endif priv_state oldpriv; if( (strcasecmp(m_vm_type.Value(), CONDOR_VM_UNIVERSE_XEN ) == MATCH) || (strcasecmp(m_vm_type.Value(), CONDOR_VM_UNIVERSE_KVM ) == MATCH) ) { oldpriv = set_root_priv(); } else { oldpriv = set_user_priv(); } int ret = my_system(systemcmd); set_priv(oldpriv); if( ret == 0 ) { dprintf( D_FULLDEBUG, "VMGahpServer::killVM() is called with " "'%s'\n", matchstring.Value()); } else { dprintf( D_FULLDEBUG, "VMGahpServer::killVM() failed!\n"); } return; }
priv_state _set_priv(priv_state s, const char *file, int line, int dologging) { priv_state PrevPrivState = CurrentPrivState; /* NOTE NOTE NOTE NOTE * This function is called from deep inside dprintf. To * avoid potentially nasty recursive situations, ONLY call * dprintf() from inside of this function if the * dologging parameter is non-zero. */ // On NT, PRIV_CONDOR = PRIV_ROOT, but we let it switch so daemoncore's // priv state checking won't get totally confused. if ( ( s == PRIV_CONDOR || s == PRIV_ROOT ) && ( CurrentPrivState == PRIV_CONDOR || CurrentPrivState == PRIV_ROOT ) ) { goto logandreturn; } // If this is PRIV_USER or PRIV_USER_FINAL, this isn't redundant if (s == CurrentPrivState) { goto logandreturn; } if (CurrentPrivState == PRIV_USER_FINAL) { if ( dologging ) { dprintf(D_ALWAYS, "warning: attempted switch out of PRIV_USER_FINAL\n"); } return PRIV_USER_FINAL; } if (CurrentPrivState == PRIV_CONDOR_FINAL) { if ( dologging ) { dprintf(D_ALWAYS, "warning: attempted switch out of PRIV_CONDOR_FINAL\n"); } return PRIV_CONDOR_FINAL; } CurrentPrivState = s; if (can_switch_ids()) { switch (s) { case PRIV_ROOT: case PRIV_CONDOR: case PRIV_CONDOR_FINAL: RevertToSelf(); break; case PRIV_USER: case PRIV_USER_FINAL: if ( dologging ) { dprintf(D_FULLDEBUG, "TokenCache contents: \n%s", cached_tokens.cacheToString().Value()); } if ( CurrUserHandle ) { if ( PrevPrivState == PRIV_UNKNOWN ) { // make certain we're back to 'condor' before impersonating RevertToSelf(); } if ( ! ImpersonateLoggedOnUser(CurrUserHandle)) { dprintf(D_ALWAYS, "ImpersonateLoggedOnUser() failed, err=%d\n", GetLastError()); } } else { // we don't want to exit here because reusing the shadow in 7.5.4 // ends up here because of a dprintf inside uninit_user_ids if ( ! UserIdsInited) { if (dologging) { dprintf(D_ALWAYS, "set_user_priv() called when UserIds not inited!\n"); } } else { // Tried to set_user_priv() but it failed, so bail out! EXCEPT("set_user_priv() failed!"); } } break; case PRIV_UNKNOWN: /* silently ignore */ break; default: if ( dologging ) { dprintf( D_ALWAYS, "set_priv: Unknown priv state %d\n", (int)s); } } } logandreturn: if (dologging) { log_priv(PrevPrivState, CurrentPrivState, file, line); } return PrevPrivState; }
int VanillaProc::StartJob() { dprintf(D_FULLDEBUG,"in VanillaProc::StartJob()\n"); // vanilla jobs, unlike standard jobs, are allowed to run // shell scripts (or as is the case on NT, batch files). so // edit the ad so we start up a shell, pass the executable as // an argument to the shell, if we are asked to run a .bat file. #ifdef WIN32 CHAR interpreter[MAX_PATH+1], systemshell[MAX_PATH+1]; const char* jobtmp = Starter->jic->origJobName(); int joblen = strlen(jobtmp); const char *extension = joblen > 0 ? &(jobtmp[joblen-4]) : NULL; bool binary_executable = ( extension && ( MATCH == strcasecmp ( ".exe", extension ) || MATCH == strcasecmp ( ".com", extension ) ) ), java_universe = ( CONDOR_UNIVERSE_JAVA == job_universe ); ArgList arguments; MyString filename, jobname, error; if ( extension && !java_universe && !binary_executable ) { /** since we do not actually know how long the extension of the file is, we'll need to hunt down the '.' in the path, if it exists */ extension = strrchr ( jobtmp, '.' ); if ( !extension ) { dprintf ( D_ALWAYS, "VanillaProc::StartJob(): Failed to extract " "the file's extension.\n" ); /** don't fail here, since we want executables to run as usual. That is, some condor jobs submit executables that do not have the '.exe' extension, but are, nonetheless, executable binaries. For instance, a submit script may contain: executable = executable$(OPSYS) */ } else { /** pull out the path to the executable */ if ( !JobAd->LookupString ( ATTR_JOB_CMD, jobname ) ) { /** fall back on Starter->jic->origJobName() */ jobname = jobtmp; } /** If we transferred the job, it may have been renamed to condor_exec.exe even though it is not an executable. Here we rename it back to a the correct extension before it will run. */ if ( MATCH == strcasecmp ( CONDOR_EXEC, condor_basename ( jobname.Value () ) ) ) { filename.formatstr ( "condor_exec%s", extension ); if (rename(CONDOR_EXEC, filename.Value()) != 0) { dprintf (D_ALWAYS, "VanillaProc::StartJob(): ERROR: " "failed to rename executable from %s to %s\n", CONDOR_EXEC, filename.Value() ); } } else { filename = jobname; } /** Since we've renamed our executable, we need to update the job ad to reflect this change. */ if ( !JobAd->Assign ( ATTR_JOB_CMD, filename ) ) { dprintf ( D_ALWAYS, "VanillaProc::StartJob(): ERROR: failed to " "set new executable name.\n" ); return FALSE; } /** We've moved the script to argv[1], so we need to add the remaining arguments to positions argv[2].. argv[/n/]. */ if ( !arguments.AppendArgsFromClassAd ( JobAd, &error ) || !arguments.InsertArgsIntoClassAd ( JobAd, NULL, &error ) ) { dprintf ( D_ALWAYS, "VanillaProc::StartJob(): ERROR: failed to " "get arguments from job ad: %s\n", error.Value () ); return FALSE; } /** Since we know already we don't want this file returned to us, we explicitly add it to an exception list which will stop the file transfer mechanism from considering it for transfer back to its submitter */ Starter->jic->removeFromOutputFiles ( filename.Value () ); } } #endif // set up a FamilyInfo structure to tell OsProc to register a family // with the ProcD in its call to DaemonCore::Create_Process // FamilyInfo fi; // take snapshots at no more than 15 seconds in between, by default // fi.max_snapshot_interval = param_integer("PID_SNAPSHOT_INTERVAL", 15); m_dedicated_account = Starter->jic->getExecuteAccountIsDedicated(); if( ThisProcRunsAlongsideMainProc() ) { // If we track a secondary proc's family tree (such as // sshd) using the same dedicated account as the job's // family tree, we could end up killing the job when we // clean up the secondary family. m_dedicated_account = NULL; } if (m_dedicated_account) { // using login-based family tracking fi.login = m_dedicated_account; // The following message is documented in the manual as the // way to tell whether the dedicated execution account // configuration is being used. dprintf(D_ALWAYS, "Tracking process family by login \"%s\"\n", fi.login); } FilesystemRemap * fs_remap = NULL; #if defined(LINUX) // on Linux, we also have the ability to track processes via // a phony supplementary group ID // gid_t tracking_gid = 0; if (param_boolean("USE_GID_PROCESS_TRACKING", false)) { if (!can_switch_ids() && (Starter->condorPrivSepHelper() == NULL)) { EXCEPT("USE_GID_PROCESS_TRACKING enabled, but can't modify " "the group list of our children unless running as " "root or using PrivSep"); } fi.group_ptr = &tracking_gid; } #endif #if defined(HAVE_EXT_LIBCGROUP) // Determine the cgroup std::string cgroup_base; param(cgroup_base, "BASE_CGROUP", ""); MyString cgroup_str; const char *cgroup = NULL; if (cgroup_base.length()) { MyString cgroup_uniq; std::string starter_name, execute_str; param(execute_str, "EXECUTE", "EXECUTE_UNKNOWN"); // Note: Starter is a global variable from os_proc.cpp Starter->jic->machClassAd()->EvalString(ATTR_NAME, NULL, starter_name); ASSERT (starter_name.size()); cgroup_uniq.formatstr("%s_%s", execute_str.c_str(), starter_name.c_str()); const char dir_delim[2] = {DIR_DELIM_CHAR, '\0'}; cgroup_uniq.replaceString(dir_delim, "_"); cgroup_str.formatstr("%s%ccondor%s", cgroup_base.c_str(), DIR_DELIM_CHAR, cgroup_uniq.Value()); cgroup = cgroup_str.Value(); ASSERT (cgroup != NULL); fi.cgroup = cgroup; dprintf(D_FULLDEBUG, "Requesting cgroup %s for job.\n", cgroup); } #endif // The chroot stuff really only works on linux #ifdef LINUX { // Have Condor manage a chroot std::string requested_chroot_name; JobAd->EvalString("RequestedChroot", NULL, requested_chroot_name); const char * allowed_root_dirs = param("NAMED_CHROOT"); if (requested_chroot_name.size()) { dprintf(D_FULLDEBUG, "Checking for chroot: %s\n", requested_chroot_name.c_str()); StringList chroot_list(allowed_root_dirs); chroot_list.rewind(); const char * next_chroot; bool acceptable_chroot = false; std::string requested_chroot; while ( (next_chroot=chroot_list.next()) ) { MyString chroot_spec(next_chroot); chroot_spec.Tokenize(); const char * chroot_name = chroot_spec.GetNextToken("=", false); if (chroot_name == NULL) { dprintf(D_ALWAYS, "Invalid named chroot: %s\n", chroot_spec.Value()); } const char * next_dir = chroot_spec.GetNextToken("=", false); if (chroot_name == NULL) { dprintf(D_ALWAYS, "Invalid named chroot: %s\n", chroot_spec.Value()); } dprintf(D_FULLDEBUG, "Considering directory %s for chroot %s.\n", next_dir, chroot_spec.Value()); if (IsDirectory(next_dir) && chroot_name && (strcmp(requested_chroot_name.c_str(), chroot_name) == 0)) { acceptable_chroot = true; requested_chroot = next_dir; } } // TODO: path to chroot MUST be all root-owned, or we have a nice security exploit. // Is this the responsibility of Condor to check, or the sysadmin who set it up? if (!acceptable_chroot) { return FALSE; } dprintf(D_FULLDEBUG, "Will attempt to set the chroot to %s.\n", requested_chroot.c_str()); std::stringstream ss; std::stringstream ss2; ss2 << Starter->GetExecuteDir() << DIR_DELIM_CHAR << "dir_" << getpid(); std::string execute_dir = ss2.str(); ss << requested_chroot << DIR_DELIM_CHAR << ss2.str(); std::string full_dir_str = ss.str(); if (is_trivial_rootdir(requested_chroot)) { dprintf(D_FULLDEBUG, "Requested a trivial chroot %s; this is a no-op.\n", requested_chroot.c_str()); } else if (IsDirectory(execute_dir.c_str())) { { TemporaryPrivSentry sentry(PRIV_ROOT); if( mkdir(full_dir_str.c_str(), S_IRWXU) < 0 ) { dprintf( D_FAILURE|D_ALWAYS, "Failed to create sandbox directory in chroot (%s): %s\n", full_dir_str.c_str(), strerror(errno) ); return FALSE; } if (chown(full_dir_str.c_str(), get_user_uid(), get_user_gid()) == -1) { EXCEPT("chown error on %s: %s", full_dir_str.c_str(), strerror(errno)); } } if (!fs_remap) { fs_remap = new FilesystemRemap(); } dprintf(D_FULLDEBUG, "Adding mapping: %s -> %s.\n", execute_dir.c_str(), full_dir_str.c_str()); if (fs_remap->AddMapping(execute_dir, full_dir_str)) { // FilesystemRemap object prints out an error message for us. return FALSE; } dprintf(D_FULLDEBUG, "Adding mapping %s -> %s.\n", requested_chroot.c_str(), "/"); std::string root_str("/"); if (fs_remap->AddMapping(requested_chroot, root_str)) { return FALSE; } } else { dprintf(D_ALWAYS, "Unable to do chroot because working dir %s does not exist.\n", execute_dir.c_str()); } } else { dprintf(D_FULLDEBUG, "Value of RequestedChroot is unset.\n"); } } // End of chroot #endif // On Linux kernel 2.4.19 and later, we can give each job its // own FS mounts. char * mount_under_scratch = param("MOUNT_UNDER_SCRATCH"); if (mount_under_scratch) { std::string working_dir = Starter->GetWorkingDir(); if (IsDirectory(working_dir.c_str())) { StringList mount_list(mount_under_scratch); free(mount_under_scratch); mount_list.rewind(); if (!fs_remap) { fs_remap = new FilesystemRemap(); } char * next_dir; while ( (next_dir=mount_list.next()) ) { if (!*next_dir) { // empty string? mount_list.deleteCurrent(); continue; } std::string next_dir_str(next_dir); // Gah, I wish I could throw an exception to clean up these nested if statements. if (IsDirectory(next_dir)) { char * full_dir = dirscat(working_dir, next_dir_str); if (full_dir) { std::string full_dir_str(full_dir); delete [] full_dir; full_dir = NULL; if (!mkdir_and_parents_if_needed( full_dir_str.c_str(), S_IRWXU, PRIV_USER )) { dprintf(D_ALWAYS, "Failed to create scratch directory %s\n", full_dir_str.c_str()); return FALSE; } dprintf(D_FULLDEBUG, "Adding mapping: %s -> %s.\n", full_dir_str.c_str(), next_dir_str.c_str()); if (fs_remap->AddMapping(full_dir_str, next_dir_str)) { // FilesystemRemap object prints out an error message for us. return FALSE; } } else { dprintf(D_ALWAYS, "Unable to concatenate %s and %s.\n", working_dir.c_str(), next_dir_str.c_str()); return FALSE; } } else { dprintf(D_ALWAYS, "Unable to add mapping %s -> %s because %s doesn't exist.\n", working_dir.c_str(), next_dir, next_dir); } } } else { dprintf(D_ALWAYS, "Unable to perform mappings because %s doesn't exist.\n", working_dir.c_str()); return FALSE; } } // have OsProc start the job // int retval = OsProc::StartJob(&fi, fs_remap); if (fs_remap != NULL) { delete fs_remap; } #if defined(HAVE_EXT_LIBCGROUP) // Set fairshare limits. Note that retval == 1 indicates success, 0 is failure. if (cgroup && retval) { std::string mem_limit; param(mem_limit, "MEMORY_LIMIT", "soft"); bool mem_is_soft = mem_limit == "soft"; std::string cgroup_string = cgroup; CgroupLimits climits(cgroup_string); if (mem_is_soft || (mem_limit == "hard")) { ClassAd * MachineAd = Starter->jic->machClassAd(); int MemMb; if (MachineAd->LookupInteger(ATTR_MEMORY, MemMb)) { uint64_t MemMb_big = MemMb; climits.set_memory_limit_bytes(1024*1024*MemMb_big, mem_is_soft); } else { dprintf(D_ALWAYS, "Not setting memory soft limit in cgroup because " "Memory attribute missing in machine ad.\n"); } } else if (mem_limit == "none") { dprintf(D_FULLDEBUG, "Not enforcing memory soft limit.\n"); } else { dprintf(D_ALWAYS, "Invalid value of MEMORY_LIMIT: %s. Ignoring.\n", mem_limit.c_str()); } // Now, set the CPU shares ClassAd * MachineAd = Starter->jic->machClassAd(); int slotWeight; if (MachineAd->LookupInteger(ATTR_SLOT_WEIGHT, slotWeight)) { climits.set_cpu_shares(slotWeight*100); } else { dprintf(D_FULLDEBUG, "Invalid value of SlotWeight in machine ClassAd; ignoring.\n"); } } #endif return retval; }
void init_condor_ids() { int scm; bool result; char* env_val = NULL; char* config_val = NULL; char* val = NULL; uid_t envCondorUid = INT_MAX; gid_t envCondorGid = INT_MAX; /* ** N.B. if we are using the yellow pages, system calls which are ** not supported by either remote system calls or file descriptor ** mapping will occur. Thus we must be in LOCAL/UNRECORDED mode here. */ scm = SetSyscalls( SYS_LOCAL | SYS_UNRECORDED ); uid_t MyUid = get_my_uid(); gid_t MyGid = get_my_gid(); /* if either of the following get_user_*() functions fail, * the default is INT_MAX */ RealCondorUid = INT_MAX; RealCondorGid = INT_MAX; pcache()->get_user_uid( myDistro->Get(), RealCondorUid ); pcache()->get_user_gid( myDistro->Get(), RealCondorGid ); const char *envName = EnvGetName( ENV_UG_IDS ); if( (env_val = getenv(envName)) ) { val = env_val; } else if( (config_val = param_without_default(envName)) ) { // I had to change this to param_without_default because there's no way // to put a default value of condor.condor in the default value table. // In the future, there should be a way to call a function to find out // the default value for a parameter, but for now this should work. val = config_val; } if( val ) { if( sscanf(val, "%d.%d", &envCondorUid, &envCondorGid) != 2 ) { fprintf( stderr, "ERROR: badly formed value in %s ", envName ); fprintf( stderr, "%s variable (%s).\n", env_val ? "environment" : "config file", val ); fprintf( stderr, "Please set %s to ", envName ); fprintf( stderr, "the '.' seperated uid, gid pair that\n" ); fprintf( stderr, "should be used by %s.\n", myDistro->Get() ); exit(1); } if( CondorUserName != NULL ) { free( CondorUserName ); CondorUserName = NULL; } result = pcache()->get_user_name( envCondorUid, CondorUserName ); if( ! result ) { /* failure to get username */ fprintf( stderr, "ERROR: the uid specified in %s ", envName ); fprintf( stderr, "%s variable (%d)\n", env_val ? "environment" : "config file", envCondorUid ); fprintf(stderr, "does not exist in your password information.\n" ); fprintf(stderr, "Please set %s to ", envName); fprintf(stderr, "the '.' seperated uid, gid pair that\n"); fprintf(stderr, "should be used by %s.\n", myDistro->Get() ); exit(1); } } if( config_val ) { free( config_val ); config_val = NULL; val = NULL; } /* If we're root, set the Condor Uid and Gid to the value specified in the "CONDOR_IDS" environment variable */ if( can_switch_ids() ) { const char *enviName = EnvGetName( ENV_UG_IDS ); if( envCondorUid != INT_MAX ) { /* CONDOR_IDS are set, use what it said */ CondorUid = envCondorUid; CondorGid = envCondorGid; } else { /* No CONDOR_IDS set, use condor.condor */ if( RealCondorUid != INT_MAX ) { CondorUid = RealCondorUid; CondorGid = RealCondorGid; if( CondorUserName != NULL ) { free( CondorUserName ); CondorUserName = NULL; } CondorUserName = strdup( myDistro->Get() ); if (CondorUserName == NULL) { EXCEPT("Out of memory. Aborting."); } } else { fprintf( stderr, "Can't find \"%s\" in the password file and " "%s not defined in %s_config or as an " "environment variable.\n", myDistro->Get(), enviName, myDistro->Get() ); exit(1); } } /* We'd like to dprintf() here, but we can't. Since this function is called from the initial time we try to enter Condor priv, if we dprintf() here, we'll still be in root priv when we service this dprintf(), and we'll have major problems. -Derek Wright 12/21/98 */ /* dprintf(D_PRIV, "running as root; privilege switching in effect\n"); */ } else { /* Non-root. Set the CondorUid/Gid to our current uid/gid */ CondorUid = MyUid; CondorGid = MyGid; if( CondorUserName != NULL ) { free( CondorUserName ); CondorUserName = NULL; } result = pcache()->get_user_name( CondorUid, CondorUserName ); if( !result ) { /* Cannot find an entry in the passwd file for this uid */ CondorUserName = strdup("Unknown"); if (CondorUserName == NULL) { EXCEPT("Out of memory. Aborting."); } } /* If CONDOR_IDS environment variable is set, and set to the same uid that we are running as, then behave as if the daemons are running as user "condor" -- i.e. allow any user to submit jobs to these daemons, not just the user running the daemons. */ if ( MyUid == envCondorUid ) { RealCondorUid = MyUid; RealCondorGid = MyGid; } } (void)endpwent(); (void)SetSyscalls( scm ); CondorIdsInited = TRUE; }
int VanillaProc::StartJob() { dprintf(D_FULLDEBUG,"in VanillaProc::StartJob()\n"); // vanilla jobs, unlike standard jobs, are allowed to run // shell scripts (or as is the case on NT, batch files). so // edit the ad so we start up a shell, pass the executable as // an argument to the shell, if we are asked to run a .bat file. #ifdef WIN32 CHAR interpreter[MAX_PATH+1], systemshell[MAX_PATH+1]; const char* jobtmp = Starter->jic->origJobName(); int joblen = strlen(jobtmp); const char *extension = joblen > 0 ? &(jobtmp[joblen-4]) : NULL; bool binary_executable = ( extension && ( MATCH == strcasecmp ( ".exe", extension ) || MATCH == strcasecmp ( ".com", extension ) ) ), java_universe = ( CONDOR_UNIVERSE_JAVA == job_universe ); ArgList arguments; MyString filename, jobname, error; if ( extension && !java_universe && !binary_executable ) { /** since we do not actually know how long the extension of the file is, we'll need to hunt down the '.' in the path, if it exists */ extension = strrchr ( jobtmp, '.' ); if ( !extension ) { dprintf ( D_ALWAYS, "VanillaProc::StartJob(): Failed to extract " "the file's extension.\n" ); /** don't fail here, since we want executables to run as usual. That is, some condor jobs submit executables that do not have the '.exe' extension, but are, nonetheless, executable binaries. For instance, a submit script may contain: executable = executable$(OPSYS) */ } else { /** pull out the path to the executable */ if ( !JobAd->LookupString ( ATTR_JOB_CMD, jobname ) ) { /** fall back on Starter->jic->origJobName() */ jobname = jobtmp; } /** If we transferred the job, it may have been renamed to condor_exec.exe even though it is not an executable. Here we rename it back to a the correct extension before it will run. */ if ( MATCH == strcasecmp ( CONDOR_EXEC, condor_basename ( jobname.Value () ) ) ) { filename.formatstr ( "condor_exec%s", extension ); if (rename(CONDOR_EXEC, filename.Value()) != 0) { dprintf (D_ALWAYS, "VanillaProc::StartJob(): ERROR: " "failed to rename executable from %s to %s\n", CONDOR_EXEC, filename.Value() ); } } else { filename = jobname; } /** Since we've renamed our executable, we need to update the job ad to reflect this change. */ if ( !JobAd->Assign ( ATTR_JOB_CMD, filename ) ) { dprintf ( D_ALWAYS, "VanillaProc::StartJob(): ERROR: failed to " "set new executable name.\n" ); return FALSE; } /** We've moved the script to argv[1], so we need to add the remaining arguments to positions argv[2].. argv[/n/]. */ if ( !arguments.AppendArgsFromClassAd ( JobAd, &error ) || !arguments.InsertArgsIntoClassAd ( JobAd, NULL, &error ) ) { dprintf ( D_ALWAYS, "VanillaProc::StartJob(): ERROR: failed to " "get arguments from job ad: %s\n", error.Value () ); return FALSE; } /** Since we know already we don't want this file returned to us, we explicitly add it to an exception list which will stop the file transfer mechanism from considering it for transfer back to its submitter */ Starter->jic->removeFromOutputFiles ( filename.Value () ); } } #endif // set up a FamilyInfo structure to tell OsProc to register a family // with the ProcD in its call to DaemonCore::Create_Process // FamilyInfo fi; // take snapshots at no more than 15 seconds in between, by default // fi.max_snapshot_interval = param_integer("PID_SNAPSHOT_INTERVAL", 15); m_dedicated_account = Starter->jic->getExecuteAccountIsDedicated(); if( ThisProcRunsAlongsideMainProc() ) { // If we track a secondary proc's family tree (such as // sshd) using the same dedicated account as the job's // family tree, we could end up killing the job when we // clean up the secondary family. m_dedicated_account = NULL; } if (m_dedicated_account) { // using login-based family tracking fi.login = m_dedicated_account; // The following message is documented in the manual as the // way to tell whether the dedicated execution account // configuration is being used. dprintf(D_ALWAYS, "Tracking process family by login \"%s\"\n", fi.login); } FilesystemRemap * fs_remap = NULL; #if defined(LINUX) // on Linux, we also have the ability to track processes via // a phony supplementary group ID // gid_t tracking_gid = 0; if (param_boolean("USE_GID_PROCESS_TRACKING", false)) { if (!can_switch_ids() && (Starter->condorPrivSepHelper() == NULL)) { EXCEPT("USE_GID_PROCESS_TRACKING enabled, but can't modify " "the group list of our children unless running as " "root or using PrivSep"); } fi.group_ptr = &tracking_gid; } // Increase the OOM score of this process; the child will inherit it. // This way, the job will be heavily preferred to be killed over a normal process. // OOM score is currently exponential - a score of 4 is a factor-16 increase in // the OOM score. setupOOMScore(4,800); #endif #if defined(HAVE_EXT_LIBCGROUP) // Determine the cgroup std::string cgroup_base; param(cgroup_base, "BASE_CGROUP", ""); MyString cgroup_str; const char *cgroup = NULL; /* Note on CONDOR_UNIVERSE_LOCAL - The cgroup setup code below * requires a unique name for the cgroup. It relies on * uniqueness of the MachineAd's Name * attribute. Unfortunately, in the local universe the * MachineAd (mach_ad elsewhere) is never populated, because * there is no machine. As a result the ASSERT on * starter_name fails. This means that the local universe * will not work on any machine that has BASE_CGROUP * configured. A potential workaround is to set * STARTER.BASE_CGROUP on any machine that is also running a * schedd, but that disables cgroup support from a * co-resident startd. Instead, I'm disabling cgroup support * from within the local universe until the intraction of * local universe and cgroups can be properly worked * out. -matt 7 nov '12 */ if (CONDOR_UNIVERSE_LOCAL != job_universe && cgroup_base.length()) { MyString cgroup_uniq; std::string starter_name, execute_str; param(execute_str, "EXECUTE", "EXECUTE_UNKNOWN"); // Note: Starter is a global variable from os_proc.cpp Starter->jic->machClassAd()->EvalString(ATTR_NAME, NULL, starter_name); if (starter_name.size() == 0) { char buf[16]; sprintf(buf, "%d", getpid()); starter_name = buf; } //ASSERT (starter_name.size()); cgroup_uniq.formatstr("%s_%s", execute_str.c_str(), starter_name.c_str()); const char dir_delim[2] = {DIR_DELIM_CHAR, '\0'}; cgroup_uniq.replaceString(dir_delim, "_"); cgroup_str.formatstr("%s%ccondor%s", cgroup_base.c_str(), DIR_DELIM_CHAR, cgroup_uniq.Value()); cgroup_str += this->CgroupSuffix(); cgroup = cgroup_str.Value(); ASSERT (cgroup != NULL); fi.cgroup = cgroup; dprintf(D_FULLDEBUG, "Requesting cgroup %s for job.\n", cgroup); } #endif // The chroot stuff really only works on linux #ifdef LINUX { // Have Condor manage a chroot std::string requested_chroot_name; JobAd->EvalString("RequestedChroot", NULL, requested_chroot_name); const char * allowed_root_dirs = param("NAMED_CHROOT"); if (requested_chroot_name.size()) { dprintf(D_FULLDEBUG, "Checking for chroot: %s\n", requested_chroot_name.c_str()); StringList chroot_list(allowed_root_dirs); chroot_list.rewind(); const char * next_chroot; bool acceptable_chroot = false; std::string requested_chroot; while ( (next_chroot=chroot_list.next()) ) { MyString chroot_spec(next_chroot); chroot_spec.Tokenize(); const char * chroot_name = chroot_spec.GetNextToken("=", false); if (chroot_name == NULL) { dprintf(D_ALWAYS, "Invalid named chroot: %s\n", chroot_spec.Value()); } const char * next_dir = chroot_spec.GetNextToken("=", false); if (chroot_name == NULL) { dprintf(D_ALWAYS, "Invalid named chroot: %s\n", chroot_spec.Value()); } dprintf(D_FULLDEBUG, "Considering directory %s for chroot %s.\n", next_dir, chroot_spec.Value()); if (IsDirectory(next_dir) && chroot_name && (strcmp(requested_chroot_name.c_str(), chroot_name) == 0)) { acceptable_chroot = true; requested_chroot = next_dir; } } // TODO: path to chroot MUST be all root-owned, or we have a nice security exploit. // Is this the responsibility of Condor to check, or the sysadmin who set it up? if (!acceptable_chroot) { return FALSE; } dprintf(D_FULLDEBUG, "Will attempt to set the chroot to %s.\n", requested_chroot.c_str()); std::stringstream ss; std::stringstream ss2; ss2 << Starter->GetExecuteDir() << DIR_DELIM_CHAR << "dir_" << getpid(); std::string execute_dir = ss2.str(); ss << requested_chroot << DIR_DELIM_CHAR << ss2.str(); std::string full_dir_str = ss.str(); if (is_trivial_rootdir(requested_chroot)) { dprintf(D_FULLDEBUG, "Requested a trivial chroot %s; this is a no-op.\n", requested_chroot.c_str()); } else if (IsDirectory(execute_dir.c_str())) { { TemporaryPrivSentry sentry(PRIV_ROOT); if( mkdir(full_dir_str.c_str(), S_IRWXU) < 0 ) { dprintf( D_FAILURE|D_ALWAYS, "Failed to create sandbox directory in chroot (%s): %s\n", full_dir_str.c_str(), strerror(errno) ); return FALSE; } if (chown(full_dir_str.c_str(), get_user_uid(), get_user_gid()) == -1) { EXCEPT("chown error on %s: %s", full_dir_str.c_str(), strerror(errno)); } } if (!fs_remap) { fs_remap = new FilesystemRemap(); } dprintf(D_FULLDEBUG, "Adding mapping: %s -> %s.\n", execute_dir.c_str(), full_dir_str.c_str()); if (fs_remap->AddMapping(execute_dir, full_dir_str)) { // FilesystemRemap object prints out an error message for us. return FALSE; } dprintf(D_FULLDEBUG, "Adding mapping %s -> %s.\n", requested_chroot.c_str(), "/"); std::string root_str("/"); if (fs_remap->AddMapping(requested_chroot, root_str)) { return FALSE; } } else { dprintf(D_ALWAYS, "Unable to do chroot because working dir %s does not exist.\n", execute_dir.c_str()); } } else { dprintf(D_FULLDEBUG, "Value of RequestedChroot is unset.\n"); } } // End of chroot #endif // On Linux kernel 2.4.19 and later, we can give each job its // own FS mounts. auto_free_ptr mount_under_scratch(param("MOUNT_UNDER_SCRATCH")); if (mount_under_scratch) { // try evaluating mount_under_scratch as a classad expression, if it is // an expression it must return a string. if it's not an expression, just // use it as a string (as we did before 8.3.6) classad::Value value; if (JobAd->EvaluateExpr(mount_under_scratch.ptr(), value)) { const char * pval = NULL; if (value.IsStringValue(pval)) { mount_under_scratch.set(strdup(pval)); } else { // was an expression, but not a string, so report and error and fail. dprintf(D_ALWAYS | D_ERROR, "ERROR: MOUNT_UNDER_SCRATCH does not evaluate to a string, it is : %s\n", ClassAdValueToString(value)); return FALSE; } } } // if execute dir is encrypted, add /tmp and /var/tmp to mount_under_scratch bool encrypt_execdir = false; JobAd->LookupBool(ATTR_ENCRYPT_EXECUTE_DIRECTORY,encrypt_execdir); if (encrypt_execdir || param_boolean_crufty("ENCRYPT_EXECUTE_DIRECTORY",false)) { // prepend /tmp, /var/tmp to whatever admin wanted. don't worry // if admin already listed /tmp etc - subdirs can appear twice // in this list because AddMapping() ok w/ duplicate entries MyString buf("/tmp,/var/tmp,"); buf += mount_under_scratch.ptr(); mount_under_scratch.set(buf.StrDup()); } if (mount_under_scratch) { std::string working_dir = Starter->GetWorkingDir(); if (IsDirectory(working_dir.c_str())) { StringList mount_list(mount_under_scratch); mount_list.rewind(); if (!fs_remap) { fs_remap = new FilesystemRemap(); } char * next_dir; while ( (next_dir=mount_list.next()) ) { if (!*next_dir) { // empty string? mount_list.deleteCurrent(); continue; } std::string next_dir_str(next_dir); // Gah, I wish I could throw an exception to clean up these nested if statements. if (IsDirectory(next_dir)) { char * full_dir = dirscat(working_dir, next_dir_str); if (full_dir) { std::string full_dir_str(full_dir); delete [] full_dir; full_dir = NULL; if (!mkdir_and_parents_if_needed( full_dir_str.c_str(), S_IRWXU, PRIV_USER )) { dprintf(D_ALWAYS, "Failed to create scratch directory %s\n", full_dir_str.c_str()); delete fs_remap; return FALSE; } dprintf(D_FULLDEBUG, "Adding mapping: %s -> %s.\n", full_dir_str.c_str(), next_dir_str.c_str()); if (fs_remap->AddMapping(full_dir_str, next_dir_str)) { // FilesystemRemap object prints out an error message for us. delete fs_remap; return FALSE; } } else { dprintf(D_ALWAYS, "Unable to concatenate %s and %s.\n", working_dir.c_str(), next_dir_str.c_str()); delete fs_remap; return FALSE; } } else { dprintf(D_ALWAYS, "Unable to add mapping %s -> %s because %s doesn't exist.\n", working_dir.c_str(), next_dir, next_dir); } } } else { dprintf(D_ALWAYS, "Unable to perform mappings because %s doesn't exist.\n", working_dir.c_str()); delete fs_remap; return FALSE; } mount_under_scratch.clear(); } #if defined(LINUX) // On Linux kernel 2.6.24 and later, we can give each // job its own PID namespace if (param_boolean("USE_PID_NAMESPACES", false)) { if (!can_switch_ids()) { EXCEPT("USE_PID_NAMESPACES enabled, but can't perform this " "call in Linux unless running as root."); } fi.want_pid_namespace = this->SupportsPIDNamespace(); if (fi.want_pid_namespace) { if (!fs_remap) { fs_remap = new FilesystemRemap(); } fs_remap->RemapProc(); } // When PID Namespaces are enabled, need to run the job // under the condor_pid_ns_init program, so that signals // propagate through to the child. // First tell the program where to log output status // via an environment variable if (param_boolean("USE_PID_NAMESPACE_INIT", true)) { Env env; MyString env_errors; MyString arg_errors; std::string filename; filename = Starter->GetWorkingDir(); filename += "/.condor_pid_ns_status"; env.MergeFrom(JobAd, &env_errors); env.SetEnv("_CONDOR_PID_NS_INIT_STATUS_FILENAME", filename); env.InsertEnvIntoClassAd(JobAd, &env_errors); Starter->jic->removeFromOutputFiles(condor_basename(filename.c_str())); this->m_pid_ns_status_filename = filename; // Now, set the job's CMD to the wrapper, and shift // over the arguments by one ArgList args; std::string cmd; JobAd->LookupString(ATTR_JOB_CMD, cmd); args.AppendArg(cmd); args.AppendArgsFromClassAd(JobAd, &arg_errors); args.InsertArgsIntoClassAd(JobAd, NULL, & arg_errors); std::string libexec; if( !param(libexec,"LIBEXEC") ) { dprintf(D_ALWAYS, "Cannot find LIBEXEC so can not run condor_pid_ns_init\n"); return 0; } std::string c_p_n_i = libexec + "/condor_pid_ns_init"; JobAd->Assign(ATTR_JOB_CMD, c_p_n_i); } } dprintf(D_FULLDEBUG, "PID namespace option: %s\n", fi.want_pid_namespace ? "true" : "false"); #endif // have OsProc start the job // int retval = OsProc::StartJob(&fi, fs_remap); if (fs_remap != NULL) { delete fs_remap; } #if defined(HAVE_EXT_LIBCGROUP) // Set fairshare limits. Note that retval == 1 indicates success, 0 is failure. // See Note near setup of param(BASE_CGROUP) if (CONDOR_UNIVERSE_LOCAL != job_universe && cgroup && retval) { std::string mem_limit; param(mem_limit, "CGROUP_MEMORY_LIMIT_POLICY", "soft"); bool mem_is_soft = mem_limit == "soft"; std::string cgroup_string = cgroup; CgroupLimits climits(cgroup_string); if (mem_is_soft || (mem_limit == "hard")) { ClassAd * MachineAd = Starter->jic->machClassAd(); int MemMb; if (MachineAd->LookupInteger(ATTR_MEMORY, MemMb)) { uint64_t MemMb_big = MemMb; m_memory_limit = MemMb_big; climits.set_memory_limit_bytes(1024*1024*MemMb_big, mem_is_soft); // Note that ATTR_VIRTUAL_MEMORY on Linux // is sum of memory and swap, in Kilobytes int VMemKb; if (MachineAd->LookupInteger(ATTR_VIRTUAL_MEMORY, VMemKb)) { uint64_t memsw_limit = ((uint64_t)1024) * VMemKb; if (VMemKb > 0) { // we're not allowed to set memsw limit < // the hard memory limit. If we haven't set the hard // memory limit, the default may be infinity. // So, if we've set soft, set hard limit to memsw - one page if (mem_is_soft) { uint64_t hard_limit = memsw_limit - 4096; climits.set_memory_limit_bytes(hard_limit, false); } climits.set_memsw_limit_bytes(memsw_limit); } } else { dprintf(D_ALWAYS, "Not setting virtual memory limit in cgroup because " "Virtual Memory attribute missing in machine ad.\n"); } } else { dprintf(D_ALWAYS, "Not setting memory limit in cgroup because " "Memory attribute missing in machine ad.\n"); } } else if (mem_limit == "none") { dprintf(D_FULLDEBUG, "Not enforcing memory limit.\n"); } else { dprintf(D_ALWAYS, "Invalid value of CGROUP_MEMORY_LIMIT_POLICY: %s. Ignoring.\n", mem_limit.c_str()); } // Now, set the CPU shares ClassAd * MachineAd = Starter->jic->machClassAd(); int numCores = 1; if (MachineAd->LookupInteger(ATTR_CPUS, numCores)) { climits.set_cpu_shares(numCores*100); } else { dprintf(D_FULLDEBUG, "Invalid value of Cpus in machine ClassAd; ignoring.\n"); } setupOOMEvent(cgroup); } m_statistics.Reconfig(); // Now that the job is started, decrease the likelihood that the starter // is killed instead of the job itself. if (retval) { setupOOMScore(0,0); } #endif return retval; }
int store_cred_service(const char *user, const char *pw, int mode) { wchar_t pwbuf[MAX_PASSWORD_LENGTH]; wchar_t userbuf[MAX_PASSWORD_LENGTH]; priv_state priv; int answer = FAILURE; lsa_mgr lsa_man; wchar_t *pw_wc; // we'll need a wide-char version of the user name later if ( user ) { swprintf_s(userbuf, COUNTOF(userbuf), L"%S", user); } if (!can_switch_ids()) { answer = FAILURE_NOT_SUPPORTED; } else { priv = set_root_priv(); switch(mode) { case ADD_MODE: bool retval; dprintf( D_FULLDEBUG, "Adding %S to credential storage.\n", userbuf ); retval = isValidCredential(user, pw); if ( ! retval ) { dprintf(D_FULLDEBUG, "store_cred: tried to add invalid credential\n"); answer=FAILURE_BAD_PASSWORD; break; // bail out } if (pw) { swprintf_s(pwbuf, COUNTOF(pwbuf), L"%S", pw); // make a wide-char copy first } // call lsa_mgr api // answer = return code if (!lsa_man.add(userbuf, pwbuf)){ answer = FAILURE; } else { answer = SUCCESS; } SecureZeroMemory(pwbuf, MAX_PASSWORD_LENGTH*sizeof(wchar_t)); break; case DELETE_MODE: dprintf( D_FULLDEBUG, "Deleting %S from credential storage.\n", userbuf ); pw_wc = lsa_man.query(userbuf); if ( !pw_wc ) { answer = FAILURE_NOT_FOUND; break; } else { SecureZeroMemory(pw_wc, wcslen(pw_wc)); delete[] pw_wc; } if (!isValidCredential(user, pw)) { dprintf(D_FULLDEBUG, "store_cred: invalid credential given for delete\n"); answer = FAILURE_BAD_PASSWORD; break; } // call lsa_mgr api // answer = return code if (!lsa_man.remove(userbuf)) { answer = FAILURE; } else { answer = SUCCESS; } break; case QUERY_MODE: { dprintf( D_FULLDEBUG, "Checking for %S in credential storage.\n", userbuf ); char passw[MAX_PASSWORD_LENGTH]; pw_wc = lsa_man.query(userbuf); if ( !pw_wc ) { answer = FAILURE_NOT_FOUND; } else { sprintf(passw, "%S", pw_wc); SecureZeroMemory(pw_wc, wcslen(pw_wc)); delete[] pw_wc; if ( isValidCredential(user, passw) ) { answer = SUCCESS; } else { answer = FAILURE_BAD_PASSWORD; } SecureZeroMemory(passw, MAX_PASSWORD_LENGTH); } break; } default: dprintf( D_ALWAYS, "store_cred: Unknown access mode (%d).\n", mode ); answer=0; break; } dprintf(D_FULLDEBUG, "Switching back to old priv state.\n"); set_priv(priv); } return answer; }