rpms/qbittorrent/devel HTTP_digest_mode_Web_UI_authentication.patch, NONE, 1.1 qbittorrent.spec, 1.70, 1.71

Leigh Scott leigh123linux at fedoraproject.org
Fri Jan 15 16:16:42 UTC 2010


Author: leigh123linux

Update of /cvs/pkgs/rpms/qbittorrent/devel
In directory cvs1.fedora.phx.redhat.com:/tmp/cvs-serv25286

Modified Files:
	qbittorrent.spec 
Added Files:
	HTTP_digest_mode_Web_UI_authentication.patch 
Log Message:
* Thu Jan 15 2010 Leigh Scott <leigh123linux at googlemail.com> - 2.1.0-0.12.rc7
- add patch to use HTTP digest mode for Web UI authentication


HTTP_digest_mode_Web_UI_authentication.patch:
 Changelog              |    2 
 src/httpconnection.cpp |    6 +-
 src/httpserver.cpp     |  118 +++++++++++++++++++++++++++++++++++++++++++------
 src/httpserver.h       |    8 ++-
 src/preferences.h      |   17 ++++---
 5 files changed, 125 insertions(+), 26 deletions(-)

--- NEW FILE HTTP_digest_mode_Web_UI_authentication.patch ---
--- trunk/Changelog	2010/01/15 07:32:33	3385
+++ trunk/Changelog	2010/01/15 14:20:20	3386
@@ -19,6 +19,8 @@
     - BUGFIX: Use XDG folders (.cache, .local) instead of .qbittorrent
     - BUGFIX: Added legal notice on startup that the user must accept
     - BUGFIX: Protect Web UI authentication against brute forcing
+    - BUGFIX: Use HTTP digest mode for Web UI authentication (instead of Basic)
+    - BUGFIX: Properly display torrents with one file in subfolder(s)
     - COSMETIC: Use checkboxes to filter torrent content instead of comboboxes
     - COSMETIC: Use alternating row colors in transfer list (set in program preferences)
     - COSMETIC: Added a spin box to speed limiting dialog for manual input
--- trunk/src/httpconnection.cpp	2010/01/15 07:32:33	3385
+++ trunk/src/httpconnection.cpp	2010/01/15 14:20:20	3386
@@ -137,14 +137,14 @@
     write();
     return;
   }
-  QStringList auth = parser.value("Authorization").split(" ", QString::SkipEmptyParts);
-  if (auth.size() != 2 || QString::compare(auth[0], "Basic", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth[1].toLocal8Bit())) {
+  QString auth = parser.value("Authorization");
+  if (QString::compare(auth.split(" ").first(), "Digest", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth.toLocal8Bit(), parser.method())) {
     // Update failed attempt counter
     parent->client_failed_attempts.insert(socket->peerAddress().toString(), nb_fail+1);
     qDebug("client IP: %s (%d failed attempts)", socket->peerAddress().toString().toLocal8Bit().data(), nb_fail);
     // Return unauthorized header
     generator.setStatusLine(401, "Unauthorized");
-    generator.setValue("WWW-Authenticate",  "Basic realm=\"you know what\"");
+    generator.setValue("WWW-Authenticate",  "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+parent->generateNonce()+"\", algorithm=\"MD5\", qop=\"auth\"");
     write();
     return;
   }
--- trunk/src/httpserver.cpp	2010/01/15 07:32:33	3385
+++ trunk/src/httpserver.cpp	2010/01/15 14:20:20	3386
@@ -33,13 +33,14 @@
 #include "httpconnection.h"
 #include "eventmanager.h"
 #include "bittorrent.h"
-#include "preferences.h"
 #include <QTimer>
 #include <QCryptographicHash>
+#include <QTime>
+#include <QRegExp>
 
 HttpServer::HttpServer(Bittorrent *_BTSession, int msec, QObject* parent) : QTcpServer(parent) {
   username = Preferences::getWebUiUsername().toLocal8Bit();
-  password_md5 = Preferences::getWebUiPassword().toLocal8Bit();
+  password_ha1 = Preferences::getWebUiPassword().toLocal8Bit();
   connect(this, SIGNAL(newConnection()), this, SLOT(newHttpConnection()));
   BTSession = _BTSession;
   manager = new EventManager(this, BTSession);
@@ -118,21 +119,110 @@
   }
 }
 
