Re: [PATCH] losetup: support password hashing and specifying the key length

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



 Hi guys,

On Wed, Aug 29, 2007 at 01:23:42PM -0600, LaMont Jones wrote:

> Subject: [PATCH] losetup: support password hashing and specifying the key length
> 
> * add support for password hashing (sha512, sha384, sha256, rmd160).
> * add support for loop-AES style strings like "twofish256" for
>   specifying the encryption algorithm and key length.

 I'd like to fix losetup, because it works incorrectly with encryption
 key size. It's really old problem. It makes sense use everything from
 Ludwig's patch **except** hash functions. It means your non-upstream
 stuff will be smaller (hash functions only) and upstream version will
 be fixed :-)

 Please, review a patch below. (Hmm.. I need some regression tests too.)

    Karel


>From 4d92944475b07250ccbaccf4c973f3df53ec3ccd Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@xxxxxxxxxx>
Date: Tue, 28 Aug 2007 12:30:07 -0600
Subject: [PATCH] losetup: specifying the key length

* add support for specifying the encryption key length
  (a new option -k | --keybits)
* add support for loop-AES style strings like "twofish256" for
  specifying the encryption algorithm and key length.

The original losetup implementation didn't work correctly, because the
key size was hard coded (e.g. LO_KEY_SIZE, 32bytes). The fixed key
size is useless for encryptions where maximal key size is less than 32
bytes (e.g. des).

For backward compatibility the default key size is still 32 bytes.

Old version:

  # losetup -e des /dev/loop0 /file
  Password:
  ioctl: LOOP_SET_STATUS: Invalid argument

Fixed version:

  # losetup -e des -k 64 /dev/loop0 /file
  Password:
  # losetup -a
  /dev/loop0: [0806]:96353 (/file), encryption des (type 18), key length 8

This patch is inspired by a patch from Suse.

Signed-off-by: Ludwig Nussel <ludwig.nussel@xxxxxxx>
Signed-off-by: LaMont Jones <lamont@xxxxxxxxxxxx>
Signed-off-by: Karel Zak <kzak@xxxxxxxxxx>
---
 mount/lomount.c |  120 ++++++++++++++++++++++++++++++++++++------------------
 mount/lomount.h |    4 +-
 mount/losetup.8 |    4 ++
 mount/mount.8   |   13 ++++++
 mount/mount.c   |   22 ++++++++--
 5 files changed, 117 insertions(+), 46 deletions(-)

