SSL handling complete
authorkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Thu, 24 Dec 2009 17:36:28 +0000 (17:36 +0000)
committerkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Thu, 24 Dec 2009 17:36:28 +0000 (17:36 +0000)
git-svn-id: https://silmor.de/svn/softmagic/smoke/trunk@350 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33

src/configdialog.cpp
src/configdialog.h
src/sslexception.cpp
src/sslexception.h

index 1ee01d3..4dc2116 100644 (file)
@@ -15,6 +15,7 @@
 #include "configdialog.h"
 #include "office.h"
 #include "listview.h"
+#include "sslexception.h"
 
 #include <QByteArray>
 #include <QCheckBox>
 #include <QMenu>
 #include <QMenuBar>
 #include <QMessageBox>
+#include <QNetworkAccessManager>
+#include <QNetworkProxy>
+#include <QNetworkReply>
 #include <QPushButton>
 #include <QSettings>
 #include <QSpinBox>
 #include <QStandardItemModel>
+#include <QTableView>
+#include <QTimer>
+#include <QUrl>
 
 MConfigDialog::MConfigDialog()
 {
        setWindowTitle(tr("Magic Smoke Configuration"));
        
        oldrow=-1;
+       sslexcept=0;
        //main layout
        QHBoxLayout*hl;
        QVBoxLayout*vl;
@@ -66,23 +74,22 @@ MConfigDialog::MConfigDialog()
        m->addAction(tr("Set &Default Label Font..."),this,SLOT(setDefaultFont()));
        mb->addMenu(MApplication::helpMenu());
        
-       //create central widget
-       QGridLayout*gl;
+       //create list view
        hl->addWidget(profiles=new MListView);
        profilemodel=new QStandardItemModel(this);
        profiles->setModel(profilemodel);
        profiles->setEditTriggers(QListView::NoEditTriggers);
        connect(profiles,SIGNAL(clicked(const QModelIndex&)),this,SLOT(loadProfile()));
        connect(profiles,SIGNAL(activated(const QModelIndex&)),this,SLOT(loadProfile()));
-       hl->addLayout(gl=new QGridLayout,10);
+       //create tabs
+       QTabWidget*tab;
+       QWidget*w;
+       hl->addWidget(tab=new QTabWidget,10);
+       tab->addTab(w=new QWidget,tr("Connection"));
+       QGridLayout*gl;
+       w->setLayout(gl=new QGridLayout);
        QLabel*lab;
        int lctr=0;
-       gl->addWidget(lab=new QLabel(tr("Hostname:")),++lctr,0);
-       lab->setAlignment(Qt::AlignRight);
-       gl->addWidget(hostname=new QLineEdit,lctr,1);
-       gl->addWidget(lab=new QLabel(tr("Hostkey:")),++lctr,0);
-       lab->setAlignment(Qt::AlignRight);
-       gl->addWidget(hostkey=new QLineEdit,lctr,1);
        gl->addWidget(lab=new QLabel(tr("Server URL:")),++lctr,0);
        lab->setAlignment(Qt::AlignRight);
        gl->addLayout(hl=new QHBoxLayout,lctr,1);
@@ -105,12 +112,37 @@ MConfigDialog::MConfigDialog()
        proxypass->setEchoMode(QLineEdit::Password);
        connect(useproxy,SIGNAL(toggled(bool)),proxyuser,SLOT(setEnabled(bool)));
        connect(useproxy,SIGNAL(toggled(bool)),proxypass,SLOT(setEnabled(bool)));
+       
+       tab->addTab(w=new QWidget,tr("Authentication"));
+       w->setLayout(gl=new QGridLayout);
+       lctr=0;
+       gl->addWidget(lab=new QLabel(tr("Hostname:")),++lctr,0);
+       lab->setAlignment(Qt::AlignRight);
+       gl->addWidget(hostname=new QLineEdit,lctr,1);
+       gl->addWidget(lab=new QLabel(tr("Hostkey:")),++lctr,0);
+       lab->setAlignment(Qt::AlignRight);
+       gl->addWidget(hostkey=new QLineEdit,lctr,1);
        gl->addWidget(lab=new QLabel(tr("Default Username:")),++lctr,0);
        lab->setAlignment(Qt::AlignRight);
        gl->addWidget(username=new QLineEdit,lctr,1);
        gl->setRowStretch(++lctr,10);
        gl->addLayout(hl=new QHBoxLayout,++lctr,0,1,2);
        
+       tab->addTab(w=new QWidget,tr("SSL Exceptions"));
+       w->setLayout(vl=new QVBoxLayout);
+       vl->addWidget(new QLabel(tr("List of non-fatal SSL exceptions:")),0);
+       vl->addWidget(ssltable=new QTableView,10);
+       ssltable->setModel(sslmodel=new QStandardItemModel(this));
+       ssltable->setEditTriggers(QListView::NoEditTriggers);
+       vl->addSpacing(10);
+       vl->addLayout(hl=new QHBoxLayout,0);
+       hl->addStretch(10);
+       QPushButton*pb;
+       hl->addWidget(pb=new QPushButton(tr("Clear")));
+       connect(pb,SIGNAL(clicked()),this,SLOT(clearSslExceptions()));
+       hl->addWidget(pb=new QPushButton(tr("Probe Server")));
+       connect(pb,SIGNAL(clicked()),this,SLOT(serverProbe()));
+       
        initProfiles();
        loadProfile();
 }
