Move get_key to common code, simplify verify flags.

(This code need rewrite anyway).

git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@352 36d66b0a-2a48-0410-832c-cd162a569da5
This commit is contained in:
Milan Broz
2010-10-26 22:08:02 +00:00
parent 3a5a1ea0e7
commit 3ae161df5d
5 changed files with 229 additions and 234 deletions

View File

@@ -18,8 +18,6 @@
#define DEFAULT_DISK_ALIGNMENT 1048576 /* 1MiB */
#define DEFAULT_MEM_ALIGNMENT 4096
#define MAX_TTY_PASSWORD_LEN 512
/* private struct crypt_options flags */
#define CRYPT_FLAG_FREE_DEVICE (1 << 24)
@@ -105,10 +103,6 @@ int device_ready(struct crypt_device *cd, const char *device, int mode);
int get_device_infos(const char *device, struct device_infos *infos, struct crypt_device *cd);
int wipe_device_header(const char *device, int sectors);
void get_key(char *prompt, char **key, unsigned int *passLen, int key_size,
const char *key_file, int timeout, int how2verify,
struct crypt_device *cd);
void logger(struct crypt_device *cd, int class, const char *file, int line, const char *format, ...);
#define log_dbg(x...) logger(NULL, CRYPT_LOG_DEBUG, __FILE__, __LINE__, x)
#define log_std(c, x...) logger(c, CRYPT_LOG_NORMAL, __FILE__, __LINE__, x)

View File

