Mercurial > trustbridge
view cinst/nssstore_linux.c @ 502:e551de11d8b6
Properly handle the case that the file does not exist.
TRUNCATE makes create file fail if the file does not exist
but we need TRUNCATE in the case that the file already exists
author | Andre Heinecke <aheinecke@intevation.de> |
---|---|
date | Mon, 28 Apr 2014 09:18:07 +0000 |
parents | 214bf504c54f |
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