0
0
mirror of https://github.com/vim/vim.git synced 2025-09-26 04:04:07 -04:00

updated for version 7.4.399

Problem:    Encryption implementation is messy.  Blowfish encryption has a
            weakness.
Solution:   Refactor the encryption, store the state in an allocated struct
            instead of using a save/restore mechanism.  Introduce the
            "blowfish2" method, which does not have the weakness and encrypts
            the whole undo file. (largely by David Leadbeater)
This commit is contained in:
Bram Moolenaar
2014-08-10 13:38:34 +02:00
parent 0106b4b891
commit 8f4ac01544
27 changed files with 1783 additions and 980 deletions

View File

@@ -24,20 +24,6 @@
#define BUFSIZE 8192 /* size of normal write buffer */
#define SMBUFSIZE 256 /* size of emergency write buffer */
#ifdef FEAT_CRYPT
/* crypt_magic[0] is pkzip crypt, crypt_magic[1] is sha2+blowfish */
static char *crypt_magic[] = {"VimCrypt~01!", "VimCrypt~02!"};
static char crypt_magic_head[] = "VimCrypt~";
# define CRYPT_MAGIC_LEN 12 /* must be multiple of 4! */
/* For blowfish, after the magic header, we store 8 bytes of salt and then 8
* bytes of seed (initialisation vector). */
static int crypt_salt_len[] = {0, 8};
static int crypt_seed_len[] = {0, 8};
#define CRYPT_SALT_LEN_MAX 8
#define CRYPT_SEED_LEN_MAX 8
#endif
/* Is there any system that doesn't have access()? */
#define USE_MCH_ACCESS
@@ -55,7 +41,6 @@ static char_u *readfile_charconvert __ARGS((char_u *fname, char_u *fenc, int *fd
static void check_marks_read __ARGS((void));
#endif
#ifdef FEAT_CRYPT
static int crypt_method_from_magic __ARGS((char *ptr, int len));
static char_u *check_for_cryptkey __ARGS((char_u *cryptkey, char_u *ptr, long *sizep, off_t *filesizep, int newfile, char_u *fname, int *did_ask));
#endif
#ifdef UNIX
@@ -116,6 +101,9 @@ struct bw_info
#ifdef HAS_BW_FLAGS
int bw_flags; /* FIO_ flags */
#endif
#ifdef FEAT_CRYPT
buf_T *bw_buffer; /* buffer being written */
#endif
#ifdef FEAT_MBYTE
char_u bw_rest[CONV_RESTLEN]; /* not converted bytes */
int bw_restlen; /* nr of bytes in bw_rest[] */
@@ -250,7 +238,6 @@ readfile(fname, sfname, from, lines_to_skip, lines_to_read, eap, flags)
#ifdef FEAT_CRYPT
char_u *cryptkey = NULL;
int did_ask_for_key = FALSE;
int crypt_method_used;
#endif
#ifdef FEAT_PERSISTENT_UNDO
context_sha256_T sha_ctx;
@@ -966,13 +953,6 @@ retry:
#endif
}
#ifdef FEAT_CRYPT
if (cryptkey != NULL)
/* Need to reset the state, but keep the key, don't want to ask for it
* again. */
crypt_pop_state();
#endif
/*
* When retrying with another "fenc" and the first time "fileformat"
* will be reset.
@@ -1174,6 +1154,15 @@ retry:
&& !read_buffer);
if (read_undo_file)
sha256_start(&sha_ctx);
#endif
#ifdef FEAT_CRYPT
if (curbuf->b_cryptstate != NULL)
{
/* Need to free the state, but keep the key, don't want to ask for
* it again. */
crypt_free_state(curbuf->b_cryptstate);
curbuf->b_cryptstate = NULL;
}
#endif
}
@@ -1339,6 +1328,76 @@ retry:
size = read_eintr(fd, ptr, size);
}
#ifdef FEAT_CRYPT
/*
* At start of file: Check for magic number of encryption.
*/
if (filesize == 0 && size > 0)
cryptkey = check_for_cryptkey(cryptkey, ptr, &size,
&filesize, newfile, sfname,
&did_ask_for_key);
/*
* Decrypt the read bytes. This is done before checking for
* EOF because the crypt layer may be buffering.
*/
if (cryptkey != NULL && size > 0)
{
if (crypt_works_inplace(curbuf->b_cryptstate))
{
crypt_decode_inplace(curbuf->b_cryptstate, ptr, size);
}
else
{
char_u *newptr = NULL;
int decrypted_size;
decrypted_size = crypt_decode_alloc(
curbuf->b_cryptstate, ptr, size, &newptr);
/* If the crypt layer is buffering, not producing
* anything yet, need to read more. */
if (size > 0 && decrypted_size == 0)
continue;
if (linerest == 0)
{
/* Simple case: reuse returned buffer (may be
* NULL, checked later). */
new_buffer = newptr;
}
else
{
long_u new_size;
/* Need new buffer to add bytes carried over. */
new_size = (long_u)(decrypted_size + linerest + 1);
new_buffer = lalloc(new_size, FALSE);
if (new_buffer == NULL)
{
do_outofmem_msg(new_size);
error = TRUE;
break;
}
mch_memmove(new_buffer, buffer, linerest);
if (newptr != NULL)
mch_memmove(new_buffer + linerest, newptr,
decrypted_size);
}
if (new_buffer != NULL)
{
vim_free(buffer);
buffer = new_buffer;
new_buffer = NULL;
line_start = buffer;
ptr = buffer + linerest;
}
size = decrypted_size;
}
}
#endif
if (size <= 0)
{
if (size < 0) /* read error */
@@ -1403,21 +1462,6 @@ retry:
}
#endif
}
#ifdef FEAT_CRYPT
/*
* At start of file: Check for magic number of encryption.
*/
if (filesize == 0)
cryptkey = check_for_cryptkey(cryptkey, ptr, &size,
&filesize, newfile, sfname,
&did_ask_for_key);
/*
* Decrypt the read bytes.
*/
if (cryptkey != NULL && size > 0)
crypt_decode(ptr, size);
#endif
}
skip_read = FALSE;
@@ -1430,10 +1474,9 @@ retry:
*/
if ((filesize == 0
# ifdef FEAT_CRYPT
|| (filesize == (CRYPT_MAGIC_LEN
+ crypt_salt_len[use_crypt_method]
+ crypt_seed_len[use_crypt_method])
&& cryptkey != NULL)
|| (cryptkey != NULL
&& filesize == crypt_get_header_len(
crypt_get_method_nr(curbuf)))
# endif
)
&& (fio_flags == FIO_UCSBOM
@@ -2262,15 +2305,15 @@ failed:
save_file_ff(curbuf); /* remember the current file format */
#ifdef FEAT_CRYPT
crypt_method_used = use_crypt_method;
if (cryptkey != NULL)
if (curbuf->b_cryptstate != NULL)
{
crypt_pop_state();
if (cryptkey != curbuf->b_p_key)
free_crypt_key(cryptkey);
/* don't set cryptkey to NULL, it's used below as a flag that
* encryption was used */
crypt_free_state(curbuf->b_cryptstate);
curbuf->b_cryptstate = NULL;
}
if (cryptkey != NULL && cryptkey != curbuf->b_p_key)
crypt_free_key(cryptkey);
/* Don't set cryptkey to NULL, it's used below as a flag that
* encryption was used. */
#endif
#ifdef FEAT_MBYTE
@@ -2457,10 +2500,7 @@ failed:
#ifdef FEAT_CRYPT
if (cryptkey != NULL)
{
if (crypt_method_used == 1)
STRCAT(IObuff, _("[blowfish]"));
else
STRCAT(IObuff, _("[crypted]"));
crypt_append_msg(curbuf);
c = TRUE;
}
#endif
@@ -2489,9 +2529,7 @@ failed:
#ifdef FEAT_CRYPT
if (cryptkey != NULL)
msg_add_lines(c, (long)linecnt, filesize
- CRYPT_MAGIC_LEN
- crypt_salt_len[use_crypt_method]
- crypt_seed_len[use_crypt_method]);
- crypt_get_header_len(crypt_get_method_nr(curbuf)));
else
#endif
msg_add_lines(c, (long)linecnt, filesize);
@@ -2881,33 +2919,6 @@ check_marks_read()
#endif
#if defined(FEAT_CRYPT) || defined(PROTO)
/*
* Get the crypt method used for a file from "ptr[len]", the magic text at the
* start of the file.
* Returns -1 when no encryption used.
*/
static int
crypt_method_from_magic(ptr, len)
char *ptr;
int len;
{
int i;
for (i = 0; i < (int)(sizeof(crypt_magic) / sizeof(crypt_magic[0])); i++)
{
if (len < (CRYPT_MAGIC_LEN + crypt_salt_len[i] + crypt_seed_len[i]))
continue;
if (memcmp(ptr, crypt_magic[i], CRYPT_MAGIC_LEN) == 0)
return i;
}
i = (int)STRLEN(crypt_magic_head);
if (len >= i && memcmp(ptr, crypt_magic_head, i) == 0)
EMSG(_("E821: File is encrypted with unknown method"));
return -1;
}
/*
* Check for magic number used for encryption. Applies to the current buffer.
* If found, the magic number is removed from ptr[*sizep] and *sizep and
@@ -2924,7 +2935,7 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask)
char_u *fname; /* file name to display */
int *did_ask; /* flag: whether already asked for key */
{
int method = crypt_method_from_magic((char *)ptr, *sizep);
int method = crypt_method_nr_from_magic((char *)ptr, *sizep);
int b_p_ro = curbuf->b_p_ro;
if (method >= 0)
@@ -2933,9 +2944,7 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask)
* Avoids accidentally overwriting the file with garbage. */
curbuf->b_p_ro = TRUE;
set_crypt_method(curbuf, method);
if (method > 0)
(void)blowfish_self_test();
crypt_set_cm_option(curbuf, method);
if (cryptkey == NULL && !*did_ask)
{
if (*curbuf->b_p_key)
@@ -2948,7 +2957,7 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask)
* Happens when retrying to detect encoding. */
smsg((char_u *)_(need_key_msg), fname);
msg_scroll = TRUE;
cryptkey = get_crypt_key(newfile, FALSE);
cryptkey = crypt_get_key(newfile, FALSE);
*did_ask = TRUE;
/* check if empty key entered */
@@ -2963,24 +2972,18 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask)
if (cryptkey != NULL)
{
int seed_len = crypt_seed_len[method];
int salt_len = crypt_salt_len[method];
int header_len;
crypt_push_state();
use_crypt_method = method;
if (method == 0)
crypt_init_keys(cryptkey);
else
{
bf_key_init(cryptkey, ptr + CRYPT_MAGIC_LEN, salt_len);
bf_cfb_init(ptr + CRYPT_MAGIC_LEN + salt_len, seed_len);
}
curbuf->b_cryptstate = crypt_create_from_header(
method, cryptkey, ptr);
crypt_set_cm_option(curbuf, method);
/* Remove cryptmethod specific header from the text. */
header_len = crypt_get_header_len(method);
*filesizep += header_len;
*sizep -= header_len;
mch_memmove(ptr, ptr + header_len, (size_t)*sizep);
/* Remove magic number from the text */
*filesizep += CRYPT_MAGIC_LEN + salt_len + seed_len;
*sizep -= CRYPT_MAGIC_LEN + salt_len + seed_len;
mch_memmove(ptr, ptr + CRYPT_MAGIC_LEN + salt_len + seed_len,
(size_t)*sizep);
/* Restore the read-only flag. */
curbuf->b_p_ro = b_p_ro;
}
@@ -2992,85 +2995,6 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask)
return cryptkey;
}
/*
* Check for magic number used for encryption. Applies to the current buffer.
* If found and decryption is possible returns OK;
*/
int
prepare_crypt_read(fp)
FILE *fp;
{
int method;
char_u buffer[CRYPT_MAGIC_LEN + CRYPT_SALT_LEN_MAX
+ CRYPT_SEED_LEN_MAX + 2];
if (fread(buffer, CRYPT_MAGIC_LEN, 1, fp) != 1)
return FAIL;
method = crypt_method_from_magic((char *)buffer,
CRYPT_MAGIC_LEN +
CRYPT_SEED_LEN_MAX +
CRYPT_SALT_LEN_MAX);
if (method < 0 || method != get_crypt_method(curbuf))
return FAIL;
crypt_push_state();
if (method == 0)
crypt_init_keys(curbuf->b_p_key);
else
{
int salt_len = crypt_salt_len[method];
int seed_len = crypt_seed_len[method];
if (fread(buffer, salt_len + seed_len, 1, fp) != 1)
return FAIL;
bf_key_init(curbuf->b_p_key, buffer, salt_len);
bf_cfb_init(buffer + salt_len, seed_len);
}
return OK;
}
/*
* Prepare for writing encrypted bytes for buffer "buf".
* Returns a pointer to an allocated header of length "*lenp".
* When out of memory returns NULL.
* Otherwise calls crypt_push_state(), call crypt_pop_state() later.
*/
char_u *
prepare_crypt_write(buf, lenp)
buf_T *buf;
int *lenp;
{
char_u *header;
int seed_len = crypt_seed_len[get_crypt_method(buf)];
int salt_len = crypt_salt_len[get_crypt_method(buf)];
char_u *salt;
char_u *seed;
header = alloc_clear(CRYPT_MAGIC_LEN + CRYPT_SALT_LEN_MAX
+ CRYPT_SEED_LEN_MAX + 2);
if (header != NULL)
{
crypt_push_state();
use_crypt_method = get_crypt_method(buf); /* select zip or blowfish */
vim_strncpy(header, (char_u *)crypt_magic[use_crypt_method],
CRYPT_MAGIC_LEN);
if (use_crypt_method == 0)
crypt_init_keys(buf->b_p_key);
else
{
/* Using blowfish, add salt and seed. */
salt = header + CRYPT_MAGIC_LEN;
seed = salt + salt_len;
sha2_seed(salt, salt_len, seed, seed_len);
bf_key_init(buf->b_p_key, salt, salt_len);
bf_cfb_init(seed, seed_len);
}
}
*lenp = CRYPT_MAGIC_LEN + salt_len + seed_len;
return header;
}
#endif /* FEAT_CRYPT */
#ifdef UNIX
@@ -3224,9 +3148,6 @@ buf_write(buf, fname, sfname, start, end, eap, append, forceit,
int write_undo_file = FALSE;
context_sha256_T sha_ctx;
#endif
#ifdef FEAT_CRYPT
int crypt_method_used;
#endif
if (fname == NULL || *fname == NUL) /* safety check */
return FAIL;
@@ -3261,6 +3182,9 @@ buf_write(buf, fname, sfname, start, end, eap, append, forceit,
# ifdef USE_ICONV
write_info.bw_iconv_fd = (iconv_t)-1;
# endif
#endif
#ifdef FEAT_CRYPT
write_info.bw_buffer = buf;
#endif
/* After writing a file changedtick changes but we don't want to display
@@ -4505,17 +4429,17 @@ restore_backup:
#ifdef FEAT_CRYPT
if (*buf->b_p_key != NUL && !filtering)
{
char_u *header;
int header_len;
char_u *header;
int header_len;
header = prepare_crypt_write(buf, &header_len);
if (header == NULL)
buf->b_cryptstate = crypt_create_for_writing(crypt_get_method_nr(buf),
buf->b_p_key, &header, &header_len);
if (buf->b_cryptstate == NULL || header == NULL)
end = 0;
else
{
/* Write magic number, so that Vim knows that this file is
* encrypted when reading it again. This also undergoes utf-8 to
* ucs-2/4 conversion when needed. */
/* Write magic number, so that Vim knows how this file is
* encrypted when reading it back. */
write_info.bw_buf = header;
write_info.bw_len = header_len;
write_info.bw_flags = FIO_NOCONVERT;
@@ -4769,12 +4693,13 @@ restore_backup:
mch_set_acl(wfname, acl);
#endif
#ifdef FEAT_CRYPT
crypt_method_used = use_crypt_method;
if (wb_flags & FIO_ENCRYPTED)
crypt_pop_state();
if (buf->b_cryptstate != NULL)
{
crypt_free_state(buf->b_cryptstate);
buf->b_cryptstate = NULL;
}
#endif
#if defined(FEAT_MBYTE) && defined(FEAT_EVAL)
if (wfname != fname)
{
@@ -4924,10 +4849,7 @@ restore_backup:
#ifdef FEAT_CRYPT
if (wb_flags & FIO_ENCRYPTED)
{
if (crypt_method_used == 1)
STRCAT(IObuff, _("[blowfish]"));
else
STRCAT(IObuff, _("[crypted]"));
crypt_append_msg(buf);
c = TRUE;
}
#endif
@@ -5740,8 +5662,26 @@ buf_write_bytes(ip)
#endif /* FEAT_MBYTE */
#ifdef FEAT_CRYPT
if (flags & FIO_ENCRYPTED) /* encrypt the data */
crypt_encode(buf, len, buf);
if (flags & FIO_ENCRYPTED)
{
/* Encrypt the data. Do it in-place if possible, otherwise use an
* allocated buffer. */
if (crypt_works_inplace(ip->bw_buffer->b_cryptstate))
{
crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len);
}
else
{
char_u *outbuf;
len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf);
if (len == 0)
return OK; /* Crypt layer is buffering, will flush later. */
wlen = write_eintr(ip->bw_fd, outbuf, len);
vim_free(outbuf);
return (wlen < len) ? FAIL : OK;
}
}
#endif
wlen = write_eintr(ip->bw_fd, buf, len);