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@648: #include andre@302: andre@302: #include "nssstore.h" andre@302: #include "logging.h" andre@302: #include "strhelp.h" andre@841: #include "util.h" andre@302: andre@1175: #define NSS_PROCESS_NAME "trustbridge-nss-installer" 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. emanuel@1053: * @param [in] uid uid of the user to install certificates for. emanuel@1053: * @param [in] gid 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@1060: char *argv[3] = {NULL, NULL, NULL}, andre@905: *envp[2] = {NULL, NULL}, andre@905: *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@841: inst_dir = get_install_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@1060: if (g_debug) andre@1060: { andre@1060: argv[1] = "--debug"; andre@1060: } andre@1060: andre@383: ret = snprintf(argv[0], exe_path_len + 1, "%s%s", inst_dir, NSS_PROCESS_NAME); andre@947: xfree (inst_dir); 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@648: if (setgid (gid) || setuid (uid)) andre@302: { andre@648: syslog_error_printf("Failed to drop privileges: %s", strerror(errno)); 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@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@905: if (stream) andre@905: { andre@905: fclose (stream); andre@905: } 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@648: struct passwd *usr_it = NULL; andre@648: uid_t my_uid = geteuid(); andre@975: pid_t childprocess = -1; andre@975: int status = -1; andre@302: andre@302: if (my_uid != 0) andre@302: { andre@302: /* Running as a user */ andre@302: char *homedir = getenv ("HOME"); andre@302: if (!homedir) andre@302: { andre@302: ERRORPRINTF ("Failed to find home directory\n"); andre@302: } andre@302: andre@975: /* Only one child for single user installation */ 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@648: andre@975: /* Start once as root to install in the system default directories. */ andre@975: childprocess = start_procces_for_user (to_install, to_remove, andre@975: my_uid, getgid(), getenv ("HOME")); andre@975: if (childprocess == -1) andre@975: { andre@975: ERRORPRINTF ("Failed to start default profile installation!\n"); andre@975: return -1; andre@975: } andre@975: andre@975: /* Wait until the default profile directories are done. */ andre@975: childprocess = waitpid (childprocess, &status, 0); andre@975: if (childprocess == -1 || !WIFEXITED(status)) andre@975: { andre@975: ERRORPRINTF ("Child process did not finish.\n"); andre@975: return -1; andre@975: } andre@975: andre@648: setpwent(); andre@648: andre@648: while ((usr_it = getpwent ()) != NULL) andre@648: { andre@648: /* Skip obvious system accounts */ andre@648: if (strcmp(usr_it->pw_shell, "/usr/sbin/nologin") == 0 || andre@648: strcmp(usr_it->pw_shell, "/bin/false") == 0) andre@648: { andre@648: continue; andre@648: } andre@648: /* A check if the home directory starts with /home might be andre@648: appropiate */ andre@648: start_procces_for_user (to_install, andre@648: to_remove, andre@648: usr_it->pw_uid, andre@648: usr_it->pw_gid, andre@648: usr_it->pw_dir); andre@648: andre@648: } andre@648: andre@648: endpwent(); andre@648: andre@648: waitpid (-1, NULL, 0); andre@648: andre@648: DEBUGPRINTF ("NSS installation done\n"); andre@302: return 0; andre@302: } andre@302: #endif