示例#1
0
文件: process.c 项目: InCNTRE/OFTT
/* Starts a subprocess with the arguments in the null-terminated argv[] array.
 * argv[0] is used as the name of the process.  Searches the PATH environment
 * variable to find the program to execute.
 *
 * All file descriptors are closed before executing the subprocess, except for
 * fds 0, 1, and 2 and the 'n_keep_fds' fds listed in 'keep_fds'.  Also, any of
 * the 'n_null_fds' fds listed in 'null_fds' are replaced by /dev/null.
 *
 * Returns 0 if successful, otherwise a positive errno value indicating the
 * error.  If successful, '*pp' is assigned a new struct process that may be
 * used to query the process's status.  On failure, '*pp' is set to NULL. */
int
process_start(char **argv,
              const int keep_fds[], size_t n_keep_fds,
              const int null_fds[], size_t n_null_fds,
              struct process **pp)
{
    sigset_t oldsigs;
    pid_t pid;
    int error;

    *pp = NULL;
    COVERAGE_INC(process_start);
    error = process_prestart(argv);
    if (error) {
        return error;
    }

    block_sigchld(&oldsigs);
    pid = fork();
    if (pid < 0) {
        unblock_sigchld(&oldsigs);
        VLOG_WARN("fork failed: %s", strerror(errno));
        return errno;
    } else if (pid) {
        /* Running in parent process. */
        *pp = process_register(argv[0], pid);
        unblock_sigchld(&oldsigs);
        return 0;
    } else {
        /* Running in child process. */
        int fd_max = get_max_fds();
        int fd;

        fatal_signal_fork();
        unblock_sigchld(&oldsigs);
        for (fd = 0; fd < fd_max; fd++) {
            if (is_member(fd, null_fds, n_null_fds)) {
                /* We can't use get_null_fd() here because we might have
                 * already closed its fd. */
                int nullfd = open("/dev/null", O_RDWR);
                dup2(nullfd, fd);
                close(nullfd);
            } else if (fd >= 3 && !is_member(fd, keep_fds, n_keep_fds)) {
                close(fd);
            }
        }
        execvp(argv[0], argv);
        fprintf(stderr, "execvp(\"%s\") failed: %s\n",
                argv[0], strerror(errno));
        _exit(1);
    }
}
示例#2
0
文件: process.c 项目: InCNTRE/OFTT
/* Destroys process 'p'. */
void
process_destroy(struct process *p)
{
    if (p) {
        sigset_t oldsigs;

        block_sigchld(&oldsigs);
        list_remove(&p->node);
        unblock_sigchld(&oldsigs);

        free(p->name);
        free(p);
    }
}
示例#3
0
/**
 * This function is the PAM conversation driver. It conducts a full
 * authentication round by invoking the GUI with various prompts.
 */
