/*************************************************************************** smb4kpasswordhandler - This class handles the passwords for Smb4K. ------------------- begin : So Jan 16 2005 copyright : (C) 2005-2007 by Alexander Reinholdt email : dustpuppy@users.berlios.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., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301 USA * ***************************************************************************/ // TQt includes #include #include #include #include #include #include #include #include // KDE includes #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #include #include #endif // application specific includes #include "smb4kpasswordhandler.h" #include "smb4kdefs.h" #include "smb4kerror.h" #include "smb4kauthinfo.h" #include "smb4ksettings.h" #ifndef __FreeBSD__ Smb4KPasswordHandler::Smb4KPasswordHandler( Smb4KHomesSharesHandler *handler, TQObject *parent, const char *name ) : TQObject( parent, name ), m_handler( handler ), m_wallet_support_disabled( false ) #else Smb4KPasswordHandler::Smb4KPasswordHandler( Smb4KHomesSharesHandler *s_handler, Smb4KSambaOptionsHandler *o_handler, TQObject *parent, const char *name ) : TQObject( parent, name ), m_handler( s_handler ), m_options_handler( o_handler ), m_wallet_support_disabled( false ) #endif { if ( !m_handler ) { kdFatal() << "Smb4KPasswordHandler: No Smb4KHomesSharesHandler object" << endl; } #ifdef __FreeBSD__ if ( !m_options_handler ) { kdFatal() << "Smb4KPasswordHandler: No Smb4KSambaOptionsHandler object" << endl; } #endif m_auth = NULL; m_dlg = NULL; m_wallet = NULL; m_temp_auth = NULL; } Smb4KPasswordHandler::~Smb4KPasswordHandler() { for ( TQValueList::Iterator it = m_auth_list.begin(); it != m_auth_list.end(); ++it ) { delete *it; } m_auth_list.clear(); delete m_wallet; } void Smb4KPasswordHandler::open_close_wallet() { if ( Smb4KSettings::useWallet() && !walletSupportIsDisabled() ) { if ( !walletIsOpen() ) { // Start the wallet manager before accessing the wallet. We // do not care about the return value, because we do not do // error handling here. if ( kapp ) { (void) kapp->tdeinitExec( "tdewalletmanager" ); } else { // Do nothing. --- Good luck! } m_wallet = TDEWallet::Wallet::openWallet( TDEWallet::Wallet::NetworkWallet(), 0, TDEWallet::Wallet::Synchronous ); if ( m_wallet ) { if ( !m_wallet->hasFolder( "Smb4K" ) ) { m_wallet->createFolder( "Smb4K" ); m_wallet->setFolder( "Smb4K" ); } else { m_wallet->setFolder( "Smb4K" ); convert_old_entries(); } } else { Smb4KError::error( ERROR_OPENING_WALLET_FAILED, TDEWallet::Wallet::NetworkWallet(), TQString() ); delete m_wallet; m_wallet = NULL; m_wallet_support_disabled = true; } } else { convert_old_entries(); } } else { if ( m_wallet ) { delete m_wallet; m_wallet = NULL; } } } void Smb4KPasswordHandler::convert_old_entries() { // Convert old entries (Smb4K 0.9.2 and earlier) to the // new map based format. if ( !m_wallet->entryList().isEmpty() ) { TQStringList entries = m_wallet->entryList(); // Since in the old format the keys contained a string like // this: HOST:USER, we only need to test whether the first // key contains a ":". If that's the case, we need to convert // the entries, otherwise we don't. if ( entries.first().contains( ":" ) != 0 ) { for ( TQStringList::Iterator it = entries.begin(); it != entries.end(); ++it ) { // Get the password. TQString pass; m_wallet->readPassword( *it, pass ); if ( (*it).startsWith( "DEFAULT:" ) ) { // Convert the data to the new format. TQMap map; map["Login"] = (*it).section( ":", 1, 1 ); map["Password"] = pass; m_wallet->writeMap( "DEFAULT_LOGIN", map ); } else { // Convert the data to the new format. TQMap map; map["Login"] = (*it).section( ":", 3, 3 ); map["Password"] = pass; if ( TQString::compare( (*it).section( ":", 0, 0 ), "*" ) != 0 ) { map["Workgroup"] = (*it).section( ":", 0, 0 ).upper(); } if ( TQString::compare( (*it).section( ":", 2, 2 ), "*" ) == 0 ) { m_wallet->writeMap( (*it).section( ":", 1, 1 ).upper(), map ); } else { m_wallet->writeMap( "//"+(*it).section( ":", 1, 1 ).upper()+ "/"+(*it).section( ":", 2, 2 ).upper(), map ); } } // Delete the old entry. m_wallet->removeEntry( *it ); } } else { // Do nothing. } } else { // Do nothing } } bool Smb4KPasswordHandler::askpass( const TQString &workgroup, const TQString &host, const TQString &share, int desc, TQWidget *parent, const char *name ) { // m_auth is NULL: m_auth = readAuth( new Smb4KAuthInfo( workgroup, host, share ) ); // Set up the askpass dialog: m_dlg = new KDialogBase( KDialogBase::Plain, i18n( "Authentication" ), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, parent, name, true, true ); TQFrame *frame = m_dlg->plainPage(); TQGridLayout *layout = new TQGridLayout( frame ); layout->setSpacing( 5 ); layout->setMargin( 0 ); TQLabel *pixmap_label = new TQLabel( frame ); pixmap_label->setPixmap( DesktopIcon( "identity" ) ); pixmap_label->adjustSize(); layout->addWidget( pixmap_label, 0, 0, TQt::AlignCenter ); TQString message; switch ( desc ) { case NewData: break; case AccessDenied: message = i18n( "The access was denied. " ); break; case BadPassword: message = i18n( "The password is not correct. " ); break; case PermDenied: message = i18n( "The permission was denied. " ); break; case AuthError: message = i18n( "An authentication error occurred. " ); break; case LogonFailure: message = i18n( "The logon failed. " ); break; default: break; } if ( m_auth->share().stripWhiteSpace().isEmpty() ) { message.append( i18n( "Please enter authentication data for server %1." ).arg( m_auth->host() ) ); } else { message.append( i18n( "Please enter authentication data for share %1." ).arg( "//"+m_auth->host()+"/"+m_auth->share() ) ); } TQLabel *message_label = new TQLabel( frame ); message_label->setText( message.stripWhiteSpace() ); message_label->setTextFormat( TQt::RichText ); layout->addWidget( message_label, 0, 1, 0 ); TQLabel *user_label = new TQLabel( i18n( "User:" ), frame ); layout->addWidget( user_label, 1, 0, 0 ); KLineEdit *user_edit = NULL; KComboBox *user_combo = NULL; if ( TQString::compare( m_auth->share(), "homes" ) != 0 ) { user_edit = new KLineEdit( frame, "AskPassUserEdit" ); user_edit->setMinimumWidth( 200 ); layout->addWidget( user_edit, 1, 1, 0 ); } else { user_combo = new KComboBox( frame, "AskPassUserCombo" ); user_combo->setEditable( true ); user_combo->setMinimumWidth( 200 ); layout->addWidget( user_combo, 1, 1, 0 ); } TQLabel *password_label = new TQLabel( i18n( "Password:" ), frame ); layout->addWidget( password_label, 2, 0, 0 ); KLineEdit *pass_edit = new KLineEdit( frame, "AskPassPasswordEdit" ); pass_edit->setEchoMode( KLineEdit::Password ); layout->addWidget( pass_edit, 2, 1, 0 ); m_dlg->setMainWidget( frame ); m_dlg->setFixedSize( 350, m_dlg->sizeHint().height() ); m_dlg->enableButtonOK( false ); // Since we have to allow empty passwords, we will only connect // the edit line for the user to enable/disable the OK button. if ( user_edit ) { connect( user_edit, TQ_SIGNAL( textChanged( const TQString & ) ), this, TQ_SLOT( slotEnableOKButton( const TQString& ) ) ); } else { connect( user_combo, TQ_SIGNAL( textChanged( const TQString & ) ), this, TQ_SLOT( slotEnableOKButton( const TQString& ) ) ); } // Process the authentication data: if ( TQString::compare( share, "homes" ) != 0 ) { user_edit->setText( m_auth->user() ); pass_edit->setText( m_auth->password() ); if ( m_auth->user().isEmpty() ) { user_edit->setFocus(); } else { pass_edit->setFocus(); } } else { TQStringList list = m_handler->homesUsers( host ); user_combo->insertStringList( list ); user_combo->setCurrentText( TQString() ); connect( user_combo, TQ_SIGNAL( activated( const TQString & ) ), this, TQ_SLOT( slotGetPassword( const TQString & ) ) ); user_combo->setFocus(); } bool ok = false; if ( m_dlg->exec() == KDialogBase::Accepted ) { if ( TQString::compare( share, "homes" ) != 0 ) { TQString user = user_edit->text(); TQString pass = pass_edit->text(); m_auth->setUser( user ); m_auth->setPassword( pass ); writeAuth( m_auth ); } else { TQString user = user_combo->currentText(); TQString pass = pass_edit->text(); m_auth->setUser( user ); m_auth->setPassword( pass ); writeAuth( m_auth ); } ok = true; } // Clean up: delete m_dlg; m_dlg = NULL; delete m_auth; m_auth = NULL; return ok; } Smb4KAuthInfo *Smb4KPasswordHandler::readAuth( Smb4KAuthInfo *authInfo ) { // Do nothing, if authInfo is NULL: if ( !authInfo ) { return authInfo; } open_close_wallet(); if ( walletIsOpen() ) { // Get the authentication information from the wallet. // Always prefer either the exact match or the information // that was provided for the host. TQMap map; if ( !authInfo->share().isEmpty() ) { m_wallet->readMap( "//"+authInfo->host().upper()+"/"+authInfo->share().upper(), map ); if ( map.isEmpty() ) { m_wallet->readMap( authInfo->host().upper(), map ); if ( map.isEmpty() ) { if ( Smb4KSettings::useDefaultLogin() ) { m_wallet->readMap( "DEFAULT_LOGIN", map ); if ( map.isEmpty() ) { return authInfo; } else { // We have authentication information. // So, do nothing here. } } else { return authInfo; } } else { // Check that the workgroup is correct. if ( map.contains( "Workgroup" ) && !authInfo->workgroup().isEmpty() ) { if ( TQString::compare( map["Workgroup"].upper(), authInfo->workgroup().upper() ) != 0 ) { return authInfo; } else { // Everything is OK. Do nothing. } } else { // We cannot check if the workgroup is OK. So, just return // the authentication information we just collected. If it // should be wrong, the user will be asked to provide the // correct data anyway. } } } else { // Check that the workgroup is correct. if ( map.contains( "Workgroup" ) && !authInfo->workgroup().isEmpty() ) { if ( TQString::compare( map["Workgroup"].upper(), authInfo->workgroup().upper() ) != 0 ) { return authInfo; } else { // Everything is OK. Do nothing. } } else { // We cannot check if the workgroup is OK. So, just process // the authentication information we just collected. If it // should be wrong, the user will be asked to provide the // correct data anyway. } } } else { m_wallet->readMap( authInfo->host().upper(), map ); if ( map.isEmpty() ) { if ( Smb4KSettings::useDefaultLogin() ) { m_wallet->readMap( "DEFAULT_LOGIN", map ); if ( map.isEmpty() ) { return authInfo; } } else { return authInfo; } } else { // Check that the workgroup is correct. if ( map.contains( "Workgroup" ) && !authInfo->workgroup().isEmpty() ) { if ( TQString::compare( map["Workgroup"].upper(), authInfo->workgroup().upper() ) != 0 ) { return authInfo; } else { // Everything is OK. Do nothing. } } else { // We cannot check if the workgroup is OK. So, just process // the authentication information we just collected. If it // should be wrong, the user will be asked to provide the // correct data anyway. } } } // We have authentication information. Put the data into the // Smb4KAuthInfo object. authInfo->setUser( map["Login"] ); authInfo->setPassword( map["Password"] ); } else { // Either the opening of the wallet failed or the user does // not want to use a wallet. // Let's see if we can get the authentication information from // the temporary list. if ( Smb4KSettings::rememberPasswords() ) { if ( !m_auth_list.isEmpty() ) { for ( TQValueList::Iterator it = m_auth_list.begin(); it != m_auth_list.end(); ++it ) { if ( !authInfo->share().isEmpty() ) { if ( TQString::compare( authInfo->host().upper(), (*it)->host().upper() ) == 0 && TQString::compare( authInfo->share().upper(), (*it)->share().upper() ) == 0 ) { if ( TQString::compare( authInfo->workgroup().upper(), (*it)->workgroup().upper() ) == 0 || (*it)->workgroup().isEmpty() ) { // Either the workgroup is OK, or we cannot check if it is OK. // In both cases we process the data at hand. authInfo->setUser( (*it)->user() ); authInfo->setPassword( (*it)->password() ); // Exact match. Stop here. break; } else { continue; } } else if ( TQString::compare( authInfo->host().upper(), (*it)->host().upper() ) == 0 ) { if ( TQString::compare( authInfo->workgroup().upper(), (*it)->workgroup().upper() ) == 0 || (*it)->workgroup().isEmpty() ) { // Either the workgroup is OK, or we cannot check if it is OK. // In both cases we process the data at hand. authInfo->setUser( (*it)->user() ); authInfo->setPassword( (*it)->password() ); // Because we always prefer the exact match, we keep // on searching. continue; } else { continue; } } else { continue; } } else { if ( TQString::compare( authInfo->host().upper(), (*it)->host().upper() ) == 0 ) { if ( TQString::compare( authInfo->workgroup().upper(), (*it)->workgroup().upper() ) == 0 || (*it)->workgroup().isEmpty() ) { // Either the workgroup is OK, or we cannot check if it is OK. // In both cases we process the data at hand. authInfo->setUser( (*it)->user() ); authInfo->setPassword( (*it)->password() ); break; } else { continue; } } else { continue; } } } } else { // Do nothing } } else { if ( m_temp_auth ) { authInfo->setUser( m_temp_auth->user() ); authInfo->setPassword( m_temp_auth->password() ); delete m_temp_auth; m_temp_auth = NULL; } } } #ifdef __FreeBSD__ writeToSMBConfFile( authInfo ); #endif return authInfo; } void Smb4KPasswordHandler::writeAuth( Smb4KAuthInfo *authInfo ) { open_close_wallet(); if ( walletIsOpen() ) { TQMap map; map["Login"] = authInfo->user(); map["Password"] = authInfo->password(); if ( !authInfo->workgroup().isEmpty() ) { map["Workgroup"] = authInfo->workgroup().upper(); } if ( !authInfo->share().isEmpty() ) { m_wallet->writeMap( "//"+authInfo->host().upper()+"/"+authInfo->share().upper(), map ); } else { m_wallet->writeMap( authInfo->host().upper(), map ); } m_wallet->sync(); } else { if ( Smb4KSettings::rememberPasswords() ) { Smb4KAuthInfo *tmp = NULL; for ( TQValueList::Iterator it = m_auth_list.begin(); it != m_auth_list.end(); ++it ) { if ( ((*it)->workgroup().isEmpty() || TQString::compare( (*it)->workgroup().upper(), authInfo->workgroup().upper() ) == 0) && TQString::compare( (*it)->host().upper(), authInfo->host().upper() ) == 0 && TQString::compare( (*it)->host().upper(), authInfo->share().upper() ) == 0 ) { tmp = *it; break; } else { continue; } } if ( tmp ) { delete tmp; } m_auth_list.append( new Smb4KAuthInfo( *authInfo ) ); } else { if ( !m_temp_auth ) { m_temp_auth = new Smb4KAuthInfo( *authInfo ); } } } #ifdef __FreeBSD__ writeToSMBConfFile( authInfo ); #endif } Smb4KAuthInfo *Smb4KPasswordHandler::readDefaultAuth( Smb4KAuthInfo *authInfo ) { if ( !authInfo ) { return authInfo; } open_close_wallet(); if ( walletIsOpen() ) { // Read the default authentication information. TQMap map; m_wallet->readMap( "DEFAULT_LOGIN", map ); if ( !map.isEmpty() ) { authInfo->setUser( map["Login"] ); authInfo->setPassword( map["Password"] ); } else { // Do nothing } } else { // Nothing to do. } return authInfo; } void Smb4KPasswordHandler::writeDefaultAuth( Smb4KAuthInfo *authInfo ) { open_close_wallet(); if ( walletIsOpen() ) { TQMap map; map["Login"] = authInfo->user(); map["Password"] = authInfo->password(); m_wallet->writeMap( "DEFAULT_LOGIN", map ); m_wallet->sync(); } } #ifdef __FreeBSD__ void Smb4KPasswordHandler::writeToSMBConfFile( Smb4KAuthInfo *authInfo ) { m_nsmbrc_auth = *authInfo; TDEProcess *p = new TDEProcess(); p->setUseShell( true ); connect( p, TQ_SIGNAL( receivedStdout( TDEProcess *, char *, int ) ), this, TQ_SLOT( slotReceivePassword( TDEProcess *, char *, int ) ) ); connect( p, TQ_SIGNAL( processExited( TDEProcess * ) ), this, TQ_SLOT( slotWritePassword( TDEProcess * ) ) ); *p << TQString( "smbutil crypt %1" ).arg( m_nsmbrc_auth.password() ); p->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput ); } #endif ///////////////////////////////////////////////////////////////////////////// // SLOT IMPLEMENTATIONS ///////////////////////////////////////////////////////////////////////////// void Smb4KPasswordHandler::slotGetPassword( const TQString &username ) { if ( m_dlg && m_auth ) { // We need to re-read the auth data here, because // of the homes shares: Smb4KAuthInfo *auth = readAuth( new Smb4KAuthInfo( m_auth->workgroup().upper(), m_auth->host().upper(), username ) ); KLineEdit *lineEdit = static_cast( m_dlg->child( "AskPassPasswordEdit", "KLineEdit", true ) ); lineEdit->setText( auth->password() ); delete auth; m_auth->setShare( username ); } } void Smb4KPasswordHandler::slotEnableOKButton( const TQString &text ) { if ( m_dlg ) { m_dlg->enableButtonOK( !text.isEmpty() ); } } #ifdef __FreeBSD__ void Smb4KPasswordHandler::slotReceivePassword( TDEProcess *, char *buffer, int buflen ) { m_buffer.append( TQString::fromLocal8Bit( buffer, buflen ) ); #else void Smb4KPasswordHandler::slotReceivePassword( TDEProcess *, char *, int ) { #endif } void Smb4KPasswordHandler::slotWritePassword( TDEProcess *proc ) { delete proc; #ifdef __FreeBSD__ TQString pass = m_buffer.remove( "\n" ).stripWhiteSpace(); m_buffer = TQString(); TQDir::setCurrent( TQDir::homeDirPath() ); TQFile file( ".nsmbrc" ); TQStringList contents; if ( file.exists() ) { if ( file.open( IO_ReadOnly ) ) { TQTextStream ts( &file ); ts.setEncoding( TQTextStream::Locale ); while ( !ts.atEnd() ) { contents.append( ts.readLine().stripWhiteSpace() ); } file.close(); } } // Check if the [default] section is present. if ( contents.find( "[default]" ) == contents.end() && contents.find( "[DEFAULT]" ) == contents.end() ) { TQMap map = m_options_handler->globalSambaOptions(); TQString workgroup = map["workgroup"]; TQString wins = m_options_handler->winsServer(); TQStringList::Iterator it = contents.prepend( "[default]" ); it++; if ( !workgroup.isEmpty() ) { it = contents.insert( it, "workgroup="+workgroup ); } if ( !wins.isEmpty() ) { it = contents.insert( it, "nbns="+wins ); } // FIXME: Should we write the charsets here, too?? } TQString section; if ( m_nsmbrc_auth.share().isEmpty() ) { section.append( TQString( "[%1:%2]" ).arg( m_nsmbrc_auth.host().upper(), m_nsmbrc_auth.user().isEmpty() ? "GUEST" : m_nsmbrc_auth.user().upper() ) ); } else { section.append( TQString( "[%1:%2:%3]" ).arg( m_nsmbrc_auth.host().upper(), m_nsmbrc_auth.user().isEmpty() ? "GUEST" : m_nsmbrc_auth.user().upper(), m_nsmbrc_auth.share().upper() ) ); } TQStringList::Iterator it = contents.find( section ); if ( it == contents.end() ) { if ( contents.last() != TQString() ) { contents.append( TQString() ); } contents.append( section ); contents.append( "password="+pass ); if ( !m_nsmbrc_auth.workgroup().isEmpty() ) { contents.append( "workgroup="+m_nsmbrc_auth.workgroup() ); } } else { for ( TQStringList::Iterator i = ++it; i != contents.end(); ++i ) { if ( (*i).contains( "password=" ) ) { TQString old_pass = (*i).section( "password=", 1, 1 ).stripWhiteSpace(); if ( TQString::compare( pass, old_pass ) == 0 ) { // The passwords are identical, so stop here. return; } else { *i = "password="+pass; break; } } else if ( (*i).startsWith( "[" ) ) { break; } else { continue; } } } TQDir::setCurrent( TQDir::homeDirPath() ); if ( file.open( IO_WriteOnly ) ) { TQTextStream ts( &file ); ts.setEncoding( TQTextStream::Locale ); for ( TQStringList::ConstIterator it = contents.begin(); it != contents.end(); ++it ) { ts << *it << endl; } file.close(); } else { Smb4KError::error( ERROR_WRITING_FILE, "~/.nsmbrc", TQString() ); return; } // Get minimal security: Fix permissions. TQString path = TQDir::homeDirPath()+"/"+file.name(); int fd; if ( (fd = open( path.ascii(), O_WRONLY )) == -1 ) { int err_no = errno; Smb4KError::error( ERROR_OPENING_FILE, path, strerror( err_no ) ); return; } else { if ( fchmod( fd, 00600 ) == -1 ) { // FIXME: Do we need to emit an error here at all? } } #endif } #include "smb4kpasswordhandler.moc"