0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

Crypt the text in the undo file if the file itself is crypted.

This commit is contained in:
Bram Moolenaar
2010-05-30 22:48:02 +02:00
parent 6ed8ed84f9
commit a3ff49fdcc
10 changed files with 260 additions and 50 deletions

View File

@@ -1,6 +1,6 @@
" Vim Compiler File " Vim Compiler File
" Compiler: Perl syntax checks (perl -Wc) " Compiler: Perl syntax checks (perl -Wc)
" Maintainer: Christian J. Robinson <infynity@onewest.net> " Maintainer: Christian J. Robinson <heptite@gmail.com>
" Last Change: 2006 Aug 13 " Last Change: 2006 Aug 13
if exists("current_compiler") if exists("current_compiler")

View File

@@ -1366,6 +1366,9 @@ this before writing the file. When reading an encrypted file it will be set
automatically to the method used when that file was written. You can change automatically to the method used when that file was written. You can change
'cryptmethod' before writing that file to change the method. 'cryptmethod' before writing that file to change the method.
When writing an undo file, the same key and method will be used for the text
in the undo file. |persistent-undo|.
*E817* *E818* *E819* *E820* *E817* *E818* *E819* *E820*
When encryption does not work properly, you would be able to write your text When encryption does not work properly, you would be able to write your text
to a file and never be able to read it back. Therefore a test is performed to to a file and never be able to read it back. Therefore a test is performed to

View File

@@ -1085,9 +1085,6 @@ Vim 7.3:
- using NSIS 2.46: install on Windows 7 works, but no "Edit with Vim" menu. - using NSIS 2.46: install on Windows 7 works, but no "Edit with Vim" menu.
Use register_shell_extension()? (George Reilly, 2010 May 26) Use register_shell_extension()? (George Reilly, 2010 May 26)
Ron's version: http://dev.ronware.org/p/vim/finfo?name=gvim.nsi Ron's version: http://dev.ronware.org/p/vim/finfo?name=gvim.nsi
- Persistent undo bugs / fixes:
- Need to check all values for evil manipulation.
- Also crypt the undo file.
- Also crypt the swap file, each block separately. Change mf_write() and - Also crypt the swap file, each block separately. Change mf_write() and
mf_read(). How to get b_p_key to these functions? mf_read(). How to get b_p_key to these functions?
- Do profiling on sha256 code to find obvious bottlenecks. - Do profiling on sha256 code to find obvious bottlenecks.

View File

@@ -225,6 +225,9 @@ after the undo file was written, to prevent corruption.
Undo files are normally saved in the same directory as the file. This can be Undo files are normally saved in the same directory as the file. This can be
changed with the 'undodir' option. changed with the 'undodir' option.
When the file is encrypted, the text in the undo file is also crypted. The
same key and method is used. |encryption|
You can also save and restore undo histories by using ":wundo" and ":rundo" You can also save and restore undo histories by using ":wundo" and ":rundo"
respectively: respectively:
*:wundo* *:rundo* *:wundo* *:rundo*

View File

@@ -1,7 +1,7 @@
" Vim indent file " Vim indent file
" Language: tf (TinyFugue) " Language: tf (TinyFugue)
" Maintainer: Christian J. Robinson <infynity@onewest.net> " Maintainer: Christian J. Robinson <heptite@gmail.com>
" URL: http://www.infynity.spodzone.com/vim/indent/tf.vim " URL: http://christianrobinson.name/vim/indent/tf.vim
" Last Change: 2002 May 29 " Last Change: 2002 May 29
" Only load this indent file when no other was loaded. " Only load this indent file when no other was loaded.

View File