void
pam_try_unlock(saver_info *si, Bool verbose_p,
	       Bool (*valid_p)(const char *typed_passwd, Bool verbose_p))
{
  const char *service = PAM_SERVICE_NAME;
  pam_handle_t *pamh = 0;
  int status = -1;
  struct pam_conv pc;
  sigset_t set;
  struct timespec timeout;

  pc.conv = &pam_conversation;
  pc.appdata_ptr = (void *) si;

  /* On SunOS 5.6, the `appdata_ptr' slot seems to be ignored, and the
     `closure' argument to pc.conv always comes in as random garbage. */
  suns_pam_implementation_blows = (void *) si;


  /* Initialize PAM.
   */
  status = pam_start (service, si->user, &pc, &pamh);
  if (verbose_p)
    fprintf (stderr, "%s: pam_start (\"%s\", \"%s\", ...) ==> %d (%s)\n",
             blurb(), service, si->user,
             status, PAM_STRERROR (pamh, status));
  if (status != PAM_SUCCESS) goto DONE;

  /* #### We should set PAM_TTY to the display we're using, but we
     don't have that handy from here.  So set it to :0.0, which is a
     good guess (and has the bonus of counting as a "secure tty" as
     far as PAM is concerned...)
   */
  {
    char *tty = strdup (":0.0");
    status = pam_set_item (pamh, PAM_TTY, tty);
    if (verbose_p)
      fprintf (stderr, "%s:   pam_set_item (p, PAM_TTY, \"%s\") ==> %d (%s)\n",
               blurb(), tty, status, PAM_STRERROR(pamh, status));
    free (tty);
  }

  /* Try to authenticate as the current user.
     We must turn off our SIGCHLD handler for the duration of the call to
     pam_authenticate(), because in some cases, the underlying PAM code
     will do this:

        1: fork a setuid subprocess to do some dirty work;
        2: read a response from that subprocess;
        3: waitpid(pid, ...) on that subprocess.

    If we (the ignorant parent process) have a SIGCHLD handler, then there's
    a race condition between steps 2 and 3: if the subprocess exits before
    waitpid() was called, then our SIGCHLD handler fires, and gets notified
    of the subprocess death; then PAM's call to waitpid() fails, because the
    process has already been reaped.

    I consider this a bug in PAM, since the caller should be able to have
    whatever signal handlers it wants -- the PAM documentation doesn't say
    "oh by the way, if you use PAM, you can't use SIGCHLD."
   */

  PAM_NO_DELAY(pamh);

  if (verbose_p)
    fprintf (stderr, "%s:   pam_authenticate (...) ...\n", blurb());

  timeout.tv_sec = 0;
  timeout.tv_nsec = 1;
  set = block_sigchld();
  status = pam_authenticate (pamh, 0);
# ifdef HAVE_SIGTIMEDWAIT
  sigtimedwait (&set, NULL, &timeout);
  /* #### What is the portable thing to do if we don't have it? */
# endif /* HAVE_SIGTIMEDWAIT */
  unblock_sigchld();

  if (verbose_p)
    fprintf (stderr, "%s:   pam_authenticate (...) ==> %d (%s)\n",
             blurb(), status, PAM_STRERROR(pamh, status));

  if (status == PAM_SUCCESS)  /* Win! */
    {
      int status2;

      /* On most systems, it doesn't matter whether the account modules
         are run, or whether they fail or succeed.

         On some systems, the account modules fail, because they were
         never configured properly, but it's necessary to run them anyway
         because certain PAM modules depend on side effects of the account
         modules having been run.

         And on still other systems, the account modules are actually
         used, and failures in them should be considered to be true!

         So:
         - We run the account modules on all systems.
         - Whether we ignore them is a configure option.

         It's all kind of a mess.
       */
      status2 = pam_acct_mgmt (pamh, 0);

      if (verbose_p)
        fprintf (stderr, "%s:   pam_acct_mgmt (...) ==> %d (%s)\n",
                 blurb(), status2, PAM_STRERROR(pamh, status2));

      /* HPUX for some reason likes to make PAM defines different from
       * everyone else's. */
#ifdef PAM_AUTHTOKEN_REQD
      if (status2 == PAM_AUTHTOKEN_REQD)
#else
      if (status2 == PAM_NEW_AUTHTOK_REQD)
#endif
        {
          status2 = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
          if (verbose_p)
            fprintf (stderr, "%s: pam_chauthtok (...) ==> %d (%s)\n",
                     blurb(), status2, PAM_STRERROR(pamh, status2));
        }

      /* If 'configure' requested that we believe the results of PAM
         account module failures, then obey that status code.
         Otherwise ignore it.
       */
#ifdef PAM_CHECK_ACCOUNT_TYPE
       status = status2;
#endif

      /* Each time we successfully authenticate, refresh credentials,
         for Kerberos/AFS/DCE/etc.  If this fails, just ignore that
         failure and blunder along; it shouldn't matter.
       */

#if defined(__linux__)
      /* Apparently the Linux PAM library ignores PAM_REFRESH_CRED and only
         refreshes credentials when using PAM_REINITIALIZE_CRED. */
      status2 = pam_setcred (pamh, PAM_REINITIALIZE_CRED);
#else
      /* But Solaris requires PAM_REFRESH_CRED or extra prompts appear. */
      status2 = pam_setcred (pamh, PAM_REFRESH_CRED);
#endif

      if (verbose_p)
        fprintf (stderr, "%s:   pam_setcred (...) ==> %d (%s)\n",
                 blurb(), status2, PAM_STRERROR(pamh, status2));
    }

 DONE:
  if (pamh)
    {
      int status2 = pam_end (pamh, status);
      pamh = 0;
      if (verbose_p)
        fprintf (stderr, "%s: pam_end (...) ==> %d (%s)\n",
                 blurb(), status2,
                 (status2 == PAM_SUCCESS ? "Success" : "Failure"));
    }

  if (status == PAM_SUCCESS)
    si->unlock_state = ul_success;	     /* yay */
  else if (si->unlock_state == ul_cancel ||
           si->unlock_state == ul_time)
    ;					     /* more specific failures ok */
  else
    si->unlock_state = ul_fail;		     /* generic failure */
}
示例#4
0
文件: process.c 项目: InCNTRE/OFTT
/* Starts the process whose arguments are given in the null-terminated array
 * 'argv' and waits for it to exit.  On success returns 0 and stores the
 * process exit value (suitable for passing to process_status_msg()) in
 * '*status'.  On failure, returns a positive errno value and stores 0 in
 * '*status'.
 *
 * If 'stdout_log' is nonnull, then the subprocess's output to stdout (up to a
 * limit of PROCESS_MAX_CAPTURE bytes) is captured in a memory buffer, which
 * when this function returns 0 is stored as a null-terminated string in
 * '*stdout_log'.  The caller is responsible for freeing '*stdout_log' (by
 * passing it to free()).  When this function returns an error, '*stdout_log'
 * is set to NULL.
 *
 * If 'stderr_log' is nonnull, then it is treated like 'stdout_log' except
 * that it captures the subprocess's output to stderr. */
