Mercurial > trustbridge
view cinst/nssstore_linux.c @ 478:214bf504c54f
Handle broken pipe as it is expected behavior
author | Andre Heinecke <aheinecke@intevation.de> |
---|---|
date | Thu, 24 Apr 2014 10:39:09 +0000 |
parents | e8d761c2d2d1 |
children | e41a2537b84d |
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 "nssstore.h" #include "logging.h" #include "strhelp.h" #define NSS_PROCESS_NAME "mozilla" /**@brief get the current path of the executable * * Looks up the current executables directory. The caller * has to free the return value. * * The returned value includes the last / * * @returns the absolute directory of the currently executed executable or NULL */ char * get_exe_dir() { char *retval = NULL, *p = NULL, buf[PATH_MAX]; ssize_t ret; size_t path_len = 0; ret = readlink ("/proc/self/exe", buf, PATH_MAX); if (ret <= 0) { ERRORPRINTF ("readlink failed\n"); return NULL; } buf[ret] = '\0'; /* cut off the filename */ p = strrchr (buf, '/'); if (p == NULL) { ERRORPRINTF ("No filename found.\n"); return NULL; } *(p + 1) = '\0'; path_len = strlen (buf); retval = xmalloc (path_len + 1); strncpy (retval, buf, path_len); retval[path_len] = '\0'; return retval; } /**@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_t uid of the user to install certificates for. * @param [in] gid_t 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[2] = {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_exe_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); ret = snprintf(argv[0], exe_path_len + 1, "%s%s", inst_dir, NSS_PROCESS_NAME); 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 (setuid (uid) || setgid (gid)) { exit(-1); } close (pipe_fd[1]); dup2 (pipe_fd[0], 0); close (pipe_fd[0]); /* TODO find path based on current executable */ 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) { uid_t my_uid = getuid(); if (my_uid != 0) { /* Running as a user */ char *homedir = getenv ("HOME"); pid_t childprocess = -1; /* Only one child for single user installation */ int status = -1; if (!homedir) { ERRORPRINTF ("Failed to find home directory\n"); } 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; } printf ("Installation as root is not yet implemented\n"); /* TODO root parse /etc/passwd for users with a home directory */ return 0; } #endif