/* * 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 * * Parts of this sourcefile is taken from openvpnserv.c from the * OpenVPN source, with approval from the author, James Yonan * . */ #include #include #include #include "main.h" #include "options.h" #include "openvpn.h" #include "scripts.h" #include "openvpn-gui-res.h" #include "passphrase.h" #include "tray.h" #include "localization.h" extern struct options o; /* Wait for a complete line (CR/LF) and return it. * Return values: * 1 - Successful. Line is available in *line. * 0 - Broken Pipe during ReadFile. * -1 - Other Error during ReadFile. * * I'm really unhappy with this code! If anyone knows of an easier * way to convert the streaming data from ReadFile() into lines, * please let me know! */ int ReadLineFromStdOut(HANDLE hStdOut, int config, char *line) { #define MAX_LINELEN 1024 CHAR lpBuffer[MAX_LINELEN]; static char lastline[MAX_CONFIGS][MAX_LINELEN]; static int charsleft[MAX_CONFIGS]; char tmpline[MAX_LINELEN]; DWORD nBytesRead; char *p; unsigned int len, i; static int first_call = 1; if (first_call) { for (i=0; i < MAX_CONFIGS; i++) charsleft[i]=0; first_call = 0; } while (true) { if (charsleft[config]) { /* Check for Passphrase prompt */ CheckPrivateKeyPassphrasePrompt(lastline[config], config); /* Check for Username/Password Auth prompt */ CheckAuthUsernamePrompt(lastline[config], config); CheckAuthPasswordPrompt(lastline[config]); p=strchr(lastline[config], '\n'); if (p == NULL) { if (!ReadFile(hStdOut,lpBuffer,sizeof(lpBuffer) - strlen(lastline[config]) - 1, &nBytesRead,NULL) || !nBytesRead) { if (GetLastError() == ERROR_BROKEN_PIPE) return(0); // pipe done - normal exit path. else { /* error reading from pipe */ ShowLocalizedMsg(GUI_NAME, IDS_ERR_READ_STDOUT_PIPE); return(-1); } } lpBuffer[nBytesRead] = '\0'; p=strchr(lpBuffer, '\n'); if (p == NULL) { strncat(lastline[config], lpBuffer, sizeof(lastline[config]) - strlen(lastline[config]) - 1); if (strlen(lastline[config]) >= (MAX_LINELEN - 1)) { strncpy(line, lastline[config], MAX_LINELEN); charsleft[config]=0; return(1); } } else { p[0] = '\0'; strncpy(line, lastline[config], MAX_LINELEN - 1); strncat(line, lpBuffer, MAX_LINELEN - strlen(line)); if (line[strlen(line) - 1] == '\r') line[strlen(line) - 1] = '\0'; if (nBytesRead > (strlen(lpBuffer) + 1)) { strncpy(lastline[config], p+1, sizeof(lastline[config]) - 1); charsleft[config]=1; return(1); } charsleft[config]=0; return(1); } } else { len = strlen(lastline[config]); p[0] = '\0'; strncpy(line, lastline[config], MAX_LINELEN - 1); if (line[strlen(line) - 1] == '\r') line[strlen(line) - 1] = '\0'; if (len > (strlen(line) + 2)) { strncpy(tmpline, p+1, sizeof(tmpline) - 1); strncpy(lastline[config], tmpline, sizeof(lastline[config]) - 1); charsleft[config]=1; return(1); } charsleft[config]=0; return(1); } } else { if (!ReadFile(hStdOut,lpBuffer,sizeof(lpBuffer) - 1, &nBytesRead,NULL) || !nBytesRead) { if (GetLastError() == ERROR_BROKEN_PIPE) return(0); // pipe done - normal exit path. else { /* error reading from pipe */ ShowLocalizedMsg(GUI_NAME, IDS_ERR_READ_STDOUT_PIPE); return(-1); } } lpBuffer[nBytesRead] = '\0'; p=strchr(lpBuffer, '\n'); if (p == NULL) { if (nBytesRead >= (MAX_LINELEN - 1)) { strncpy(line, lpBuffer, MAX_LINELEN); charsleft[config]=0; return(1); } else { strncpy(lastline[config], lpBuffer, sizeof(lastline[config]) - 1); charsleft[config]=1; } } else { p[0] = '\0'; strncpy(line, lpBuffer, MAX_LINELEN - 1); if (line[strlen(line) - 1] == '\r') line[strlen(line) - 1] = '\0'; if (nBytesRead > strlen(line)) { strncpy(lastline[config], p+1, sizeof(lastline[config]) - 1); charsleft[config]=1; return(1); } } } } } /* * Monitor the openvpn log output while CONNECTING */ void monitor_openvpnlog_while_connecting(int config, char *line) { char msg[200]; unsigned int i; char *linepos; /* Check for Connected message */ if (strstr(line, o.connect_string) != NULL) { /* Run Connect Script */ RunConnectScript(config, false); /* Save time when we got connected. */ o.cnn[config].connected_since = time(NULL); o.cnn[config].connect_status = CONNECTED; SetMenuStatus(config, CONNECTED); SetTrayIcon(CONNECTED); /* Remove Proxy Auth file */ DeleteFile(o.proxy_authfile); /* Zero psw attempt counter */ o.cnn[config].failed_psw_attempts = 0; /* UserInfo: Connected */ SetDlgItemText(o.cnn[config].hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_CONNECTED)); SetStatusWinIcon(o.cnn[config].hwndStatus, ID_ICO_CONNECTED); /* Show Tray Balloon msg */ if (o.show_balloon[0] != '0') { LoadLocalizedStringBuf(msg, sizeof(msg)/sizeof(*msg), IDS_NFO_NOW_CONNECTED, o.cnn[config].config_name); if (strlen(o.cnn[config].ip) > 0) { ShowTrayBalloon(msg, LoadLocalizedString(IDS_NFO_ASSIGN_IP, o.cnn[config].ip)); } else { ShowTrayBalloon(msg, " "); } } /* Hide Status Window */ ShowWindow(o.cnn[config].hwndStatus, SW_HIDE); } /* Check for failed passphrase log message */ if ((strstr(line, "TLS Error: Need PEM pass phrase for private key") != NULL) || (strstr(line, "EVP_DecryptFinal:bad decrypt") != NULL) || (strstr(line, "PKCS12_parse:mac verify failure") != NULL) || (strstr(line, "Received AUTH_FAILED control message") != NULL) || (strstr(line, "Auth username is empty") != NULL)) { o.cnn[config].failed_psw_attempts++; o.cnn[config].failed_psw=1; o.cnn[config].restart=true; } /* Check for "certificate has expired" message */ if ((strstr(line, "error=certificate has expired") != NULL)) { StopOpenVPN(config); /* Cert expired... */ ShowLocalizedMsg(GUI_NAME, IDS_ERR_CERT_EXPIRED); } /* Check for "certificate is not yet valid" message */ if ((strstr(line, "error=certificate is not yet valid") != NULL)) { StopOpenVPN(config); /* Cert not yet valid */ ShowLocalizedMsg(GUI_NAME, IDS_ERR_CERT_NOT_YET_VALID); } /* Check for "Notified TAP-Win32 driver to set a DHCP IP" message */ if (((linepos=strstr(line, "Notified TAP-Win32 driver to set a DHCP IP")) != NULL)) { strncpy(o.cnn[config].ip, linepos+54, 15); /* Copy IP address */ for (i=0; i < strlen(o.cnn[config].ip); i++) if (o.cnn[config].ip[i] == '/' || o.cnn[config].ip[i] == ' ') break; o.cnn[config].ip[i] = '\0'; } } /* * Monitor the openvpn log output while CONNECTED */ void monitor_openvpnlog_while_connected(int config, char *line) { /* Check for Ping-Restart message */ if (strstr(line, "process restarting") != NULL) { /* Set connect_status = ReConnecting */ o.cnn[config].connect_status = RECONNECTING; CheckAndSetTrayIcon(); /* Set Status Window Controls "ReConnecting" */ SetDlgItemText(o.cnn[config].hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_RECONNECTING)); SetStatusWinIcon(o.cnn[config].hwndStatus, ID_ICO_CONNECTING); } } /* * Monitor the openvpn log output while RECONNECTING */ void monitor_openvpnlog_while_reconnecting(int config, char *line) { char msg[200]; char *linepos; size_t i; /* Check for Connected message */ if (strstr(line, o.connect_string) != NULL) { o.cnn[config].connect_status = CONNECTED; SetTrayIcon(CONNECTED); /* Set Status Window Controls "Connected" */ SetDlgItemText(o.cnn[config].hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_CONNECTED)); SetStatusWinIcon(o.cnn[config].hwndStatus, ID_ICO_CONNECTED); /* Show Tray Balloon msg */ if (o.show_balloon[0] == '2') { LoadLocalizedStringBuf(msg, sizeof(msg)/sizeof(*msg), IDS_NFO_NOW_CONNECTED, o.cnn[config].config_name); if (strlen(o.cnn[config].ip) > 0) { ShowTrayBalloon(msg, LoadLocalizedString(IDS_NFO_ASSIGN_IP, o.cnn[config].ip)); } else { ShowTrayBalloon(msg, " "); } } } /* Check for failed passphrase log message */ if ((strstr(line, "TLS Error: Need PEM pass phrase for private key") != NULL) || (strstr(line, "EVP_DecryptFinal:bad decrypt") != NULL) || (strstr(line, "PKCS12_parse:mac verify failure") != NULL) || (strstr(line, "Received AUTH_FAILED control message") != NULL) || (strstr(line, "Auth username is empty") != NULL)) { o.cnn[config].failed_psw_attempts++; o.cnn[config].failed_psw=1; o.cnn[config].restart=true; } /* Check for "certificate has expired" message */ if ((strstr(line, "error=certificate has expired") != NULL)) { /* Cert expired */ StopOpenVPN(config); ShowLocalizedMsg(GUI_NAME, IDS_ERR_CERT_EXPIRED); } /* Check for "certificate is not yet valid" message */ if ((strstr(line, "error=certificate is not yet valid") != NULL)) { StopOpenVPN(config); /* Cert not yet valid */ ShowLocalizedMsg(GUI_NAME, IDS_ERR_CERT_NOT_YET_VALID); } /* Check for "Notified TAP-Win32 driver to set a DHCP IP" message */ if (((linepos=strstr(line, "Notified TAP-Win32 driver to set a DHCP IP")) != NULL)) { strncpy(o.cnn[config].ip, linepos+54, 15); /* Copy IP address */ for (i=0; i < strlen(o.cnn[config].ip); i++) if (o.cnn[config].ip[i] == '/' || o.cnn[config].ip[i] == ' ') break; o.cnn[config].ip[i] = '\0'; } } /* * Opens a log file and monitors the started OpenVPN process. * All output from OpenVPN is written both to the logfile and * to the status window. * * The output from OpenVPN is also watch for diffrent messages * and appropriate actions are taken. */ void WatchOpenVPNProcess(int config) { char line[1024]; int ret; char filemode[2] = "w\0"; FILE *fd; int LogLines = 0; int logpos; HWND LogWindow; /* set log file append/truncate filemode */ if (o.append_string[0] == '1') filemode[0] = 'a'; /* Set Connect_Status = "Connecting" */ o.cnn[config].connect_status = CONNECTING; /* Set Tray Icon = "Connecting" if no other connections are running */ CheckAndSetTrayIcon(); /* Set MenuStatus = "Connecting" */ SetMenuStatus(config, CONNECTING); /* Clear failed_password flag */ o.cnn[config].failed_psw = 0; /* Open log file */ if ((fd=fopen(o.cnn[config].log_path, filemode)) == NULL) ShowLocalizedMsg (GUI_NAME, IDS_ERR_OPEN_LOG_WRITE, o.cnn[config].log_path); LogWindow = GetDlgItem(o.cnn[config].hwndStatus, ID_EDT_LOG); while(TRUE) { if ((ret=ReadLineFromStdOut(o.cnn[config].hStdOut, config, line)) == 1) { /* Do nothing if line length = 0 */ if (strlen(line) == 0) continue; /* Write line to Log file */ if (fd != NULL) { fputs (line, fd); fputc ('\n', fd); fflush (fd); } /* Remove lines from LogWindow if it is getting full */ LogLines++; if (LogLines > MAX_LOG_LINES) { logpos = SendMessage(LogWindow, EM_LINEINDEX, DEL_LOG_LINES, 0); SendMessage(LogWindow, EM_SETSEL, 0, logpos); SendMessage(LogWindow, EM_REPLACESEL, FALSE, (LPARAM) ""); LogLines -= DEL_LOG_LINES; } /* Write line to LogWindow */ strcat(line, "\r\n"); SendMessage(LogWindow, EM_SETSEL, (WPARAM) -1, (LPARAM) -1); SendMessage(LogWindow, EM_REPLACESEL, FALSE, (LPARAM) line); if (o.cnn[config].connect_status == CONNECTING) /* Connecting state */ monitor_openvpnlog_while_connecting(config, line); if (o.cnn[config].connect_status == CONNECTED) /* Connected state */ monitor_openvpnlog_while_connected(config, line); if (o.cnn[config].connect_status == RECONNECTING) /* ReConnecting state */ monitor_openvpnlog_while_reconnecting(config, line); } else break; } /* OpenVPN has been shutdown */ /* Close logfile filedesc. */ if (fd != NULL) fclose(fd); /* Close StdIn/StdOut handles */ CloseHandle(o.cnn[config].hStdIn); CloseHandle(o.cnn[config].hStdOut); /* Close exitevent handle */ CloseHandle(o.cnn[config].exit_event); o.cnn[config].exit_event = NULL; /* Enable/Disable menuitems for this connections */ SetMenuStatus(config, DISCONNECTING); /* Remove Proxy Auth file */ DeleteFile(o.proxy_authfile); /* Process died outside our control */ if(o.cnn[config].connect_status == CONNECTED) { /* Zero psw attempt counter */ o.cnn[config].failed_psw_attempts = 0; /* Set connect_status = "Not Connected" */ o.cnn[config].connect_status=DISCONNECTED; /* Change tray icon if no more connections is running */ CheckAndSetTrayIcon(); /* Show Status Window */ SetDlgItemText(o.cnn[config].hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_DISCONNECTED)); SetStatusWinIcon(o.cnn[config].hwndStatus, ID_ICO_DISCONNECTED); EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_DISCONNECT), FALSE); EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_RESTART), FALSE); SetForegroundWindow(o.cnn[config].hwndStatus); ShowWindow(o.cnn[config].hwndStatus, SW_SHOW); ShowLocalizedMsg(GUI_NAME, IDS_NFO_CONN_TERMINATED, o.cnn[config].config_name); /* Close Status Window */ SendMessage(o.cnn[config].hwndStatus, WM_CLOSE, 0, 0); } /* We have failed to connect */ else if(o.cnn[config].connect_status == CONNECTING) { /* Set connect_status = "DisConnecting" */ o.cnn[config].connect_status=DISCONNECTING; /* Change tray icon if no more connections is running */ CheckAndSetTrayIcon(); if ((o.cnn[config].failed_psw) && (o.cnn[config].failed_psw_attempts < o.psw_attempts)) { /* Restart OpenVPN to make another attempt to connect */ PostMessage(o.hWnd, WM_COMMAND, (WPARAM) IDM_CONNECTMENU + config, 0); } else { /* Show Status Window */ SetDlgItemText(o.cnn[config].hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_FAILED)); SetStatusWinIcon(o.cnn[config].hwndStatus, ID_ICO_DISCONNECTED); EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_DISCONNECT), FALSE); EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_RESTART), FALSE); SetForegroundWindow(o.cnn[config].hwndStatus); ShowWindow(o.cnn[config].hwndStatus, SW_SHOW); /* Zero psw attempt counter */ o.cnn[config].failed_psw_attempts = 0; ShowLocalizedMsg(GUI_NAME, IDS_NFO_CONN_FAILED, o.cnn[config].config_name); /* Set connect_status = "Not Connected" */ o.cnn[config].connect_status=DISCONNECTED; /* Close Status Window */ SendMessage(o.cnn[config].hwndStatus, WM_CLOSE, 0, 0); /* Reset restart flag */ o.cnn[config].restart=false; } } /* We have failed to reconnect */ else if(o.cnn[config].connect_status == RECONNECTING) { /* Set connect_status = "DisConnecting" */ o.cnn[config].connect_status=DISCONNECTING; /* Change tray icon if no more connections is running */ CheckAndSetTrayIcon(); if ((o.cnn[config].failed_psw) && (o.cnn[config].failed_psw_attempts < o.psw_attempts)) { /* Restart OpenVPN to make another attempt to connect */ PostMessage(o.hWnd, WM_COMMAND, (WPARAM) IDM_CONNECTMENU + config, 0); } else { /* Show Status Window */ SetDlgItemText(o.cnn[config].hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_FAILED_RECONN)); SetStatusWinIcon(o.cnn[config].hwndStatus, ID_ICO_DISCONNECTED); EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_DISCONNECT), FALSE); EnableWindow(GetDlgItem(o.cnn[config].hwndStatus, ID_RESTART), FALSE); SetForegroundWindow(o.cnn[config].hwndStatus); ShowWindow(o.cnn[config].hwndStatus, SW_SHOW); /* Zero psw attempt counter */ o.cnn[config].failed_psw_attempts = 0; ShowLocalizedMsg(GUI_NAME, IDS_NFO_RECONN_FAILED, o.cnn[config].config_name); /* Set connect_status = "Not Connected" */ o.cnn[config].connect_status=DISCONNECTED; /* Close Status Window */ SendMessage(o.cnn[config].hwndStatus, WM_CLOSE, 0, 0); /* Reset restart flag */ o.cnn[config].restart=false; } } /* We have chosed to Disconnect */ else if(o.cnn[config].connect_status == DISCONNECTING) { /* Zero psw attempt counter */ o.cnn[config].failed_psw_attempts = 0; /* Set connect_status = "Not Connected" */ o.cnn[config].connect_status=DISCONNECTED; /* Change tray icon if no more connections is running */ CheckAndSetTrayIcon(); if (o.cnn[config].restart) { /* Restart OpenVPN */ StartOpenVPN(config); } else { /* Close Status Window */ SendMessage(o.cnn[config].hwndStatus, WM_CLOSE, 0, 0); } } /* We have chosed to Suspend */ else if(o.cnn[config].connect_status == SUSPENDING) { /* Zero psw attempt counter */ o.cnn[config].failed_psw_attempts = 0; /* Set connect_status = "SUSPENDED" */ o.cnn[config].connect_status=SUSPENDED; SetDlgItemText(o.cnn[config].hwndStatus, ID_TXT_STATUS, LoadLocalizedString(IDS_NFO_STATE_SUSPENDED)); /* Change tray icon if no more connections is running */ CheckAndSetTrayIcon(); } /* Enable/Disable menuitems for this connections */ SetMenuStatus(config, DISCONNECTED); }