diff --git a/mount/lomount.c b/mount/lomount.c
index ace474c..3bc2152 100644
--- a/mount/lomount.c
+++ b/mount/lomount.c
@@ -95,12 +95,13 @@ show_loop(char *device) {
 
 		if (loopinfo64.lo_encrypt_type ||
 		    loopinfo64.lo_crypt_name[0]) {
-			char *e = loopinfo64.lo_crypt_name;
+			unsigned char *e = loopinfo64.lo_crypt_name;
 
 			if (*e == 0 && loopinfo64.lo_encrypt_type == 1)
-				e = "XOR";
-			printf(_(", encryption %s (type %d)"),
-			       e, loopinfo64.lo_encrypt_type);
+				e = (unsigned char *) "XOR";
+			printf(_(", encryption %s (type %d), key length %u"),
+			       e, loopinfo64.lo_encrypt_type,
+			       loopinfo64.lo_encrypt_key_size);
 		}
 		printf("\n");
 		close (fd);
@@ -109,7 +110,7 @@ show_loop(char *device) {
 
 	if (ioctl(fd, LOOP_GET_STATUS, &loopinfo) == 0) {
 		printf ("%s: [%04x]:%ld (%s)",
-			device, loopinfo.lo_device, loopinfo.lo_inode,
+			device, (unsigned int) loopinfo.lo_device, loopinfo.lo_inode,
 			loopinfo.lo_name);
 
 		if (loopinfo.lo_offset)
@@ -259,14 +260,14 @@ xgetpass(int pfd, const char *prompt) {
 	}
 
 	if (pass == NULL)
-		return "";
+		return NULL;
 
 	pass[i] = 0;
 	return pass;
 }
 
 static int
-digits_only(const char *s) {
+digits_only(unsigned char *s) {
 	while (*s)
 		if (!isdigit(*s++))
 			return 0;
@@ -275,10 +276,10 @@ digits_only(const char *s) {
 
 int
 set_loop(const char *device, const char *file, unsigned long long offset,
-	 const char *encryption, int pfd, int *loopro) {
-	struct loop_info64 loopinfo64;
+		 const char *encryption, int pfd, int *loopro, int keysz) {
+	struct loop_info64 lo64;
 	int fd, ffd, mode, i;
-	char *pass;
+	char *pass = NULL;
 
 	mode = (*loopro ? O_RDONLY : O_RDWR);
 	if ((ffd = open(file, mode)) < 0) {
@@ -295,28 +296,55 @@ set_loop(const char *device, const char *file, unsigned long long offset,
 	}
 	*loopro = (mode == O_RDONLY);
 
-	memset(&loopinfo64, 0, sizeof(loopinfo64));
-
-	xstrncpy(loopinfo64.lo_file_name, file, LO_NAME_SIZE);
+	memset(&lo64, 0, sizeof(lo64));
+	xstrncpy((char*)lo64.lo_file_name, file, LO_NAME_SIZE);
+	lo64.lo_encrypt_key_size = 0;
+	lo64.lo_offset = offset;
 
+	/*
+	 * Set encrypt_type, crypt_name and key_size
+	 */
 	if (encryption && *encryption) {
-		if (digits_only(encryption)) {
-			loopinfo64.lo_encrypt_type = atoi(encryption);
+
+		if (digits_only((unsigned char *) encryption)) {
+			lo64.lo_encrypt_type = atoi(encryption);
+			lo64.lo_encrypt_key_size = keysz ? keysz >> 3 : LO_KEY_SIZE;
 		} else {
-			loopinfo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI;
-			snprintf(loopinfo64.lo_crypt_name, LO_NAME_SIZE,
-				 "%s", encryption);
+			/* CryptoAPI */
+			int ks = 0;
+			unsigned int len = strlen(encryption);
+			unsigned char *kn = lo64.lo_crypt_name;
+
+			snprintf((char *) kn, LO_NAME_SIZE, "%s", encryption);
+
+			if (keysz) {
+				/* -k option always overload nameSIZE convention */
+				ks = keysz >> 3;
+			} else if (len > 3 && digits_only(kn + (len-3))) {
+				/* convert "nameSIZE" to crypt_name and key_size */
+				ks = atoi((char *) kn + (len-3)) >> 3;
+				kn[len-3] = '\0';
+			}
+			else
+				/* LO_KEY_SIZE is nonsense for encryptions
+				 * where is max keysize < 32 (e.g. des), but
+				 * it's backwardly compatible with
+				 * util-linux <= 2.13
+				 */
+				ks = LO_KEY_SIZE;
+
+			lo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI;
+			lo64.lo_encrypt_key_size = ks;
 		}
-	}
 
-	loopinfo64.lo_offset = offset;
+	}
 
 #ifdef MCL_FUTURE
 	/*
 	 * Oh-oh, sensitive data coming up. Better lock into memory to prevent
 	 * passwd etc being swapped out and left somewhere on disk.
 	 */
-	if (loopinfo64.lo_encrypt_type != LO_CRYPT_NONE) {
+	if (lo64.lo_encrypt_type != LO_CRYPT_NONE) {
 		if(mlockall(MCL_CURRENT | MCL_FUTURE)) {
 			perror("memlock");
 			fprintf(stderr, _("Couldn't lock into memory, exiting.\n"));
@@ -325,20 +353,18 @@ set_loop(const char *device, const char *file, unsigned long long offset,
 	}
 #endif
 
-	switch (loopinfo64.lo_encrypt_type) {
-	case LO_CRYPT_NONE:
-		loopinfo64.lo_encrypt_key_size = 0;
-		break;
-	case LO_CRYPT_XOR:
-		pass = getpass(_("Password: "));
-		goto gotpass;
-	default:
+	if (lo64.lo_encrypt_type != LO_CRYPT_NONE) {
 		pass = xgetpass(pfd, _("Password: "));
-	gotpass:
-		memset(loopinfo64.lo_encrypt_key, 0, LO_KEY_SIZE);
-		xstrncpy(loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE);
+		if(!pass)
+			return 1;
+
+		if (lo64.lo_encrypt_key_size > LO_KEY_SIZE)
+			lo64.lo_encrypt_key_size = LO_KEY_SIZE;
+
+		memset(lo64.lo_encrypt_key, 0, LO_KEY_SIZE);
+		xstrncpy((char *) lo64.lo_encrypt_key, pass, LO_KEY_SIZE);
+
 		memset(pass, 0, strlen(pass));
-		loopinfo64.lo_encrypt_key_size = LO_KEY_SIZE;
 	}
 
 	if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
@@ -355,12 +381,12 @@ set_loop(const char *device, const char *file, unsigned long long offset,
 	}
 	close (ffd);
 
-	i = ioctl(fd, LOOP_SET_STATUS64, &loopinfo64);
+	i = ioctl(fd, LOOP_SET_STATUS64, &lo64);
 	if (i) {
 		struct loop_info loopinfo;
 		int errsv = errno;
 
-		i = loop_info64_to_old(&loopinfo64, &loopinfo);
+		i = loop_info64_to_old(&lo64, &loopinfo);
 		if (i) {
 			errno = errsv;
 			perror("ioctl: LOOP_SET_STATUS64");
@@ -371,7 +397,7 @@ set_loop(const char *device, const char *file, unsigned long long offset,
 		}
 		memset(&loopinfo, 0, sizeof(loopinfo));
 	}
-	memset(&loopinfo64, 0, sizeof(loopinfo64));
+	memset(&lo64, 0, sizeof(lo64));
 
 	if (i) {
 		ioctl (fd, LOOP_CLR_FD, 0);
@@ -416,8 +442,8 @@ mutter(void) {
 }  
 
 int
-set_loop (const char *device, const char *file, unsigned long long offset,
-	  const char *encryption, int *loopro) {
+set_loop(const char *device, const char *file, unsigned long long offset,
+	 const char *encryption, const char* phash, int pfd, int *loopro, int keysz) {
 	mutter();
 	return 1;
 }
@@ -457,6 +483,11 @@ usage(void) {
   "\nOptions:\n"
   " -e | --encryption <type> enable data encryption with specified <name/num>\n"
   " -h | --help              this help\n"
+  " -k | --keybits <num>     specify number of bits in the hashed key given\n"
+  "                          to the cipher.  Some ciphers support several key\n"
+  "                          sizes and might be more efficient with a smaller\n"
+  "                          key size.  Key sizes < 128 are generally not\n"
+  "                          recommended\n"
   " -o | --offset <num>      start at offset <num> into file\n"
   " -p | --pass-fd <num>     read passphrase from file descriptor <num>\n"
   " -r | --read-only         setup read-only loop device\n"
@@ -497,11 +528,13 @@ error (const char *fmt, ...) {
 int
 main(int argc, char **argv) {
 	char *p, *offset, *encryption, *passfd, *device, *file;
+	char *keysize;
 	int delete, find, c, all;
 	int res = 0;
 	int showdev = 0;
 	int ro = 0;
 	int pfd = -1;
+	int keysz = 0;
 	unsigned long long off;
 	struct option longopts[] = {
 		{ "all", 0, 0, 'a' },
@@ -509,6 +542,7 @@ main(int argc, char **argv) {
 		{ "encryption", 1, 0, 'e' },
 		{ "find", 0, 0, 'f' },
 		{ "help", 0, 0, 'h' },
+		{ "keybits", 1, 0, 'k' },
 		{ "offset", 1, 0, 'o' },
 		{ "pass-fd", 1, 0, 'p' },
 		{ "read-only", 0, 0, 'r' },
@@ -524,12 +558,13 @@ main(int argc, char **argv) {
 	delete = find = all = 0;
 	off = 0;
 	offset = encryption = passfd = NULL;
+	keysize = NULL;
 
 	progname = argv[0];
 	if ((p = strrchr(progname, '/')) != NULL)
 		progname = p+1;
 
-	while ((c = getopt_long(argc, argv, "ade:E:fho:p:rsv",
+	while ((c = getopt_long(argc, argv, "ade:E:fhk:o:p:rsv",
 				longopts, NULL)) != -1) {
 		switch (c) {
 		case 'a':
@@ -548,6 +583,9 @@ main(int argc, char **argv) {
 		case 'f':
 			find = 1;
 			break;
+		case 'k':
+			keysize = optarg;
+			break;
 		case 'o':
 			offset = optarg;
 			break;
@@ -611,8 +649,10 @@ main(int argc, char **argv) {
 			usage();
 		if (passfd && sscanf(passfd, "%d", &pfd) != 1)
 			usage();
+		if (keysize && sscanf(keysize,"%d",&keysz) != 1)
+			usage();
 		do {
-			res = set_loop(device, file, off, encryption, pfd, &ro);
+			res = set_loop(device, file, off, encryption, pfd, &ro, keysz);
 			if (res == 2 && find) {
 				if (verbose)
 					printf("stolen loop=%s...trying again\n",
diff --git a/mount/lomount.h b/mount/lomount.h
index 89695cd..34eaa92 100644
--- a/mount/lomount.h
+++ b/mount/lomount.h
@@ -1,6 +1,6 @@
 extern int verbose;
-extern int set_loop(const char *, const char *, unsigned long long,
-		    const char *, int, int *);
+extern int set_loop(const char *device, const char *file, unsigned long long offset,
+	 const char *encryption, int pfd, int *loopro, int keysz);
 extern int del_loop(const char *);
 extern int is_loop_device(const char *);
 extern char * find_unused_loop_device(void);
diff --git a/mount/losetup.8 b/mount/losetup.8
index db2929f..26bb2c8 100644
--- a/mount/losetup.8
+++ b/mount/losetup.8
@@ -76,6 +76,8 @@ find the first unused loop device. If a
 argument is present, use this device. Otherwise, print its name.
 .IP "\fB\-h, \-\-help\fP"
 print help
+.IP "\fB\-k, \-\-keybits \fInum\fP"
+set the number of bits to use in key to \fInum\fP.
 .IP "\fB\-o, \-\-offset \fIoffset\fP"
 The data start is moved \fIoffset\fP bytes into the specified file or
 device.
@@ -140,6 +142,8 @@ the command
 .fi
 .SH RESTRICTION
 DES encryption is painfully slow. On the other hand, XOR is terribly weak.
+Both are insecure nowadays. Some ciphers may require a licence for you to be
+allowed to use them.
 
 Cryptoloop is deprecated in favor of dm-crypt. For more details see
 .B cryptsetup(8).
diff --git a/mount/mount.8 b/mount/mount.8
index 54b11d4..b2ca397 100644
--- a/mount/mount.8
+++ b/mount/mount.8
@@ -615,6 +615,15 @@ This option implies the options
 (unless overridden by subsequent options, as in the option line
 .BR group,dev,suid ).
 .TP
+.B encryption
+Specifies an encryption algorithm to use.  Used in conjunction with the
+.BR loop " option."
+.TP
+.B keybits
+Specifies the key size to use for an encryption algorithm. Used in conjunction
+with the
+.BR loop " and " encryption " options."
+.TP
 .B mand
 Allow mandatory locks on this filesystem. See
 .BR fcntl (2).
@@ -2010,6 +2019,10 @@ that are really options to
 .BR \%losetup (8).
 (These options can be used in addition to those specific
 to the filesystem type.)
+If the mount requires a passphrase, you will be prompted for one unless
+you specify a file descriptor to read from instead with the
+.BR \-\-pass-fd
+option.
 
 If no explicit loop device is mentioned
 (but just an option `\fB\-o loop\fP' is given), then
diff --git a/mount/mount.c b/mount/mount.c
index 2e458ca..0ca7fea 100644
--- a/mount/mount.c
+++ b/mount/mount.c
@@ -93,6 +93,9 @@ static int suid = 0;
 /* Contains the fd to read the passphrase from, if any. */
 static int pfd = -1;
 
+/* Contains the preferred keysize in bits we want to use */
+static int keysz = 0;
+
 /* Map from -o and fstab option strings to the flag argument to mount(2).  */
 struct opt_map {
   const char *opt;		/* option name */
@@ -187,6 +190,7 @@ static const struct opt_map opt_map[] = {
 
 static const char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_encryption,
 	*opt_speed, *opt_comment, *opt_uhelper;
+static const char *opt_keybits;
 
 static int mounted (const char *spec0, const char *node0);
 static int check_special_mountprog(const char *spec, const char *node,
@@ -201,6 +205,7 @@ static struct string_opt_map {
   { "vfs=",	1, &opt_vfstype },
   { "offset=",	0, &opt_offset },
   { "encryption=", 0, &opt_encryption },
+  { "keybits=", 0, &opt_keybits },
   { "speed=", 0, &opt_speed },
   { "comment=", 1, &opt_comment },
   { "uhelper=", 0, &opt_uhelper },
@@ -860,7 +865,7 @@ loop_check(const char **spec, const char **type, int *flags,
       *type = opt_vfstype;
   }
 
-  *loop = ((*flags & MS_LOOP) || *loopdev || opt_offset || opt_encryption);
+  *loop = ((*flags & MS_LOOP) || *loopdev || opt_offset || opt_encryption || opt_keybits);
   *loopfile = *spec;
 
   if (*loop) {
@@ -881,9 +886,10 @@ loop_check(const char **spec, const char **type, int *flags,
 	  return EX_SYSERR;	/* no more loop devices */
 	if (verbose)
 	  printf(_("mount: going to use the loop device %s\n"), *loopdev);
-
+	if (!keysz && opt_keybits)
+	  keysz  = strtoul(opt_keybits, NULL, 0);
 	if ((res = set_loop(*loopdev, *loopfile, offset,
-			    opt_encryption, pfd, &loopro))) {
+			    opt_encryption, pfd, &loopro, keysz))) {
 	  if (res == 2) {
 	     /* loop dev has been grabbed by some other process,
 	        try again, if not given explicitly */
@@ -1628,6 +1634,7 @@ static struct option longopts[] = {
 	{ "options", 1, 0, 'o' },
 	{ "test-opts", 1, 0, 'O' },
 	{ "pass-fd", 1, 0, 'p' },
+	{ "keybits", 1, 0, 'k' },
 	{ "types", 1, 0, 't' },
 	{ "bind", 0, 0, 128 },
 	{ "replace", 0, 0, 129 },
@@ -1780,6 +1787,7 @@ main(int argc, char *argv[]) {
 	char *options = NULL, *test_opts = NULL, *node;
 	const char *spec = NULL;
 	char *label = NULL;
+	char *keysize = NULL;
 	char *uuid = NULL;
 	char *types = NULL;
 	char *p;
@@ -1810,7 +1818,7 @@ main(int argc, char *argv[]) {
 	initproctitle(argc, argv);
 #endif
 
-	while ((c = getopt_long (argc, argv, "afFhilL:no:O:p:rsU:vVwt:",
+	while ((c = getopt_long (argc, argv, "afFhik:lL:no:O:p:rsU:vVwt:",
 				 longopts, NULL)) != -1) {
 		switch (c) {
 		case 'a':	       /* mount everything in fstab */
@@ -1828,6 +1836,9 @@ main(int argc, char *argv[]) {
 		case 'i':
 			external_allowed = 0;
 			break;
+		case 'k':
+			keysize = optarg;
+			break;
 		case 'l':
 			list_with_volumelabel = 1;
 			break;
@@ -1974,6 +1985,9 @@ main(int argc, char *argv[]) {
 		create_mtab ();
 	}
 
+	if (keysize && sscanf(keysize,"%d",&keysz) != 1)
+		die (EX_USAGE, _("mount: argument to --keybits or -k must be a number"));
+
 	switch (argc+specseen) {
 	case 0:
 		/* mount -a */
-- 
1.5.3.1


-- 
 Karel Zak  <kzak@xxxxxxxxxx>
-
To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux