Skip to content

Commit 6ee7b52

Browse files
ychinchrisbra
authored andcommitted
patch 9.0.1968: cmdline completion should consider key option
Problem: cmdline completion should consider key option Solution: Disable cmdline completion for key option, slightly refactor how P_NO_CMD_EXPAND is handled Harden crypto 'key' option: turn off cmdline completion, disable set-= "set-=" can be used maliciously with a crypto key, as it allows an attacker (who either has access to the computer or a plugin author) to guess a substring by observing the modified state. Simply turn off set+=/-=/^= for this option as there is no good reason for them to be used. Update docs to make that clear as well. Also, don't allow cmdline completion for 'key' as it just shows ***** which is not useful and confusing to the user what it means (if the user accidentally hits enter they will have replaced their key with "*****" instead). Move logic to better location, don't use above 32-bit for flags Move P_NO_CMD_EXPAND to use the unused 0x20 instead of going above 32-bits, as currently the flags parameter is only 32-bits on some systems. Left a comment to warn that future additions will need to change how the flags work either by making it 64-bit or split into two member vars. Also, move the logic for detecting P_NO_CMD_EXPAND earlier so it's not up to each handler to decide, and you won't see the temporary "..." that Vim shows while waiting for completion handler to complete. closes: #13224 Signed-off-by: Christian Brabandt <[email protected]> Co-authored-by: Yee Cheng Chin <[email protected]>
1 parent 7ece036 commit 6ee7b52

11 files changed

Lines changed: 74 additions & 19 deletions

File tree

runtime/doc/options.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4928,6 +4928,8 @@ A jump table for the options with a short description can be found at |Q_op|.
49284928
"echo &key". This is to avoid showing it to someone who shouldn't
49294929
know. It also means you cannot see it yourself once you have set it,
49304930
be careful not to make a typing error!
4931+
You also cannot use |:set-=|, |:set+=|, |:set^=| on this option to
4932+
prevent an attacker from guessing substrings in your key.
49314933
You can use "&key" in an expression to detect whether encryption is
49324934
enabled. When 'key' is set it returns "*****" (five stars).
49334935

