/** * Userspace daemon which responds to the eCryptfs kernel module's requests * * Copyright (C) 2004-2006 International Business Machines Corp. * Author(s): Tyler Hicks * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/ecryptfs.h" static char *pidfile = NULL; static char *prompt_prog = NULL; static int prompt_callback(char *prompt_type, char *prompt, char *input, int input_size) { int status; pid_t pid = -1; int fds[2] = {-1, -1}; int r = 0; int rc; /* * Make sure we don't reuse input */ if (input) { memset (input, 0, input_size); } /* * Make sure enough buffer size for at least 1 char and EOS */ if ((input_size < 2) || (prompt_prog == NULL)) { rc = -EINVAL; goto out; } if (pipe (fds) == -1) { rc = -errno; goto out; } if ((pid = fork ()) == -1) { rc = -errno; goto out; } if (pid == 0) { close (fds[0]); fds[0] = -1; if (dup2 (fds[1], 1) == -1) { exit (1); } close (fds[1]); fds[1] = -1; execl ( prompt_prog, prompt_prog, "-t", prompt_type, prompt, NULL ); exit (1); } close (fds[1]); fds[1] = -1; while ( (r=waitpid (pid, &status, 0)) == 0 || (r == -1 && errno == EINTR) ); if (r == -1) { rc = -errno; goto out; } if (!WIFEXITED (status)) { rc = -EFAULT; goto out; } if (WEXITSTATUS (status) != 0) { rc = -EIO; goto out; } if (!strcmp (prompt_type, "password")) { if ((r = read (fds[0], input, input_size - 1)) == -1) { rc = -errno; goto out; } input[r] = '\0'; if (strlen (input) > 0 && input[strlen (input)-1] == '\n') { input[strlen (input)-1] = '\0'; } } rc = 0; out: if (rc != 0) { if (input) { memset (input, 0, input_size); } } if (fds[0] != -1) { close (fds[0]); fds[0] = -1; } if (fds[1] != -1) { close (fds[1]); fds[1] = -1; } return rc; } struct ecryptfs_messaging_ctx mctx; pthread_mutex_t mctx_mux; static void ecryptfsd_exit(struct ecryptfs_messaging_ctx *mctx, int retval) { int rc = 0; if (pidfile != NULL) { unlink(pidfile); free(pidfile); pidfile = NULL; } rc = ecryptfs_send_message(mctx, NULL, ECRYPTFS_MSG_QUIT, 0, 0); if (rc) syslog(LOG_ERR, "%s: Error attempting to send quit message to " "kernel; rc = [%d]\n", __FUNCTION__, rc); rc = ecryptfs_messaging_exit(mctx); if (rc) syslog(LOG_ERR, "%s: Error attempting to shut down messaging; " "rc = [%d]\n", __FUNCTION__, rc); ecryptfs_syslog(LOG_INFO, "Closing eCryptfs userspace daemon\n"); exit(retval); } void daemonize(void) { pid_t pid; int fd; int null; if(getppid() == 1) return; /* Already a daemon */ if ((pid=fork()) == -1) { fprintf(stderr, "Failed to create daemon process: %m\n"); exit(1); } if (pid != 0) exit(0); setsid(); umask(027); if (chdir("/") == -1 ) { syslog(LOG_ERR, "Failed to change directory: %m"); exit(1); } if ((pid=fork()) == -1) { /* Fork in new session */ syslog(LOG_ERR, "Failed to create daemon process: %m\n"); exit(1); } if (pid != 0) exit(0); /* Make std handles write to null; close all others. */ if ((null = open("/dev/null", O_RDWR)) == -1) { syslog(LOG_ERR, "Cannot open /dev/null"); exit(1); } for (fd=0; fd < 3; fd++) { if (dup2(null, fd) == -1) { syslog(LOG_ERR, "Failed to dup null: %m\n"); exit(1); } } for (fd = (getdtablesize() - 1); fd > 2; fd--) close(fd); /* Ignore major signals */ if (signal(SIGHUP, SIG_IGN) == SIG_ERR || signal(SIGTERM, SIG_IGN) == SIG_ERR || signal(SIGINT, SIG_IGN) == SIG_ERR) { syslog(LOG_ERR, "Failed to setup initial signals"); exit(1); } } void sigterm_handler(int sig) { pthread_mutex_lock(&mctx_mux); ecryptfsd_exit(&mctx, 0); pthread_mutex_unlock(&mctx_mux); } void usage(const char *const me, const struct option *const options, const char *const short_options) { const struct option *opt; printf("Usage: %s [options]", me); for (opt = options; opt->name; opt++) { const char *descr = opt->name + strlen(opt->name) + 1; if (strchr(short_options, opt->val)) printf("\n -%c, --%s", opt->val, opt->name); else printf("\n --%s", opt->name); if (opt->has_arg) printf(" <%s>", opt->name); if (strlen(descr)) printf("\t%s",descr); } printf("\n"); } int main(int argc, char **argv) { static struct option long_options[] = { {"pidfile\0\tSet pid file name", required_argument, NULL, 'p'}, {"foreground\0\t\tDon't fork into background", no_argument, NULL, 'f'}, {"chroot\0\t\tChroot to directory", required_argument, NULL, 'C'}, {"prompt-prog\0Program to execute for user prompt", required_argument, NULL, 'R'}, {"version\0\t\t\tShow version information", no_argument, NULL, 'V'}, {"help\0\t\t\tShow usage information", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; static char *short_options = "p:fC:R:Vh"; int long_options_ret; struct rlimit core = {0, 0}; int foreground = 0; char *chrootdir = NULL; char *tty = NULL; uint32_t version; int rc = 0; while ((long_options_ret = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { switch (long_options_ret) { case 'p': pidfile = strdup(optarg); break; case 'f': foreground = 1; break; case 'C': chrootdir = strdup(optarg); break; case 'R': prompt_prog = strdup(optarg); break; case 'V': printf(("%s (%s) %s\n" "\n" "This is free software. You may " "redistribute copies of it under the " "terms of\n" "the GNU General Public License " ".\n" "There is NO WARRANTY, to the extent " "permitted by law.\n"), basename(argv[0]), PACKAGE_NAME, PACKAGE_VERSION); exit(0); break; case 'h': default: usage(basename(argv[0]), long_options, short_options); exit(1); break; } } rc = ecryptfs_get_version(&version); if (!rc && !(version & ECRYPTFS_VERSIONING_MISCDEV)) { rc = -EPROTONOSUPPORT; syslog(LOG_ERR, "%s: Current kernel does not have support for " "/dev/ecryptfs; please use 2.6.26 or newer\n", __func__); exit(rc); } openlog(argv[0], LOG_PID | (foreground ? LOG_PERROR : 0), 0); if (rc) { syslog(LOG_WARNING, "%s: Unable to retrieve versioning " "info from kernel module; assuming /dev/ecryptfs is " "available\n " , __FUNCTION__); } tty = ttyname(0); /* We may need the tty name later */ if (tty != NULL) setenv ("TERM_DEVICE", tty, 0); if (!foreground) daemonize(); /* This will exit if cannot be completed */ /* Disallow core file; secret values may be in it */ if (setrlimit(RLIMIT_CORE, &core) == -1) { rc = -errno; syslog(LOG_ERR, "Cannot setrlimit: %m"); goto daemon_out; } if (chrootdir != NULL) { if (chroot(chrootdir) == -1) { rc = -errno; syslog(LOG_ERR, "Failed to chroot to '%s': %m\n", chrootdir); goto daemon_out; } free(chrootdir); chrootdir = NULL; } if (pidfile != NULL) { FILE *fp = fopen(pidfile, "w"); if (fp == NULL) { rc = -errno; syslog(LOG_ERR, "Failed to open pid file '%s': %m\n", pidfile); goto daemon_out; } fprintf(fp, "%d", (int)getpid()); fclose(fp); } if (signal(SIGTERM, sigterm_handler) == SIG_ERR) { rc = -ENOTSUP; syslog(LOG_ERR, "Failed to attach handler to SIGTERM"); goto daemon_out; } if (signal(SIGINT, sigterm_handler) == SIG_ERR) { rc = -ENOTSUP; syslog(LOG_ERR, "Failed to attach handler to SIGINT"); goto daemon_out; } cryptfs_get_ctx_opts()->prompt = prompt_callback; pthread_mutex_init(&mctx_mux, NULL); pthread_mutex_lock(&mctx_mux); rc = ecryptfs_init_messaging(&mctx, ECRYPTFS_MESSAGING_TYPE_MISCDEV); if (rc) { syslog(LOG_ERR, "%s: Failed to initialize messaging; rc = " "[%d]\n", __FUNCTION__, rc); pthread_mutex_unlock(&mctx_mux); goto daemon_out; } rc = ecryptfs_send_message(&mctx, NULL, ECRYPTFS_MSG_HELO, 0, 0); if (rc) { syslog(LOG_ERR, "%s: Error attempting to send message to " "eCryptfs kernel module via /dev/ecryptfs; rc = [%d]\n", __func__, rc); pthread_mutex_unlock(&mctx_mux); goto daemon_out; } mctx.state |= ECRYPTFS_MESSAGING_STATE_LISTENING; pthread_mutex_unlock(&mctx_mux); rc = ecryptfs_run_daemon(&mctx); pthread_mutex_lock(&mctx_mux); mctx.state &= ~ECRYPTFS_MESSAGING_STATE_LISTENING; pthread_mutex_unlock(&mctx_mux); daemon_out: ecryptfsd_exit(&mctx, rc); return rc; }