@@ -165,7 +165,6 @@ static int keyslot_verify_or_find_empty(struct crypt_device *cd, int *keyslot)
static int verify_other_keyslot(struct crypt_device *cd,
const char *key_file,
unsigned int flags,
int keyIndex)
{
struct volume_key *vk;
@@ -174,8 +173,8 @@ static int verify_other_keyslot(struct crypt_device *cd,
char *password = NULL;
unsigned int passwordLen;
get_key(_("Enter any remaining LUKS passphrase: "), &password,
&passwordLen, 0, key_file, cd->timeout, flags, cd);
crypt_get_key(_("Enter any remaining LUKS passphrase: "), &password,
&passwordLen, 0, key_file, cd->timeout, cd->password_verify, cd);
if(!password)
return -EINVAL;
@@ -201,7 +200,6 @@ static int verify_other_keyslot(struct crypt_device *cd,
static int find_keyslot_by_passphrase(struct crypt_device *cd,
const char *key_file,
unsigned int flags,
char *message)
{
struct volume_key *vk;
@@ -209,8 +207,8 @@ static int find_keyslot_by_passphrase(struct crypt_device *cd,
unsigned int passwordLen;
int keyIndex;
get_key(message,&password,&passwordLen, 0, key_file,
cd->timeout, flags, cd);
crypt_get_key(message,&password,&passwordLen, 0, key_file,
cd->timeout, cd->password_verify, cd);
if(!password)
return -EINVAL;
@@ -266,14 +264,14 @@ static int luks_remove_helper(struct crypt_device *cd,
int r = -EINVAL;
if (key_slot == CRYPT_ANY_SLOT) {
key_slot = find_keyslot_by_passphrase(cd, key_file, 0,
key_slot = find_keyslot_by_passphrase(cd, key_file,
_("Enter LUKS passphrase to be deleted: "));
if(key_slot < 0) {
r = -EPERM;
goto out;
}
log_std(cd, _("key slot %d selected for deletion.\n"), key_slot);
log_std(cd, _("Key slot %d selected for deletion.\n"), key_slot);
}
ki = crypt_keyslot_status(cd, key_slot);
@@ -298,7 +296,7 @@ static int luks_remove_helper(struct crypt_device *cd,
}
if(verify)
r = verify_other_keyslot(cd, other_key_file, 0, key_slot);
r = verify_other_keyslot(cd, other_key_file, key_slot);
else
r = 0;
@@ -422,7 +420,7 @@ int crypt_confirm(struct crypt_device *cd, const char *msg)
static void key_from_terminal(struct crypt_device *cd, char *msg, char **key,
unsigned int *key_len, int force_verify)
{
int r, flags = 0;
int r;
if (cd->password) {
*key = crypt_safe_alloc(MAX_TTY_PASSWORD_LEN);
@@ -434,11 +432,9 @@ static void key_from_terminal(struct crypt_device *cd, char *msg, char **key,
*key = NULL;
} else
*key_len = r;
} else {
if (force_verify || cd->password_verify)
flags |= CRYPT_FLAG_VERIFY_IF_POSSIBLE;
get_key(msg, key, key_len, 0, NULL, cd->timeout, flags, cd);
}
} else
crypt_get_key(msg, key, key_len, 0, NULL, cd->timeout,
(force_verify || cd->password_verify), cd);
}
static int volume_key_by_terminal_passphrase(struct crypt_device *cd, int keyslot,
@@ -484,7 +480,7 @@ static void key_from_file(struct crypt_device *cd, char *msg,
char **key, unsigned int *key_len,
const char *key_file, size_t key_size)
{
get_key(msg, key, key_len, key_size, key_file, cd->timeout, 0, cd);
crypt_get_key(msg, key, key_len, key_size, key_file, cd->timeout, 0, cd);
}
static int _crypt_init(struct crypt_device **cd,
@@ -577,8 +573,8 @@ static int crypt_create_and_update_device(struct crypt_options *options, int upd
if (r)
return r;
get_key(_("Enter passphrase: "), &key, &keyLen, options->key_size,
options->key_file, cd->timeout, options->flags, cd);
crypt_get_key(_("Enter passphrase: "), &key, &keyLen, options->key_size,
options->key_file, cd->timeout, cd->password_verify, cd);
if (!key)
r = -ENOENT;
else
@@ -742,8 +738,8 @@ int crypt_luksFormat(struct crypt_options *options)
goto out;
}
get_key(_("Enter LUKS passphrase: "), &password, &passwordLen, 0,
options->new_key_file, options->timeout, options->flags, cd);
crypt_get_key(_("Enter LUKS passphrase: "), &password, &passwordLen, 0,
options->new_key_file, cd->timeout, cd->password_verify, cd);
if(!password) {
r = -EINVAL;

View File

@@ -237,214 +237,6 @@ ssize_t write_lseek_blockwise(int fd, const char *buf, size_t count, off_t offse
return write_blockwise(fd, buf, count) + innerCount;
}
/* Password reading helpers */
static int untimed_read(int fd, char *pass, size_t maxlen)
{
ssize_t i;
i = read(fd, pass, maxlen);
if (i > 0) {
pass[i-1] = '\0';
i = 0;
} else if (i == 0) { /* EOF */
*pass = 0;
i = -1;
}
return i;
}
static int timed_read(int fd, char *pass, size_t maxlen, long timeout)
{
struct timeval t;
fd_set fds;
int failed = -1;
FD_ZERO(&fds);
FD_SET(fd, &fds);
t.tv_sec = timeout;
t.tv_usec = 0;
if (select(fd+1, &fds, NULL, NULL, &t) > 0)
failed = untimed_read(fd, pass, maxlen);
return failed;
}
static int interactive_pass(const char *prompt, char *pass, size_t maxlen,
long timeout)
{
struct termios orig, tmp;
int failed = -1;
int infd = STDIN_FILENO, outfd;
if (maxlen < 1)
goto out_err;
/* Read and write to /dev/tty if available */
if ((infd = outfd = open("/dev/tty", O_RDWR)) == -1) {
infd = STDIN_FILENO;
outfd = STDERR_FILENO;
}
if (tcgetattr(infd, &orig))
goto out_err;
memcpy(&tmp, &orig, sizeof(tmp));
tmp.c_lflag &= ~ECHO;
if (write(outfd, prompt, strlen(prompt)) < 0)
goto out_err;
tcsetattr(infd, TCSAFLUSH, &tmp);
if (timeout)
failed = timed_read(infd, pass, maxlen, timeout);
else
failed = untimed_read(infd, pass, maxlen);
tcsetattr(infd, TCSAFLUSH, &orig);
out_err:
if (!failed && write(outfd, "\n", 1));
if (infd != STDIN_FILENO)
close(infd);
return failed;
}
/*
* Password reading behaviour matrix of get_key
* FIXME: rewrite this from scratch.
* p v n h
* -----------------+---+---+---+---
* interactive | Y | Y | Y | Inf
* from fd | N | N | Y | Inf
* from binary file | N | N | N | Inf or options->key_size
*
* Legend: p..prompt, v..can verify, n..newline-stop, h..read horizon
*
* Note: --key-file=- is interpreted as a read from a binary file (stdin)
*/
void get_key(char *prompt, char **key, unsigned int *passLen, int key_size,
const char *key_file, int timeout, int how2verify,
struct crypt_device *cd)
{
int fd = -1;
const int verify = how2verify & CRYPT_FLAG_VERIFY;
const int verify_if_possible = how2verify & CRYPT_FLAG_VERIFY_IF_POSSIBLE;
char *pass = NULL;
int read_horizon;
int regular_file = 0;
int read_stdin;
int r;
struct stat st;
/* Passphrase read from stdin? */
read_stdin = (!key_file || !strcmp(key_file, "-")) ? 1 : 0;
/* read_horizon applies only for real keyfile, not stdin or terminal */
read_horizon = (key_file && !read_stdin) ? key_size : 0 /* until EOF */;
/* Setup file descriptior */
fd = read_stdin ? STDIN_FILENO : open(key_file, O_RDONLY);
if (fd < 0) {
log_err(cd, _("Failed to open key file %s.\n"), key_file ?: "-");
goto out_err;
}
/* Interactive case */
if(isatty(fd)) {
int i;
pass = crypt_safe_alloc(MAX_TTY_PASSWORD_LEN);
if (!pass || (i = interactive_pass(prompt, pass, MAX_TTY_PASSWORD_LEN, timeout))) {
log_err(cd, _("Error reading passphrase from terminal.\n"));
goto out_err;
}
if (verify || verify_if_possible) {
char pass_verify[MAX_TTY_PASSWORD_LEN];
i = interactive_pass(_("Verify passphrase: "), pass_verify, sizeof(pass_verify), timeout);
if (i || strcmp(pass, pass_verify) != 0) {
log_err(cd, _("Passphrases do not match.\n"));
goto out_err;
}
memset(pass_verify, 0, sizeof(pass_verify));
}
*passLen = strlen(pass);
*key = pass;
} else {
/*
* This is either a fd-input or a file, in neither case we can verify the input,
* however we don't stop on new lines if it's a binary file.
*/
int buflen, i;
if(verify) {
log_err(cd, _("Can't do passphrase verification on non-tty inputs.\n"));
goto out_err;
}
/* The following for control loop does an exhausting
* read on the key material file, if requested with
* key_size == 0, as it's done by LUKS. However, we
* should warn the user, if it's a non-regular file,
* such as /dev/random, because in this case, the loop
* will read forever.
*/
if(!read_stdin && read_horizon == 0) {
if(stat(key_file, &st) < 0) {
log_err(cd, _("Failed to stat key file %s.\n"), key_file);
goto out_err;
}
if(!S_ISREG(st.st_mode))
log_std(cd, _("Warning: exhausting read requested, but key file %s"
" is not a regular file, function might never return.\n"),
key_file);
else
regular_file = 1;
}
buflen = 0;
for(i = 0; read_horizon == 0 || i < read_horizon; i++) {
if(i >= buflen - 1) {
buflen += 128;
pass = crypt_safe_realloc(pass, buflen);
if (!pass) {
log_err(cd, _("Out of memory while reading passphrase.\n"));
goto out_err;
}
}
r = read(fd, pass + i, 1);
if (r < 0) {
log_err(cd, _("Error reading passphrase.\n"));
goto out_err;
}
/* Stop on newline only if not requested read from keyfile */
if(r == 0 || (!key_file && pass[i] == '\n'))
break;
}
/* Fail if piped input dies reading nothing */
if(!i && !regular_file) {
log_dbg("Error reading passphrase.");
goto out_err;
}
pass[i] = 0;
*key = pass;
*passLen = i;
}
if(fd != STDIN_FILENO)
close(fd);
return;
out_err:
if(fd >= 0 && fd != STDIN_FILENO)
close(fd);
if(pass)
crypt_safe_free(pass);
*key = NULL;
*passLen = 0;
}
int device_ready(struct crypt_device *cd, const char *device, int mode)
{
int devfd, r = 1;

View File

@@ -85,3 +85,208 @@ void *crypt_safe_realloc(void *data, size_t size)
crypt_safe_free(data);
return new_data;
}
/* Password reading helpers */
static int untimed_read(int fd, char *pass, size_t maxlen)
{
ssize_t i;
i = read(fd, pass, maxlen);
if (i > 0) {
pass[i-1] = '\0';
i = 0;
} else if (i == 0) { /* EOF */
*pass = 0;
i = -1;
}
return i;
}
static int timed_read(int fd, char *pass, size_t maxlen, long timeout)
{
struct timeval t;
fd_set fds;
int failed = -1;
FD_ZERO(&fds);
FD_SET(fd, &fds);
t.tv_sec = timeout;
t.tv_usec = 0;
if (select(fd+1, &fds, NULL, NULL, &t) > 0)
failed = untimed_read(fd, pass, maxlen);
return failed;
}
static int interactive_pass(const char *prompt, char *pass, size_t maxlen,
long timeout)
{
struct termios orig, tmp;
int failed = -1;
int infd = STDIN_FILENO, outfd;
if (maxlen < 1)
goto out_err;
/* Read and write to /dev/tty if available */
if ((infd = outfd = open("/dev/tty", O_RDWR)) == -1) {
infd = STDIN_FILENO;
outfd = STDERR_FILENO;
}
if (tcgetattr(infd, &orig))
goto out_err;
memcpy(&tmp, &orig, sizeof(tmp));
tmp.c_lflag &= ~ECHO;
if (write(outfd, prompt, strlen(prompt)) < 0)
goto out_err;
tcsetattr(infd, TCSAFLUSH, &tmp);
if (timeout)
failed = timed_read(infd, pass, maxlen, timeout);
else
failed = untimed_read(infd, pass, maxlen);
tcsetattr(infd, TCSAFLUSH, &orig);
out_err:
if (!failed && write(outfd, "\n", 1));
if (infd != STDIN_FILENO)
close(infd);
return failed;
}
/*
* Password reading behaviour matrix of get_key
* FIXME: rewrite this from scratch.
* p v n h
* -----------------+---+---+---+---
* interactive | Y | Y | Y | Inf
* from fd | N | N | Y | Inf
* from binary file | N | N | N | Inf or options->key_size
*
* Legend: p..prompt, v..can verify, n..newline-stop, h..read horizon
*
* Note: --key-file=- is interpreted as a read from a binary file (stdin)
*/
void crypt_get_key(char *prompt, char **key, unsigned int *passLen, int key_size,
const char *key_file, int timeout, int verify,
struct crypt_device *cd)
{
int fd = -1;
char *pass = NULL;
int read_horizon;
int regular_file = 0;
int read_stdin;
int r;
struct stat st;
/* Passphrase read from stdin? */
read_stdin = (!key_file || !strcmp(key_file, "-")) ? 1 : 0;
/* read_horizon applies only for real keyfile, not stdin or terminal */
read_horizon = (key_file && !read_stdin) ? key_size : 0 /* until EOF */;
/* Setup file descriptior */
fd = read_stdin ? STDIN_FILENO : open(key_file, O_RDONLY);
if (fd < 0) {
crypt_log(cd, CRYPT_LOG_ERROR,
_("Failed to open key file.\n"));
goto out_err;
}
/* Interactive case */
if(isatty(fd)) {
int i;
pass = crypt_safe_alloc(MAX_TTY_PASSWORD_LEN);
if (!pass || (i = interactive_pass(prompt, pass, MAX_TTY_PASSWORD_LEN, timeout))) {
crypt_log(cd, CRYPT_LOG_ERROR,
_("Error reading passphrase from terminal.\n"));
goto out_err;
}
if (verify) {
char pass_verify[MAX_TTY_PASSWORD_LEN];
i = interactive_pass(_("Verify passphrase: "), pass_verify, sizeof(pass_verify), timeout);
if (i || strcmp(pass, pass_verify) != 0) {
crypt_log(cd, CRYPT_LOG_ERROR,
_("Passphrases do not match.\n"));
goto out_err;
}
memset(pass_verify, 0, sizeof(pass_verify));
}
*passLen = strlen(pass);
*key = pass;
} else {
/*
* This is either a fd-input or a file, in neither case we can verify the input,
* however we don't stop on new lines if it's a binary file.
*/
int buflen, i;
/* The following for control loop does an exhausting
* read on the key material file, if requested with
* key_size == 0, as it's done by LUKS. However, we
* should warn the user, if it's a non-regular file,
* such as /dev/random, because in this case, the loop
* will read forever.
*/
if(!read_stdin && read_horizon == 0) {
if(stat(key_file, &st) < 0) {
crypt_log(cd, CRYPT_LOG_ERROR,
_("Failed to stat key file.\n"));
goto out_err;
}
if(!S_ISREG(st.st_mode))
crypt_log(cd, CRYPT_LOG_NORMAL,
_("Warning: exhausting read requested, but key file"
" is not a regular file, function might never return.\n"));
else
regular_file = 1;
}
buflen = 0;
for(i = 0; read_horizon == 0 || i < read_horizon; i++) {
if(i >= buflen - 1) {
buflen += 128;
pass = crypt_safe_realloc(pass, buflen);
if (!pass) {
crypt_log(cd, CRYPT_LOG_ERROR,
_("Out of memory while reading passphrase.\n"));
goto out_err;
}
}
r = read(fd, pass + i, 1);
if (r < 0) {
crypt_log(cd, CRYPT_LOG_ERROR,
_("Error reading passphrase.\n"));
goto out_err;
}
/* Stop on newline only if not requested read from keyfile */
if(r == 0 || (!key_file && pass[i] == '\n'))
break;
}
/* Fail if piped input dies reading nothing */
if(!i && !regular_file)
goto out_err;
pass[i] = 0;
*key = pass;
*passLen = i;
}
if(fd != STDIN_FILENO)
close(fd);
return;
out_err:
if(fd >= 0 && fd != STDIN_FILENO)
close(fd);
if(pass)
crypt_safe_free(pass);
*key = NULL;
*passLen = 0;
}

View File

@@ -4,8 +4,16 @@
#define MAX_CIPHER_LEN 32
#define MAX_CIPHER_LEN_STR "32"
#define MAX_TTY_PASSWORD_LEN 512
struct crypt_device;
int crypt_parse_name_and_mode(const char *s, char *cipher, char *cipher_mode);
void crypt_get_key(char *prompt, char **key, unsigned int *passLen, int key_size,
const char *key_file, int timeout, int how2verify,
struct crypt_device *cd);
void *crypt_safe_alloc(size_t size);
void crypt_safe_free(void *data);
void *crypt_safe_realloc(void *data, size_t size);