--- util-linux-2.12/mount/lomount.c-fixed-key-size	2003-09-21 18:14:41.000000000 -0400
+++ util-linux-2.12/mount/lomount.c	2003-09-21 18:43:17.000000000 -0400
@@ -24,12 +24,15 @@
 #include <ctype.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <limits.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
 #include <sys/sysmacros.h>
+#include <regex.h>
 
 #include "loop.h"
 #include "lomount.h"
@@ -38,6 +41,7 @@
 
 extern int verbose;
 extern char *xstrdup (const char *s);	/* not: #include "sundries.h" */
+extern void *xmalloc (size_t size);		/* idem */
 extern void error (const char *fmt, ...);	/* idem */
 
 #ifdef LOOP_SET_FD
@@ -237,6 +241,115 @@
 	}
 }
 
+/* A function to check the encryption parameters against /proc/crypto.	*
+ * Returns 1 if everything checks out, 0 if there's any problem.	*
+ * The purpose of this function is not so much to verify the parameters	*
+ * before setting up the loop device; that task is made difficult by	*
+ * the possibility of loadable encryption modules and the necessity of	*
+ * falling back to the old (kerneli.org) CryptoAPI. Rather the intent	*
+ * is to come up with a more useful error message after the setup	*
+ * fails. Thus we return 1 in the event that some system error prevents	*
+ * us from getting the info we want from /proc/crypto.			*/
+static int
+check_crypto(struct loop_info64 *loopinfo64) {
+	char *s = xmalloc(LINE_MAX), *name = xmalloc(LINE_MAX),
+		cipher_name[LO_NAME_SIZE+1];
+	int cipher_found = 0, min_size = 0, max_size = 0, retval;
+	FILE *fp;
+	struct stat st;
+
+	if (stat("/proc/crypto", &st) == -1) {
+		retval = 1;
+		goto end;
+	} else if (S_ISDIR(st.st_mode)) {
+		/* Hm... must be using the old CryptoAPI. */
+		retval = 1;
+		goto end;
+	} else if ((fp = fopen("/proc/crypto", "r")) == NULL) {
+		retval = 1;
+		goto end;
+	}
+
+	xstrncpy(cipher_name, loopinfo64->lo_crypt_name, LO_NAME_SIZE);
+	/* Chop off the cipher mode (ie. everything after the dash) */
+	cipher_name[strcspn(cipher_name, "-")] = '\0';
+
+	while (fgets(s, LINE_MAX, fp) != NULL) {
+		if (sscanf(s, "name : %s", name) > 0) {
+			if (strcmp(name, cipher_name) == 0)
+				cipher_found = 1;
+			else if (cipher_found)
+				break;
+		} else {
+			sscanf(s, "min keysize : %d", &min_size);
+			sscanf(s, "max keysize : %d", &max_size);
+		}
+	}
+	fclose(fp);
+
+	if (!cipher_found) {
+		fprintf(stderr, _("Invalid cipher \"%s\"\n"), cipher_name);
+		retval = 0;
+		goto end;
+	} else if (loopinfo64->lo_encrypt_key_size < min_size ||
+		   loopinfo64->lo_encrypt_key_size > max_size) {
+		fprintf(stderr, _("Invalid key length (%d) for %s cipher\n"),
+			loopinfo64->lo_encrypt_key_size, cipher_name);
+		retval = 0;
+		goto end;
+	}
+
+	retval = 1;
+end:
+	free(s);
+	free(name);
+	return retval;
+}
+
+/* Same thing, for the old CryptoAPI. */
+static int
+check_crypto_old(struct loop_info *loopinfo) {
+	char *s = xmalloc(LINE_MAX), *name = xmalloc(PATH_MAX+1),
+		cipher_name[LO_NAME_SIZE+1];
+	int mask = 0, retval;
+	FILE *fp;
+	struct stat st;
+
+	if (stat("/proc/crypto/cipher", &st) == -1) {
+		retval = 1;
+		goto end;
+	} else if (!S_ISDIR(st.st_mode)) {
+		retval = 1;
+		goto end;
+	}
+		
+	xstrncpy(cipher_name, loopinfo->lo_name, LO_NAME_SIZE);
+	snprintf(name, PATH_MAX+1, "/proc/crypto/cipher/%s", cipher_name);
+	if ((fp = fopen(name, "r")) == NULL) {
+		fprintf(stderr, _("Invalid cipher \"%s\"\n"), cipher_name);
+		retval = 0;
+		goto end;
+	}
+
+	while (fgets(s, LINE_MAX, fp) != NULL) {
+		sscanf(s, "keysize_mask : %i", &mask);
+	}
+	fclose(fp);
+
+	if (!(mask & (1 << (loopinfo->lo_encrypt_key_size - 1)))) {
+		fprintf(stderr, _("Invalid key length (%d) for %s cipher\n"),
+			loopinfo->lo_encrypt_key_size, cipher_name);
+		retval = 0;
+		goto end;
+	}
+
+	retval = 1;
+end:
+	free(s);
+	free(name);
+	return retval;
+}
+
 static int
 digits_only(const char *s) {
 	while (*s)
@@ -275,9 +388,40 @@
 		if (digits_only(encryption)) {
 			loopinfo64.lo_encrypt_type = atoi(encryption);
 		} else {
+			regex_t keysize_re;
+			regmatch_t keysize_rm;
+			int rerror;
+			char *rerror_buf, *encryption_dup = xstrdup(encryption);
+
 			loopinfo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI;
+
+			if ((rerror = regcomp(&keysize_re, "-[[:digit:]]+$",
+					      REG_EXTENDED)) != 0) {
+				fprintf(stderr, _("RE error: "));	
+				rerror_buf = xmalloc(LINE_MAX+1);
+				regerror(rerror, &keysize_re, rerror_buf, LINE_MAX);
+				fprintf(stderr, "%s\n", rerror_buf);
+				free(rerror_buf);
+				return -1;
+			}
+			rerror = regexec(&keysize_re, encryption_dup,
+					 1, &keysize_rm, 0);
+			regfree(&keysize_re);
+			if (rerror) {
+				fprintf(stderr,
+					_("You must specify a key size (in bits) "
+					  "for use with CryptoAPI encryption.\n"));
+				return -1;
+			}
+
+			/* convert the key size from #bits to #bytes */
+			loopinfo64.lo_encrypt_key_size =
+				atoi(encryption_dup + keysize_rm.rm_so + 1) / 8;
+			/* now cut off the key size */
+			encryption_dup[keysize_rm.rm_so] = '\0';
 			snprintf(loopinfo64.lo_crypt_name, LO_NAME_SIZE,
-				 "%s", encryption);
+				 "%s", encryption_dup);
+			free(encryption_dup);
 		}
 	}
 
