--- 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 #include #include +#include #include #include +#include #include #include #include #include +#include #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")); + goto fail; + } + 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")); + goto fail; + } + + 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;