int main(int argc, char** argv) { #if HAVE_DECL_PTRACE_SETOPTIONS && HAVE_DECL_PTRACE_O_TRACEVFORKDONE int exit_status; _parse_opts(argc, argv); if (fp == NULL) { fp = stderr; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, psync) == -1) { tst_resm(TBROK | TERRNO, "socketpair() failed"); } else { child = fork(); if (child == -1) { tst_resm(TBROK | TERRNO, "fork() failed"); } else if (child == 0) { int rc = EXIT_FAILURE; tst_sig(FORK, DEF_HANDLER, child_cleanup); if (close(psync[1])) { tst_resm(TBROK, "close(psync[1]) failed)"); } else { /* sleep until the parent wakes us up */ await_mutex(psync[0]); rc = do_vfork(num_vforks); } _exit(rc); } else { tst_sig(FORK, kill_child, parent_cleanup); close(psync[0]); /* Set up ptrace */ if (ptrace(PTRACE_ATTACH, child, NULL, NULL) == -1) { tst_resm(TBROK | TERRNO, "ptrace(ATTACH) failed"); tst_exit(); } if (waitpid(child, NULL, 0) != child) { tst_resm(TBROK | TERRNO, "waitpid(%d) failed", child); kill_child(); } else { if (ptrace(PTRACE_SETOPTIONS, child, NULL, PTRACE_O_TRACEVFORK) == -1) { tst_resm(TINFO | TERRNO, "ptrace(PTRACE_SETOPTIONS) " "failed."); } if (ptrace(PTRACE_CONT, child, NULL, NULL) == -1) { tst_resm(TINFO | TERRNO, "ptrace(PTRACE_CONT) failed."); } send_mutex(psync[1]); close(psync[1]); tst_resm(TINFO, "Child spawn's pid=%d", child); fprintf(fp, "%d\n", child); fflush(fp); exit_status = do_trace(child, ++num_vforks); tst_resm(exit_status == 0 ? TPASS : TFAIL, "do_trace %s", (exit_status == 0 ? "succeeded" : "failed")); parent_cleanup(); } } } #else tst_resm(TCONF, "System doesn't support have required ptrace " "capabilities."); #endif tst_resm(TINFO, "Exiting..."); tst_exit(); }
/* Parse arguments, etc then get my socket address/port information. Attempt to * adopt this process into a job in the following order: * 1) If the user has only one job on the node, pick that one * 2) Send RPC to source IP of socket. If there is a slurmd at the IP * address, ask it which job I belong to. On success, pick that one * 3) Pick a job semi-randomly (default) or skip the adoption (if * configured) */ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __attribute__((unused)), int argc, const char **argv) { int retval = PAM_IGNORE, rc, slurmrc, bufsize, user_jobs; char *user_name; List steps = NULL; step_loc_t *stepd = NULL; struct passwd pwd, *pwd_result; char *buf = NULL; _init_opts(); _parse_opts(pamh, argc, argv); _log_init(opts.log_level); switch (opts.action_generic_failure) { case CALLERID_ACTION_DENY: rc = PAM_PERM_DENIED; break; case CALLERID_ACTION_ALLOW: rc = PAM_SUCCESS; break; case CALLERID_ACTION_IGNORE: rc = PAM_IGNORE; break; /* Newer gcc versions warn if enum cases are missing */ default: error("The code is broken!!!!"); } retval = pam_get_item(pamh, PAM_USER, (void *) &user_name); if (user_name == NULL || retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "No username in PAM_USER? Fail!"); return PAM_SESSION_ERR; } /* Check for an unsafe config that might lock out root. This is a very * basic check that shouldn't be 100% relied on */ if (!opts.ignore_root && (opts.action_unknown == CALLERID_ACTION_DENY || opts.action_no_jobs != CALLERID_ACTION_ALLOW || opts.action_adopt_failure != CALLERID_ACTION_ALLOW || opts.action_generic_failure != CALLERID_ACTION_ALLOW )) { /* Let's get verbose */ info("==============================="); info("Danger!!!"); info("A crazy admin set ignore_root=0 and some unsafe actions"); info("You might lock out root!"); info("If this is desirable, modify the source code"); info("Setting ignore_root=1 and continuing"); opts.ignore_root = 1; } /* Ignoring root is probably best but the admin can allow it */ if (!strcmp(user_name, "root")) { if (opts.ignore_root) { info("Ignoring root user"); return PAM_IGNORE; } else { /* This administrator is crazy */ info("Danger!!! This is a connection attempt by root and ignore_root=0 is set! Hope for the best!"); } } /* Calculate buffer size for getpwnam_r */ bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) bufsize = 16384; /* take a large guess */ buf = xmalloc(bufsize); retval = getpwnam_r(user_name, &pwd, buf, bufsize, &pwd_result); if (pwd_result == NULL) { if (retval == 0) { error("getpwnam_r could not locate %s", user_name); } else { errno = retval; error("getpwnam_r: %m"); } xfree(buf); return PAM_SESSION_ERR; } if (_load_cgroup_config() != SLURM_SUCCESS) return rc; /* Check if there are any steps on the node from any user. A failure here * likely means failures everywhere so exit on failure or if no local jobs * exist. */ steps = stepd_available(NULL, opts.node_name); if (!steps) { error("Error obtaining local step information."); goto cleanup; } /* Check to see if this user has only one job on the node. If so, choose * that job and adopt this process into it (unless configured not to) */ user_jobs = _user_job_count(steps, pwd.pw_uid, &stepd); if (user_jobs == 0) { if (opts.action_no_jobs == CALLERID_ACTION_DENY) { send_user_msg(pamh, "Access denied by " PAM_MODULE_NAME ": you have no active jobs on this node"); rc = PAM_PERM_DENIED; } else { debug("uid %u owns no jobs but action_no_jobs=ignore", pwd.pw_uid); rc = PAM_IGNORE; } goto cleanup; } else if (user_jobs == 1) { if (opts.single_job_skip_rpc) { info("Connection by user %s: user has only one job %u", user_name, stepd->jobid); slurmrc = _adopt_process(getpid(), stepd); /* If adoption into the only job fails, it is time to * exit. Return code is based on the * action_adopt_failure setting */ if (slurmrc == SLURM_SUCCESS || (opts.action_adopt_failure == CALLERID_ACTION_ALLOW)) rc = PAM_SUCCESS; else rc = PAM_PERM_DENIED; goto cleanup; } } else { debug("uid %u has %d jobs", pwd.pw_uid, user_jobs); } /* Single job check turned up nothing (or we skipped it). Make RPC call * to slurmd at source IP. If it can tell us the job, the function calls * _adopt_process */ rc = _try_rpc(&pwd); if (rc == PAM_SUCCESS) goto cleanup; /* The source of the connection either didn't reply or couldn't * determine the job ID at the source. Proceed to action_unknown */ rc = _action_unknown(pamh, &pwd, steps); cleanup: FREE_NULL_LIST(steps); xfree(buf); xfree(slurm_cgroup_conf); xfree(opts.node_name); return rc; }