diff options
Diffstat (limited to 'src/utils/mount.ecryptfs.c')
-rw-r--r-- | src/utils/mount.ecryptfs.c | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/src/utils/mount.ecryptfs.c b/src/utils/mount.ecryptfs.c new file mode 100644 index 0000000..4b83979 --- /dev/null +++ b/src/utils/mount.ecryptfs.c @@ -0,0 +1,646 @@ +/** + * Copyright (C) 2006 International Business Machines + * Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com> + * Stephan Mueller <smueller@chronox.de> + * Tyler Hicks <tyhicks@ou.edu> + * Michael C. Thompson <mcthomps@us.ibm.com> + * + * 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 <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <keyutils.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pwd.h> +#include <sys/mman.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include "ecryptfs.h" +#include "decision_graph.h" +#include "io.h" + +#define NUM_REQUIRED_ARGS 3 + +static void usage() +{ + fprintf(stderr, "\teCryptfs mount helper\n\tusage: " + "mount -t ecryptfs [lower directory] [ecryptfs mount point]\n" + "\n" + "See the README file in the ecryptfs-utils package for " + "complete usage guidelines.\n" + ); + exit(-EINVAL); +} + +/** + * This function will malloc any argument that is passed in as non-NULL. + * In the event of a failure, all returned pointers are invalid and no + * memory is left allocated. + * + * returns 0 on success + */ +static int parse_arguments(int argc, char **argv, char **src, char **target, + char **options) +{ + int rc = 0; + char *ptr; + size_t len; + + if (src) + *src = NULL; + if (target) + *target = NULL; + if (options) + *options = NULL; + + if (src) { + ptr = argv[1]; + len = strlen(ptr) + 1; /* + NULL terminator */ + *src = malloc(len); + if (!*src) { + fprintf(stderr, "Unable to allocate source buffer\n"); + rc = -ENOMEM; + goto out; + } + memcpy(*src, ptr, len); + if ((*src)[len - 1] == '/') + (*src)[len - 1] = '\0'; + } + if (target) { + ptr = argv[2]; + len = strlen(ptr) + 1; /* + NULL-terminator */ + *target = malloc(len); + if (!*target) { + fprintf(stderr, "Unable to allocate target buffer\n"); + rc = -ENOMEM; + goto out; + } + memcpy(*target, ptr, len); + if ((*target)[len - 1] == '/') + (*target)[len - 1] = '\0'; + } + if ((options) && (argc >= NUM_REQUIRED_ARGS)) { + int i; + + ptr = NULL; + for (i = 3; i < (argc-1); i++) + if (!strcmp("-o", argv[i])) { + ptr = argv[i+1]; + break; + } + if (!ptr) { + fprintf(stderr, "Unable to find a list of options to " + "parse, defaulting to interactive " + "mount\n"); + return 0; + } + len = strlen(ptr) + 1; /* + NULL-terminator */ + *options = malloc(len); + if (!*options){ + fprintf(stderr, "Unable to allocate memory for options " + "buffer\n"); + rc = -ENOMEM; + goto out; + } + memcpy(*options, ptr, len); + } + return 0; +out: + if (src && *src) + free(*src); + if (target && *target) + free(*target); + if (options && *options) + free(*options); + return rc; +} + +char *parameters_to_scrub[] = { + "key=", + "cipher=", + "passthrough", + "ecryptfs_passthrough", + "hmac", + "ecryptfs_hmac", + "xattr", + "ecryptfs_xattr", + "encrypted_view", + "ecryptfs_encrypted_view", + "user", + "sig", + "no_sig_cache", + "verbose", + "verbosity", + "ecryptfs_enable_filename_crypto", + NULL +}; + +char *parameters_to_not_scrub[] = { + "xattr_user", + NULL +}; + +static int parameter_should_not_be_scrubbed(char *str) { + int i; + + for (i = 0; parameters_to_not_scrub[i]; i++) + if (strstr(str, parameters_to_not_scrub[i]) == str) + return 1; + return 0; +} + +static int parameter_should_be_scrubbed(char *str) +{ + int i; + + for (i = 0; parameters_to_scrub[i]; i++) + if (strstr(str, parameters_to_scrub[i]) == str + && !parameter_should_not_be_scrubbed(str)) + return 1; + return 0; +} + +/** + * Remove from the options string known options which should not be passed + * into the kernel. Any options that are unknown will be passed in. + * This is to account for options like "rw". + * + * Returns zero on success, non-zero otherwise + */ +static int strip_userland_opts(char *options) +{ + char *cur = NULL, *next = NULL; + char *temp, *temp_end; + size_t len; + int used = 0, first = 1; + + if (!options) + return 0; + + len = (strlen(options) + 1); + if ((temp = (char*)malloc(len)) == NULL) { + fprintf(stderr, "Out of memory\n"); + return -1; + } + temp_end = temp; + memset(temp, 0, len); + cur = options; + while (cur) { + int opt_len; + + next = strstr(cur, ","); + if (next) { + *next='\0'; + next++; + } + if (!parameter_should_be_scrubbed(cur)) { + if (!first) { + memcpy(temp_end, ",", 1); + temp_end++; + } + opt_len = strlen(cur); + memcpy(temp_end, cur, opt_len); + temp_end = temp_end + opt_len; + used += opt_len; + first = 0; + } + cur = next; + } + memcpy(options,temp,len); + free(temp); + return 0; +} + +static int process_sig(char *auth_tok_sig, struct passwd *pw) +{ + char *home; + char *sig_cache_filename = NULL; + char *dot_ecryptfs_dir; + int flags; + char *yesno = NULL; + int rc; + int tries; + + home = pw->pw_dir; + rc = asprintf(&dot_ecryptfs_dir, "%s/.ecryptfs", home); + if (rc == -1) { + rc = -ENOMEM; + goto out; + } + (void)mkdir(dot_ecryptfs_dir, S_IRWXU); + if (chown(dot_ecryptfs_dir, getuid(), getgid()) == -1) + printf("Can't change ownership of sig file; " + "errno = [%d]; [%m]\n", errno); + free(dot_ecryptfs_dir); + rc = asprintf(&sig_cache_filename, "%s/.ecryptfs/sig-cache.txt", + home); + if (rc == -1) { + rc = -ENOMEM; + goto out; + } + flags = 0; + if ((rc = ecryptfs_check_sig(auth_tok_sig, sig_cache_filename, + &flags))) + goto out; + if (flags & ECRYPTFS_SIG_FLAG_NOENT) { + printf("WARNING: Based on the contents of [%s],\n" + "it looks like you have never mounted with this key \n" + "before. This could mean that you have typed your \n" + "passphrase wrong.\n\n", sig_cache_filename); + tries = 0; + yesno = NULL; + do { + free(yesno); + if ((rc = get_string_stdin(&yesno, + "Would you like to proceed with " + "the mount (yes/no)? ",ECRYPTFS_ECHO_ON))) + goto out; + } while ((rc = strcmp(yesno, "yes")) && strcmp(yesno, "no") + && (++tries < 5)); + if (rc == 0) { + tries = 0; + do { + free(yesno); + printf("Would you like to append sig [%s] to\n" + "[%s] \nin order to avoid this warning " + "in the future", auth_tok_sig, + sig_cache_filename); + if ((rc = get_string_stdin(&yesno," (yes/no)? ", + ECRYPTFS_ECHO_ON))) + goto out; + } while ((rc = strcmp(yesno, "yes")) + && strcmp(yesno, "no") && (++tries < 5)); + if (rc == 0) { + if ((rc = ecryptfs_append_sig( + auth_tok_sig, + sig_cache_filename))) { + printf("Error appending to [%s]; rc = " + "[%d]. Aborting mount.\n", + sig_cache_filename, rc); + goto out; + } + printf("Successfully appended new sig to user " + "sig cache file\n"); + } else { + if (strcmp(yesno,"no")) + rc = -EINVAL; + else { + printf("Not adding sig to user sig " + "cache file; continuing with " + "mount.\n"); + rc = 0; + } + } + } else { + if (strcmp(yesno,"no")) + rc = -EINVAL; + printf("Aborting mount.\n"); + rc = ECANCELED; + goto out; + } + } +out: + free(yesno); + free(sig_cache_filename); + return rc; +} + +static int opts_str_contains_option(char *options, char *option) +{ + char *opt; + char *next_opt; + + if (!options || !option) + return 0; + + int option_len = strlen(option); + + opt = options; + while (opt) { + if ((next_opt = strchr(opt, ','))) + next_opt++; + if (!strncmp(opt, option, option_len)) + return 1; + else + opt = next_opt; + } + return 0; +} + +char *required_mount_opts[] = { + "ecryptfs_key_bytes=", + NULL +}; + +static int ecryptfs_validate_mount_opts(char *opts) +{ + int i = 0; + int rc = 0; + + while (required_mount_opts[i]) { + if (!opts_str_contains_option(opts, required_mount_opts[i])) { + printf("Required mount option not provided: [%s]\n", + required_mount_opts[i]); + rc = -EINVAL; + goto out; + } + i++; + } +out: + return rc; +} + +int ecryptfs_mount(char *source, char *target, char *opts) +{ + pid_t pid, pid_child; + char *fullpath_source = NULL; + char *fullpath_target = NULL; + int rc, status; + + if (!source) { + rc = -EINVAL; + syslog(LOG_ERR, "Invalid source directory\n"); + goto out; + } + + if (!target) { + rc = -EINVAL; + syslog(LOG_ERR, "Invalid target directory\n"); + goto out; + } + + /* source & target are canonicalized here, so the correct error + * is sent to syslog. + * /bin/mount tells you the error on normal output only, not to syslog. + */ + fullpath_source = realpath(source, NULL); + if (!fullpath_source) { + rc = -errno; + syslog(LOG_ERR, "could not resolve full path for source %s [%d]", + source, -errno); + goto out; + } + + fullpath_target = realpath(target, NULL); + if (!fullpath_target) { + rc = -errno; + syslog(LOG_ERR, "could not resolve full path for target %s [%d]", + target, -errno); + goto out; + } + + pid = fork(); + if (pid == -1) { + syslog(LOG_ERR, "Could not fork process to mount eCryptfs: [%d]\n", -errno); + rc = -errno; + } else if (pid == 0) { + execl("/bin/mount", "mount", "-i", "--no-canonicalize", "-t", "ecryptfs", fullpath_source, fullpath_target, "-o", opts, NULL); + + /* error message shown in console to let users know what was wrong */ + /* i.e. /bin/mount does not exist */ + perror("Failed to execute /bin/mount command"); + exit(errno); + } else { + pid_child = waitpid(pid, &status, 0); + if (pid_child == -1) { + syslog(LOG_ERR, "Failed waiting for /bin/mount process: [%d]\n", -errno); + rc = -errno; + goto out; + } + + rc = -EPERM; + if (WIFEXITED(status)) + rc = -WEXITSTATUS(status); + + if (rc) { + syslog(LOG_ERR, "Failed to perform eCryptfs mount: [%d]\n", rc); + if (-EPIPE == rc) { + rc = -EPERM; + } + } + } + +out: + free(fullpath_source); + free(fullpath_target); + + return rc; +} + +/** + * ecryptfs_do_mount + * @params: + * + * The mount options actually sent to the kernel are in the @params + * linked list of struct val_node objects. params <- + * ecryptfs_process_decision_graph(head) -> decision_graph_mount(head) + * -> eval_param_tree(val_stack_head) -> do_transition(val_stack_head) + * -> trans_func(head). + */ +static int ecryptfs_do_mount(int argc, char **argv, struct val_node *mnt_params, + int sig_cache, struct passwd *pw) +{ + int rc; + char *src = NULL, *targ = NULL, *opts = NULL, *new_opts = NULL, *temp; + char *val; + + if ((rc = parse_arguments(argc, argv, &src, &targ, &opts))) { + fprintf(stderr, "Unable to understand the mount options\n"); + goto out; + } + rc = strip_userland_opts(opts); + if (rc) + goto out; + if (!(temp = strdup("ecryptfs_unlink_sigs"))) { + rc = -ENOMEM; + goto out; + } + if ((rc = stack_push(&mnt_params, (void *)temp))) + goto out; + printf("Attempting to mount with the following options:\n"); + new_opts = opts; + opts = NULL; + while (!stack_pop_val(&mnt_params, (void *)&val)) { + if(!val) + break; + temp = new_opts; + printf(" %s\n", val); + if (sig_cache && memcmp(val, "ecryptfs_sig=", 13) == 0) { + if ((rc = process_sig(&val[13], pw))) { + if (rc != ECANCELED) + printf("Error processing sig; " + "rc = [%d]\n", rc); + goto out; + } + } + if (!temp || !strstr(temp, val)) { + rc = asprintf(&new_opts, "%s%c%s", val, + ((temp && *temp) ? ',' : '\0'), temp); + if (rc == -1) { + new_opts = NULL; + rc = -ENOMEM; + goto out; + } + free(temp); + } + rc = 0; + } + if ((rc = ecryptfs_validate_mount_opts(new_opts)) != 0) { + printf("Invalid mount options; aborting. rc = [%d]\n", + rc); + goto out; + } + rc = ecryptfs_mount(src, targ, new_opts); +out: + free(src); + free(targ); + free(opts); + free(new_opts); + return rc; +} + +static int dump_args = 0; + +int main(int argc, char **argv) +{ + uint32_t version; + char *opts_str; + struct val_node *mnt_params; + struct ecryptfs_ctx ctx; + int sig_cache = 1; + int rc; + struct passwd *pw; + + rc = mlockall(MCL_FUTURE); + if (rc) { + fprintf(stderr, "Exiting. Unable to mlockall address space: %m\n"); + return -1; + } + + pw = getpwuid(getuid()); + if (!pw) { + fprintf(stderr, "Exiting. Unable to obtain passwd info\n"); + rc = -EIO; + goto out; + } + + if (dump_args) { + int i; + + for (i = 0; i < argc; i++) + printf("argv[%d] = [%s]\n", i, argv[i]); + } + if (argc < NUM_REQUIRED_ARGS) { + fprintf(stderr, "Insufficient number of arguments\n"); + usage(); + rc = -EINVAL; + goto out; + } + rc = ecryptfs_get_version(&version); + if (rc) { + printf("\nUnable to get the version number of the kernel\n"); + printf("module. Please make sure that you have the eCryptfs\n"); + printf("kernel module loaded, you have sysfs mounted, and\n"); + printf("the sysfs mount point is in /etc/mtab. This is\n"); + printf("necessary so that the mount helper knows which \n"); + printf("kernel options are supported.\n\n"); + printf("Make sure that your system is set up to auto-load\n" + "your filesystem kernel module on mount.\n\n"); + printf("Enabling passphrase-mode only for now.\n\n"); + version = ECRYPTFS_VERSIONING_PASSPHRASE; + } + if ((rc = ecryptfs_validate_keyring())) { + printf("Unable to link the KEY_SPEC_USER_KEYRING into the " + "KEY_SPEC_SESSION_KEYRING; there is something wrong " + "with your kernel keyring. Did you build key retention " + "support into your kernel?\n"); + goto out; + } + mnt_params = malloc(sizeof(struct val_node)); + memset(mnt_params, 0, sizeof(struct val_node)); + memset(&ctx, 0, sizeof(struct ecryptfs_ctx)); + ctx.get_string = &get_string_stdin; + if ((rc = parse_arguments(argc, argv, NULL, NULL, &opts_str))) + goto out; + if (opts_str_contains_option(opts_str, "verbose")) + ecryptfs_verbosity = 1; + if (!opts_str_contains_option(opts_str, "remount")) { + if (opts_str_contains_option(opts_str, "no_sig_cache")) + sig_cache = 0; + if (opts_str_contains_option(opts_str, "no_prompt") + || opts_str_contains_option(opts_str, "wild_ass_guess")) { + if (!opts_str_contains_option(opts_str, + "verbosity=0")) { + char *tmp; + + rc = asprintf(&tmp, "%s,verbosity=0", opts_str); + if (rc == -1) { + rc = -ENOMEM; + goto out; + } + rc = 0; + opts_str = tmp; + } + } + if (opts_str_contains_option(opts_str, "verbosity=0")) + sig_cache = 0; + rc = ecryptfs_process_decision_graph( + &ctx, &mnt_params, version, opts_str, + ECRYPTFS_ASK_FOR_ALL_MOUNT_OPTIONS); + if (rc) { + if (rc > 0) + rc = -EINVAL; + printf("Error attempting to evaluate mount options: " + "[%d] %s\nCheck your system logs for details " + "on why this happened.\nTry updating your " + "ecryptfs-utils package, and/or\nsubmit a bug " + "report on https://bugs.launchpad.net/ecryptfs\n", + rc, strerror(-rc)); + goto out; + } + rc = ecryptfs_do_mount(argc, argv, mnt_params, sig_cache, pw); + if (rc == ECANCELED) { + rc = 0; + goto out; + } + if (rc) { + if (rc > 0) + rc = -rc; + printf("Error mounting eCryptfs: [%d] %s\n" + "Check your system logs; visit " + "<http://ecryptfs.org/support.html>\n", + rc, strerror(-rc)); + if (rc == -ENODEV) + printf("Try ``modprobe ecryptfs''\n"); + } else + printf("Mounted eCryptfs\n"); + } else { + fprintf(stderr, "When remounting eCryptfs, you need " + "to pass the mount utility the -i parameter to avoid " + "calling the mount helper\n"); + rc = -EINVAL; + } + +out: + munlockall(); + return rc; +} |