aheinecke@404: /* Copyright (C) 2014 by Bundesamt für Sicherheit in der Informationstechnik aheinecke@404: * Software engineering by Intevation GmbH aheinecke@404: * aheinecke@404: * This file is Free Software under the GNU GPL (v>=2) aheinecke@404: * and comes with ABSOLUTELY NO WARRANTY! aheinecke@404: * See LICENSE.txt for details. aheinecke@404: */ andre@302: #ifndef WIN32 andre@302: aheinecke@321: /* @file aheinecke@321: @brief Linux implementation of nssstore process control. aheinecke@321: */ aheinecke@321: andre@302: #include andre@302: #include andre@302: #include andre@302: #include andre@302: #include andre@302: #include andre@302: #include andre@383: #include aheinecke@439: #include andre@302: andre@302: #include "nssstore.h" andre@302: #include "logging.h" andre@302: #include "strhelp.h" andre@302: andre@383: #define NSS_PROCESS_NAME "mozilla" andre@383: andre@383: /**@brief get the current path of the executable andre@383: * andre@383: * Looks up the current executables directory. The caller andre@383: * has to free the return value. andre@383: * andre@383: * The returned value includes the last / andre@383: * andre@383: * @returns the absolute directory of the currently executed executable or NULL andre@383: */ andre@383: char * andre@383: get_exe_dir() andre@383: { andre@383: char *retval = NULL, andre@383: *p = NULL, andre@383: buf[PATH_MAX]; andre@383: ssize_t ret; andre@383: size_t path_len = 0; andre@383: andre@383: ret = readlink ("/proc/self/exe", buf, PATH_MAX); andre@383: if (ret <= 0) andre@383: { andre@383: ERRORPRINTF ("readlink failed\n"); andre@383: return NULL; andre@383: } andre@383: andre@383: buf[ret] = '\0'; andre@383: andre@383: /* cut off the filename */ andre@383: p = strrchr (buf, '/'); andre@383: if (p == NULL) andre@383: { andre@383: ERRORPRINTF ("No filename found.\n"); andre@383: return NULL; andre@383: } andre@383: *(p + 1) = '\0'; andre@383: andre@383: path_len = strlen (buf); andre@383: retval = xmalloc (path_len + 1); andre@383: strncpy (retval, buf, path_len); aheinecke@476: retval[path_len] = '\0'; andre@383: andre@383: return retval; andre@383: } andre@383: andre@302: /**@brief Start the process to install / remove andre@302: * andre@302: * This forks the process and executes the NSS installation andre@302: * process. It also writes the Instructions to that process. andre@302: * andre@302: * @param [in] to_install strv of DER encoded certificates to be added. andre@302: * @param [in] to_remove strv of DER encoded certificates to be remvoed. andre@302: * @param [in] uid_t uid of the user to install certificates for. andre@302: * @param [in] gid_t the gid of the user to install certificates for. andre@302: * @param [in] homedir the homedir of the user. andre@302: * andre@302: * @returns childs pid on success. -1 on failure andre@302: */ andre@302: static int andre@302: start_procces_for_user (char **to_install, char **to_remove, andre@302: uid_t uid, gid_t gid, char *homedir) andre@302: { andre@302: int pipe_fd[2]; andre@302: pid_t pid = 0; andre@383: char *argv[2] = {NULL, NULL}, andre@383: *envp[2] = {NULL, NULL}, andre@383: *inst_dir = NULL; andre@383: size_t homedir_len = 0, andre@383: exe_path_len = 0; andre@302: int ret = -1, andre@302: i = 0; andre@302: FILE *stream = NULL; andre@302: bool success = false; andre@302: andre@302: if (homedir == NULL) andre@302: { andre@302: ERRORPRINTF ("Invalid call to start_process_for_user\n"); andre@302: return -1; andre@302: } andre@302: andre@302: homedir_len = strlen (homedir); andre@302: andre@302: /* Allocate space for HOME=homedir\0 */ andre@302: envp[0] = xmalloc (homedir_len + 6); andre@302: envp[1] = NULL; andre@302: andre@302: ret = snprintf (envp[0], homedir_len + 6, "HOME=%s", homedir); andre@302: andre@302: if (ret < 0 || (size_t) ret != homedir_len + 5) andre@302: { andre@302: ERRORPRINTF ("Error setting home env variable.\n"); andre@302: xfree (envp[0]); andre@302: return -1; andre@302: } andre@302: andre@383: /* Set up the file name of the installer process */ andre@383: inst_dir = get_exe_dir(); andre@383: if (inst_dir == NULL) andre@383: { andre@383: ERRORPRINTF ("Failed to find installation directory.\n"); andre@383: xfree (envp[0]); andre@383: return -1; andre@383: } andre@383: andre@383: exe_path_len = strlen(inst_dir) + strlen(NSS_PROCESS_NAME); andre@383: argv[0] = xmalloc (exe_path_len + 1); andre@383: andre@383: ret = snprintf(argv[0], exe_path_len + 1, "%s%s", inst_dir, NSS_PROCESS_NAME); andre@383: if (ret < 0 || (size_t) ret != exe_path_len) andre@383: { andre@383: ERRORPRINTF ("Error setting executable variable.\n"); andre@383: xfree (argv[0]); andre@383: return -1; andre@383: } andre@302: andre@302: if (pipe (pipe_fd)) andre@302: { andre@302: ERRORPRINTF ("Failed to create pipe.\n"); andre@302: return -1; andre@302: } andre@302: andre@302: pid = fork(); andre@302: andre@302: if (pid == (pid_t) -1) andre@302: { andre@302: ERRORPRINTF ("Failed to fork child.\n"); andre@302: return -1; andre@302: } andre@302: andre@302: if (pid == (pid_t) 0) andre@302: { andre@302: /* Drop privileges */ andre@302: if (setuid (uid) || setgid (gid)) andre@302: { andre@302: exit(-1); andre@302: } andre@302: andre@302: close (pipe_fd[1]); andre@302: dup2 (pipe_fd[0], 0); andre@302: close (pipe_fd[0]); andre@302: /* TODO find path based on current executable */ andre@383: execve (argv[0], argv, envp); andre@302: exit (127); andre@302: } andre@302: andre@302: close (pipe_fd[0]); andre@302: stream = fdopen(pipe_fd[1], "w"); andre@302: if (stream == NULL) andre@302: { andre@302: ERRORPRINTF ("Failed to open pipe for writing\n"); andre@302: goto done; andre@302: } andre@302: andre@384: /* The NSS installer may exit on error before we are done andre@384: * telling it what to do. We want to handle that rather andre@384: * then die unexpectedly. */ andre@384: signal(SIGPIPE, SIG_IGN); andre@384: andre@302: /* Send the instructions */ andre@302: for (i = 0; to_install && to_install[i]; i++) andre@302: { andre@302: if (fprintf (stream, "I:%s\n", to_install[i]) <= 3) andre@302: { aheinecke@478: int err = errno; aheinecke@478: ERRORPRINTF ("Write failed: %s \n", strerror(err)); aheinecke@478: if (err == 32) aheinecke@478: { aheinecke@478: /* Broken pipe is expected if there are no NSS stores aheinecke@478: to be found the process just exits. That's ok */ aheinecke@478: success = true; aheinecke@478: } andre@302: goto done; andre@302: } andre@302: } andre@302: andre@302: for (i = 0; to_remove && to_remove[i]; i++) andre@302: { andre@302: if (fprintf (stream, "R:%s\n", to_remove[i]) <= 3) andre@302: { aheinecke@478: int err = errno; aheinecke@478: ERRORPRINTF ("Write failed: %s \n", strerror(err)); aheinecke@478: if (err == 32) aheinecke@478: { aheinecke@478: /* Broken pipe is expected if there are no NSS stores aheinecke@478: to be found the process just exits. That's ok */ aheinecke@478: success = true; aheinecke@478: } andre@302: goto done; andre@302: } andre@302: } andre@302: andre@302: success = true; andre@302: andre@302: done: andre@302: if (stream) { andre@302: fclose (stream); andre@302: } andre@383: xfree (argv[0]); andre@302: xfree (envp[0]); andre@383: close (pipe_fd[0]); andre@383: close (pipe_fd[1]); andre@302: andre@302: if (success) andre@302: { andre@302: return pid; andre@302: } andre@302: return -1; andre@302: } andre@302: andre@302: int andre@302: write_stores_nss (char **to_install, char **to_remove) andre@302: { andre@302: uid_t my_uid = getuid(); andre@302: andre@302: if (my_uid != 0) andre@302: { andre@302: /* Running as a user */ andre@302: char *homedir = getenv ("HOME"); andre@302: pid_t childprocess = -1; /* Only one child for single user installation */ andre@302: int status = -1; andre@302: if (!homedir) andre@302: { andre@302: ERRORPRINTF ("Failed to find home directory\n"); andre@302: } andre@302: andre@302: childprocess = start_procces_for_user (to_install, to_remove, andre@302: my_uid, getgid(), homedir); andre@302: andre@302: if (childprocess == -1) andre@302: { andre@302: ERRORPRINTF ("Failed to start childprocess!\n"); andre@302: return -1; andre@302: } andre@302: andre@302: childprocess = waitpid (childprocess, &status, 0); andre@302: if (childprocess == -1 || !WIFEXITED(status)) andre@302: { andre@302: ERRORPRINTF ("Waitpid failed.\n"); andre@302: return -1; andre@302: } andre@302: andre@302: return 0; andre@302: } andre@302: printf ("Installation as root is not yet implemented\n"); andre@302: /* TODO root parse /etc/passwd for users with a home directory */ andre@302: return 0; andre@302: } andre@302: #endif