@@ -2827,7 +2827,7 @@ check_marks_read()
} }
#endif #endif
#ifdef FEAT_CRYPT #if defined(FEAT_CRYPT) || defined(PROTO)
/* /*
* Get the crypt method used for a file from "ptr[len]", the magic text at the * Get the crypt method used for a file from "ptr[len]", the magic text at the
* start of the file. * start of the file.
@@ -2926,7 +2926,129 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, did_ask)
return cryptkey; return cryptkey;
} }
#endif
/*
* 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_SEED_LEN_MAX + 2];
if (fread(buffer, CRYPT_MAGIC_LEN, 1, fp) != 1)
return FAIL;
method = get_crypt_method((char *)buffer,
CRYPT_MAGIC_LEN + CRYPT_SEED_LEN_MAX);
if (method < 0 || method != curbuf->b_p_cm)
return FAIL;
if (method == 0)
crypt_init_keys(curbuf->b_p_key);
else
{
int seed_len = crypt_seed_len[method];
if (fread(buffer, seed_len, 1, fp) != 1)
return FAIL;
bf_key_init(curbuf->b_p_key);
bf_ofb_init(buffer, seed_len);
}
return OK;
}
/*
* Prepare for writing encrypted bytes for buffer "buf".
* Returns a pointer to an allocated header of length "*lenp".
*/
char_u *
prepare_crypt_write(buf, lenp)
buf_T *buf;
int *lenp;
{
char_u *header;
int seed_len = crypt_seed_len[buf->b_p_cm];
header = alloc_clear(CRYPT_MAGIC_LEN + CRYPT_SEED_LEN_MAX + 2);
if (header != NULL)
{
use_crypt_method = buf->b_p_cm; /* select pkzip or blowfish */
vim_strncpy(header, (char_u *)crypt_magic[use_crypt_method],
CRYPT_MAGIC_LEN);
if (buf->b_p_cm == 0)
crypt_init_keys(buf->b_p_key);
else
{
/* Using blowfish, add seed. */
sha2_seed(header + CRYPT_MAGIC_LEN, seed_len); /* create iv */
bf_ofb_init(header + CRYPT_MAGIC_LEN, seed_len);
bf_key_init(buf->b_p_key);
}
}
*lenp = CRYPT_MAGIC_LEN + seed_len;
return header;
}
/*
* Like fwrite() but crypt the bytes when 'key' is set.
* Returns 1 if successful.
*/
size_t
fwrite_crypt(buf, ptr, len, fp)
buf_T *buf;
char_u *ptr;
size_t len;
FILE *fp;
{
char_u *copy;
char_u small_buf[100];
int ztemp, t;
size_t i;
if (*buf->b_p_key == NUL)
return fwrite(ptr, len, (size_t)1, fp);
if (len < 100)
copy = small_buf; /* no malloc()/free() for short strings */
else
{
copy = lalloc(len, FALSE);
if (copy == NULL)
return 0;
}
for (i = 0; i < len; ++i)
{
ztemp = ptr[i];
copy[i] = ZENCODE(ztemp, t);
}
i = fwrite(copy, len, (size_t)1, fp);
if (copy != small_buf)
vim_free(copy);
return i;
}
/*
* Read a string of length "len" from "fd".
* When 'key' is set decrypt the bytes.
*/
char_u *
read_string_decrypt(buf, fd, len)
buf_T *buf;
FILE *fd;
int len;
{
char_u *ptr;
char_u *p;
ptr = read_string(fd, len);
if (ptr != NULL || *buf->b_p_key != NUL)
for (p = ptr; p < ptr + len; ++p)
ZDECODE(*p);
return ptr;
}
#endif /* FEAT_CRYPT */
#ifdef UNIX #ifdef UNIX
static void static void
@@ -4323,34 +4445,25 @@ restore_backup:
#ifdef FEAT_CRYPT #ifdef FEAT_CRYPT
if (*buf->b_p_key && !filtering) if (*buf->b_p_key && !filtering)
{ {
char_u header[CRYPT_MAGIC_LEN + CRYPT_SEED_LEN_MAX + 2]; char_u *header;
int seed_len = crypt_seed_len[buf->b_p_cm]; int header_len;
use_crypt_method = buf->b_p_cm; /* select pkzip or blowfish */ header = prepare_crypt_write(buf, &header_len);
if (header == NULL)
vim_memset(header, 0, sizeof(header)); end = 0;
vim_strncpy(header, (char_u *)crypt_magic[use_crypt_method],
CRYPT_MAGIC_LEN);
if (buf->b_p_cm == 0)
crypt_init_keys(buf->b_p_key);
else else
{ {
/* Using blowfish, add seed. */
sha2_seed(header + CRYPT_MAGIC_LEN, seed_len); /* create iv */
bf_ofb_init(header + CRYPT_MAGIC_LEN, seed_len);
bf_key_init(buf->b_p_key);
}
/* Write magic number, so that Vim knows that this file is /* Write magic number, so that Vim knows that this file is
* encrypted when reading it again. This also undergoes utf-8 to * encrypted when reading it again. This also undergoes utf-8 to
* ucs-2/4 conversion when needed. */ * ucs-2/4 conversion when needed. */
write_info.bw_buf = (char_u *)header; write_info.bw_buf = header;
write_info.bw_len = CRYPT_MAGIC_LEN + seed_len; write_info.bw_len = header_len;
write_info.bw_flags = FIO_NOCONVERT; write_info.bw_flags = FIO_NOCONVERT;
if (buf_write_bytes(&write_info) == FAIL) if (buf_write_bytes(&write_info) == FAIL)
end = 0; end = 0;
wb_flags |= FIO_ENCRYPTED; wb_flags |= FIO_ENCRYPTED;
vim_free(header);
}
} }
#endif #endif

