/*************************************************************************** * Copyright (C) 2006 by Michael Kaufmann * * michael@enlighter.de * * * * 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; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "configwizard.h" #include "authmanager.h" using namespace std; configWizard::configWizard( QWidget *parent, const char *name ) : configWizardBase( parent, name ) { // mScanPath = QString::null; // QObject::connect( scanURL, SIGNAL( urlSelected( const QString & ) ), this, SLOT( slotUrlSelected( const QString & ) ) ); mStartingPort = 11194; configOptions.append( "^[\\s]*remote[\\s]+[\\w][\\d\\S\\w]*" ); // QObject::connect( scanButtonGroup, SIGNAL( clicked( int ) ), this, SLOT( clicked( int ) ) ); QObject::connect( scanDir, SIGNAL( toggled( bool ) ), this, SLOT( scanPlaceToggled( bool ) ) ); mAuthManager = authManager::self(); QObject::connect( mAuthManager, SIGNAL ( newPasswordGUI( bool &, bool & , const QString &, QString &, const QString & ) ), this, SLOT ( newPasswordGUI( bool &, bool & , const QString & , QString &, const QString & ) ) ); setFinishEnabled ( finalPage, true ); // static QRegExp realTimeNotifications( "^>(.*):.*\\r\\n$" ); /* That's the general real time notification format of openvpn. */ // static QRegExp realTimeStartup( "^>INFO:OpenVPN Management Interface Version 1.*$" ); /* Detects corrct startup message */ // static QRegExp realTimeState( "^>STATE:(.*).*\\r\\n$" ); /* Detects state changes */ // static QRegExp realTimeLog( "^>LOG:(.*).*\\r\\n$" ); /* Detects real-time LOG messages */ // static QRegExp realTimeHold( "^>HOLD:(.*).*\\r\\n$" ); /* Detects corrct startup message */ // static QRegExp realTimePassword( "^>PASSWORD:(.*).*\\r\\n$" ); /* Detects Auth requests */ // static QRegExp outputSuccess( "^SUCCESS:.*\\r\\n$" ); // static QRegExp outputError( "^ERROR:.*\\r\\n$" ); // static QRegExp singleLine( "^(.*)\\r\\n$" ); /* Cuts of the \r\n at the end of a line. */ } configWizard::~ configWizard( ) {} /* This function checks if the file is a OpenVPN config file or not. */ bool configWizard::isConfigFile( const QString & path ) { debug( "isConfigFile", QString( "Examing %1" ).arg( path ) ); bool isConfigFile = false; static QRegExp managementRegExp( "^[\\s]*management[\\s]+([\\w\\d\\.]+)[\\s]+([\\d]+)[\\s]*$" ); static QRegExp management_pwfileRegExp( "^[\\s]*management[\\s]+([\\w\\d\\.]+)[\\s]+([\\d]+)[\\s]+([\\w\\d\\./]+)[\\s]*$" ); static QRegExp management_holdRegExp( "^[\\s]*management-hold[\\s]*$" ); static QRegExp management_query_passwordsRegExp( "^[\\s]*management-query-passwords[\\s]*$" ); static QRegExp auth_retryRegExp( "^[\\s]*auth-retry interact[\\s]*$" ); static QRegExp nameRegExp( "^.*([^/]*).conf$" ); nameRegExp.search( path ); QString name = nameRegExp.cap( 1 ); QString host = QString::null; unsigned int port = 0; QString pwfile = QString::null; bool management = false; bool management_query_passwords = false; bool management_hold = false; bool auth_retry = false; QStringList fileBuffer; // debug( "isConfigFile", QString( "Results for %1 (%2)" ).arg( name ).arg( path ) ); // debug( "isConfigFile", QString( "%1 management %2 %3 %4" ).arg( management ).arg( host ).arg( port ).arg( pwfile ) ); // debug( "isConfigFile", QString( "%1 management-hold" ).arg( management_hold ) ); // debug( "isConfigFile", QString( "%1 management-query-passwords" ).arg( management_query_passwords ) ); // debug( "isConfigFile", QString( "%1 auth_retry interact" ).arg( auth_retry ) ); QRegExp option; QFile file( path ); if ( file.open( IO_ReadOnly ) ) { debug( "isConfigFile", "File opened" ); /* From he QT docu */ QTextStream stream( &file ); QString line; // }; /* Read the whole file */ while ( !stream.atEnd() ) { line = stream.readLine(); // line of text excluding '\n' /* Check if this can be a OpenVPN configuration file. */ for ( QStringList::Iterator it = configOptions.begin(); it != configOptions.end(); ++it ) { option.setPattern( *it ); if ( option.search( line ) != -1 ) { isConfigFile = true; } } /* Check for management interface related options */ if ( management_pwfileRegExp.search( line ) != -1 ) { debug( "isConfigFile", "Found management pwfile" ); management = true; host = management_pwfileRegExp.cap( 1 ); port = management_pwfileRegExp.cap( 2 ).toUInt(); pwfile = management_pwfileRegExp.cap( 3 ); fileBuffer.append( ";" + line ); } else if ( managementRegExp.search( line ) != -1 ) { debug( "isConfigFile", "Found management" ); management = true; host = managementRegExp.cap( 1 ); port = managementRegExp.cap( 2 ).toUInt(); pwfile = QString::null; /* The last one counts */ fileBuffer.append( ";" + line ); } else if ( management_holdRegExp.search( line ) != -1 ) { debug( "isConfigFile", "Found management-hold" ); management_hold = true; fileBuffer.append( ";" + line ); } else if ( management_query_passwordsRegExp.search( line ) != -1 ) { debug( "isConfigFile", "Found management-query-passwords" ); management_query_passwords = true; fileBuffer.append( ";" + line ); } else if ( auth_retryRegExp.search( line ) != -1 ) { debug( "isConfigFile", "Found auth-retry" ); auth_retry = true; fileBuffer.append( ";" + line ); } else { fileBuffer.append( line ); } } if ( port > 0 ) { /* If there's a conflict: mark it */ if ( mReservedPorts.contains( port ) ) { debug( "isConfigFile", QString( "%1 ALREADY RESERVED" ).arg( port ) ); for ( QValueList::Iterator it = mReservedPorts.begin(); it != mReservedPorts.end(); ++it ) { debug( "isConfigFile", QString( "RESERVED PORTS = %1" ).arg( *it ) ); } port = 0; /* Search a new one */ } else { debug( "isConfigFile", QString( "ADDING %1 TO THE LIST OF RESERVED PORTS" ).arg( port ) ); mReservedPorts.append( port ); } } if ( isConfigFile ) { mConfigurations[ path ].name = nameRegExp.cap( 1 ); mConfigurations[ path ].host = host; mConfigurations[ path ].port = port; mConfigurations[ path ].pwfile = pwfile; mConfigurations[ path ].management = management; mConfigurations[ path ].management_query_passwords = management_query_passwords; mConfigurations[ path ].management_hold = management_hold; mConfigurations[ path ].auth_retry = auth_retry; mConfigurations[ path ].file = fileBuffer; } file.close(); debug( "isConfigFile", QString( "Results for %1 (%2)" ).arg( name ).arg( path ) ); debug( "isConfigFile", QString( "%1 management %2 %3 %4" ).arg( management ).arg( host ).arg( port ).arg( pwfile ) ); debug( "isConfigFile", QString( "%1 management-hold" ).arg( management_hold ) ); debug( "isConfigFile", QString( "%1 management-query-passwords" ).arg( management_query_passwords ) ); debug( "isConfigFile", QString( "%1 auth_retry interact" ).arg( auth_retry ) ); } return isConfigFile; } /* This function does the actual scan job. */ void configWizard::scan( const QString & scanPath ) { debug( "scan", QString( "scanPath = %1" ).arg( scanPath ) ); QString path = QString::null; /* Path to a matching file */ QStringList dirsInDir; QStringList filesInDir; QDir dir( scanPath ); /* Inform the user what I am doing at the moment */ // scanProgress->setText( QString( "Scanning %1" ).arg( scanPath ) ); if ( dir.exists() && dir.isReadable() ) { dir.setFilter( QDir::Dirs | QDir::Readable | QDir::Executable ); /* Show only dirs for diving into them */ dirsInDir = dir.entryList(); for ( QStringList::Iterator it = dirsInDir.begin(); it != dirsInDir.end(); ++it ) { /* This are all subdirectories in the current directory. Dive into them. */ if ( *it != "." && *it != ".." ) { scan( QString( "%1/%2" ).arg( scanPath ).arg( *it ) ); } } dir.setFilter( QDir::Files | QDir::Readable ); /* Show only dirs for diving into them */ dir.setNameFilter( "*.conf" ); filesInDir = dir.entryList(); for ( QStringList::Iterator it = filesInDir.begin(); it != filesInDir.end(); ++it ) { /* This are all subdirectories in the current directory. Dive into them. */ debug( "scan", QString( "Files in %1: %2" ).arg( scanPath ).arg( *it ) ); /* Inform the user what I am doing at the moment */ // scanProgress->setText( QString( "Scanning %1/%2" ).arg( scanPath ).arg( *it ) ); /* Check it this file may be a config file and if so, add it to the list */ if ( isConfigFile( QString( "%1/%2" ).arg( scanPath ).arg( *it ).replace( "//", "/" ) ) ) { mScanResults.append( QString( "%1/%2" ).arg( scanPath ).arg( *it ).replace( "//", "/" ) ); } } } } /* This function calls the actual scan function and makes sensful things with the results. */ void configWizard::scan( ) { debug( "scan" ); mScanResults.clear(); if ( scanDir->isChecked() ) { scan( scanURL->url() ); } else { scan( " / " ); } /* Add the found files to the list */ foundConfigurations->clear(); /* Add found configuration files to the list, so that the user can select them */ foundConfigurations->insertStringList( mScanResults ); } void configWizard::next( ) { debug( "next" ); if ( currentPage() == scanPage ) { scan(); } else if ( currentPage() == resultPage ) { modify(); } configWizardBase::next(); } void configWizard::debug( const QString & method, const QString & message ) { #ifdef DEBUG QString myMethod ( method ); static unsigned int maxLen = 0; maxLen = QMAX( myMethod.length(), maxLen ); cout << "configWizard::" << myMethod.leftJustify( maxLen ) << " = > " << message << endl; #endif } void configWizard::modify( const QString & path ) { debug( "modify", path ); bool newPort = false; /* Is a new port assigned to solve conflicts? */ QFile file( path ); unsigned int port = mStartingPort; debug( "modify", QString( "Results for %1" ).arg( path ) ); debug( "modify", QString( "%1 management (%2) (%3) (%4)" ).arg( mConfigurations[ path ].management ).arg( mConfigurations[ path ].host ).arg( mConfigurations[ path ].port ).arg( mConfigurations[ path ].pwfile ) ); debug( "modify", QString( "%1 management-hold" ).arg( mConfigurations[ path ].management_hold ) ); debug( "modify", QString( "%1 management-query-passwords" ).arg( mConfigurations[ path ].management_query_passwords ) ); debug( "modify", QString( "%1 auth_retry interact" ).arg( mConfigurations[ path ].auth_retry ) ); if ( file.open( IO_WriteOnly | IO_Truncate ) ) { debug( "modify", QString( "File %1 opened" ).arg( path ) ); QTextStream stream( &file ); QString line; debug( "modify", QString( "port = %1" ).arg( mConfigurations[ path ].port ) ); /* Write the old config file with some modifications (outcommented parts) */ for ( QStringList::Iterator it = mConfigurations[ path ].file.begin(); it != mConfigurations[ path ].file.end(); ++it ) { stream << *it << endl; } /* Search a free port */ if ( mConfigurations[ path ].port == 0 ) { /* A new port must be unused and not reserved */ while ( mUsedPorts.contains( port ) || mReservedPorts.contains( port ) ) { if ( mUsedPorts.contains( port ) ) debug( "modify", QString( "Port %1 is already in use" ).arg( port ) ); if ( mReservedPorts.contains( port ) ) debug( "modify", QString( "Port %1 is reserved" ).arg( port ) ); port++; } /* Port found, remeber that */ mUsedPorts.append( port ); /* In some cases I would not alter the config file. But it the port has been changed I have to do it though! */ if ( mConfigurations[ path ].port != port ) { newPort = true; } mConfigurations[ path ].port = port; } else { /* Port found, remeber that */ mUsedPorts.append( mConfigurations[ path ].port ); } debug( "modify", QString( "port = %1" ).arg( mConfigurations[ path ].port ) ); /* MAKE CHANGED PERMANENT */ stream << "################################################################################" << endl; stream << "# Options added by kovpnsetup #" << endl; stream << "################################################################################" << endl; /* don't write management option if it is already there in a form we like it. */ // B && C || A && B && C if ( enableMIPassword->isChecked() && mConfigurations[ path ].pwfile == QString::null ) { /* CASE: Management Password should be there, but it isn't currently */ mConfigurations[ path ].pwfile = QString( path ).replace( ".conf", "_pw.txt" ); if ( createPasswordFile( mConfigurations[ path ].pwfile, mConfigurations[ path ].name ) ) { stream << QString( "management localhost %1 %2" ).arg( mConfigurations[ path ].port ).arg( mConfigurations[ path ].pwfile ) << endl; } else { stream << QString( "management localhost %1" ).arg( mConfigurations[ path ].port ) << endl; } } else if ( mConfigurations[ path ].pwfile != QString::null ) { stream << QString( "management localhost %1 %2" ).arg( mConfigurations[ path ].port ).arg( mConfigurations[ path ].pwfile ) << endl; } // } else if ( ! enableMIPassword->isChecked() ) { // && mConfigurations[ path ].pwfile != QString::null ) { /* CASE: Management Password should not be there but it is there. Don't lower security, so don't write */ // stream << QString( "management localhost %1" ).arg( port ) << endl; else if ( ! enableMIPassword->isChecked() ) { // && mConfigurations[ path ].pwfile == QString::null ) { /* CASE: Management Password should not be there and it is not there. No security change */ stream << QString( "management localhost %1" ).arg( port ) << endl; } // } else if ( enableMIPassword->isChecked() && mConfigurations[ path ].pwfile != QString::null && newPort ) { // stream << QString( "management localhost %1 %2" ).arg( mConfigurations[ path ].port ).arg( mConfigurations[ path ].pwfile ) << endl; // } // if ( !mConfigurations[ path ].management_query_passwords ) { stream << QString( "management-query-passwords" ) << endl; // } // if ( !mConfigurations[ path ].management_hold ) { stream << QString( "management-hold" ) << endl; // } // if ( !mConfigurations[ path ].auth_retry ) { stream << QString( "auth-retry interact" ) << endl; // } file.close(); } } void configWizard::modify( ) { debug( "modify" ); /* get selected files. QListBox is somewhat ugly, because it cannot give me a list of the selected items. */ for ( unsigned int i = 0; i < foundConfigurations->count(); i++ ) { if ( foundConfigurations->isSelected( i ) ) { /* Modify if selected */ modify( foundConfigurations->text( i ) ); /* Add the values to Kovpn */ } } } void configWizard::scanPlaceToggled( bool ) { if ( scanAll->isChecked() ) { scanURL->setEnabled( false ); } else { scanURL->setEnabled( true ); } } bool configWizard::createPasswordFile( const QString &pwfile, const QString &ressource ) { debug( "createPasswordFile", pwfile ); QFile file ( pwfile ); QString password = QString::null; QString message = i18n( "

