diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b5855d4..8a8b777a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(${PROJECT_NAME} WIN32 proxy.c registry.c save_pass.c + totp_concat.c scripts.c service.c tray.c diff --git a/Makefile.am b/Makefile.am index dc00a7e4..b727cd65 100644 --- a/Makefile.am +++ b/Makefile.am @@ -105,6 +105,7 @@ openvpn_gui_SOURCES = \ access.c access.h \ chartable.h \ save_pass.c save_pass.h \ + totp_concat.c totp_concat.h \ env_set.c env_set.h \ echo.c echo.h \ as.c as.h \ diff --git a/misc.c b/misc.c index 4f278f6a..51564102 100644 --- a/misc.c +++ b/misc.c @@ -261,10 +261,68 @@ ManagementCommandFromInput(connection_t *c, LPCSTR fmt, HWND hDlg, int id) return retval; } +/* + * Generate a management command from double user inputs and send it + */ +BOOL +ManagementCommandFromTwoInputs(connection_t* c, LPCSTR fmt, HWND hDlg, int id, int id2) +{ + BOOL retval = FALSE; + LPSTR input, input2, cmd; + int input_len, input2_len, cmd_len; + + GetDlgItemTextUtf8(hDlg, id, &input, &input_len); + GetDlgItemTextUtf8(hDlg, id2, &input2, &input2_len); + + /* Escape input if needed */ + char* input_e = escape_string(input); + if (!input_e) + { + goto out; + } + free(input); + input = input_e; + input_len = strlen(input); + /* Escape input if needed */ + char* input2_e = escape_string(input2); + if (!input2_e) + { + goto out; + } + free(input2); + input2 = input2_e; + input2_len = strlen(input2); + + cmd_len = input_len + input2_len + strlen(fmt); + cmd = malloc(cmd_len); + if (cmd) + { + snprintf(cmd, cmd_len, fmt, input, input2); + retval = ManagementCommand(c, cmd, NULL, regular); + free(cmd); + } + +out: + /* Clear buffers with potentially secret content */ + if (input_len) + { + memset(input, 'x', input_len); + SetDlgItemTextA(hDlg, id, input); + free(input); + } + if (input2_len) + { + memset(input2, 'x', input2_len); + SetDlgItemTextA(hDlg, id2, input2); + free(input2); + } + + return retval; +} /* - * Generate a management command from double user inputs and send it + * Generate a management command from double base64-encoded user inputs and send it */ BOOL ManagementCommandFromTwoInputsBase64(connection_t *c, LPCSTR fmt, HWND hDlg, int id, int id2) diff --git a/misc.h b/misc.h index 7da7dc23..c6bd2017 100644 --- a/misc.h +++ b/misc.h @@ -28,6 +28,8 @@ BOOL ManagementCommandFromInput(connection_t *, LPCSTR, HWND, int); +BOOL ManagementCommandFromTwoInputs(connection_t*, LPCSTR, HWND, int, int); + BOOL ManagementCommandFromTwoInputsBase64(connection_t *, LPCSTR, HWND, int, int); BOOL ManagementCommandFromInputBase64(connection_t *, LPCSTR, HWND, int); diff --git a/openvpn.c b/openvpn.c index 9f832250..6b24e7d3 100644 --- a/openvpn.c +++ b/openvpn.c @@ -57,6 +57,7 @@ #include "misc.h" #include "access.h" #include "save_pass.h" +#include "totp_concat.h" #include "env_set.h" #include "echo.h" #include "pkcs11.h" @@ -580,6 +581,11 @@ UserAuthDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) } } + else if (IsTotpConcatEnabled(param->c->config_name)) + { + HWND wnd_challenge = GetDlgItem(hwndDlg, ID_EDT_AUTH_CHALLENGE); + SendMessage(wnd_challenge, EM_SETPASSWORDCHAR, 0, 0); + } if (RecallUsername(param->c->config_name, username)) { SetDlgItemTextW(hwndDlg, ID_EDT_AUTH_USER, username); @@ -589,6 +595,7 @@ UserAuthDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { SetDlgItemTextW(hwndDlg, ID_EDT_AUTH_PASS, password); if (username[0] != L'\0' && !(param->flags & FLAG_CR_TYPE_SCRV1) + && !IsTotpConcatEnabled(param->c->config_name) && password[0] != L'\0' && param->c->failed_auth_attempts == 0) { /* user/pass available and no challenge response needed: skip dialog @@ -604,7 +611,8 @@ UserAuthDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { SendMessage(GetDlgItem(hwndDlg, ID_EDT_AUTH_PASS), EM_SETSEL, 0, MAKELONG(0, -1)); } - else if (param->flags & FLAG_CR_TYPE_SCRV1) + else if ((param->flags & FLAG_CR_TYPE_SCRV1) + || IsTotpConcatEnabled(param->c->config_name)) { SetFocus(GetDlgItem(hwndDlg, ID_EDT_AUTH_CHALLENGE)); } @@ -661,7 +669,8 @@ UserAuthDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) /* enable OK button only if username and either password or response are filled */ BOOL enableOK = GetWindowTextLength(GetDlgItem(hwndDlg, ID_EDT_AUTH_USER)) && (GetWindowTextLength(GetDlgItem(hwndDlg, ID_EDT_AUTH_PASS)) - || ((param->flags & FLAG_CR_TYPE_SCRV1) + || (((param->flags & FLAG_CR_TYPE_SCRV1) + || IsTotpConcatEnabled(param->c->config_name)) && GetWindowTextLength(GetDlgItem(hwndDlg, ID_EDT_AUTH_CHALLENGE))) ); EnableWindow(GetDlgItem(hwndDlg, IDOK), enableOK); @@ -712,6 +721,10 @@ UserAuthDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { ManagementCommandFromTwoInputsBase64(param->c, "password \"Auth\" \"SCRV1:%s:%s\"", hwndDlg, ID_EDT_AUTH_PASS, ID_EDT_AUTH_CHALLENGE); } + else if (IsTotpConcatEnabled(param->c->config_name)) + { + ManagementCommandFromTwoInputs(param->c, "password \"Auth\" \"%s%s\"", hwndDlg, ID_EDT_AUTH_PASS, ID_EDT_AUTH_CHALLENGE); + } else { ManagementCommandFromInput(param->c, "password \"Auth\" \"%s\"", hwndDlg, ID_EDT_AUTH_PASS); @@ -1418,6 +1431,10 @@ OnPassword(connection_t *c, char *msg) param->str = strdup(chstr + 5); LocalizedDialogBoxParamEx(ID_DLG_AUTH_CHALLENGE, c->hwndStatus, UserAuthDialogFunc, (LPARAM) param); } + else if (IsTotpConcatEnabled(c->config_name)) + { + LocalizedDialogBoxParamEx(ID_DLG_AUTH_CHALLENGE, c->hwndStatus, UserAuthDialogFunc, (LPARAM) param); + } else { LocalizedDialogBoxParamEx(ID_DLG_AUTH, c->hwndStatus, UserAuthDialogFunc, (LPARAM) param); diff --git a/plap/stub.c b/plap/stub.c index cad373c8..c34bf780 100644 --- a/plap/stub.c +++ b/plap/stub.c @@ -175,6 +175,11 @@ IsKeyPassSaved(UNUSED const WCHAR *config_name) { return 0; } +BOOL +IsTotpConcatEnabled(const WCHAR* config_name) +{ + return 0; +} void env_item_del_all(UNUSED struct env_item *head) { diff --git a/res/openvpn-gui-res-en.rc b/res/openvpn-gui-res-en.rc index 515ab780..885b14e2 100644 --- a/res/openvpn-gui-res-en.rc +++ b/res/openvpn-gui-res-en.rc @@ -69,7 +69,7 @@ BEGIN EDITTEXT ID_EDT_AUTH_PASS, 60, 23, 94, 12, ES_PASSWORD | ES_AUTOHSCROLL ICON ID_ICO_EYE, ID_PASSWORD_REVEAL, 156, 24, 14, 14, SS_ICON|SS_NOTIFY|SS_REALSIZEIMAGE LTEXT "&Response:", ID_LTEXT_RESPONSE, 6, 60, 50, 10 - LTEXT "", ID_TXT_AUTH_CHALLENGE, 6, 43, 148, 10 + LTEXT "TOTP", ID_TXT_AUTH_CHALLENGE, 6, 43, 148, 10 EDITTEXT ID_EDT_AUTH_CHALLENGE, 60, 57, 94, 12, ES_PASSWORD | ES_AUTOHSCROLL CHECKBOX "&Save password", ID_CHK_SAVE_PASS, 6, 76, 100, 10 PUSHBUTTON "&OK", IDOK, 20, 92, 50, 14, BS_PUSHBUTTON | WS_TABSTOP | WS_DISABLED diff --git a/totp_concat.c b/totp_concat.c new file mode 100644 index 00000000..7ea5ae5f --- /dev/null +++ b/totp_concat.c @@ -0,0 +1,44 @@ +/* + * This file is a part of OpenVPN-GUI -- A Windows GUI for OpenVPN. + * + * Copyright (C) 2024 Didier Loiseau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "main.h" +#include "registry.h" +#include "totp_concat.h" +#include "misc.h" + +#define TOTP_CONCAT_DATA L"totp-concat" + +/* check if TOTP concatenation is enabled */ +BOOL +IsTotpConcatEnabled(const WCHAR *config_name) +{ + DWORD len = 0; + BYTE totp_enabled = 0; + len = GetConfigRegistryValue(config_name, TOTP_CONCAT_DATA, &totp_enabled, sizeof(totp_enabled)); + PrintDebug(L"checking totp-concat in registry returned len = %d", len); + return (len > 0) && totp_enabled; +} diff --git a/totp_concat.h b/totp_concat.h new file mode 100644 index 00000000..a6b73b77 --- /dev/null +++ b/totp_concat.h @@ -0,0 +1,27 @@ +/* + * This file is a part of OpenVPN-GUI -- A Windows GUI for OpenVPN. + * + * Copyright (C) 2024 Didier Loiseau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TOTPCONCAT_H +#define TOTPCONCAT_H + +#include + +#endif /* ifndef TOTPCONCAT_H */