View File

@@ -2,6 +2,10 @@
void filemess __ARGS((buf_T *buf, char_u *name, char_u *s, int attr)); void filemess __ARGS((buf_T *buf, char_u *name, char_u *s, int attr));
int readfile __ARGS((char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_skip, linenr_T lines_to_read, exarg_T *eap, int flags)); int readfile __ARGS((char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_skip, linenr_T lines_to_read, exarg_T *eap, int flags));
int prep_exarg __ARGS((exarg_T *eap, buf_T *buf)); int prep_exarg __ARGS((exarg_T *eap, buf_T *buf));
int prepare_crypt_read __ARGS((FILE *fp));
char_u *prepare_crypt_write __ARGS((buf_T *buf, int *lenp));
size_t fwrite_crypt __ARGS((buf_T *buf, char_u *ptr, size_t len, FILE *fp));
char_u *read_string_decrypt __ARGS((buf_T *buf, FILE *fd, int len));
int check_file_readonly __ARGS((char_u *fname, int perm)); int check_file_readonly __ARGS((char_u *fname, int perm));
int buf_write __ARGS((buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_T end, exarg_T *eap, int append, int forceit, int reset_changed, int filtering)); int buf_write __ARGS((buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_T end, exarg_T *eap, int append, int forceit, int reset_changed, int filtering));
void msg_add_fname __ARGS((buf_T *buf, char_u *fname)); void msg_add_fname __ARGS((buf_T *buf, char_u *fname));

View File

@@ -49,6 +49,55 @@ dd:set ul=100
:e Xtestfile :e Xtestfile
uuu:w >>test.out uuu:w >>test.out
:" :"
:" And now with encryption, cryptmethod=0
:e! Xtestfile
:set undofile cm=0
ggdG
imonday
tuesday
wednesday
thursday
friday:set ul=100
kkkdd:set ul=100
dd:set ul=100
dd:set ul=100
:X
foobar
foobar
:w!
:bwipe!
:e Xtestfile
foobar
:set key=
uu:w >>test.out
:"
:"
:" With encryption, cryptmethod=1
:e! Xtestfile
:set undofile cm=1
ggdG
ijan
feb
mar
apr
jun:set ul=100
kk0ifoo :set ul=100
dd:set ul=100
ibar :set ul=100
:X
foobar
foobar
:w!
:bwipe!
:e Xtestfile
foobar
:set key=
/bar
:.w >>test.out
u:.w >>test.out
u:.w >>test.out
u:.w >>test.out
:"
:" Rename the undo file so that it gets cleaned up. :" Rename the undo file so that it gets cleaned up.
:call rename(".Xtestfile.un~", "Xtestundo") :call rename(".Xtestfile.un~", "Xtestundo")
:qa! :qa!