-void HttpServer::setAuthorization(QString _username, QString _password_md5) {
+QString HttpServer::generateNonce() const {
+  QCryptographicHash md5(QCryptographicHash::Md5);
+  md5.addData(QTime::currentTime().toString("hhmmsszzz").toLocal8Bit());
+  md5.addData(":");
+  md5.addData(QBT_REALM);
+  return md5.result().toHex();
+}
+
+void HttpServer::setAuthorization(QString _username, QString _password_ha1) {
   username = _username.toLocal8Bit();
-  password_md5 = _password_md5.toLocal8Bit();
+  password_ha1 = _password_ha1.toLocal8Bit();
 }
 
-bool HttpServer::isAuthorized(QByteArray auth) const {
-  // Decode Auth
-  QByteArray decoded = QByteArray::fromBase64(auth);
-  QList<QByteArray> creds = decoded.split(':');
-  if(creds.size() != 2) return false;
-  QByteArray prop_username = creds.first();
-  if(prop_username != username) return false;
-  QCryptographicHash md5(QCryptographicHash::Md5);
-  md5.addData(creds.last());
-  return (password_md5 == md5.result().toHex());
+// AUTH string is: Digest username="chris",
+// realm="Web UI Access",
+// nonce="570d04de93444b7fd3eaeaecb00e635e",
+// uri="/", algorithm=MD5,
+// response="ba886766d19b45313c0e2195e4344264",
+// qop=auth, nc=00000001, cnonce="e8ac970779c17075"
+bool HttpServer::isAuthorized(QByteArray auth, QString method) const {
+  qDebug("AUTH string is %s", auth.data());
+  // Get user name
+  QRegExp regex_user(".*username=\"([^\"]+)\".*");
+  if(regex_user.indexIn(auth) < 0) return false;
+  QString prop_user = regex_user.cap(1);
+  qDebug("AUTH: Proposed username is %s, real username is %s", prop_user.toLocal8Bit().data(), username.data());
+  if(prop_user != username) {
+    // User name is invalid, we can reject already
+    qDebug("AUTH-PROB: Username is invalid");
+    return false;
+  }
+  // Get realm
+  QRegExp regex_realm(".*realm=\"([^\"]+)\".*");
+  if(regex_realm.indexIn(auth) < 0) {
+    qDebug("AUTH-PROB: Missing realm");
+    return false;
+  }
+  QByteArray prop_realm = regex_realm.cap(1).toLocal8Bit();
+  if(prop_realm != QBT_REALM) {
+    qDebug("AUTH-PROB: Wrong realm");
+    return false;
+  }
+  // get nonce
+  QRegExp regex_nonce(".*nonce=\"([^\"]+)\".*");
+  if(regex_nonce.indexIn(auth) < 0) {
+    qDebug("AUTH-PROB: missing nonce");
+    return false;
+  }
+  QByteArray prop_nonce = regex_nonce.cap(1).toLocal8Bit();
+  qDebug("prop nonce is: %s", prop_nonce.data());
+  // get uri
+  QRegExp regex_uri(".*uri=\"([^\"]+)\".*");
+  if(regex_uri.indexIn(auth) < 0) {
+    qDebug("AUTH-PROB: Missing uri");
+    return false;
+  }
+  QByteArray prop_uri = regex_uri.cap(1).toLocal8Bit();
+  qDebug("prop uri is: %s", prop_uri.data());
+  // get response
+  QRegExp regex_response(".*response=\"([^\"]+)\".*");
+  if(regex_response.indexIn(auth) < 0) {
+    qDebug("AUTH-PROB: Missing response");
+    return false;
+  }
+  QByteArray prop_response = regex_response.cap(1).toLocal8Bit();
+  qDebug("prop response is: %s", prop_response.data());
+  // Compute correct reponse
+  QCryptographicHash md5_ha2(QCryptographicHash::Md5);
+  md5_ha2.addData(method.toLocal8Bit() + ":" + prop_uri);
+  QByteArray ha2 = md5_ha2.result().toHex();
+  QByteArray response = "";
+  if(auth.contains("qop=")) {
+    QCryptographicHash md5_ha(QCryptographicHash::Md5);
+    // Get nc
+    QRegExp regex_nc(".*nc=(\\w+).*");
+    if(regex_nc.indexIn(auth) < 0) {
+      qDebug("AUTH-PROB: qop but missing nc");
+      return false;
+    }
+    QByteArray prop_nc = regex_nc.cap(1).toLocal8Bit();
+    qDebug("prop nc is: %s", prop_nc.data());
+    QRegExp regex_cnonce(".*cnonce=\"([^\"]+)\".*");
+    if(regex_cnonce.indexIn(auth) < 0) {
+      qDebug("AUTH-PROB: qop but missing cnonce");
+      return false;
+    }
+    QByteArray prop_cnonce = regex_cnonce.cap(1).toLocal8Bit();
+    qDebug("prop cnonce is: %s", prop_cnonce.data());
+    QRegExp regex_qop(".*qop=(\\w+).*");
+    if(regex_qop.indexIn(auth) < 0) {
+      qDebug("AUTH-PROB: missing qop");
+      return false;
+    }
+    QByteArray prop_qop = regex_qop.cap(1).toLocal8Bit();
+    qDebug("prop qop is: %s", prop_qop.data());
+    md5_ha.addData(password_ha1+":"+prop_nonce+":"+prop_nc+":"+prop_cnonce+":"+prop_qop+":"+ha2);
+    response = md5_ha.result().toHex();
+  } else {
+    QCryptographicHash md5_ha(QCryptographicHash::Md5);
+    md5_ha.addData(password_ha1+":"+prop_nonce+":"+ha2);
+    response = md5_ha.result().toHex();
+  }
+  qDebug("AUTH: comparing reponses");
+  return prop_response == response;
 }
 
 EventManager* HttpServer::eventManager() const
--- trunk/src/httpserver.h	2010/01/15 07:32:33	3385
+++ trunk/src/httpserver.h	2010/01/15 14:20:20	3386
@@ -36,6 +36,7 @@
 #include <QTcpServer>
 #include <QByteArray>
 #include <QHash>
+#include "preferences.h"
 
 class Bittorrent;
 class QTimer;
@@ -46,7 +47,7 @@
 
 	private:
                 QByteArray username;
-                QByteArray password_md5;
+                QByteArray password_ha1;
                 Bittorrent *BTSession;
 		EventManager *manager;
 		QTimer *timer;
@@ -54,9 +55,10 @@
 	public:
                 HttpServer(Bittorrent *BTSession, int msec, QObject* parent = 0);
 		~HttpServer();
-                void setAuthorization(QString username, QString password_md5);
-		bool isAuthorized(QByteArray auth) const;
+                void setAuthorization(QString username, QString password_ha1);
+                bool isAuthorized(QByteArray auth, QString method) const;
 		EventManager *eventManager() const;
+                QString generateNonce() const;
                 QHash<QString, int> client_failed_attempts;
 
 	private slots:
--- trunk/src/preferences.h	2010/01/15 07:32:33	3385
+++ trunk/src/preferences.h	2010/01/15 14:20:20	3386
@@ -36,6 +36,8 @@
 #include <QPair>
 #include <QDir>
 
+#define QBT_REALM "Web UI Access"
+
 class Preferences {
 public:
   // General options
@@ -708,9 +710,10 @@
     if(current_pass_md5 == new_password) return;
     // Encode to md5 and save
     QCryptographicHash md5(QCryptographicHash::Md5);
+    md5.addData(getWebUiUsername().toLocal8Bit()+":"+QBT_REALM+":");
     md5.addData(new_password.toLocal8Bit());
     QSettings settings("qBittorrent", "qBittorrent");
-    settings.setValue("Preferences/WebUI/Password_md5", md5.result().toHex());
+    settings.setValue("Preferences/WebUI/Password_ha1", md5.result().toHex());
   }
 
   static QString getWebUiPassword() {
@@ -720,18 +723,20 @@
       QString clear_pass = settings.value("Preferences/WebUI/Password", "adminadmin").toString();
       settings.remove("Preferences/WebUI/Password");
       QCryptographicHash md5(QCryptographicHash::Md5);
+      md5.addData(getWebUiUsername().toLocal8Bit()+":"+QBT_REALM+":");
       md5.addData(clear_pass.toLocal8Bit());
       QString pass_md5(md5.result().toHex());
-      settings.setValue("Preferences/WebUI/Password_md5", pass_md5);
+      settings.setValue("Preferences/WebUI/Password_ha1", pass_md5);
       return pass_md5;
     }
-    QString pass_md5 = settings.value("Preferences/WebUI/Password_md5", "").toString();
-    if(pass_md5.isEmpty()) {
+    QString pass_ha1 = settings.value("Preferences/WebUI/Password_ha1", "").toString();
+    if(pass_ha1.isEmpty()) {
       QCryptographicHash md5(QCryptographicHash::Md5);
+      md5.addData(getWebUiUsername().toLocal8Bit()+":"+QBT_REALM+":");
       md5.addData("adminadmin");
-      pass_md5 = md5.result().toHex();
+      pass_ha1 = md5.result().toHex();
     }
-    return pass_md5;
+    return pass_ha1;
   }
 
 };


Index: qbittorrent.spec
===================================================================
RCS file: /cvs/pkgs/rpms/qbittorrent/devel/qbittorrent.spec,v
retrieving revision 1.70
retrieving revision 1.71
diff -u -p -r1.70 -r1.71
--- qbittorrent.spec	14 Jan 2010 22:17:49 -0000	1.70
+++ qbittorrent.spec	15 Jan 2010 16:16:42 -0000	1.71
@@ -3,7 +3,7 @@
 Name:    qbittorrent
 Summary: A Bittorrent Client 
 Version: 2.1.0
-Release: 0.11.rc7%{?dist}
+Release: 0.12.rc7%{?dist}
 # The source for this package was pulled from upstream's vcs.  Use the
 # following commands to generate the tarball:
 # svn checkout -r %{svn_rev} https://qbittorrent.svn.sourceforge.net/svnroot/qbittorrent/trunk qbittorrent
@@ -15,6 +15,12 @@ Source0: http://downloads.sf.net/qbittor
 # https://bugzilla.redhat.com/show_bug.cgi?id=548491
 Patch1:  gcc.patch
 
+# Upstream patches
+
+# svn commit 3386 
+# Use HTTP digest mode for Web UI authentication (instead of Basic)
+Patch2:  HTTP_digest_mode_Web_UI_authentication.patch
+
 URL: http://sourceforge.net/projects/qbittorrent
 Group: Applications/Internet
 BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
@@ -44,6 +50,7 @@ It aims to be as fast as possible and to
 %prep
 %setup -q -n %{name}-%{version}rc7
 %patch1 -p1 -b .gcc
+%patch2 -p1 -b .HTTP_digest_mode_Web_UI_authentication
 
 %build
 # use ./configure instead of %%configure as it doesn't work
@@ -107,6 +114,9 @@ gtk-update-icon-cache %{_datadir}/icons/
 
 
 %changelog
+* Thu Jan 15 2010 Leigh Scott <leigh123linux at googlemail.com> - 2.1.0-0.12.rc7
+- add patch to use HTTP digest mode for Web UI authentication
+
 * Thu Jan 14 2010 Leigh Scott <leigh123linux at googlemail.com> - 2.1.0-0.11.rc7
 - update to 2.1.0rc7
 



More information about the scm-commits mailing list