@@ -150,6 +182,8 @@ void MConfigDialog::loadProfile()
 {
        //save old
        saveProfile();
+       if(sslexcept)delete sslexcept;
+       sslexcept=0;
        //get new
        QModelIndex idx=profiles->currentIndex();
        if(!idx.isValid())return;
@@ -170,6 +204,8 @@ void MConfigDialog::loadProfile()
        proxyuser->setEnabled(useproxy->isChecked());
        proxypass->setEnabled(useproxy->isChecked());
        username->setText(set.value("username").toString());
+       sslexcept=new MSslExceptions(::dataDir+"/profile."+key+"/sslexceptions.xml");
+       sslFillModel();
 }
 
 void MConfigDialog::saveProfile()
@@ -187,6 +223,7 @@ void MConfigDialog::saveProfile()
        set.setValue("proxyuser",proxyuser->text());
        set.setValue("proxypass",proxypass->text());
        set.setValue("username",username->text());
+       if(sslexcept)sslexcept->savesslexcept();
 }
 
 void MConfigDialog::newProfile()
@@ -334,6 +371,11 @@ void MConfigDialog::exportKey()
 
 void MConfigDialog::generateKey()
 {
+       //ask nicely
+       if(hostkey->text()!="")
+       if(QMessageBox::question(this,tr("Generate Hostkey"), tr("Do you really want to generate a new host key for this profile? This may disable all accounts from this host."), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
+               return;
+       //actually create new key
        QString name;
        MKeyGen mkg(this);
        QString k=mkg.getKey();
@@ -432,3 +474,75 @@ void MConfigDialog::setDefaultFont()
        if(df.isEmpty())return;
        QSettings().setValue("defaultfont",df);
 }
+
+void MConfigDialog::serverProbe()
+{
+       //sanity check
+       if(!sslexcept)return;
+       sslexcept->clearRecorded();
+       //set up mini-environment
+       QEventLoop loop;
+       QNetworkAccessManager man;
+       connect(&man,SIGNAL(finished(QNetworkReply*)),&loop,SLOT(quit()));
+       QTimer::singleShot(30000,&loop,SLOT(quit()));
+       connect(&man,SIGNAL(sslErrors(QNetworkReply*,const QList<QSslError>&)), this,SLOT(sslErrors(QNetworkReply*,const QList<QSslError>&)));
+       if(useproxy->isChecked())
+               man.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy,proxyname->text(),proxyport->value(),proxyuser->text(),proxypass->text()));
+       //query server
+       QNetworkRequest rq(QUrl("https://"+serverurl->text()));
+       QNetworkReply *rp=man.get(rq);
+       //wait for result
+       setEnabled(false);
+       loop.exec();
+       setEnabled(true);
+       //probe result
+       if(rp->error()==QNetworkReply::NoError){
+               QMessageBox::information(this,tr("Server Probe"),tr("The request finished without errors."));
+       }else{
+               QMessageBox::warning(this,tr("Server Probe"),tr("The request finished with an error: %1").arg(rp->errorString()));
+       }
+       delete rp;
+}
+
+void MConfigDialog::sslErrors(QNetworkReply*rp,const QList<QSslError>&errs)
+{
+       if(!sslexcept)return;
+       //check/record/clear
+       if(sslexcept->checksslexcept(errs))rp->ignoreSslErrors();
+       //convert to string
+       QString err=tr("SSL Errors encountered:\n");
+       for(int i=0;i<errs.size();i++){
+               QSslCertificate c=errs[i].certificate();
+               err+=tr("Certificate \"%1\"\n  Fingerprint (sha1): %2\n  Error: %3\n")
+                       .arg(c.subjectInfo(QSslCertificate::CommonName))
+                       .arg(QString::fromAscii(c.digest(QCryptographicHash::Sha1).toHex()))
+                       .arg(errs[i].errorString());
+       }
+       err+=tr("Accept connection anyway?");
+       if(QMessageBox::warning(this,tr("SSL Warning"),err,QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes){
+               rp->ignoreSslErrors();
+               sslexcept->acceptRecorded();
+       }
+       sslFillModel();
+}
+
+void MConfigDialog::clearSslExceptions()
+{
+       if(sslexcept)sslexcept->clear();
+       sslmodel->clear();
+}
+
+void MConfigDialog::sslFillModel()
+{
+       sslmodel->clear();
+       if(!sslexcept)return;
+       sslmodel->insertColumns(0,3);
+       sslmodel->setHorizontalHeaderLabels(QStringList()<<tr("Common Name")<<tr("SHA-1 Digest")<<tr("Error Type"));
+       QList<QPair<QSslCertificate,int> >lst=sslexcept->nonFatalExceptions();
+       sslmodel->insertRows(0,lst.size());
+       for(int i=0;i<lst.size();i++){
+               sslmodel->setData(sslmodel->index(i,0),lst[i].first.subjectInfo(QSslCertificate::CommonName));
+               sslmodel->setData(sslmodel->index(i,1),QString::fromAscii(lst[i].first.digest(QCryptographicHash::Sha1).toHex()));
+               sslmodel->setData(sslmodel->index(i,2),QSslError((QSslError::SslError)lst[i].second).errorString());
+       }
+}
index 4d78ff5..279653a 100644 (file)
 #define MAGICSMOKE_CONFIGDIALOG_H
 
 #include <QDialog>
+#include <QList>
+#include <QSslError>
 
 class QCheckBox;
 class QLineEdit;
 class QListView;
+class QNetworkReply;
 class QSpinBox;
 class QStandardItemModel;
+class QTableView;
+
+class MSslExceptions;
 
 /**login and profile configuration window*/
 class MConfigDialog:public QDialog
@@ -34,7 +40,10 @@ class MConfigDialog:public QDialog
                QLineEdit*hostname,*hostkey,*serverurl,*proxyname,*username,*proxyuser,*proxypass;
                QSpinBox*proxyport;
                QListView*profiles;
-               QStandardItemModel*profilemodel;
+               QStandardItemModel*profilemodel,*sslmodel;
+               QTableView*ssltable;
+               
+               MSslExceptions*sslexcept;
                
                int oldrow;
                
@@ -58,6 +67,11 @@ class MConfigDialog:public QDialog
                void generateKey();
                void openOfficeCfg();
                void setDefaultFont();
+               //ssl server probe
+               void serverProbe();
+               void sslErrors(QNetworkReply*,const QList<QSslError>&);
+               void clearSslExceptions();
+               void sslFillModel();
 };
 
 #endif