src/option.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,13 +1314,6 @@ ex_set(exarg_T *eap)
13141314
/*
13151315
* :set operator types
13161316
*/
1317-
typedef enum {
1318-
OP_NONE = 0,
1319-
OP_ADDING, // "opt+=arg"
1320-
OP_PREPENDING, // "opt^=arg"
1321-
OP_REMOVING, // "opt-=arg"
1322-
} set_op_T;
1323-
13241317
typedef enum {
13251318
PREFIX_NO = 0, // "no" prefix
13261319
PREFIX_NONE, // no prefix
@@ -1935,7 +1928,7 @@ do_set_option_string(
19351928
char_u **argp,
19361929
int nextchar,
19371930
set_op_T op_arg,
1938-
int flags,
1931+
long_u flags,
19391932
int cp_val,
19401933
char_u *varp_arg,
19411934
char *errbuf,
@@ -2037,7 +2030,7 @@ do_set_option_string(
20372030
// be triggered that can cause havoc.
20382031
*errmsg = did_set_string_option(
20392032
opt_idx, (char_u **)varp, oldval, newval, errbuf,
2040-
opt_flags, value_checked);
2033+
opt_flags, op, value_checked);
20412034

20422035
secure = secure_saved;
20432036
}
@@ -7376,6 +7369,15 @@ set_context_in_set_cmd(
73767369
else
73777370
expand_option_idx = opt_idx;
73787371

7372+
if (!is_term_option)
7373+
{
7374+
if (options[opt_idx].flags & P_NO_CMD_EXPAND)
7375+
{
7376+
xp->xp_context=EXPAND_UNSUCCESSFUL;
7377+
return;
7378+
}
7379+
}
7380+
73797381
xp->xp_pattern = p + 1;
73807382
expand_option_start_col = (int)(p + 1 - xp->xp_line);
73817383

src/option.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
// the same.
2626
#define P_EXPAND 0x10 // environment expansion. NOTE: P_EXPAND can
2727
// never be used for local or hidden options!
28+
#define P_NO_CMD_EXPAND 0x20 // don't perform cmdline completions
2829
#define P_NODEFAULT 0x40 // don't set to default value
2930
#define P_DEF_ALLOCED 0x80 // default value is in allocated memory, must
3031
// use vim_free() when assigning new value
@@ -61,6 +62,10 @@
6162
#define P_MLE 0x20000000L // under control of 'modelineexpr'
6263
#define P_FUNC 0x40000000L // accept a function reference or a lambda
6364
#define P_COLON 0x80000000L // values use colons to create sublists
65+
// Warning: Currently we have used all 32 bits for option flags. On some 32-bit
66+
// systems, the flags are stored as a 32-bit integer, and adding more
67+
// flags will overflow it. Adding another flag will need to change how
68+
// it's stored first.
6469

6570
// Returned by get_option_value().
6671
typedef enum {

src/optiondefs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1467,7 +1467,7 @@ static struct vimoption options[] =
14671467
{"jumpoptions", "jop", P_STRING|P_VI_DEF|P_VIM|P_ONECOMMA|P_NODUP,
14681468
(char_u *)&p_jop, PV_NONE, did_set_jumpoptions, expand_set_jumpoptions,
14691469
{(char_u *)"", (char_u *)0L} SCTX_INIT},
1470-
{"key", NULL, P_STRING|P_ALLOCED|P_VI_DEF|P_NO_MKRC,
1470+
{"key", NULL, P_STRING|P_ALLOCED|P_VI_DEF|P_NO_MKRC|P_NO_CMD_EXPAND,
14711471
#ifdef FEAT_CRYPT
14721472
(char_u *)&p_key, PV_KEY, did_set_cryptkey, NULL,
14731473
{(char_u *)"", (char_u *)0L}

src/optionstr.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ set_string_option(
579579
}
580580
#endif
581581
if ((errmsg = did_set_string_option(opt_idx, varp, oldval, value, errbuf,
582-
opt_flags, &value_checked)) == NULL)
582+
opt_flags, OP_NONE, &value_checked)) == NULL)
583583
did_set_option(opt_idx, opt_flags, TRUE, value_checked);
584584

585585
#if defined(FEAT_EVAL)
@@ -1578,6 +1578,10 @@ did_set_cryptkey(optset_T *args)
15781578
// history.
15791579
remove_key_from_history();
15801580

1581+
if (args->os_op != OP_NONE)
1582+
// Don't allow set+=/-=/^= as they can allow for substring guessing
1583+
return e_invalid_argument;
1584+
15811585
if (STRCMP(curbuf->b_p_key, args->os_oldval.string) != 0)
15821586
{
15831587
// Need to update the swapfile.
@@ -4209,6 +4213,7 @@ did_set_string_option(
42094213
char_u *value, // new value of the option
42104214
char *errbuf, // buffer for errors, or NULL
42114215
int opt_flags, // OPT_LOCAL and/or OPT_GLOBAL
4216+
set_op_T op, // OP_ADDING/OP_PREPENDING/OP_REMOVING
42124217
int *value_checked) // value was checked to be safe, no
42134218
// need to set P_INSECURE
42144219
{
@@ -4247,6 +4252,7 @@ did_set_string_option(
42474252
args.os_varp = (char_u *)varp;
42484253
args.os_idx = opt_idx;
42494254
args.os_flags = opt_flags;
4255+
args.os_op = op;
42504256
args.os_oldval.string = oldval;
42514257
args.os_newval.string = value;
42524258
args.os_errbuf = errbuf;

src/proto/optionstr.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ char *did_set_wildmode(optset_T *args);
121121
char *did_set_wildoptions(optset_T *args);
122122
char *did_set_winaltkeys(optset_T *args);
123123
char *did_set_wincolor(optset_T *args);
124-
char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char_u *value, char *errbuf, int opt_flags, int *value_checked);
124+
char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char_u *value, char *errbuf, int opt_flags, set_op_T op, int *value_checked);
125125
int expand_set_ambiwidth(optexpand_T *args, int *numMatches, char_u ***matches);
126126
int expand_set_background(optexpand_T *args, int *numMatches, char_u ***matches);
127127
int expand_set_backspace(optexpand_T *args, int *numMatches, char_u ***matches);

src/structs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,13 @@ typedef enum {
588588
XP_PREFIX_INV, // "inv" prefix for bool option
589589
} xp_prefix_T;
590590

591+
typedef enum {
592+
OP_NONE = 0,
593+
OP_ADDING, // "opt+=arg"
594+
OP_PREPENDING, // "opt^=arg"
595+
OP_REMOVING, // "opt-=arg"
596+
} set_op_T;
597+
591598
/*
592599
* used for completion on the command line
593600
*/
@@ -4876,6 +4883,7 @@ typedef struct
48764883
char_u *os_varp;
48774884
int os_idx;
48784885
int os_flags;
4886+
set_op_T os_op;
48794887

48804888
// old value of the option (can be a string, number or a boolean)
48814889
union

src/testdir/test_crypt.vim

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,4 +438,27 @@ func Test_crypt_set_key_segfault()
438438
bwipe!
439439
endfunc
440440

441+
func Test_crypt_set_key_disallow_append_subtract()
442+
new Xtest4
443+
444+
set key=foobar
445+
call assert_true(&modified)
446+
setl nomodified
447+
448+
call assert_fails('set key-=foo', 'E474:')
449+
call assert_fails('set key-=bar', 'E474:')
450+
call assert_fails('set key-=foobar', 'E474:')
451+
call assert_fails('set key-=test1', 'E474:')
452+
453+
call assert_false(&modified)
454+
call assert_equal('*****', &key)
455+
456+
call assert_fails('set key+=test2', 'E474:')
457+
call assert_fails('set key^=test3', 'E474:')
458+
459+
call assert_false(&modified)
460+
set key=
461+
bwipe!
462+
endfunc
463+
441464
" vim: shiftwidth=2 sts=2 expandtab

src/testdir/test_history.vim

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,13 @@ endfunc
244244
" Test for making sure the key value is not stored in history
245245
func Test_history_crypt_key()
246246
CheckFeature cryptv
247+
247248
call feedkeys(":set bs=2 key=abc ts=8\<CR>", 'xt')
248249
call assert_equal('set bs=2 key= ts=8', histget(':'))
250+
251+
call assert_fails("call feedkeys(':set bs=2 key-=abc ts=8\<CR>', 'xt')")
252+
call assert_equal('set bs=2 key-= ts=8', histget(':'))
253+
249254
set key& bs& ts&
250255
endfunc
251256

src/testdir/test_options.vim

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,15 @@ func Test_set_completion()
365365
call feedkeys(":set spellsuggest=best,file:test_options.v\<Tab>\<C-B>\"\<CR>", 'xt')
366366
call assert_equal("\"set spellsuggest=best,file:test_options.vim", @:)
367367

368-
" Expand value for 'key'
369-
set key=abcd
370-
call feedkeys(":set key=\<Tab>\<C-B>\"\<CR>", 'xt')
371-
call assert_equal('"set key=*****', @:)
372-
call feedkeys(":set key-=\<Tab>\<C-B>\"\<CR>", 'xt')
373-
call assert_equal('"set key-=*****', @:)
374-
set key=
368+
" Expanding value for 'key' is disallowed
369+
if exists('+key')
370+
set key=abcd
371+
call feedkeys(":set key=\<Tab>\<C-B>\"\<CR>", 'xt')
372+
call assert_equal('"set key=', @:)
373+
call feedkeys(":set key-=\<Tab>\<C-B>\"\<CR>", 'xt')
374+
call assert_equal('"set key-=', @:)
375+
set key=
376+
endif
375377

376378
" Expand values for 'filetype'
377379
call feedkeys(":set filetype=sshdconfi\<Tab>\<C-B>\"\<CR>", 'xt')

0 commit comments

Comments
 (0)