/* * Run a prolog or epilog script (does NOT drop privileges) * name IN: class of program (prolog, epilog, etc.), * path IN: pathname of program to run * job_id IN: info on associated job * max_wait IN: maximum time to wait in seconds, -1 for no limit * env IN: environment variables to use on exec, sets minimal environment * if NULL * uid IN: user ID of job owner * RET 0 on success, -1 on failure. */ static int _run_one_script(const char *name, const char *path, uint32_t job_id, int max_wait, char **env, uid_t uid) { int status; pid_t cpid; xassert(env); if (path == NULL || path[0] == '\0') return 0; if (job_id) { debug("[job %u] attempting to run %s [%s]", job_id, name, path); } else debug("attempting to run %s [%s]", name, path); if (access(path, R_OK | X_OK) < 0) { error("Can not run %s [%s]: %m", name, path); return -1; } if ((cpid = fork()) < 0) { error ("executing %s: fork: %m", name); return -1; } if (cpid == 0) { char *argv[2]; /* container_g_join needs to be called in the forked process part of the fork to avoid a race condition where if this process makes a file or detacts itself from a child before we add the pid to the container in the parent of the fork. */ if (container_g_join(job_id, getuid()) != SLURM_SUCCESS) error("container_g_join(%u): %m", job_id); argv[0] = (char *)xstrdup(path); argv[1] = NULL; setpgid(0, 0); execve(path, argv, env); error("execve(%s): %m", path); exit(127); } if (waitpid_timeout(name, cpid, &status, max_wait) < 0) return (-1); return status; }
/* * Run a prolog or epilog script (does NOT drop privileges) * name IN: class of program (prolog, epilog, etc.), * path IN: pathname of program to run * jobid IN: info on associated job * max_wait IN: maximum time to wait in seconds, -1 for no limit * env IN: environment variables to use on exec, sets minimal environment * if NULL * RET 0 on success, -1 on failure. */ static int run_one_script(const char *name, const char *path, uint32_t jobid, int max_wait, char **env) { int status; pid_t cpid; xassert(env); if (path == NULL || path[0] == '\0') return 0; if (jobid) { debug("[job %u] attempting to run %s [%s]", jobid, name, path); } else debug("attempting to run %s [%s]", name, path); if (access(path, R_OK | X_OK) < 0) { error("Can not run %s [%s]: %m", name, path); return -1; } if ((cpid = fork()) < 0) { error ("executing %s: fork: %m", name); return -1; } if (cpid == 0) { char *argv[2]; argv[0] = (char *)xstrdup(path); argv[1] = NULL; #ifdef SETPGRP_TWO_ARGS setpgrp(0, 0); #else setpgrp(); #endif execve(path, argv, env); error("execve(): %m"); exit(127); } if (waitpid_timeout(name, cpid, &status, max_wait) < 0) return (-1); return status; }
/* Poll the requested URL until a failure or timeout occurs, or until the child terminates on its own. Returns 1 on HTTP failure or timeout, 0 on self-termination. In either case, *status_ptr is filled in with the status value returned by waitpid().*/ int do_watchdog(int *status_ptr) { #ifndef HAVE_LIBCURL fprintf(stderr, "Cannot watchdog; no libcurl available.\n"); return 0; #else /* HAVE_LIBCURL */ CURL *curl; CURLcode res; char error_buffer[CURL_ERROR_SIZE]; pid_t wresult; // Before we start polling the URL, wait at least start milliseconds. wresult = waitpid_timeout(child_pid, status_ptr, watchdog_start_sec * 1000); if (wresult == child_pid) { // The child terminated on its own before we got started. return 0; } curl = curl_easy_init(); if (!curl) { fprintf(stderr, "Cannot watchdog; curl failed to init.\n"); return 0; } curl_easy_setopt(curl, CURLOPT_URL, watchdog_url); /*curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);*/ curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, watchdog_timeout_sec * 1000); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, watchdog_bitbucket); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer); curl_easy_setopt(curl, CURLOPT_USERAGENT, "autorestart"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1); curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1); res = curl_easy_perform(curl); while (res == 0) { /* 0: The HTTP request finished successfully (but might or might not have returned an error code like a 404). */ long http_response = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_response); if ((http_response / 100) != 2) { /* Anything in the 200 range is deemed success. Anything else is deemed failure. */ fprintf(stderr, "%s returned %ld\n", watchdog_url, http_response); break; } wresult = waitpid_timeout(child_pid, status_ptr, watchdog_cycle_sec * 1000); if (wresult == child_pid) { /* The process terminated on its own. Return 0 to indicate this. */ return 0; } res = curl_easy_perform(curl); } curl_easy_cleanup(curl); /* Failed to retrieve the watchdog URL. */ if (res != 0) { fprintf(stderr, "Failed to contact %s: %s\n", watchdog_url, error_buffer); } /* Kill the child process and wait for it to go away. */ kill(child_pid, SIGTERM); pid_t result = waitpid_timeout(child_pid, status_ptr, MAX_WAITTERM_SEC * 1000); if (result != child_pid) { if (result == -1) { perror("waitpid"); } else { /* SIGTERM didn't make the process die. Try SIGKILL. */ fprintf(stderr, "Force-killing child process\n"); kill(child_pid, SIGKILL); result = waitpid_timeout(child_pid, status_ptr, MAX_WAITTERM_SEC * 1000); if (result == -1) { perror("waitpid"); } } } /* Return 1 to indicate we killed the child due to an HTTP error. */ return 1; #endif /* HAVE_LIBCURL */ }
PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) { const char *user = NULL, *passwd = NULL; struct passwd *pwd; int rval, status; pid_t pid; // For checking mount paths: (mount from + target) char path[PATH_MAX]; char targetpath[PATH_MAX]; char encfs_options[USERNAME_MAX]; char fuse_options[USERNAME_MAX]; char *targetpath_store; strcpy(default_encfs_options, ""); strcpy(default_fuse_options, ""); // For execing: char *arg[USERNAME_MAX]; int arg_pos = 0; int i; int inpipe[2], outpipe[2]; rval = pam_get_user(pamh, &user, NULL); if ((rval != PAM_SUCCESS) || (!user)) { _pam_log(LOG_ERR, "can't get username: %s", pam_strerror(pamh, rval)); return PAM_AUTH_ERR; } rval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) (void *) &passwd); if (rval != PAM_SUCCESS) { _pam_log(LOG_ERR, "Could not retrieve user's password"); return PAM_AUTH_ERR; } if (!passwd) { rval = _set_auth_tok(pamh, flags, argc, argv); if (rval != PAM_SUCCESS) { return rval; } rval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) (void *) &passwd); if (rval != PAM_SUCCESS || passwd == NULL) { _pam_log(LOG_ERR, "Could not retrieve user's password"); return PAM_AUTH_ERR; } } if ((pwd = getpwnam(user)) == NULL) { _pam_log(LOG_ERR, "Could not getpwnam"); return PAM_AUTH_ERR; } // Read configfile if (!readconfig (pwd, pamh, pwd->pw_name, path, targetpath, encfs_options, fuse_options)) { // DEBUG _pam_log(LOG_ERR,"No entry for user found in log"); return PAM_IGNORE; } //DEBUG _pam_log(LOG_ERR,"Username : %s, Encpath : %s, Targetmount : %s",pwd->pw_name,path,targetpath); //Store targetpath targetpath_store = strdup(targetpath); if ((i = pam_set_data(pamh, "encfs_targetpath", targetpath_store, targetpath_cleanup)) != PAM_SUCCESS) { _pam_log(LOG_ERR, "Storing targetpath FAIL"); free(targetpath_store); return i; } // Check if we're mounted already. if (checkmnt(targetpath)) { //DEBUG _pam_log(LOG_ERR,"Already mounted"); return PAM_IGNORE; } /* _pam_log(LOG_ERR,"Config output for %s:",user); _pam_log(LOG_ERR," path : %s",path); _pam_log(LOG_ERR," targetpath : %s",targetpath); _pam_log(LOG_ERR," encfs : %s %s",default_encfs_options,encfs_options); _pam_log(LOG_ERR," fuse : %s %s",default_fuse_options,fuse_options); */ arg_pos += buildCmd(arg, arg_pos, "encfs"); arg_pos += buildCmd(arg, arg_pos, "-S"); arg_pos += buildCmd(arg, arg_pos, default_encfs_options); arg_pos += buildCmd(arg, arg_pos, encfs_options); arg_pos += buildCmd(arg, arg_pos, path); arg_pos += buildCmd(arg, arg_pos, targetpath); if (strlen(default_fuse_options) > 0 && strlen(fuse_options) > 0) strcat(fuse_options, ","); strcat(fuse_options,default_fuse_options); if (strlen(fuse_options) > 0) { arg_pos += buildCmd(arg, arg_pos, "--"); arg_pos += buildCmd(arg, arg_pos, "-o"); arg_pos += buildCmd(arg, arg_pos, fuse_options); } arg[arg_pos] = NULL; /* printf("Arguments : "); for (i = 0; i < arg_pos+1;i++) { _pam_log(LOG_ERR,"Data : %s",arg[i]); } _pam_log(LOG_ERR,"Number of arguments : %d",arg_pos); */ /* arg[0] = cmd; arg[1] = params; // arg[2] = params2; arg[2] = params3; arg[3] = path; arg[4] = targetpath; arg[5] = fuseparams; arg[6] = fuseparams2; arg[7] = NULL; */ if (pipe(inpipe) || pipe(outpipe)) { _pam_log(LOG_ERR, "Failed to create pipe"); return PAM_IGNORE; } // Execute switch (pid = fork()) { case -1: _pam_log(LOG_ERR, "Fork failed"); return PAM_SERVICE_ERR; case 0: if (drop_permissions == 1) if ((initgroups(pwd->pw_name, pwd->pw_gid) == -1) || (setgid(pwd->pw_gid) == -1) || (setuid(pwd->pw_uid) == -1)) { _pam_log(LOG_ERR, "Dropping permissions failed"); return PAM_SERVICE_ERR; } close(outpipe[WRITE_END]); dup2(outpipe[READ_END], fileno(stdin)); close(outpipe[READ_END]); close(inpipe[READ_END]); dup2(inpipe[WRITE_END], fileno(stdout)); close(inpipe[WRITE_END]); // For some reason the current directory has to be set to targetpath (or path?) before exec'ing encfs through gdm chdir(targetpath); execvp("encfs", arg); char errstr[128]; snprintf(errstr, 127, "%d - %s", errno, strerror(errno)); _pam_log(LOG_ERR, "Exec failed - %s", errstr); exit(127); } int len; close(inpipe[WRITE_END]); close(outpipe[READ_END]); if (waitpid(pid, &status, WNOHANG) == 0) { len = write(outpipe[WRITE_END], passwd, (size_t) strlen(passwd)); if ((len != (size_t) strlen(passwd)) || (write(outpipe[WRITE_END], "\n", 1) != 1)) _pam_log(LOG_ERR, "Did not send password to pipe (%d sent)", len); close(outpipe[WRITE_END]); } if (waitpid_timeout(pid, &status, 0)) { _pam_log(LOG_ERR, "Timed out waiting for encfs, killing\n"); kill(pid, SIGKILL); } int exitstatus = WEXITSTATUS(status); char buff[512]; len = read(inpipe[READ_END], &buff, 511); close(inpipe[READ_END]); buff[len] = 0; if (!checkmnt(targetpath) && (len > 0 || exitstatus > 0)) { _pam_log(LOG_ERR, "exitcode : %d, errorstring : %s", exitstatus, buff); return PAM_AUTH_ERR; } else { return PAM_IGNORE; } return PAM_AUTH_ERR; }