index c9decc0..c060d0a 100644 (file)
@@ -17,7 +17,6 @@
 #include <QDomElement>
 #include <QDomNodeList>
 #include <QFile>
-#include <QSslCertificate>
 
 MSslExceptions::MSslExceptions(QString p)
 {
@@ -29,21 +28,81 @@ MSslExceptions::MSslExceptions(QString p)
                if(!doc.setContent(&fd))return;
                fd.close();
                QDomElement root=doc.documentElement();
+               QDomNodeList nl=root.elementsByTagName("SSL-Exception");
+               for(int i=0;i<nl.size();i++){
+                       QDomElement el=nl.at(i).toElement();
+                       int e=el.attribute("errorId",0).toInt();
+                       if(e<=0)continue;
+                       QSslCertificate c(el.text().toAscii());
+                       if(c.isNull())continue;
+                       sslexcept.append(QPair<QSslCertificate,int>(c,e));
+               }
        }
 }
 
-void MSslExceptions::savesslexcept(){}
+void MSslExceptions::savesslexcept()
+{
+       QDomDocument doc;
+       QDomElement root=doc.createElement("SSL-Exceptions");
+       for(int i=0;i<sslexcept.size();i++){
+               QDomElement el=doc.createElement("SSL-Exception");
+               el.setAttribute("errorId",sslexcept[i].second);
+               el.appendChild(doc.createTextNode(sslexcept[i].first.toPem()));
+               root.appendChild(el);
+       }
+       doc.appendChild(root);
+       QFile fd(path);
+       if(fd.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+               fd.write(doc.toByteArray());
+               fd.close();
+       }
+}
+
+void MSslExceptions::clear()
+{
+       sslexcept.clear();
+       sslrecord.clear();
+}
 