Please enter a password for the management interface of %1.

If you press cancel no password will be set.

" ).arg( ressource ); /* If user submittet a password then... */ if ( authManager::self() ->newPassword( ressource, "mi", password, message, false ) ) { debug( "createPasswordFile", QString( "New Password = %1" ).arg( password ) ); /* ...try to open the password file */ if ( unlink( pwfile ) || file.open( IO_ReadWrite ) ) { QTextStream stream( &file ); /* And write the passwort */ stream << password << endl; file.close(); ( void ) chmod( pwfile, S_IRUSR ); return true; } else { /* File could not be opened */ return false; } } else { /* User didn't submit a password */ return false; } } void configWizard::newPasswordGUI( bool & cancel, bool & failure, const QString & ressource, QString & passphrase, const QString & message ) { debug( "newPasswordGUI" ); QCString tempPassphrase; KPasswordDialog * myPassDlg = new KPasswordDialog( KPasswordDialog::NewPassword, false, 0 ); myPassDlg->setAllowEmptyPasswords( false ); myPassDlg->setPrompt( message ); // myPassDlg->addLine( i18n( "Network" ), ressource ); myPassDlg->disableCoreDumps(); if ( myPassDlg->exec() ) { passphrase = myPassDlg->password(); failure = false; } else { debug( "passRequestGUI", "Failure" ); failure = true; passphrase = QString::null; } myPassDlg->clearPassword(); delete myPassDlg; } // void configWizard::slotUrlSelected( const QString & scanPath ) // { // debug( "slotUrlSelected", QString( "scanPath = %1" ).arg( scanPath ) ); // mScanPath = scanPath; // } #include "configwizard.moc"