view cinst/nssstore_linux.c @ 1070:f110a3f6e387

(issue114) Fine tune ACL propagation using mkdir_p the ACL of the parent directories would propagate to all subdirectories and objects in the directory. Now we only use ACL propagation in the last directory to make sure that files we might create in that directory inherit the correct (resitricted) ACL
author Andre Heinecke <andre.heinecke@intevation.de>
date Wed, 10 Sep 2014 16:41:36 +0200
parents 317ee9dc4684
children e210ecc32d69
line wrap: on
line source
/* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU GPL (v>=2)
 * and comes with ABSOLUTELY NO WARRANTY!
 * See LICENSE.txt for details.
 */
#ifndef WIN32

/* @file
   @brief Linux implementation of nssstore process control.
*/

#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <pwd.h>

#include "nssstore.h"
#include "logging.h"
#include "strhelp.h"
#include "util.h"

#define NSS_PROCESS_NAME "mozilla"


/**@brief Start the process to install / remove
 *
 * This forks the process and executes the NSS installation
 * process. It also writes the Instructions to that process.
 *
 * @param [in] to_install strv of DER encoded certificates to be added.
 * @param [in] to_remove strv of DER encoded certificates to be remvoed.
 * @param [in] uid uid of the user to install certificates for.
 * @param [in] gid the gid of the user to install certificates for.
 * @param [in] homedir the homedir of the user.
 *
 * @returns childs pid on success. -1 on failure
 */
static int
start_procces_for_user (char **to_install, char **to_remove,
                        uid_t uid, gid_t gid, char *homedir)
{
  int pipe_fd[2];
  pid_t pid = 0;
  char *argv[3] = {NULL, NULL, NULL},
                  *envp[2] = {NULL, NULL},
                             *inst_dir = NULL;
  size_t homedir_len = 0,
         exe_path_len = 0;
  int ret = -1,
      i = 0;
  FILE *stream = NULL;
  bool success = false;

  if (homedir == NULL)
    {
      ERRORPRINTF ("Invalid call to start_process_for_user\n");
      return -1;
    }

  homedir_len = strlen (homedir);

  /* Allocate space for HOME=homedir\0 */
  envp[0] = xmalloc (homedir_len + 6);
  envp[1] = NULL;

  ret = snprintf (envp[0], homedir_len + 6, "HOME=%s", homedir);

  if (ret < 0 || (size_t) ret != homedir_len + 5)
    {
      ERRORPRINTF ("Error setting home env variable.\n");
      xfree (envp[0]);
      return -1;
    }

  /* Set up the file name of the installer process */
  inst_dir = get_install_dir();
  if (inst_dir == NULL)
    {
      ERRORPRINTF ("Failed to find installation directory.\n");
      xfree (envp[0]);
      return -1;
    }

  exe_path_len = strlen(inst_dir) + strlen(NSS_PROCESS_NAME);
  argv[0] = xmalloc (exe_path_len + 1);

  if (g_debug)
    {
      argv[1] = "--debug";
    }

  ret = snprintf(argv[0], exe_path_len + 1, "%s%s", inst_dir, NSS_PROCESS_NAME);
  xfree (inst_dir);
  if (ret < 0 || (size_t) ret != exe_path_len)
    {
      ERRORPRINTF ("Error setting executable variable.\n");
      xfree (argv[0]);
      return -1;
    }

  if (pipe (pipe_fd))
    {
      ERRORPRINTF ("Failed to create pipe.\n");
      return -1;
    }

  pid = fork();

  if (pid == (pid_t) -1)
    {
      ERRORPRINTF ("Failed to fork child.\n");
      return -1;
    }

  if (pid == (pid_t) 0)
    {
      /* Drop privileges */
      if (setgid (gid) || setuid (uid))
        {
          syslog_error_printf("Failed to drop privileges: %s", strerror(errno));
          exit(-1);
        }

      close (pipe_fd[1]);
      dup2 (pipe_fd[0], 0);
      close (pipe_fd[0]);
      execve (argv[0], argv, envp);
      exit (127);
    }

  close (pipe_fd[0]);
  stream = fdopen(pipe_fd[1], "w");
  if (stream == NULL)
    {
      ERRORPRINTF ("Failed to open pipe for writing\n");
      goto done;
    }

  /* The NSS installer may exit on error before we are done
   * telling it what to do. We want to handle that rather
   * then die unexpectedly. */
  signal(SIGPIPE, SIG_IGN);

  /* Send the instructions */
  for (i = 0; to_install && to_install[i]; i++)
    {
      if (fprintf (stream, "I:%s\n", to_install[i]) <= 3)
        {
          int err = errno;
          ERRORPRINTF ("Write failed: %s \n", strerror(err));
          if (err == 32)
            {
              /* Broken pipe is expected if there are no NSS stores
                 to be found the process just exits. That's ok */
              success = true;
            }
          goto done;
        }
    }

  for (i = 0; to_remove && to_remove[i]; i++)
    {
      if (fprintf (stream, "R:%s\n", to_remove[i]) <= 3)
        {
          int err = errno;
          ERRORPRINTF ("Write failed: %s \n", strerror(err));
          if (err == 32)
            {
              /* Broken pipe is expected if there are no NSS stores
                 to be found the process just exits. That's ok */
              success = true;
            }
          goto done;
        }
    }

  success = true;

done:
  if (stream)
    {
      fclose (stream);
    }
  xfree (argv[0]);
  xfree (envp[0]);
  close (pipe_fd[0]);
  close (pipe_fd[1]);

  if (success)
    {
      return pid;
    }
  return -1;
}

