mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-06-30 19:12:48 -05:00
Certificate trust scope: change to a boolean-expression system.
This replaces the previous placeholder scheme of having a list of hostname wildcards with implicit logical-OR semantics (if any wildcard matched then the certificate would be trusted to sign for that host). That scheme didn't allow for exceptions within a domain ('everything in example.com except extra-high-security-machine.example.com'), and also had no way to specify port numbers. In the new system, you can still write a hostname wildcard by itself in the simple case, but now those are just atomic subexpressions in a boolean-logic domain-specific language I've made up. So if you want multiple wildcards, you can separate them with || in a single longer expression, and also you can use && and ! to impose exceptions on top of that. Full details of the expression language are in the comment at the top of utils/cert-expr.c. It'll need documenting properly before release, of course. For the sake of backwards compatibility for early adopters who've already set up configuration in the old system, I've put in some code that will read the old MatchHosts configuration and automatically translate it into the equivalent boolean expression (by simply stringing together the list of wildcards with || between them).
This commit is contained in:
138
ssh/ca-config.c
138
ssh/ca-config.c
@ -18,12 +18,10 @@ struct ca_state {
|
||||
dlgcontrol *ca_reclist;
|
||||
dlgcontrol *ca_pubkey_edit;
|
||||
dlgcontrol *ca_pubkey_info;
|
||||
dlgcontrol *ca_wclist;
|
||||
dlgcontrol *ca_wc_edit;
|
||||
dlgcontrol *ca_validity_edit;
|
||||
dlgcontrol *rsa_type_checkboxes[NRSATYPES];
|
||||
char *name, *pubkey, *wc;
|
||||
char *name, *pubkey, *validity;
|
||||
tree234 *ca_names; /* stores plain 'char *' */
|
||||
tree234 *host_wcs; /* stores plain 'char *' */
|
||||
ca_options opts;
|
||||
strbuf *ca_pubkey_blob;
|
||||
};
|
||||
@ -45,10 +43,8 @@ static void ca_state_free(void *vctx)
|
||||
struct ca_state *st = (struct ca_state *)vctx;
|
||||
clear_string_tree(st->ca_names);
|
||||
freetree234(st->ca_names);
|
||||
clear_string_tree(st->host_wcs);
|
||||
freetree234(st->host_wcs);
|
||||
sfree(st->name);
|
||||
sfree(st->wc);
|
||||
sfree(st->validity);
|
||||
sfree(st);
|
||||
}
|
||||
|
||||
@ -86,13 +82,8 @@ static void set_from_hca(struct ca_state *st, host_ca *hca)
|
||||
else
|
||||
st->pubkey = dupstr("");
|
||||
|
||||
clear_string_tree(st->host_wcs);
|
||||
for (size_t i = 0; i < hca->n_hostname_wildcards; i++) {
|
||||
char *name = dupstr(hca->hostname_wildcards[i]);
|
||||
char *added = add234(st->host_wcs, name);
|
||||
if (added != name)
|
||||
sfree(name); /* de-duplicate, just in case */
|
||||
}
|
||||
st->validity = dupstr(hca->validity_expression ?
|
||||
hca->validity_expression : "");
|
||||
|
||||
st->opts = hca->opts; /* structure copy */
|
||||
}
|
||||
@ -194,7 +185,7 @@ static void ca_load_selected_record(struct ca_state *st, dlgparam *dp)
|
||||
|
||||
dlg_refresh(st->ca_name_edit, dp);
|
||||
dlg_refresh(st->ca_pubkey_edit, dp);
|
||||
dlg_refresh(st->ca_wclist, dp);
|
||||
dlg_refresh(st->ca_validity_edit, dp);
|
||||
for (size_t i = 0; i < NRSATYPES; i++)
|
||||
dlg_refresh(st->rsa_type_checkboxes[i], dp);
|
||||
ca_refresh_pubkey_info(st, dp);
|
||||
@ -259,8 +250,23 @@ static void ca_save_handler(dlgcontrol *ctrl, dlgparam *dp,
|
||||
{
|
||||
struct ca_state *st = (struct ca_state *)ctrl->context.p;
|
||||
if (event == EVENT_ACTION) {
|
||||
if (!count234(st->host_wcs)) {
|
||||
dlg_error_msg(dp, "No hostnames configured for this key");
|
||||
if (!*st->validity) {
|
||||
dlg_error_msg(dp, "No validity expression configured "
|
||||
"for this key");
|
||||
return;
|
||||
}
|
||||
|
||||
char *error_msg;
|
||||
ptrlen error_loc;
|
||||
if (!cert_expr_valid(st->validity, &error_msg, &error_loc)) {
|
||||
char *error_full = dupprintf("Error in expression: %s", error_msg);
|
||||
dlg_error_msg(dp, error_full);
|
||||
dlg_set_focus(st->ca_validity_edit, dp);
|
||||
dlg_editbox_select_range(
|
||||
st->ca_validity_edit, dp,
|
||||
(const char *)error_loc.ptr - st->validity, error_loc.len);
|
||||
sfree(error_msg);
|
||||
sfree(error_full);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -274,10 +280,7 @@ static void ca_save_handler(dlgcontrol *ctrl, dlgparam *dp,
|
||||
hca->name = dupstr(st->name);
|
||||
hca->ca_public_key = strbuf_dup(ptrlen_from_strbuf(
|
||||
st->ca_pubkey_blob));
|
||||
hca->n_hostname_wildcards = count234(st->host_wcs);
|
||||
hca->hostname_wildcards = snewn(hca->n_hostname_wildcards, char *);
|
||||
for (size_t i = 0; i < hca->n_hostname_wildcards; i++)
|
||||
hca->hostname_wildcards[i] = dupstr(index234(st->host_wcs, i));
|
||||
hca->validity_expression = dupstr(st->validity);
|
||||
hca->opts = st->opts; /* structure copy */
|
||||
|
||||
char *error = host_ca_save(hca);
|
||||
@ -360,73 +363,15 @@ static void ca_pubkey_file_handler(dlgcontrol *ctrl, dlgparam *dp,
|
||||
}
|
||||
}
|
||||
|
||||
static void ca_wclist_handler(dlgcontrol *ctrl, dlgparam *dp,
|
||||
void *data, int event)
|
||||
static void ca_validity_handler(dlgcontrol *ctrl, dlgparam *dp,
|
||||
void *data, int event)
|
||||
{
|
||||
struct ca_state *st = (struct ca_state *)ctrl->context.p;
|
||||
if (event == EVENT_REFRESH) {
|
||||
dlg_update_start(ctrl, dp);
|
||||
dlg_listbox_clear(ctrl, dp);
|
||||
const char *name;
|
||||
for (int i = 0; (name = index234(st->host_wcs, i)) != NULL; i++)
|
||||
dlg_listbox_add(ctrl, dp, name);
|
||||
dlg_update_done(ctrl, dp);
|
||||
}
|
||||
}
|
||||
|
||||
static void ca_wc_edit_handler(dlgcontrol *ctrl, dlgparam *dp,
|
||||
void *data, int event)
|
||||
{
|
||||
struct ca_state *st = (struct ca_state *)ctrl->context.p;
|
||||
if (event == EVENT_REFRESH) {
|
||||
dlg_editbox_set(ctrl, dp, st->wc);
|
||||
dlg_editbox_set(ctrl, dp, st->validity);
|
||||
} else if (event == EVENT_VALCHANGE) {
|
||||
sfree(st->wc);
|
||||
st->wc = dlg_editbox_get(ctrl, dp);
|
||||
}
|
||||
}
|
||||
|
||||
static void ca_wc_add_handler(dlgcontrol *ctrl, dlgparam *dp,
|
||||
void *data, int event)
|
||||
{
|
||||
struct ca_state *st = (struct ca_state *)ctrl->context.p;
|
||||
if (event == EVENT_ACTION) {
|
||||
if (!st->wc) {
|
||||
dlg_beep(dp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (add234(st->host_wcs, st->wc) == st->wc) {
|
||||
dlg_refresh(st->ca_wclist, dp);
|
||||
} else {
|
||||
sfree(st->wc);
|
||||
}
|
||||
|
||||
st->wc = dupstr("");
|
||||
dlg_refresh(st->ca_wc_edit, dp);
|
||||
}
|
||||
}
|
||||
|
||||
static void ca_wc_rem_handler(dlgcontrol *ctrl, dlgparam *dp,
|
||||
void *data, int event)
|
||||
{
|
||||
struct ca_state *st = (struct ca_state *)ctrl->context.p;
|
||||
if (event == EVENT_ACTION) {
|
||||
int i = dlg_listbox_index(st->ca_wclist, dp);
|
||||
if (i < 0) {
|
||||
dlg_beep(dp);
|
||||
return;
|
||||
}
|
||||
char *wc = delpos234(st->host_wcs, i);
|
||||
if (!wc) {
|
||||
dlg_beep(dp);
|
||||
return;
|
||||
}
|
||||
|
||||
sfree(st->wc);
|
||||
st->wc = wc;
|
||||
dlg_refresh(st->ca_wclist, dp);
|
||||
dlg_refresh(st->ca_wc_edit, dp);
|
||||
sfree(st->validity);
|
||||
st->validity = dlg_editbox_get(ctrl, dp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -454,8 +399,7 @@ void setup_ca_config_box(struct controlbox *b)
|
||||
b, sizeof(struct ca_state), ca_state_free);
|
||||
memset(st, 0, sizeof(*st));
|
||||
st->ca_names = newtree234(ca_name_compare);
|
||||
st->host_wcs = newtree234(ca_name_compare);
|
||||
st->wc = dupstr("");
|
||||
st->validity = dupstr("");
|
||||
ca_refresh_name_list(st);
|
||||
|
||||
/* Initialise the settings to a default blank host_ca */
|
||||
@ -520,25 +464,9 @@ void setup_ca_config_box(struct controlbox *b)
|
||||
|
||||
s = ctrl_getset(b, "Main", "options", "What this CA is trusted to do");
|
||||
|
||||
c = ctrl_listbox(s, "Hostname patterns this key is trusted to certify",
|
||||
NO_SHORTCUT, HELPCTX(no_help), ca_wclist_handler, P(st));
|
||||
c->listbox.height = 3;
|
||||
st->ca_wclist = c;
|
||||
|
||||
ctrl_columns(s, 3, 70, 15, 15);
|
||||
c = ctrl_editbox(s, "Hostname pattern to add", 'h', 100,
|
||||
HELPCTX(no_help), ca_wc_edit_handler, P(st), P(NULL));
|
||||
c->column = 0;
|
||||
st->ca_wc_edit = c;
|
||||
c = ctrl_pushbutton(s, "Add", NO_SHORTCUT, HELPCTX(no_help),
|
||||
ca_wc_add_handler, P(st));
|
||||
c->align_next_to = st->ca_wc_edit;
|
||||
c->column = 1;
|
||||
c = ctrl_pushbutton(s, "Remove", NO_SHORTCUT, HELPCTX(no_help),
|
||||
ca_wc_rem_handler, P(st));
|
||||
c->align_next_to = st->ca_wc_edit;
|
||||
c->column = 2;
|
||||
ctrl_columns(s, 1, 100);
|
||||
c = ctrl_editbox(s, "Valid hosts this key is trusted to certify", 'h', 100,
|
||||
HELPCTX(no_help), ca_validity_handler, P(st), P(NULL));
|
||||
st->ca_validity_edit = c;
|
||||
|
||||
ctrl_columns(s, 4, 44, 18, 18, 18);
|
||||
c = ctrl_text(s, "Signature types (RSA keys only):", HELPCTX(no_help));
|
||||
|
@ -690,16 +690,9 @@ static void ssh2_write_kexinit_lists(
|
||||
if (!hca)
|
||||
continue;
|
||||
|
||||
bool match = false;
|
||||
for (size_t i = 0, e = hca->n_hostname_wildcards;
|
||||
i < e; i++) {
|
||||
if (wc_match(hca->hostname_wildcards[i], hk_host)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match && hca->ca_public_key) {
|
||||
if (hca->ca_public_key &&
|
||||
cert_expr_match_str(hca->validity_expression,
|
||||
hk_host, hk_port)) {
|
||||
accept_certs = true;
|
||||
add234(host_cas, hca);
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user