int
process_run_capture(char **argv, char **stdout_log, char **stderr_log,
                    int *status)
{
    struct stream s_stdout, s_stderr;
    sigset_t oldsigs;
    pid_t pid;
    int error;

    COVERAGE_INC(process_run_capture);
    if (stdout_log) {
        *stdout_log = NULL;
    }
    if (stderr_log) {
        *stderr_log = NULL;
    }
    *status = 0;
    error = process_prestart(argv);
    if (error) {
        return error;
    }

    error = stream_open(&s_stdout);
    if (error) {
        return error;
    }

    error = stream_open(&s_stderr);
    if (error) {
        stream_close(&s_stdout);
        return error;
    }

    block_sigchld(&oldsigs);
    pid = fork();
    if (pid < 0) {
        int error = errno;

        unblock_sigchld(&oldsigs);
        VLOG_WARN("fork failed: %s", strerror(error));

        stream_close(&s_stdout);
        stream_close(&s_stderr);
        *status = 0;
        return error;
    } else if (pid) {
        /* Running in parent process. */
        struct process *p;

        p = process_register(argv[0], pid);
        unblock_sigchld(&oldsigs);

        close(s_stdout.fds[1]);
        close(s_stderr.fds[1]);
        while (!process_exited(p)) {
            stream_read(&s_stdout);
            stream_read(&s_stderr);

            stream_wait(&s_stdout);
            stream_wait(&s_stderr);
            process_wait(p);
            poll_block();
        }
        stream_read(&s_stdout);
        stream_read(&s_stderr);

        if (stdout_log) {
            *stdout_log = ds_steal_cstr(&s_stdout.log);
        }
        if (stderr_log) {
            *stderr_log = ds_steal_cstr(&s_stderr.log);
        }

        stream_close(&s_stdout);
        stream_close(&s_stderr);

        *status = process_status(p);
        process_destroy(p);
        return 0;
    } else {
        /* Running in child process. */
        int max_fds;
        int i;

        fatal_signal_fork();
        unblock_sigchld(&oldsigs);

        dup2(get_null_fd(), 0);
        dup2(s_stdout.fds[1], 1);
        dup2(s_stderr.fds[1], 2);

        max_fds = get_max_fds();
        for (i = 3; i < max_fds; i++) {
            close(i);
        }

        execvp(argv[0], argv);
        fprintf(stderr, "execvp(\"%s\") failed: %s\n",
                argv[0], strerror(errno));
        exit(EXIT_FAILURE);
    }
}
static int
gs_auth_thread_func (int auth_operation_fd)
{
        static const int flags = 0;
        int              status;
        int              status2;
        struct timespec  timeout;
        sigset_t         set;
        const void      *p;

        timeout.tv_sec = 0;
        timeout.tv_nsec = 1;

        set = block_sigchld ();

        status = pam_authenticate (pam_handle, flags);

        sigtimedwait (&set, NULL, &timeout);
        unblock_sigchld ();

        if (gs_auth_get_verbose ()) {
                g_message ("   pam_authenticate (...) ==> %d (%s)",
                           status,
                           PAM_STRERROR (pam_handle, status));
        }

        if (status != PAM_SUCCESS) {
                goto done;
        }

        if ((status = pam_get_item (pam_handle, PAM_USER, &p)) != PAM_SUCCESS) {
                /* is not really an auth problem, but it will
                   pretty much look as such, it shouldn't really
                   happen */
                goto done;
        }

        /* We don't actually care if the account modules fail or succeed,
         * but we need to run them anyway because certain pam modules
         * depend on side effects of the account modules getting run.
         */
        status2 = pam_acct_mgmt (pam_handle, 0);

        if (gs_auth_get_verbose ()) {
                g_message ("pam_acct_mgmt (...) ==> %d (%s)\n",
                           status2,
                           PAM_STRERROR (pam_handle, status2));
        }

        /* FIXME: should we handle these? */
        switch (status2) {
        case PAM_SUCCESS:
                break;
        case PAM_NEW_AUTHTOK_REQD:
                status2 = pam_chauthtok (pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);

                if (status2 != PAM_SUCCESS) {
                    g_message ("pam_acct_mgmt (...) ==> %d (%s)\n",
                           status2,
                           PAM_STRERROR (pam_handle, status2));
		    status = status2;
                }
                break;
        case PAM_AUTHINFO_UNAVAIL:
                break;
        case PAM_ACCT_EXPIRED:
                break;
        case PAM_PERM_DENIED:
                break;
        default :
                break;
        }

        /* Each time we successfully authenticate, refresh credentials,
           for Kerberos/AFS/DCE/etc.  If this fails, just ignore that
           failure and blunder along; it shouldn't matter.

           Note: this used to be PAM_REFRESH_CRED instead of
           PAM_REINITIALIZE_CRED, but Jason Heiss <*****@*****.**>
           says that the Linux PAM library ignores that one, and only refreshes
           credentials when using PAM_REINITIALIZE_CRED.
        */
        status2 = pam_setcred (pam_handle, PAM_REINITIALIZE_CRED);
        if (gs_auth_get_verbose ()) {
                g_message ("   pam_setcred (...) ==> %d (%s)",
                           status2,
                           PAM_STRERROR (pam_handle, status2));
        }

 done:
        /* we're done, close the fd and wake up the main
         * loop
         */
        close (auth_operation_fd);

        return status;
}