/* * OpenVPN-GUI -- A Windows GUI for OpenVPN. * * Copyright (C) 2004 Mathias Sundman * * 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 */ #include #include "config.h" #include "main.h" #include "options.h" #include "passphrase.h" #include "openvpn.h" #include "openvpn-gui-res.h" #include "chartable.h" #include "localization.h" #ifndef DISABLE_CHANGE_PASSWORD #include #include #include #endif WCHAR passphrase[256]; extern options_t o; int ConvertUnicode2Ascii(WCHAR str_unicode[], char str_ascii[], unsigned int str_ascii_size) { unsigned int i; unsigned int j; int illegal_chars = false; for (i=0; (i < wcslen(str_unicode)) && (i < (str_ascii_size - 1)); i++) { for (j=0; j <= 256; j++) { if (j == 256) { illegal_chars = true; j = 0x2e; break; } if (str_unicode[i] == unicode_to_ascii[j]) break; } str_ascii[i] = (char) j; } str_ascii[i] = '\0'; if (illegal_chars) return(false); else return(true); } void CheckPrivateKeyPassphrasePrompt (char *line, int config) { DWORD nCharsWritten; char passphrase_ascii[256]; /* Check for Passphrase prompt */ if (strncmp(line, "Enter PEM pass phrase:", 22) == 0) { CLEAR(passphrase); if (LocalizedDialogBox(ID_DLG_PASSPHRASE, PassphraseDialogFunc) == IDCANCEL) { StopOpenVPN(config); } if (wcslen(passphrase) > 0) { CLEAR(passphrase_ascii); ConvertUnicode2Ascii(passphrase, passphrase_ascii, sizeof(passphrase_ascii)); if (!WriteFile(o.conn[config].hStdIn, passphrase_ascii, strlen(passphrase_ascii), &nCharsWritten, NULL)) { /* PassPhrase -> stdin failed */ ShowLocalizedMsg(IDS_ERR_PASSPHRASE2STDIN); } } if (!WriteFile(o.conn[config].hStdIn, "\r\n", 2, &nCharsWritten, NULL)) { /* CR -> stdin failed */ ShowLocalizedMsg(IDS_ERR_CR2STDIN); } /* Remove Passphrase prompt from lastline buffer */ line[0]='\0'; /* Clear passphrase buffer */ CLEAR(passphrase); CLEAR(passphrase_ascii); } /* Check for new passphrase prompt introduced with OpenVPN 2.0-beta12. */ if (strncmp(line, "Enter Private Key Password:", 27) == 0) { CLEAR(passphrase); if (LocalizedDialogBox(ID_DLG_PASSPHRASE, PassphraseDialogFunc) == IDCANCEL) { StopOpenVPN(config); } if (wcslen(passphrase) > 0) { CLEAR(passphrase_ascii); ConvertUnicode2Ascii(passphrase, passphrase_ascii, sizeof(passphrase_ascii)); if (!WriteFile(o.conn[config].hStdIn, passphrase_ascii, strlen(passphrase_ascii), &nCharsWritten, NULL)) { /* PassPhrase -> stdin failed */ ShowLocalizedMsg(IDS_ERR_PASSPHRASE2STDIN); } } else { if (!WriteFile(o.conn[config].hStdIn, "\n", 1, &nCharsWritten, NULL)) { /* CR -> stdin failed */ ShowLocalizedMsg(IDS_ERR_CR2STDIN); } } /* Remove Passphrase prompt from lastline buffer */ line[0]='\0'; /* Clear passphrase buffer */ CLEAR(passphrase); CLEAR(passphrase_ascii); } } void CheckAuthUsernamePrompt (char *line, int config) { DWORD nCharsWritten; struct user_auth user_auth; /* Check for Passphrase prompt */ if (strncmp(line, "Enter Auth Username:", 20) == 0) { CLEAR(user_auth); if (LocalizedDialogBoxParam(ID_DLG_AUTH, AuthPasswordDialogFunc, (LPARAM)&user_auth) == IDCANCEL) { StopOpenVPN(config); } if (strlen(user_auth.username) > 0) { if (!WriteFile(o.conn[config].hStdIn, user_auth.username, strlen(user_auth.username), &nCharsWritten, NULL)) { ShowLocalizedMsg(IDS_ERR_AUTH_USERNAME2STDIN); } } else { if (!WriteFile(o.conn[config].hStdIn, "\n", 1, &nCharsWritten, NULL)) { ShowLocalizedMsg(IDS_ERR_CR2STDIN); } } if (strlen(user_auth.password) > 0) { if (!WriteFile(o.conn[config].hStdIn, user_auth.password, strlen(user_auth.password), &nCharsWritten, NULL)) { ShowLocalizedMsg(IDS_ERR_AUTH_PASSWORD2STDIN); } } else { if (!WriteFile(o.conn[config].hStdIn, "\n", 1, &nCharsWritten, NULL)) { ShowLocalizedMsg(IDS_ERR_CR2STDIN); } } /* Remove Username prompt from lastline buffer */ line[0]='\0'; /* Clear user_auth buffer */ CLEAR(user_auth); } } void CheckAuthPasswordPrompt (char *line) { /* Check for Passphrase prompt */ if (strncmp(line, "Enter Auth Password:", 20) == 0) { /* Remove Passphrase prompt from lastline buffer */ line[0]='\0'; } } BOOL CALLBACK PassphraseDialogFunc (HWND hwndDlg, UINT msg, WPARAM wParam, UNUSED LPARAM lParam) { static TCHAR empty_string[100]; switch (msg) { case WM_INITDIALOG: SetForegroundWindow(hwndDlg); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: // button GetDlgItemTextW(hwndDlg, ID_EDT_PASSPHRASE, passphrase, sizeof(passphrase)/2 - 1); /* Clear buffer */ SetDlgItemText(hwndDlg, ID_EDT_PASSPHRASE, empty_string); EndDialog(hwndDlg, LOWORD(wParam)); return TRUE; case IDCANCEL: // button EndDialog(hwndDlg, LOWORD(wParam)); return TRUE; } break; case WM_CLOSE: EndDialog(hwndDlg, LOWORD(wParam)); return TRUE; } return FALSE; } BOOL CALLBACK AuthPasswordDialogFunc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { static struct user_auth *user_auth; static TCHAR empty_string[100]; WCHAR username_unicode[50]; WCHAR password_unicode[50]; switch (msg) { case WM_INITDIALOG: user_auth = (struct user_auth *) lParam; SetForegroundWindow(hwndDlg); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: // button GetDlgItemTextW(hwndDlg, ID_EDT_AUTH_USER, username_unicode, sizeof(username_unicode)/2 - 1); GetDlgItemTextW(hwndDlg, ID_EDT_AUTH_PASS, password_unicode, sizeof(password_unicode)/2 - 1); /* Convert username/password from Unicode to Ascii (CP850) */ ConvertUnicode2Ascii(username_unicode, user_auth->username, sizeof(user_auth->username) - 1); ConvertUnicode2Ascii(password_unicode, user_auth->password, sizeof(user_auth->password) - 1); /* Clear buffers */ SetDlgItemText(hwndDlg, ID_EDT_AUTH_USER, empty_string); SetDlgItemText(hwndDlg, ID_EDT_AUTH_PASS, empty_string); EndDialog(hwndDlg, LOWORD(wParam)); return TRUE; case IDCANCEL: // button EndDialog(hwndDlg, LOWORD(wParam)); return TRUE; } break; case WM_CLOSE: EndDialog(hwndDlg, LOWORD(wParam)); return TRUE; } return FALSE; } #ifndef DISABLE_CHANGE_PASSWORD const int KEYFILE_FORMAT_PKCS12 = 1; const int KEYFILE_FORMAT_PEM = 2; void ShowChangePassphraseDialog(int config) { HANDLE hThread; DWORD IDThread; /* Start a new thread to have our own message-loop for this dialog */ hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ChangePassphraseThread, (int *) config, // pass config nr 0, &IDThread); if (hThread == NULL) { /* error creating thread */ ShowLocalizedMsg(IDS_ERR_CREATE_PASS_THREAD); return; } } void ChangePassphraseThread(int config) { HWND hwndChangePSW; MSG messages; TCHAR conn_name[100]; TCHAR keyfilename[MAX_PATH]; int keyfile_format=0; /* Cut of extention from config filename. */ _tcsncpy(conn_name, o.conn[config].config_file, _tsizeof(conn_name)); conn_name[_tcslen(conn_name) - (_tcslen(o.ext_string)+1)]=0; /* Get Key filename from config file */ if (!GetKeyFilename(config, keyfilename, _tsizeof(keyfilename), &keyfile_format)) { ExitThread(1); } /* Show ChangePassphrase Dialog */ hwndChangePSW = CreateLocalizedDialog(ID_DLG_CHGPASS, ChangePassphraseDialogFunc); if (!hwndChangePSW) return; SetDlgItemText(hwndChangePSW, ID_TXT_KEYFILE, keyfilename); SetDlgItemInt(hwndChangePSW, ID_TXT_KEYFORMAT, (UINT) keyfile_format, FALSE); SetWindowText(hwndChangePSW, LoadLocalizedString(IDS_NFO_CHANGE_PWD, conn_name)); ShowWindow(hwndChangePSW, SW_SHOW); /* Run the message loop. It will run until GetMessage() returns 0 */ while (GetMessage (&messages, NULL, 0, 0)) { if(!IsDialogMessage(hwndChangePSW, &messages)) { TranslateMessage(&messages); DispatchMessage(&messages); } } ExitThread(0); } BOOL CALLBACK ChangePassphraseDialogFunc (HWND hwndDlg, UINT msg, WPARAM wParam, UNUSED LPARAM lParam) { HICON hIcon; TCHAR keyfile[MAX_PATH]; int keyfile_format; BOOL Translated; switch (msg) { case WM_INITDIALOG: hIcon = LoadLocalizedIcon(ID_ICO_APP); if (hIcon) { SendMessage(hwndDlg, WM_SETICON, (WPARAM) (ICON_SMALL), (LPARAM) (hIcon)); SendMessage(hwndDlg, WM_SETICON, (WPARAM) (ICON_BIG), (LPARAM) (hIcon)); } return FALSE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: /* Check if the type new passwords match. */ if (!ConfirmNewPassword (hwndDlg)) { /* passwords don't match */ ShowLocalizedMsg(IDS_ERR_PWD_DONT_MATCH); break; } /* Check minimum length of password */ if (NewPasswordLengh(hwndDlg) < MIN_PASSWORD_LEN) { ShowLocalizedMsg(IDS_ERR_PWD_TO_SHORT, MIN_PASSWORD_LEN); break; } /* Check if the new password is empty. */ if (NewPasswordLengh(hwndDlg) == 0) { if (MessageBox(NULL, LoadLocalizedString(IDS_NFO_EMPTY_PWD), _T(PACKAGE_NAME), MB_YESNO) != IDYES) break; } GetDlgItemText(hwndDlg, ID_TXT_KEYFILE, keyfile, _tsizeof(keyfile) - 1); keyfile_format=GetDlgItemInt(hwndDlg, ID_TXT_KEYFORMAT, &Translated, FALSE); if (keyfile_format == KEYFILE_FORMAT_PEM) { /* Change password of a PEM file */ if (ChangePasswordPEM(hwndDlg) == -1) /* Wrong password */ break; } else if (keyfile_format == KEYFILE_FORMAT_PKCS12) { /* Change password of a .P12 file */ if (ChangePasswordPKCS12(hwndDlg) == -1) /* Wrong password */ break; } else { /* Unknown key format */ ShowLocalizedMsg(IDS_ERR_UNKNOWN_KEYFILE_FORMAT); } DestroyWindow(hwndDlg); break; case IDCANCEL: DestroyWindow(hwndDlg); break; } break; case WM_DESTROY: PostQuitMessage(0); break; case WM_CLOSE: DestroyWindow(hwndDlg); return FALSE; } return FALSE; } /* Return TRUE if new passwords match */ int ConfirmNewPassword(HWND hwndDlg) { TCHAR newpsw[50]; TCHAR newpsw2[50]; GetDlgItemText(hwndDlg, ID_EDT_PASS_NEW, newpsw, _tsizeof(newpsw) - 1); GetDlgItemText(hwndDlg, ID_EDT_PASS_NEW2, newpsw2, _tsizeof(newpsw2) - 1); if (_tcsncmp(newpsw, newpsw2, _tsizeof(newpsw)) == 0) return true; else return false; } /* Return lengh of the new password */ int NewPasswordLengh(HWND hwndDlg) { TCHAR newpsw[50]; GetDlgItemText(hwndDlg, ID_EDT_PASS_NEW, newpsw, _tsizeof(newpsw) - 1); return (_tcslen(newpsw)); } int ParseKeyFilenameLine(int config, TCHAR *keyfilename, size_t keyfilenamesize, char *line) { const int STATE_INITIAL = 0; const int STATE_READING_QUOTED_PARM = 1; const int STATE_READING_UNQUOTED_PARM = 2; int i=0; unsigned int j=0; int state = STATE_INITIAL; int backslash=0; TCHAR temp_filename[MAX_PATH]; while(line[i] != '\0') { if (state == STATE_INITIAL) { if (line[i] == '\"') { state=STATE_READING_QUOTED_PARM; } else if ((line[i] == 0x0A) || (line[i] == 0x0D)) break; else if ((line[i] == ';') || (line[i] == '#')) break; else if ((line[i] != ' ') && (line[i] != '\t')) { if (line[i] == '\\') { if(!backslash) { keyfilename[j++]=line[i]; state=STATE_READING_UNQUOTED_PARM; backslash=1; } else { backslash=0; } } else { if (backslash) backslash=0; keyfilename[j++]=line[i]; state=STATE_READING_UNQUOTED_PARM; } } } else if (state == STATE_READING_QUOTED_PARM) { if (line[i] == '\"') break; if ((line[i] == 0x0A) || (line[i] == 0x0D)) break; if (line[i] == '\\') { if (!backslash) { keyfilename[j++]=line[i]; backslash=1; } else { backslash=0; } } else { if (backslash) backslash=0; keyfilename[j++]=line[i]; } } else if (state == STATE_READING_UNQUOTED_PARM) { if (line[i] == '\"') break; if ((line[i] == 0x0A) || (line[i] == 0x0D)) break; if ((line[i] == ';') || (line[i] == '#')) break; if (line[i] == ' ') break; if (line[i] == '\t') break; if (line[i] == '\\') { if (!backslash) { keyfilename[j++]=line[i]; backslash=1; } else { backslash=0; } } else { if (backslash) backslash=0; keyfilename[j++]=line[i]; } } if (j >= (keyfilenamesize - 1)) { /* key filename to long */ ShowLocalizedMsg(IDS_ERR_KEY_FILENAME_TO_LONG); return(0); } i++; } keyfilename[j]='\0'; /* Prepend filename with configdir path if needed */ if ((keyfilename[0] != '\\') && (keyfilename[0] != '/') && (keyfilename[1] != ':')) { _tcsncpy(temp_filename, o.conn[config].config_dir, _tsizeof(temp_filename)); if (temp_filename[_tcslen(temp_filename) - 1] != '\\') _tcscat(temp_filename, _T("\\")); _tcsncat(temp_filename, keyfilename, _tsizeof(temp_filename) - _tcslen(temp_filename) - 1); _tcsncpy(keyfilename, temp_filename, keyfilenamesize - 1); } return(1); } /* ChangePasswordPEM() returns: * -1 Wrong password * 0 Changing password failed for unknown reason * 1 Password changed successfully */ int ChangePasswordPEM(HWND hwndDlg) { TCHAR keyfile[MAX_PATH]; char oldpsw[50]; char newpsw[50]; WCHAR oldpsw_unicode[50]; WCHAR newpsw_unicode[50]; FILE *fp; EVP_PKEY *privkey; /* Get filename, old_psw and new_psw from Dialog */ GetDlgItemText(hwndDlg, ID_TXT_KEYFILE, keyfile, _tsizeof(keyfile) - 1); GetDlgItemTextW(hwndDlg, ID_EDT_PASS_CUR, oldpsw_unicode, sizeof(oldpsw_unicode)/2 - 1); GetDlgItemTextW(hwndDlg, ID_EDT_PASS_NEW, newpsw_unicode, sizeof(newpsw_unicode)/2 - 1); /* Convert Unicode to ASCII (CP850) */ ConvertUnicode2Ascii(oldpsw_unicode, oldpsw, sizeof(oldpsw)); if (!ConvertUnicode2Ascii(newpsw_unicode, newpsw, sizeof(newpsw))) { ShowLocalizedMsg(IDS_ERR_INVALID_CHARS_IN_PSW); return(-1); } privkey = EVP_PKEY_new(); /* Open old keyfile for reading */ if (! (fp = _tfopen (keyfile, _T("r")))) { /* can't open key file */ ShowLocalizedMsg(IDS_ERR_OPEN_PRIVATE_KEY_FILE, keyfile); return(0); } /* Import old key */ if (! (privkey = PEM_read_PrivateKey (fp, NULL, NULL, oldpsw))) { /* wrong password */ ShowLocalizedMsg(IDS_ERR_OLD_PWD_INCORRECT); fclose(fp); return(-1); } fclose(fp); /* Open keyfile for writing */ if (! (fp = _tfopen (keyfile, _T("w")))) { /* can't open file rw */ ShowLocalizedMsg(IDS_ERR_OPEN_WRITE_KEY, keyfile); EVP_PKEY_free(privkey); return(0); } /* Write new key to file */ if (strlen(newpsw) == 0) { /* No passphrase */ if ( !(PEM_write_PrivateKey(fp, privkey, \ NULL, NULL, /* Use NO encryption */ 0, 0, NULL))) { /* error writing new key */ ShowLocalizedMsg(IDS_ERR_WRITE_NEW_KEY, keyfile); EVP_PKEY_free(privkey); fclose(fp); return(0); } } else { /* Use passphrase */ if ( !(PEM_write_PrivateKey(fp, privkey, \ EVP_des_ede3_cbc(), /* Use 3DES encryption */ (UCHAR*) newpsw, (int) strlen(newpsw), 0, NULL))) { /* can't write new key */ ShowLocalizedMsg(IDS_ERR_WRITE_NEW_KEY, keyfile); EVP_PKEY_free(privkey); fclose(fp); return(0); } } EVP_PKEY_free(privkey); fclose(fp); /* signal success to user */ ShowLocalizedMsg(IDS_NFO_PWD_CHANGED); return(1); } /* ChangePasswordPKCS12() returns: * -1 Wrong password * 0 Changing password failed for unknown reason * 1 Password changed successfully */ int ChangePasswordPKCS12(HWND hwndDlg) { TCHAR keyfile[MAX_PATH]; char oldpsw[50]; char newpsw[50]; WCHAR oldpsw_unicode[50]; WCHAR newpsw_unicode[50]; FILE *fp; EVP_PKEY *privkey; X509 *cert; STACK_OF(X509) *ca = NULL; PKCS12 *p12; char *alias; /* Get filename, old_psw and new_psw from Dialog */ GetDlgItemText(hwndDlg, ID_TXT_KEYFILE, keyfile, _tsizeof(keyfile) - 1); GetDlgItemTextW(hwndDlg, ID_EDT_PASS_CUR, oldpsw_unicode, sizeof(oldpsw_unicode)/2 - 1); GetDlgItemTextW(hwndDlg, ID_EDT_PASS_NEW, newpsw_unicode, sizeof(newpsw_unicode)/2 - 1); /* Convert Unicode to ASCII (CP850) */ ConvertUnicode2Ascii(oldpsw_unicode, oldpsw, sizeof(oldpsw)); if (!ConvertUnicode2Ascii(newpsw_unicode, newpsw, sizeof(newpsw))) { ShowLocalizedMsg(IDS_ERR_INVALID_CHARS_IN_PSW); return(-1); } /* Load the PKCS #12 file */ if (!(fp = _tfopen(keyfile, _T("rb")))) { /* error opening file */ ShowLocalizedMsg(IDS_ERR_OPEN_PRIVATE_KEY_FILE, keyfile); return(0); } p12 = d2i_PKCS12_fp(fp, NULL); fclose (fp); if (!p12) { /* error reading PKCS #12 */ ShowLocalizedMsg(IDS_ERR_READ_PKCS12, keyfile); return(0); } /* Parse the PKCS #12 file */ if (!PKCS12_parse(p12, oldpsw, &privkey, &cert, &ca)) { /* old password incorrect */ ShowLocalizedMsg(IDS_ERR_OLD_PWD_INCORRECT); PKCS12_free(p12); return(-1); } /* Free old PKCS12 object */ PKCS12_free(p12); /* Get FriendlyName of old cert */ alias = (char*) X509_alias_get0(cert, NULL); /* Create new PKCS12 object */ p12 = PKCS12_create(newpsw, alias, privkey, cert, ca, 0,0,0,0,0); if (!p12) { /* create failed */ ShowLocalizedMsg(IDS_ERR_CREATE_PKCS12); return(0); } /* Free old key, cert and ca */ EVP_PKEY_free(privkey); X509_free(cert); sk_X509_pop_free(ca, X509_free); /* Open keyfile for writing */ if (!(fp = _tfopen(keyfile, _T("wb")))) { ShowLocalizedMsg(IDS_ERR_OPEN_WRITE_KEY, keyfile); PKCS12_free(p12); return(0); } /* Write new key to file */ i2d_PKCS12_fp(fp, p12); PKCS12_free(p12); fclose(fp); /* signal success to user */ ShowLocalizedMsg(IDS_NFO_PWD_CHANGED); return(1); } int LineBeginsWith(char *line, const char *keyword, const unsigned int len) { if (strncmp(line, keyword, len) == 0) { if ((line[len] == '\t') || (line[len] == ' ')) return true; } return false; } int GetKeyFilename(int config, TCHAR *keyfilename, size_t keyfilenamesize, int *keyfile_format) { FILE *fp; char line[256]; int found_key=0; int found_pkcs12=0; TCHAR configfile_path[MAX_PATH]; _tcsncpy(configfile_path, o.conn[config].config_dir, _tsizeof(configfile_path)); if (!(configfile_path[_tcslen(configfile_path)-1] == '\\')) _tcscat(configfile_path, _T("\\")); _tcsncat(configfile_path, o.conn[config].config_file, _tsizeof(configfile_path) - _tcslen(configfile_path) - 1); if (!(fp=_tfopen(configfile_path, _T("r")))) { /* can't open config file */ ShowLocalizedMsg(IDS_ERR_OPEN_CONFIG, configfile_path); return(0); } while (fgets(line, sizeof (line), fp)) { if (LineBeginsWith(line, "key", 3)) { if (found_key) { /* only one key option */ ShowLocalizedMsg(IDS_ERR_ONLY_ONE_KEY_OPTION); return(0); } if (found_pkcs12) { /* key XOR pkcs12 */ ShowLocalizedMsg(IDS_ERR_ONLY_KEY_OR_PKCS12); return(0); } found_key=1; *keyfile_format = KEYFILE_FORMAT_PEM; if (!ParseKeyFilenameLine(config, keyfilename, keyfilenamesize, &line[4])) return(0); } if (LineBeginsWith(line, "pkcs12", 6)) { if (found_pkcs12) { /* only one pkcs12 option */ ShowLocalizedMsg(IDS_ERR_ONLY_ONE_PKCS12_OPTION); return(0); } if (found_key) { /* only key XOR pkcs12 */ ShowLocalizedMsg(IDS_ERR_ONLY_KEY_OR_PKCS12); return(0); } found_pkcs12=1; *keyfile_format = KEYFILE_FORMAT_PKCS12; if (!ParseKeyFilenameLine(config, keyfilename, keyfilenamesize, &line[7])) return(0); } } if ((!found_key) && (!found_pkcs12)) { /* must have key or pkcs12 option */ ShowLocalizedMsg(IDS_ERR_HAVE_KEY_OR_PKCS12); return(0); } return(1); } #endif