int
write_stores_nss (char **to_install, char **to_remove)
{
  struct passwd *usr_it = NULL;
  uid_t my_uid = geteuid();
  pid_t childprocess = -1;
  int status = -1;

  if (my_uid != 0)
    {
      /* Running as a user */
      char *homedir = getenv ("HOME");
      if (!homedir)
        {
          ERRORPRINTF ("Failed to find home directory\n");
        }

      /* Only one child for single user installation */
      childprocess = start_procces_for_user (to_install, to_remove,
                                             my_uid, getgid(), homedir);

      if (childprocess == -1)
        {
          ERRORPRINTF ("Failed to start childprocess!\n");
          return -1;
        }

      childprocess = waitpid (childprocess, &status, 0);
      if (childprocess == -1 || !WIFEXITED(status))
        {
          ERRORPRINTF ("Waitpid failed.\n");
          return -1;
        }

      return 0;
    }

  /* Start once as root to install in the system default directories. */
  childprocess = start_procces_for_user (to_install, to_remove,
                                         my_uid, getgid(), getenv ("HOME"));
  if (childprocess == -1)
    {
      ERRORPRINTF ("Failed to start default profile installation!\n");
      return -1;
    }

  /* Wait until the default profile directories are done. */
  childprocess = waitpid (childprocess, &status, 0);
  if (childprocess == -1 || !WIFEXITED(status))
    {
      ERRORPRINTF ("Child process did not finish.\n");
      return -1;
    }

  setpwent();

  while ((usr_it = getpwent ()) != NULL)
    {
      /* Skip obvious system accounts */
      if (strcmp(usr_it->pw_shell, "/usr/sbin/nologin") == 0 ||
          strcmp(usr_it->pw_shell, "/bin/false") == 0)
        {
          continue;
        }
      /* A check if the home directory starts with /home might be
         appropiate */
      start_procces_for_user (to_install,
                              to_remove,
                              usr_it->pw_uid,
                              usr_it->pw_gid,
                              usr_it->pw_dir);

    }

  endpwent();

  waitpid (-1, NULL, 0);

  DEBUGPRINTF ("NSS installation done\n");
  return 0;
}
#endif

http://wald.intevation.org/projects/trustbridge/