@@ -309,7 +453,6 @@
 	default:
 		pass = xgetpass(pfd, _("Password: "));
 		xstrncpy(loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE);
-		loopinfo64.lo_encrypt_key_size = LO_KEY_SIZE;
 	}
 
 	if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
@@ -322,6 +465,14 @@
 		struct loop_info loopinfo;
 		int errsv = errno;
 
+		if (errno == EINVAL &&
+		    loopinfo64.lo_encrypt_type == LO_CRYPT_CRYPTOAPI)
+			if (!check_crypto(&loopinfo64)) {
+				fprintf(stderr,
+					_("Error in crypto parameters, exiting\n"));
+				exit(1);
+			}
+
 		errno = loop_info64_to_old(&loopinfo64, &loopinfo);
 		if (errno) {
 			errno = errsv;
@@ -330,6 +481,17 @@
 		}
 
 		if (ioctl(fd, LOOP_SET_STATUS, &loopinfo) < 0) {
+			errsv = errno;
+
+			if (errno == EINVAL &&
+			    loopinfo.lo_encrypt_type == LO_CRYPT_CRYPTOAPI)
+				if (!check_crypto_old(&loopinfo)) {
+					fprintf(stderr,
+						_("Error in crypto parameters, exiting\n"));
+					exit(1);
+				}
+
+			errno = errsv;
 			perror("ioctl: LOOP_SET_STATUS");
 			goto fail;
 		}
@@ -416,6 +578,22 @@
 	exit(1);
 }
 
+void *
+xmalloc (size_t size) {
+	void *t;
+
+	if (size == 0)
+		return NULL;
+
+	t = malloc (size);
+	if (t == NULL) {
+		fprintf(stderr, _("not enough memory"));
+		exit(1);
+	}
+
+	return t;
+}
+
 char *
 xstrdup (const char *s) {
 	char *t;
