Logo Search packages:      
Sourcecode: ecryptfs-utils version File versions

mount.ecryptfs.c

/**
 * 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 <gcrypt.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 "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"
            "Available options:\n"
            "\tkey=<pki alias>\n"
            "\n\t\tAvailable pki aliases: passphrase\n"
            "\tpassthrough\n"
            "\tplaintext_passthrough\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)) {
            fprintf(stderr, "Not enough remaining data for options\n");
            return -EINVAL;
      } else 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");
                  rc = -ENOENT;
                  goto out;
            }
            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;
}

static int ecryptfs_generate_mount_flags(char *options, int *flags)
{
      char *opt;
      char *next_opt;
      char *end;
      int num_options = 0;

      end = strchr(options, '\0');
      opt = options;
      while (opt) {
            if ((next_opt = strchr(opt, ',')))
                  next_opt++;
            /* the following mount options should be
             * stripped out from what is passed into the
             * kernel since these eight options are best
             * passed as the mount flags rather than
             * redundantly to the kernel and could
             * generate spurious warnings depending on the
             * level of the corresponding cifs vfs kernel
             * code */
            if (!strncmp(opt, "nosuid", 6))
                  *flags |= MS_NOSUID;
            else if (!strncmp(opt, "suid", 4))
                  *flags &= ~MS_NOSUID;
            else if (!strncmp(opt, "nodev", 5))
                  *flags |= MS_NODEV;
            else if (!strncmp(opt, "dev", 3))
                  *flags &= ~MS_NODEV;
            else if (!strncmp(opt, "noexec", 6))
                  *flags |= MS_NOEXEC;
            else if (!strncmp(opt, "exec", 4))
                  *flags &= ~MS_NOEXEC;
            else if (!strncmp(opt, "ro", 2))
                  *flags |= MS_RDONLY;
            else if (!strncmp(opt, "rw", 2))
                  *flags &= ~MS_RDONLY;
            else if (!strncmp(opt, "remount", 7))
                  *flags |= MS_REMOUNT;
            else {
                  opt = next_opt;
                  num_options++;
                  continue;
            }
            if (!next_opt) {
                  if (opt != options)
                        end = --opt;
                  else
                        end = options;
                  *end = '\0';
                  break;
            }
            memcpy(opt, next_opt, end - next_opt);
            end = end - (next_opt - opt);
            *end = '\0';
      }
      return num_options;
}

char *parameters_to_scrub[] = {
      "key=",
      "cipher=",
      "passthrough",
      "xattr",
      "encrypted_view",
      "user",
      "sig",
      "no_sig_cache",
      "verbose",
      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;

      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)
{
      char *home;
      struct passwd *pw;
      uid_t id;
      char *sig_cache_filename = NULL;
      char *dot_ecryptfs_dir;
      int flags;
      char yesno[4];
      int i;
      int rc;

      id = getuid();
      pw = getpwuid(id);
      home = pw->pw_dir;
      rc = asprintf(&dot_ecryptfs_dir, "%s/.ecryptfs", home);
      if (rc == -1) {
            rc = -ENOMEM;
            goto out;
      }
      mkdir(dot_ecryptfs_dir, S_IRWXU);
      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);
            printf("Would you like to proceed with the mount (yes/no)? ");
            i = 0;
            do {
                  yesno[i++] = getchar();
            } while (yesno[i-1] != '\n' && i < 3);
            yesno[i] = '\0';
            if (yesno[i-1] != '\n')
                  while (getchar() != '\n');
            if (memcmp(yesno, "yes", 3) == 0) {
                  printf("Would you like to append sig [%s] to\n"
                         "[%s] \n"
                         "in order to avoid this warning in the future "
                         "(yes/no)? ", auth_tok_sig, sig_cache_filename);
                  i = 0;
                  do {
                        yesno[i++] = getchar();
                  } while (yesno[i-1] != '\n' && i < 3);
                  yesno[i] = '\0';
                  if (yesno[i-1] != '\n')
                        while (getchar() != '\n');
                  if (memcmp(yesno, "yes", 3) == 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
                        printf("Not adding sig to user sig cache "
                               "file; continuing with mount.\n");
            } else {
                  printf("Aborting mount.\n");
                  rc = -EINVAL;
                  goto out;
            }
      }
out:
      free(sig_cache_filename);
      return rc;
}

static int opts_str_contains_option(char *options, char *option)
{
      char *opt;
      char *next_opt;
      char *end;
      int option_len = strlen(option);

      end = strchr(options, '\0');
      opt = options;
      while (opt) {
            if ((next_opt = strchr(opt, ',')))
                  next_opt++;
            if (!strncmp(opt, option, option_len))
                  return 1;
            else {
                  opt = next_opt;
                  continue;
            }
            if (!next_opt) {
                  if (opt != options)
                        end = --opt;
                  else
                        end = options;
                  *end = '\0';
                  break;
            }
            memcpy(opt, next_opt, end - next_opt);
            end = end - (next_opt - opt);
            *end = '\0';
      }
      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 = 1;
                  goto out;
            }
            i++;
      }
out:
      return rc;
}

static int ecryptfs_do_mount(int argc, char **argv, struct val_node *params,
                       int sig_cache)
{
      int rc;
      int flags = 0;
      int num_opts = 0;
      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;
      num_opts = ecryptfs_generate_mount_flags(opts, &flags);
      asprintf(&new_opts, "%s", opts);
      printf("Attempting to mount with the following options:\n");
      while (!stack_pop_val(&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]))) {
                        printf("Error processing sig; rc = [%d]\n", rc);
                        goto out;
                  }
            }
            rc = asprintf(&new_opts, "%s,%s", val, temp);
            if (rc == -1) {
                  new_opts = NULL;
                  rc = -ENOMEM;
                  goto out;
            }
            rc = 0;
            free(temp);
      }
      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, flags, new_opts);
out:
      free(src);
      free(targ);
      free(opts);
      free(new_opts);
      return rc;
}

int main(int argc, char **argv)
{
      uint32_t version;
      char *opts_str;
      struct val_node *head; /* Will contain final mount options */
      struct ecryptfs_ctx ctx;
      int sig_cache = 1;
      int rc;

      rc = mlockall(MCL_FUTURE);
      if (rc) {
            fprintf(stderr, "Exiting. Unable to mlockall address space\n");
            return 0;
      }
      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;
      }
      head = malloc(sizeof(struct val_node));
      memset(head, 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;
            rc = ecryptfs_process_decision_graph(&ctx, &head, version,
                                         opts_str);
            if (rc) {
                  printf("Error attempting to evaluate mount options; "
                         "rc = [%d]. Try updating/reinstalling your "
                         "ecryptfs-utils package, or submit a bug "
                         "report.\n", rc);
                  goto out;
            }
            rc = ecryptfs_do_mount(argc, argv, head, sig_cache);
            if (rc) {
                  printf("Error mounting eCryptfs; rc = [%d]; strerr = "
                         "[%s]. See <http://ecryptfs.sourceforge.net/"
                         "ecryptfs-faq.html>.\n", rc, strerror(-rc));
                  if (rc == -ENODEV)
                        printf("Try ``modprobe ecryptfs''\n");
            } else
                  printf("Mounted eCryptfs\n");
      } else {
            fprintf(stderr, "When are 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;
}

Generated by  Doxygen 1.6.0   Back to index