diff --git a/config.c b/config.c index 062fe9b6..94b434a7 100644 --- a/config.c +++ b/config.c @@ -3316,364 +3316,3 @@ void setup_config_box(struct controlbox *b, bool midsession, I(CONF_supdup_scroll)); } } - -struct ca_state { - dlgcontrol *ca_name_edit; - dlgcontrol *ca_reclist; - dlgcontrol *ca_pubkey_edit; - dlgcontrol *ca_wclist; - dlgcontrol *ca_wc_edit; - char *name, *pubkey, *wc; - tree234 *ca_names; /* stores plain 'char *' */ - tree234 *host_wcs; /* stores plain 'char *' */ -}; - -static int ca_name_compare(void *av, void *bv) -{ - return strcmp((const char *)av, (const char *)bv); -} - -static inline void clear_string_tree(tree234 *t) -{ - char *p; - while ((p = delpos234(t, 0)) != NULL) - sfree(p); -} - -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); -} - -static void ca_refresh_name_list(struct ca_state *st) -{ - clear_string_tree(st->ca_names); - - host_ca_enum *hce = enum_host_ca_start(); - if (hce) { - strbuf *namebuf = strbuf_new(); - - while (strbuf_clear(namebuf), enum_host_ca_next(hce, namebuf)) { - char *name = dupstr(namebuf->s); - char *added = add234(st->ca_names, name); - /* Just imaginable that concurrent filesystem access might - * cause a repetition; avoid leaking memory if so */ - if (added != name) - sfree(name); - } - - strbuf_free(namebuf); - enum_host_ca_finish(hce); - } -} - -static void ca_load_selected_record(struct ca_state *st, dlgparam *dp) -{ - int i = dlg_listbox_index(st->ca_reclist, dp); - if (i < 0) { - dlg_beep(dp); - return; - } - const char *name = index234(st->ca_names, i); - if (!name) { /* in case the list box and the tree got out of sync */ - dlg_beep(dp); - return; - } - host_ca *hca = host_ca_load(name); - if (!hca) { - char *msg = dupprintf("Unable to load host CA record '%s'", name); - dlg_error_msg(dp, msg); - sfree(msg); - return; - } - - sfree(st->name); - st->name = dupstr(hca->name); - - sfree(st->pubkey); - st->pubkey = strbuf_to_str( - base64_encode_sb(ptrlen_from_strbuf(hca->ca_public_key), 0)); - - 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 */ - } - - host_ca_free(hca); - - dlg_refresh(st->ca_name_edit, dp); - dlg_refresh(st->ca_pubkey_edit, dp); - dlg_refresh(st->ca_wclist, dp); -} - -static void ca_ok_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - if (event == EVENT_ACTION) - dlg_end(dp, 0); -} - -static void ca_name_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->name); - } else if (event == EVENT_VALCHANGE) { - sfree(st->name); - st->name = dlg_editbox_get(ctrl, dp); - - /* - * Try to auto-select the typed name in the list. - */ - int index; - if (!findrelpos234(st->ca_names, st->name, NULL, REL234_GE, &index)) - index = count234(st->ca_names) - 1; - if (index >= 0) - dlg_listbox_select(st->ca_reclist, dp, index); - } -} - -static void ca_reclist_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->ca_names, i)) != NULL; i++) - dlg_listbox_add(ctrl, dp, name); - dlg_update_done(ctrl, dp); - } else if (event == EVENT_ACTION) { - /* Double-clicking a session loads it */ - ca_load_selected_record(st, dp); - } -} - -static void ca_load_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_ACTION) { - ca_load_selected_record(st, dp); - } -} - -static void ca_save_handler(dlgcontrol *ctrl, dlgparam *dp, - void *data, int event) -{ - struct ca_state *st = (struct ca_state *)ctrl->context.p; - if (event == EVENT_ACTION) { - host_ca *hca = snew(host_ca); - memset(hca, 0, sizeof(*hca)); - hca->name = dupstr(st->name); - hca->ca_public_key = base64_decode_sb(ptrlen_from_asciz(st->pubkey)); - 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)); - char *error = host_ca_save(hca); - host_ca_free(hca); - - if (error) { - dlg_error_msg(dp, error); - sfree(error); - } else { - ca_refresh_name_list(st); - dlg_refresh(st->ca_reclist, dp); - } - } -} - -static void ca_delete_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_reclist, dp); - if (i < 0) { - dlg_beep(dp); - return; - } - const char *name = index234(st->ca_names, i); - if (!name) { /* in case the list box and the tree got out of sync */ - dlg_beep(dp); - return; - } - - char *error = host_ca_delete(name); - if (error) { - dlg_error_msg(dp, error); - sfree(error); - } else { - ca_refresh_name_list(st); - dlg_refresh(st->ca_reclist, dp); - } - } -} - -static void ca_pubkey_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->pubkey); - } else if (event == EVENT_VALCHANGE) { - sfree(st->pubkey); - st->pubkey = dlg_editbox_get(ctrl, dp); - } -} - -static void ca_wclist_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); - } 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); - } -} - -void setup_ca_config_box(struct controlbox *b) -{ - struct controlset *s; - dlgcontrol *c; - - /* Internal state for manipulating the host CA system */ - struct ca_state *st = (struct ca_state *)ctrl_alloc_with_free( - b, sizeof(struct ca_state), ca_state_free); - memset(st, 0, sizeof(*st)); - st->name = dupstr(""); - st->pubkey = dupstr(""); - st->ca_names = newtree234(ca_name_compare); - st->host_wcs = newtree234(ca_name_compare); - ca_refresh_name_list(st); - - /* Action area, with the Done button in it */ - s = ctrl_getset(b, "", "", ""); - ctrl_columns(s, 5, 20, 20, 20, 20, 20); - c = ctrl_pushbutton(s, "Done", 'o', HELPCTX(no_help), - ca_ok_handler, P(st)); - c->button.isdefault = true; - c->column = 4; - - /* Load/save box, as similar as possible to the main saved sessions one */ - s = ctrl_getset(b, "Main", "loadsave", - "Load, save or delete a host CA record"); - ctrl_columns(s, 2, 75, 25); - c = ctrl_editbox(s, "Name for this CA (shown in log messages)", - 'n', 100, HELPCTX(no_help), - ca_name_handler, P(st), P(NULL)); - c->column = 0; - st->ca_name_edit = c; - /* Reset columns so that the buttons are alongside the list, rather - * than alongside that edit box. */ - ctrl_columns(s, 1, 100); - ctrl_columns(s, 2, 75, 25); - c = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(no_help), - ca_reclist_handler, P(st)); - c->column = 0; - c->listbox.height = 6; - st->ca_reclist = c; - c = ctrl_pushbutton(s, "Load", 'l', HELPCTX(no_help), - ca_load_handler, P(st)); - c->column = 1; - c = ctrl_pushbutton(s, "Save", 'v', HELPCTX(no_help), - ca_save_handler, P(st)); - c->column = 1; - c = ctrl_pushbutton(s, "Delete", 'd', HELPCTX(no_help), - ca_delete_handler, P(st)); - c->column = 1; - - /* Box containing the details of a specific CA record */ - s = ctrl_getset(b, "Main", "details", "Details of a host CA record"); - c = ctrl_editbox(s, "Public key of certification authority", 'k', 100, - HELPCTX(no_help), ca_pubkey_handler, P(st), P(NULL)); - st->ca_pubkey_edit = c; - 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->column = 1; - c = ctrl_pushbutton(s, "Remove", NO_SHORTCUT, HELPCTX(no_help), - ca_wc_rem_handler, P(st)); - c->column = 2; -} diff --git a/putty.h b/putty.h index fd5eea44..e16a5d04 100644 --- a/putty.h +++ b/putty.h @@ -2589,6 +2589,7 @@ void setup_ca_config_box(struct controlbox *b); /* Platforms provide this to be called from config.c */ void show_ca_config_box(dlgparam *dlg); +extern const bool has_ca_config_box; /* false if, e.g., we're PuTTYtel */ /* Visible outside config.c so that platforms can use it to recognise * the proxy type control */ diff --git a/ssh/CMakeLists.txt b/ssh/CMakeLists.txt index 4b0e03fe..d2b35311 100644 --- a/ssh/CMakeLists.txt +++ b/ssh/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(sshcommon OBJECT bpp1.c bpp2.c bpp-bare.c + ca-config.c censor1.c censor2.c common.c diff --git a/ssh/ca-config.c b/ssh/ca-config.c new file mode 100644 index 00000000..cc902db9 --- /dev/null +++ b/ssh/ca-config.c @@ -0,0 +1,372 @@ +/* + * Define and handle the configuration dialog box for SSH host CAs, + * using the same portable dialog specification API as config.c. + */ + +#include "putty.h" +#include "dialog.h" +#include "storage.h" +#include "tree234.h" + +const bool has_ca_config_box = true; + +struct ca_state { + dlgcontrol *ca_name_edit; + dlgcontrol *ca_reclist; + dlgcontrol *ca_pubkey_edit; + dlgcontrol *ca_wclist; + dlgcontrol *ca_wc_edit; + char *name, *pubkey, *wc; + tree234 *ca_names; /* stores plain 'char *' */ + tree234 *host_wcs; /* stores plain 'char *' */ +}; + +static int ca_name_compare(void *av, void *bv) +{ + return strcmp((const char *)av, (const char *)bv); +} + +static inline void clear_string_tree(tree234 *t) +{ + char *p; + while ((p = delpos234(t, 0)) != NULL) + sfree(p); +} + +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); +} + +static void ca_refresh_name_list(struct ca_state *st) +{ + clear_string_tree(st->ca_names); + + host_ca_enum *hce = enum_host_ca_start(); + if (hce) { + strbuf *namebuf = strbuf_new(); + + while (strbuf_clear(namebuf), enum_host_ca_next(hce, namebuf)) { + char *name = dupstr(namebuf->s); + char *added = add234(st->ca_names, name); + /* Just imaginable that concurrent filesystem access might + * cause a repetition; avoid leaking memory if so */ + if (added != name) + sfree(name); + } + + strbuf_free(namebuf); + enum_host_ca_finish(hce); + } +} + +static void ca_load_selected_record(struct ca_state *st, dlgparam *dp) +{ + int i = dlg_listbox_index(st->ca_reclist, dp); + if (i < 0) { + dlg_beep(dp); + return; + } + const char *name = index234(st->ca_names, i); + if (!name) { /* in case the list box and the tree got out of sync */ + dlg_beep(dp); + return; + } + host_ca *hca = host_ca_load(name); + if (!hca) { + char *msg = dupprintf("Unable to load host CA record '%s'", name); + dlg_error_msg(dp, msg); + sfree(msg); + return; + } + + sfree(st->name); + st->name = dupstr(hca->name); + + sfree(st->pubkey); + st->pubkey = strbuf_to_str( + base64_encode_sb(ptrlen_from_strbuf(hca->ca_public_key), 0)); + + 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 */ + } + + host_ca_free(hca); + + dlg_refresh(st->ca_name_edit, dp); + dlg_refresh(st->ca_pubkey_edit, dp); + dlg_refresh(st->ca_wclist, dp); +} + +static void ca_ok_handler(dlgcontrol *ctrl, dlgparam *dp, + void *data, int event) +{ + if (event == EVENT_ACTION) + dlg_end(dp, 0); +} + +static void ca_name_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->name); + } else if (event == EVENT_VALCHANGE) { + sfree(st->name); + st->name = dlg_editbox_get(ctrl, dp); + + /* + * Try to auto-select the typed name in the list. + */ + int index; + if (!findrelpos234(st->ca_names, st->name, NULL, REL234_GE, &index)) + index = count234(st->ca_names) - 1; + if (index >= 0) + dlg_listbox_select(st->ca_reclist, dp, index); + } +} + +static void ca_reclist_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->ca_names, i)) != NULL; i++) + dlg_listbox_add(ctrl, dp, name); + dlg_update_done(ctrl, dp); + } else if (event == EVENT_ACTION) { + /* Double-clicking a session loads it */ + ca_load_selected_record(st, dp); + } +} + +static void ca_load_handler(dlgcontrol *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->context.p; + if (event == EVENT_ACTION) { + ca_load_selected_record(st, dp); + } +} + +static void ca_save_handler(dlgcontrol *ctrl, dlgparam *dp, + void *data, int event) +{ + struct ca_state *st = (struct ca_state *)ctrl->context.p; + if (event == EVENT_ACTION) { + host_ca *hca = snew(host_ca); + memset(hca, 0, sizeof(*hca)); + hca->name = dupstr(st->name); + hca->ca_public_key = base64_decode_sb(ptrlen_from_asciz(st->pubkey)); + 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)); + char *error = host_ca_save(hca); + host_ca_free(hca); + + if (error) { + dlg_error_msg(dp, error); + sfree(error); + } else { + ca_refresh_name_list(st); + dlg_refresh(st->ca_reclist, dp); + } + } +} + +static void ca_delete_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_reclist, dp); + if (i < 0) { + dlg_beep(dp); + return; + } + const char *name = index234(st->ca_names, i); + if (!name) { /* in case the list box and the tree got out of sync */ + dlg_beep(dp); + return; + } + + char *error = host_ca_delete(name); + if (error) { + dlg_error_msg(dp, error); + sfree(error); + } else { + ca_refresh_name_list(st); + dlg_refresh(st->ca_reclist, dp); + } + } +} + +static void ca_pubkey_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->pubkey); + } else if (event == EVENT_VALCHANGE) { + sfree(st->pubkey); + st->pubkey = dlg_editbox_get(ctrl, dp); + } +} + +static void ca_wclist_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); + } 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); + } +} + +void setup_ca_config_box(struct controlbox *b) +{ + struct controlset *s; + dlgcontrol *c; + + /* Internal state for manipulating the host CA system */ + struct ca_state *st = (struct ca_state *)ctrl_alloc_with_free( + b, sizeof(struct ca_state), ca_state_free); + memset(st, 0, sizeof(*st)); + st->name = dupstr(""); + st->pubkey = dupstr(""); + st->ca_names = newtree234(ca_name_compare); + st->host_wcs = newtree234(ca_name_compare); + ca_refresh_name_list(st); + + /* Action area, with the Done button in it */ + s = ctrl_getset(b, "", "", ""); + ctrl_columns(s, 5, 20, 20, 20, 20, 20); + c = ctrl_pushbutton(s, "Done", 'o', HELPCTX(no_help), + ca_ok_handler, P(st)); + c->button.isdefault = true; + c->column = 4; + + /* Load/save box, as similar as possible to the main saved sessions one */ + s = ctrl_getset(b, "Main", "loadsave", + "Load, save or delete a host CA record"); + ctrl_columns(s, 2, 75, 25); + c = ctrl_editbox(s, "Name for this CA (shown in log messages)", + 'n', 100, HELPCTX(no_help), + ca_name_handler, P(st), P(NULL)); + c->column = 0; + st->ca_name_edit = c; + /* Reset columns so that the buttons are alongside the list, rather + * than alongside that edit box. */ + ctrl_columns(s, 1, 100); + ctrl_columns(s, 2, 75, 25); + c = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(no_help), + ca_reclist_handler, P(st)); + c->column = 0; + c->listbox.height = 6; + st->ca_reclist = c; + c = ctrl_pushbutton(s, "Load", 'l', HELPCTX(no_help), + ca_load_handler, P(st)); + c->column = 1; + c = ctrl_pushbutton(s, "Save", 'v', HELPCTX(no_help), + ca_save_handler, P(st)); + c->column = 1; + c = ctrl_pushbutton(s, "Delete", 'd', HELPCTX(no_help), + ca_delete_handler, P(st)); + c->column = 1; + + /* Box containing the details of a specific CA record */ + s = ctrl_getset(b, "Main", "details", "Details of a host CA record"); + c = ctrl_editbox(s, "Public key of certification authority", 'k', 100, + HELPCTX(no_help), ca_pubkey_handler, P(st), P(NULL)); + st->ca_pubkey_edit = c; + 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->column = 1; + c = ctrl_pushbutton(s, "Remove", NO_SHORTCUT, HELPCTX(no_help), + ca_wc_rem_handler, P(st)); + c->column = 2; +} diff --git a/stubs/no-ca-config.c b/stubs/no-ca-config.c new file mode 100644 index 00000000..573f770f --- /dev/null +++ b/stubs/no-ca-config.c @@ -0,0 +1,14 @@ +/* + * Stub version of setup_ca_config_box, for tools that don't have SSH + * code linked in. + */ + +#include "putty.h" +#include "dialog.h" + +const bool has_ca_config_box = false; + +void setup_ca_config_box(struct controlbox *b) +{ + unreachable("should never call setup_ca_config_box in this application"); +} diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 6bb275d9..248ccf27 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -134,6 +134,7 @@ if(GTK_FOUND) pterm.c main-gtk-simple.c ${CMAKE_SOURCE_DIR}/stubs/nogss.c + ${CMAKE_SOURCE_DIR}/stubs/no-ca-config.c ${CMAKE_SOURCE_DIR}/proxy/nosshproxy.c pty.c) be_list(pterm pterm) @@ -148,6 +149,7 @@ if(GTK_FOUND) main-gtk-application.c ${CMAKE_SOURCE_DIR}/stubs/nocmdline.c ${CMAKE_SOURCE_DIR}/stubs/nogss.c + ${CMAKE_SOURCE_DIR}/stubs/no-ca-config.c ${CMAKE_SOURCE_DIR}/proxy/nosshproxy.c pty.c) be_list(ptermapp pterm) @@ -184,6 +186,7 @@ if(GTK_FOUND) putty.c main-gtk-simple.c ${CMAKE_SOURCE_DIR}/stubs/nogss.c + ${CMAKE_SOURCE_DIR}/stubs/no-ca-config.c ${CMAKE_SOURCE_DIR}/stubs/norand.c ${CMAKE_SOURCE_DIR}/proxy/nocproxy.c ${CMAKE_SOURCE_DIR}/proxy/nosshproxy.c) diff --git a/unix/main-gtk-simple.c b/unix/main-gtk-simple.c index b08a0acc..2f09cfbc 100644 --- a/unix/main-gtk-simple.c +++ b/unix/main-gtk-simple.c @@ -532,8 +532,9 @@ bool do_cmdline(int argc, char **argv, bool do_everything, Conf *conf) pgp_fingerprints(); exit(1); - } else if (!strcmp(p, "-host-ca") || !strcmp(p, "--host-ca") || - !strcmp(p, "-host_ca") || !strcmp(p, "--host_ca")) { + } else if (has_ca_config_box && + (!strcmp(p, "-host-ca") || !strcmp(p, "--host-ca") || + !strcmp(p, "-host_ca") || !strcmp(p, "--host_ca"))) { show_ca_config_box_synchronously(); exit(0); diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 7bfa955c..5f829ba8 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -117,6 +117,7 @@ add_executable(puttytel putty.c help.c ${CMAKE_SOURCE_DIR}/stubs/nogss.c + ${CMAKE_SOURCE_DIR}/stubs/no-ca-config.c ${CMAKE_SOURCE_DIR}/stubs/norand.c ${CMAKE_SOURCE_DIR}/proxy/nocproxy.c ${CMAKE_SOURCE_DIR}/proxy/nosshproxy.c @@ -158,6 +159,7 @@ if(HAVE_CONPTY) help.c conpty.c ${CMAKE_SOURCE_DIR}/stubs/nogss.c + ${CMAKE_SOURCE_DIR}/stubs/no-ca-config.c ${CMAKE_SOURCE_DIR}/stubs/norand.c ${CMAKE_SOURCE_DIR}/proxy/nosshproxy.c pterm.rc) diff --git a/windows/putty.c b/windows/putty.c index fca1d4bb..53c8b4a5 100644 --- a/windows/putty.c +++ b/windows/putty.c @@ -87,8 +87,9 @@ void gui_term_process_cmdline(Conf *conf, char *cmdline) } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints_msgbox(NULL); exit(1); - } else if (!strcmp(p, "-host-ca") || !strcmp(p, "--host-ca") || - !strcmp(p, "-host_ca") || !strcmp(p, "--host_ca")) { + } else if (has_ca_config_box && + (!strcmp(p, "-host-ca") || !strcmp(p, "--host-ca") || + !strcmp(p, "-host_ca") || !strcmp(p, "--host_ca"))) { show_ca_config_box(NULL); exit(0); } else if (!strcmp(p, "-demo-config-box")) {