View File

@@ -7,3 +7,11 @@ seven
eight eight
nine nine
ten ten
monday
wednesday
thursday
friday
bar apr
apr
foo mar
mar

View File

@@ -103,9 +103,9 @@ static void u_freeentry __ARGS((u_entry_T *, long));
static void corruption_error __ARGS((char *msg, char_u *file_name)); static void corruption_error __ARGS((char *msg, char_u *file_name));
static void u_free_uhp __ARGS((u_header_T *uhp)); static void u_free_uhp __ARGS((u_header_T *uhp));
static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash)); static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash));
static int serialize_uhp __ARGS((FILE *fp, u_header_T *uhp)); static int serialize_uhp __ARGS((FILE *fp, buf_T *buf, u_header_T *uhp));
static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name)); static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name));
static int serialize_uep __ARGS((u_entry_T *uep, FILE *fp)); static int serialize_uep __ARGS((FILE *fp, buf_T *buf, u_entry_T *uep));
static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name)); static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name));
static void serialize_pos __ARGS((pos_T pos, FILE *fp)); static void serialize_pos __ARGS((pos_T pos, FILE *fp));
static void unserialize_pos __ARGS((pos_T *pos, FILE *fp)); static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
@@ -670,6 +670,7 @@ nomem:
# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */ # define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */ # define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
# define UF_VERSION 1 /* 2-byte undofile version number */ # define UF_VERSION 1 /* 2-byte undofile version number */
# define UF_VERSION_CRYPT 0x8001 /* idem, encrypted */
static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s"); static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
@@ -811,8 +812,29 @@ serialize_header(fp, buf, hash)
/* Start writing, first the magic marker and undo info version. */ /* Start writing, first the magic marker and undo info version. */
if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1) if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
return FAIL; return FAIL;
/* If the buffer is encrypted then all text bytes following will be
* encrypted. Numbers and other info is not crypted. */
#ifdef FEAT_CRYPT
if (*buf->b_p_key)
{
char_u *header;
int header_len;
put_bytes(fp, (long_u)UF_VERSION_CRYPT, 2);
header = prepare_crypt_write(buf, &header_len);
if (header == NULL)
return FAIL;
len = fwrite(header, (size_t)header_len, (size_t)1, fp);
vim_free(header);
if (len != 1)
return FAIL;
}
else
#endif
put_bytes(fp, (long_u)UF_VERSION, 2); put_bytes(fp, (long_u)UF_VERSION, 2);
/* Write a hash of the buffer text, so that we can verify it is still the /* Write a hash of the buffer text, so that we can verify it is still the
* same when reading the buffer text. */ * same when reading the buffer text. */
if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1) if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
@@ -822,7 +844,7 @@ serialize_header(fp, buf, hash)
put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4); put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0; len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
put_bytes(fp, (long_u)len, 4); put_bytes(fp, (long_u)len, 4);
if (len > 0 && fwrite(buf->b_u_line_ptr, (size_t)len, (size_t)1, fp) != 1) if (len > 0 && fwrite_crypt(buf, buf->b_u_line_ptr, (size_t)len, fp) != 1)
return FAIL; return FAIL;
put_bytes(fp, (long_u)buf->b_u_line_lnum, 4); put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
put_bytes(fp, (long_u)buf->b_u_line_colnr, 4); put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
@@ -841,8 +863,9 @@ serialize_header(fp, buf, hash)
} }
static int static int
serialize_uhp(fp, uhp) serialize_uhp(fp, buf, uhp)
FILE *fp; FILE *fp;
buf_T *buf;
u_header_T *uhp; u_header_T *uhp;
{ {
int i; int i;
@@ -882,7 +905,7 @@ serialize_uhp(fp, uhp)
for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
{ {
put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2); put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
if (serialize_uep(uep, fp) == FAIL) if (serialize_uep(fp, buf, uep) == FAIL)
return FAIL; return FAIL;
} }
put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2); put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
@@ -971,9 +994,10 @@ unserialize_uhp(fp, file_name)
* Serialize "uep" to "fp". * Serialize "uep" to "fp".
*/ */
static int static int
serialize_uep(uep, fp) serialize_uep(fp, buf, uep)
u_entry_T *uep;
FILE *fp; FILE *fp;
buf_T *buf;
u_entry_T *uep;
{ {
int i; int i;
size_t len; size_t len;
@@ -987,7 +1011,7 @@ serialize_uep(uep, fp)
len = STRLEN(uep->ue_array[i]); len = STRLEN(uep->ue_array[i]);
if (put_bytes(fp, (long_u)len, 4) == FAIL) if (put_bytes(fp, (long_u)len, 4) == FAIL)
return FAIL; return FAIL;
if (len > 0 && fwrite(uep->ue_array[i], len, (size_t)1, fp) != 1) if (len > 0 && fwrite_crypt(buf, uep->ue_array[i], len, fp) != 1)
return FAIL; return FAIL;
} }
return OK; return OK;
@@ -1034,7 +1058,7 @@ unserialize_uep(fp, error, file_name)
{ {
line_len = get4c(fp); line_len = get4c(fp);
if (line_len >= 0) if (line_len >= 0)
line = read_string(fp, line_len); line = read_string_decrypt(curbuf, fp, line_len);
else else
{ {
line = NULL; line = NULL;
@@ -1333,7 +1357,7 @@ u_write_undo(name, forceit, buf, hash)
#ifdef U_DEBUG #ifdef U_DEBUG
++headers_written; ++headers_written;
#endif #endif
if (serialize_uhp(fp, uhp) == FAIL) if (serialize_uhp(fp, buf, uhp) == FAIL)
goto write_error; goto write_error;
} }
@@ -1478,7 +1502,20 @@ u_read_undo(name, hash, orig_name)
goto error; goto error;
} }
version = get2c(fp); version = get2c(fp);
if (version != UF_VERSION) if (version == UF_VERSION_CRYPT)
{
#ifdef FEAT_CRYPT
if (prepare_crypt_read(fp) == FAIL)
{
EMSG2(_("E826: Undo file decryption failed: %s"), file_name);
goto error;
}
#else
EMSG2(_("E826: Undo file is encrypted: %s"), file_name);
goto error;
#endif
}
else if (version != UF_VERSION)
{ {
EMSG2(_("E824: Incompatible undo file: %s"), file_name); EMSG2(_("E824: Incompatible undo file: %s"), file_name);
goto error; goto error;
@@ -1510,7 +1547,7 @@ u_read_undo(name, hash, orig_name)
if (str_len < 0) if (str_len < 0)
goto error; goto error;
if (str_len > 0) if (str_len > 0)
line_ptr = read_string(fp, str_len); line_ptr = read_string_decrypt(curbuf, fp, str_len);
line_lnum = (linenr_T)get4c(fp); line_lnum = (linenr_T)get4c(fp);
line_colnr = (colnr_T)get4c(fp); line_colnr = (colnr_T)get4c(fp);
if (line_lnum < 0 || line_colnr < 0) if (line_lnum < 0 || line_colnr < 0)
@@ -1634,10 +1671,6 @@ u_read_undo(name, hash, orig_name)
curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx]; curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx]; curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx]; curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
#ifdef U_DEBUG
if (curbuf->b_u_curhead != NULL)
corruption_error("curhead not NULL", file_name);
#endif
curbuf->b_u_line_ptr = line_ptr; curbuf->b_u_line_ptr = line_ptr;
curbuf->b_u_line_lnum = line_lnum; curbuf->b_u_line_lnum = line_lnum;
curbuf->b_u_line_colnr = line_colnr; curbuf->b_u_line_colnr = line_colnr;