-void MSslExceptions::showdialog(QWidget*){}
+void MSslExceptions::acceptRecorded()
+{
+       for(int i=0;i<sslrecord.size();i++){
+               bool known=false;
+               for(int j=0;j<sslexcept.size();j++){
+                       if(sslrecord[i] == sslexcept[j]){
+                               known=true;
+                               break;
+                       }
+               }
+               if(!known)sslexcept.append(sslrecord[i]);
+       }
+}
 
 bool MSslExceptions::checksslexcept(const QList<QSslError>&errs)
 {
-       qDebug("!!!!!!!!!!!!!!!!!!! %i SSL Exceptions!",errs.size());
-       return true;
+       //stage 1: record unknown exceptions
+       for(int i=0;i<errs.size();i++){
+               QPair<QSslCertificate,int>p(errs[i].certificate(),errs[i].error());
+               bool known=false;
+               for(int j=0;j<sslrecord.size();j++){
+                       if(p==sslrecord[j]){
+                               known=true;
+                               break;
+                       }
+               }
+               if(!known)sslrecord.append(p);
+       }
+       //stage 2: compare to acceptable exceptions
        for(int i=0;i<errs.size();i++){
-               QByteArray cert=errs[i].certificate().toPem();
-               if(!sslexcept.contains(cert))return false;
-               if(!sslexcept[cert].contains(errs[i].error()))return false;
+               QPair<QSslCertificate,int>p(errs[i].certificate(),errs[i].error());
+               bool known=false;
+               for(int j=0;j<sslexcept.size();j++){
+                       if(p==sslexcept[j]){
+                               known=true;
+                               break;
+                       }
+               }
+               if(!known)return false;
        }
        return true;
 }
index 67bc157..61fddf8 100644 (file)
 #include <QByteArray>
 #include <QList>
 #include <QMap>
+#include <QPair>
+#include <QSslCertificate>
 #include <QSslError>
 #include <QString>
 
 class QWidget;
 
+/**Helper class: stores and compares SSL-Exceptions*/
 class MSslExceptions
 {
        public:
+               /**create instance from file in path*/
                MSslExceptions(QString path);
 
-               //ssl helper: save the exceptions
+               /**saves the exceptions to config file for next session*/
                void savesslexcept();
-               //ssl helper: check errors agains the exception list
+               /**checks errors against the exception list, records all exceptions*/
                bool checksslexcept(const QList<QSslError>&);
                
-               void showdialog(QWidget*);
-       
+               /**returns the current list of acceptable exceptions*/
+               QList<QPair<QSslCertificate,int> > nonFatalExceptions()const{return sslexcept;}
+               
+               /**returns the list of collected exceptions*/
+               QList<QPair<QSslCertificate,int> > collectedExceptions()const{return sslrecord;}
+               
+               /**clears the internal lists of exceptions*/
+               void clear();
+               
+               /**clears the list of recorded exceptions*/
+               void clearRecorded(){sslrecord.clear();}
+               
+               /**accepts the recorded exceptions*/
+               void acceptRecorded();
+               
        private:
-               QMap<QByteArray,QList<int> > sslexcept;
+               QList<QPair<QSslCertificate,int> > sslexcept,sslrecord;
                QString path;
 };