From e6af0842c76544b98843dbe001fdf540f49f5cfa Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 5 Feb 2009 22:08:14 -0600 Subject: [PATCH] Automatically unlink fekek and fnek at unmount with mount helpers A feature request was made for the automatic removal of the fekek and fnek from the keyring at unmount time when using the mount helpers. The best way to do this involved creating an eCryptfs umount helper, but I didn't want it to affect the users who don't use the eCryptfs mount helper. This patch creates a new eCryptfs umount helper that looks in the mount options for ecryptfs_unlink_sigs. If that option is found, it will determine the ecryptfs_sig and the ecryptfs_fnek_sig, if one exists, and unlink those auth toks from the user keyring. If the ecryptfs_unlink_sigs option is not detected, a normal umount will occur. This will not affect users who have pre-existing fstab entries and are not expecting their keys to unlinked at umount. Signed-off-by: Tyler Hicks --- src/include/ecryptfs.h | 1 + src/libecryptfs/key_management.c | 23 +++++ src/utils/Makefile.am | 4 + src/utils/mount.ecryptfs.c | 2 +- src/utils/umount.ecryptfs.c | 171 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 200 insertions(+), 1 deletions(-) create mode 100644 src/utils/umount.ecryptfs.c diff --git a/src/include/ecryptfs.h b/src/include/ecryptfs.h index 14d47a9..9a8dc9e 100644 --- a/src/include/ecryptfs.h +++ b/src/include/ecryptfs.h @@ -620,6 +620,7 @@ int ecryptfs_parse_stat(struct ecryptfs_crypt_stat_user *crypt_stat, char *buf, binary_data ecryptfs_passphrase_blob(char *salt, char *passphrase); binary_data ecryptfs_passphrase_sig_from_blob(char *blob); int ecryptfs_add_passphrase_blob_to_keyring(char *blob, char *sig); +int ecryptfs_remove_auth_tok_from_keyring(char *auth_tok_sig); int ecryptfs_add_auth_tok_to_keyring(struct ecryptfs_auth_tok *auth_tok, char *auth_tok_sig); int ecryptfs_add_blob_to_keyring(char *blob, char *sig); diff --git a/src/libecryptfs/key_management.c b/src/libecryptfs/key_management.c index d9ee2a5..9bcf650 100644 --- a/src/libecryptfs/key_management.c +++ b/src/libecryptfs/key_management.c @@ -135,6 +135,29 @@ out: return bd; } + +int ecryptfs_remove_auth_tok_from_keyring(char *auth_tok_sig) +{ + int rc; + + rc = (int)keyctl_search(KEY_SPEC_USER_KEYRING, "user", auth_tok_sig, 0); + if (rc < 0) { + rc = errno; + syslog(LOG_ERR, "Failed to find with sig [%s]: %m\n", + auth_tok_sig); + goto out; + } + rc = keyctl_unlink(rc, KEY_SPEC_USER_KEYRING); + if (rc < 0) { + rc = errno; + syslog(LOG_ERR, "Failed to unlink key with sig [%s]: %s\n", + auth_tok_sig, strerror(rc)); + goto out; + } + rc = 0; +out: + return rc; +} int ecryptfs_add_auth_tok_to_keyring(struct ecryptfs_auth_tok *auth_tok, char *auth_tok_sig) { diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index ebc480e..0e9f653 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -3,6 +3,7 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in EXTRA_DIST=ecryptfsrc ecryptfs-setup-private ecryptfs-mount-private ecryptfs-umount-private rootsbin_PROGRAMS=mount.ecryptfs \ + umount.ecryptfs \ mount.ecryptfs_private bin_PROGRAMS=ecryptfs-manager ecryptfs-wrap-passphrase \ ecryptfs-unwrap-passphrase \ @@ -28,6 +29,9 @@ INCLUDES = -I$(top_srcdir)/src/include mount_ecryptfs_SOURCES = mount.ecryptfs.c io.c io.h gen_key.c plaintext_decision_graph.c mount_ecryptfs_CFLAGS = $(AM_CFLAGS) $(KEYUTILS_CFLAGS) $(LIBGCRYPT_CFLAGS) mount_ecryptfs_LDADD = $(top_builddir)/src/libecryptfs/libecryptfs.la $(KEYUTILS_LIBS) $(LIBGCRYPT_LIBS) +umount_ecryptfs_SOURCES = umount.ecryptfs.c +umount_ecryptfs_CFLAGS = $(AM_CFLAGS) $(KEYUTILS_CFLAGS) +umount_ecryptfs_LDADD = $(top_builddir)/src/libecryptfs/libecryptfs.la ecryptfs_manager_SOURCES = manager.c io.c io.h gen_key.c ecryptfs_manager_CFLAGS = $(AM_CFLAGS) $(KEYUTILS_CFLAGS) $(LIBGCRYPT_CFLAGS) ecryptfs_manager_LDADD = $(top_builddir)/src/libecryptfs/libecryptfs.la $(KEYUTILS_LIBS) $(LIBGCRYPT_LIBS) diff --git a/src/utils/mount.ecryptfs.c b/src/utils/mount.ecryptfs.c index eb2dfcc..62f77c5 100644 --- a/src/utils/mount.ecryptfs.c +++ b/src/utils/mount.ecryptfs.c @@ -461,7 +461,7 @@ static int ecryptfs_do_mount(int argc, char **argv, struct val_node *mnt_params, if (rc) goto out; num_opts = ecryptfs_generate_mount_flags(opts, &flags); - if (asprintf(&new_opts, "%s", opts) == -1) { + if (asprintf(&new_opts, "%secryptfs_unlink_sigs,", opts) == -1) { new_opts = NULL; rc = -ENOMEM; goto out; diff --git a/src/utils/umount.ecryptfs.c b/src/utils/umount.ecryptfs.c new file mode 100644 index 0000000..96ca9b4 --- /dev/null +++ b/src/utils/umount.ecryptfs.c @@ -0,0 +1,171 @@ +/** + * Copyright (C) 2009 International Business Machines + * 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 "ecryptfs.h" + + /** + * Parses a string of mount options, searching for an option name, and returns + * a pointer to the option value. For example, if name was "ecryptfs_sig=", + * it would set value to a string containing the sig, up to the first + * comma or NULL character in the mount options. Name must end with an = sign. + * value must be freed by the caller. + */ +static int get_mount_opt_value(char *mnt_opts, char *name, char **value) +{ + char *name_start, *val_start, *val_stop, *copied_val; + size_t name_len, val_len; + int rc = 0; + + name_len = strlen(name); + if (name[name_len - 1] != '=') { + rc = EINVAL; + goto out; + } + + name_start = strstr(mnt_opts, name); + if (!name_start) { + rc = EINVAL; + goto out; + } + + val_start = name_start + name_len; + val_stop = strstr(val_start, ","); + if (!val_stop) + val_stop = mnt_opts + strlen(mnt_opts); + + val_len = val_stop - val_start; + *value = malloc(val_len + 1); + if (!(*value)) { + rc = ENOMEM; + goto out; + } + memcpy(*value, val_start, val_len); + (*value)[val_len] = '\0'; +out: + return rc; +} + +static int unlink_keys_from_keyring(const char *mnt_point) +{ + struct mntent *mntent; + FILE *file; + char *fekek_sig = NULL, *fnek_sig = NULL; + int fekek_fail = 0, fnek_fail = 0; + int rc; + + file = setmntent("/etc/mtab", "r"); + if (!file) { + rc = EINVAL; + goto out; + } + while ((mntent = getmntent(file))) { + if (strcmp("ecryptfs", mntent->mnt_type)) + continue; + if (strcmp(mnt_point, mntent->mnt_dir)) + continue; + break; + } + if (!mntent) { + rc = EINVAL; + goto end_out; + } + if (!hasmntopt(mntent, "ecryptfs_unlink_sigs")) { + rc = 0; + goto end_out; + } + rc = get_mount_opt_value(mntent->mnt_opts, "ecryptfs_sig=", &fekek_sig); + if (!rc) { + fekek_fail = ecryptfs_remove_auth_tok_from_keyring(fekek_sig); + if (fekek_fail) + fprintf(stderr, "Failed to remove fekek with sig [%s] " + "from keyring: %s\n", strerror(rc)); + } else + fekek_fail = rc; + if (!get_mount_opt_value(mntent->mnt_opts, + "ecryptfs_fnek_sig=", &fnek_sig) + && strcmp(fekek_sig, fnek_sig)) { + fnek_fail = ecryptfs_remove_auth_tok_from_keyring(fnek_sig); + if (fekek_fail) + fprintf(stderr, "Failed to remove fnek with sig [%s] " + "from keyring: %s\n", strerror(rc)); + } + free(fekek_sig); + free(fnek_sig); +end_out: + endmntent(file); +out: + return (fekek_fail ? fekek_fail : (fnek_fail ? fnek_fail : rc)); +} + +static int construct_umount_args(int argc, char **argv, char ***new_argv) +{ + int new_argc = argc + 1; + int i, rc; + + *new_argv = malloc(sizeof(char *) * (new_argc + 1)); + if (!new_argv) { + rc = errno; + goto out; + } + (*new_argv)[0] = "umount"; + (*new_argv)[1] = "-i"; + for (i = 2; i < new_argc; i++) + (*new_argv)[i] = argv[i - 1]; + (*new_argv)[i] = NULL; + rc = 0; +out: + return rc; +} + +#define UMOUNT_PATH "/bin/umount" +int main(int argc, char **argv) +{ + char **new_argv; + char *mnt_opts, *fekek_sig, *fnek_sig; + int rc; + + if (unlink_keys_from_keyring(argv[1])) + fprintf(stderr, "Could not unlink the key(s) from your keying. " + "Please use `keyctl unlink` if you wish to remove the " + "key(s). Proceeding with umount.\n"); + rc = construct_umount_args(argc, argv, &new_argv); + if (rc) { + fprintf(stderr, "Failed to construct umount arguments: %s\n", + strerror(rc)); + goto out; + } + rc = execv(UMOUNT_PATH, new_argv); + if (rc < 0) { + rc = errno; + fprintf(stderr, "Failed to execute %s: %m\n", UMOUNT_PATH); + goto free_out; + } + rc = 0; +free_out: + free(new_argv); +out: + return rc; +} + -- 1.5.3.7