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:
346
src/fileio.c
346
src/fileio.c
@@ -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);
|
||||
|
Reference in New Issue
Block a user