can login now
authorkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Fri, 14 Sep 2007 14:28:29 +0000 (14:28 +0000)
committerkonrad <konrad@6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33>
Fri, 14 Sep 2007 14:28:29 +0000 (14:28 +0000)
git-svn-id: https://silmor.de/svn/softmagic/smoke/trunk@22 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33

14 files changed:
src/mainwindow.cpp
src/mainwindow.h
src/overview.h [new file with mode: 0644]
src/smoke.pro
src/webrequest.cpp [new file with mode: 0644]
src/webrequest.h [new file with mode: 0644]
www/inc/cauth_hash.php [new file with mode: 0644]
www/inc/cauth_mhash.php [new file with mode: 0644]
www/inc/cauth_string.php [new file with mode: 0644]
www/inc/db.php
www/inc/db_mysql.php
www/inc/random.php [new file with mode: 0644]
www/inc/session.php
www/machine.php

index 49fc519..fa306ab 100644 (file)
@@ -12,6 +12,8 @@
 
 #include "mainwindow.h"
 #include "keygen.h"
+#include "webrequest.h"
+#include "overview.h"
 
 #include <QHttp>
 #include <QFile>
@@ -29,6 +31,7 @@
 #include <QPushButton>
 #include <QSettings>
 #include <QInputDialog>
+#include <QMessageBox>
 
 MMainWindow::MMainWindow()
 {
@@ -36,15 +39,15 @@ MMainWindow::MMainWindow()
        //create Menu Bar
        QMenuBar*mb=menuBar();
        QMenu*m=mb->addMenu("&File");
-       m->addAction("&Test",this,SLOT(testrequest()));
+       m->addAction("&New Profile...",this,SLOT(newProfile()));
+       m->addAction("&Save Profile",this,SLOT(saveProfile()));
        m->addSeparator();
-       m->addAction("&Close Session",this,SLOT(close()));
+       m->addAction("&Close Window",this,SLOT(close()));
+       m=mb->addMenu("&Configure");
        
-       //create central stack
-       setCentralWidget(main=new QStackedWidget);
-       
-       //create login widget
-       main->addWidget(loginwidget=new QWidget);
+       //create central widget
+       QWidget *loginwidget;
+       setCentralWidget(loginwidget=new QWidget);
        QGridLayout*gl;
        loginwidget->setLayout(gl=new QGridLayout);
        QLabel*lab;
@@ -68,6 +71,15 @@ MMainWindow::MMainWindow()
        proxyport->setRange(1,65535);
        connect(useproxy,SIGNAL(toggled(bool)),proxyname,SLOT(setEnabled(bool)));
        connect(useproxy,SIGNAL(toggled(bool)),proxyport,SLOT(setEnabled(bool)));
+       gl->addWidget(lab=new QLabel(tr("Proxy Username:")),++lctr,0);
+       lab->setAlignment(Qt::AlignRight);
+       gl->addWidget(proxyuser=new QLineEdit,lctr,1);
+       gl->addWidget(lab=new QLabel(tr("Proxy Password:")),++lctr,0);
+       lab->setAlignment(Qt::AlignRight);
+       gl->addWidget(proxypass=new QLineEdit,lctr,1);
+       password->setEchoMode(QLineEdit::Password);
+       connect(useproxy,SIGNAL(toggled(bool)),proxyuser,SLOT(setEnabled(bool)));
+       connect(useproxy,SIGNAL(toggled(bool)),proxypass,SLOT(setEnabled(bool)));
        QFrame*frm;
        gl->addWidget(frm=new QFrame,++lctr,0,1,2);
        frm->setFrameShape(QFrame::HLine);
@@ -83,18 +95,14 @@ MMainWindow::MMainWindow()
        QPushButton*p;
        hl->addWidget(p=new QPushButton("new Profile"),0);
        connect(p,SIGNAL(clicked()),this,SLOT(newProfile()));
+       hl->addWidget(p=new QPushButton("save Profile"),0);
+       connect(p,SIGNAL(clicked()),this,SLOT(saveProfile()));
        hl->addStretch(10);
        hl->addWidget(p=new QPushButton("Login"),0);
        connect(p,SIGNAL(clicked()),this,SLOT(saveProfile()));
        connect(p,SIGNAL(clicked()),this,SLOT(startLogin()));
-       hl->addWidget(p=new QPushButton("Exit"),0);
-       connect(p,SIGNAL(clicked()),this,SLOT(close()));
        initProfiles();
        loadProfile();
-       
-       //make login the default
-       mb->setEnabled(false);
-       main->setCurrentWidget(loginwidget);
 }
 
 void MMainWindow::initProfiles()
@@ -120,8 +128,12 @@ void MMainWindow::loadProfile()
        useproxy->setChecked(set.value("useproxy",false).toBool());
        proxyname->setText(set.value("proxyname","proxy").toString());
        proxyport->setValue(set.value("proxyport",74).toInt());
+       proxyname->setText(set.value("proxyuser").toString());
+       proxyname->setText(set.value("proxypass").toString());
        proxyname->setEnabled(useproxy->isChecked());
        proxyport->setEnabled(useproxy->isChecked());
+       proxyuser->setEnabled(useproxy->isChecked());
+       proxypass->setEnabled(useproxy->isChecked());
        username->setText(set.value("username").toString());
        password->setText("");
 }
@@ -137,6 +149,8 @@ void MMainWindow::saveProfile()
        set.setValue("useproxy",useproxy->isChecked());
        set.setValue("proxyname",proxyname->text());
        set.setValue("proxyport",proxyport->value());
+       set.setValue("proxyuser",proxyuser->text());
+       set.setValue("proxypass",proxypass->text());
        set.setValue("username",username->text());
 }
 
@@ -175,36 +189,25 @@ void MMainWindow::startLogin()
 {
        //make it impossible for the user to interfere
        setEnabled(false);
+       //create request object
+       MWebRequest *mw=new MWebRequest;
+       mw->setUrl(serverurl->text());
+       if(useproxy->isChecked())
+               mw->setProxy(proxyname->text(),proxyport->value(),proxyuser->text(),proxypass->text());
        //start request
-       startRequest("startsession",getRandom(32).toHex(),SLOT(loginStage2(int,bool)));
-}
-
-void MMainWindow::startRequest(QString hreq,QByteArray data,char*myslot)
-{
-       req=new QHttp("localhost");
-       connect(req,SIGNAL(requestFinished(int,bool)),this,myslot);
-       QHttpRequestHeader hrh("POST","/~konrad/smoke/machine.php");
-       hrh.setValue("Host","localhost");
-       hrh.setValue("X-MagicSmoke-Request",hreq);
-       hrh.setValue("X-MagicSmoke-Session",sessionid);
-       hrh.setContentLength(data.size());
-       hrh.setContentType("application/x-magicsmoke");
-       qDebug("Requesting data with:\n%s",hrh.toString().toAscii().data());
-       req->request(hrh,data);
-}
-
-void MMainWindow::loginStage2(int id,bool err)
-{
-       qDebug("Received Request finish for ID=%i Status=%s",id,err?"Error":"Ok");
-       if(err){
-               qDebug("HTTP Error: %s",req->errorString().toAscii().data());
-       }else{
-               QHttpResponseHeader hrh=req->lastResponse();
-               QByteArray data=req->readAll();
-               qDebug("HTTP Response Headers:\n%s",hrh.toString().toAscii().data());
-               qDebug("HTTP Response Body:\n%s\n<-->",data.data());
+       QString hn;
+       if(usealterhost->isChecked())hn=alterhostname->text();
+       else hn=QSettings().value("hostname").toString();
+       if(!mw->login(username->text(),password->text(),hn)){
+               QMessageBox::warning(this,tr("Warning"),tr("Unable to log in. Error: %1").arg(mw->errorString()));
+               mw->deleteLater();
+               setEnabled(true);
+               return;
        }
-       req->disconnect();
-       req->deleteLater();
+       //open window
+       MOverview *mo=new MOverview;
+       mw->setParent(mo);
+       mo->show();
+       
        setEnabled(true);
 }
index 272f605..f4091ea 100644 (file)
@@ -24,6 +24,7 @@ class QLineEdit;
 class QCheckBox;
 class QSpinBox;
 
+/**login and profile configuration window*/
 class MMainWindow:public QMainWindow
 {
        Q_OBJECT
@@ -32,10 +33,8 @@ class MMainWindow:public QMainWindow
        private:
                QHttp*req;
                QString sessionid;
-               QStackedWidget*main;
-               QWidget*loginwidget;
                QCheckBox*usealterhost,*useproxy;
-               QLineEdit*alterhostname,*serverurl,*proxyname,*username,*password;
+               QLineEdit*alterhostname,*serverurl,*proxyname,*username,*password,*proxyuser,*proxypass;
                QSpinBox*proxyport;
                QComboBox*profiles;
                
@@ -46,10 +45,6 @@ class MMainWindow:public QMainWindow
                void saveProfile();
                void newProfile();
                void startLogin();
-               void loginStage2(int,bool);
-               
-               //request handling
-               void startRequest(QString,QByteArray,char*);
 };
 
 #endif
diff --git a/src/overview.h b/src/overview.h
new file mode 100644 (file)
index 0000000..f410bed
--- /dev/null
@@ -0,0 +1,22 @@
+//
+// C++ Interface: overview
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_OVERVIEW_H
+#define MAGICSMOKE_OVERVIEW_H
+
+#include <QMainWindow>
+
+class MOverview:public QMainWindow
+{
+};
+
+#endif
index 6fbdb6e..a76239b 100644 (file)
@@ -19,11 +19,13 @@ SOURCES = \
        keygen.cpp \
        mainwindow.cpp \
        hmac.cpp \
-       code39.cpp
+       code39.cpp \
+       webrequest.cpp
 HEADERS = \
        keygen.h \
        mainwindow.h \
-       hmac.h
+       hmac.h \
+       webrequest.h
 
 TRANSLATIONS = \
        smoke_de.ts \
diff --git a/src/webrequest.cpp b/src/webrequest.cpp
new file mode 100644 (file)
index 0000000..16a96ad
--- /dev/null
@@ -0,0 +1,226 @@
+//
+// C++ Implementation: webrequest
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#include "webrequest.h"
+#include "keygen.h"
+#include "hmac.h"
+
+#include <QEventLoop>
+#include <QTimer>
+#include <QDomDocument>
+#include <QDomNodeList>
+#include <QDomElement>
+#include <QSettings>
+
+MWebRequest::MWebRequest()
+{
+       webtimeout=30000;
+       finid=0;finerr=fin=false;
+       proxyport=-1;
+       connect(&req,SIGNAL(requestFinished(int,bool)),this,SLOT(httpFin(int,bool)));
+}
+MWebRequest::~MWebRequest(){}
+
+bool MWebRequest::request(QString hreq,QByteArray data)
+{
+       //set up request
+       QEventLoop loop(this);
+       connect(this,SIGNAL(requestFinInt()),&loop,SLOT(quit()));
+       int port=url.port();
+       if(port<=0){
+               if(url.scheme().toLower()=="http")port=80;
+               else port=443;
+       }
+       qDebug("connecting to host=%s port=%i scheme=%s",url.host().toAscii().data(),(int)port,url.scheme().toAscii().data());
+       QHttp::ConnectionMode conm;
+       if(url.scheme().toLower()=="http")conm=QHttp::ConnectionModeHttp;
+       else conm=QHttp::ConnectionModeHttps;
+       req.setHost(url.host(),conm,port);
+       if(proxyport>0)req.setProxy(proxyname,proxyport,proxyuser,proxypass);
+       QHttpRequestHeader hrh("POST","/~konrad/smoke/machine.php");
+       hrh.setValue("Host","localhost");
+       hrh.setValue("X-MagicSmoke-Request",hreq);
+       hrh.setValue("X-MagicSmoke-Session",sessionid);
+       hrh.setContentLength(data.size());
+       hrh.setContentType("application/x-magicsmoke");
+       qDebug("Requesting data with:\n%s",hrh.toString().toAscii().data());
+       waitid=req.request(hrh,data);
+       qDebug("started req %i",waitid);
+       fin=false;errstr="";
+       rspstatus="";rspdata.clear();
+       //start loop
+       QTimer::singleShot(webtimeout,&loop,SLOT(quit()));
+       loop.exec();
+       //process result
+       if(!fin){
+               //it did not finish yet, caught a timeout.
+               req.abort();
+               errstr="Web Request timed out.";
+               return false;
+       }
+       qDebug("Received Request finish for ID=%i Status=%s",finid,finerr?"Error":"Ok");
+       //finished with low-level error?
+       if(finerr){
+               errstr="HTTP Error: "+req.errorString();
+               qDebug("%s",errstr.toAscii().data());
+               return false;
+       }
+       QHttpResponseHeader rsph=req.lastResponse();
+       //check for high level error
+       if(rsph.statusCode()!=200){
+               errstr="HTTP Error, return code "+QString::number(rsph.statusCode())+" "+rsph.reasonPhrase();
+               return false;
+       }
+       //get data
+       rspdata=req.readAll();
+       rspstatus=rsph.value("x-magicsmoke-status").remove('"').trimmed();
+       //
+       qDebug("HTTP Response Headers:\n%s",rsph.toString().toAscii().data());
+       qDebug("HTTP Response Body:\n%s\n<-->",rspdata.data());
+       return true;
+}
+
+void MWebRequest::setTimeout(int t)
+{
+       if(t>0)
+               webtimeout=t;
+}
+
+bool MWebRequest::setUrl(QUrl u)
+{
+       QString s=u.scheme().toLower();
+       if(s!="http"&&s!="https")return false;
+       url=u;
+       return true;
+}
+
+void MWebRequest::httpFin(int i,bool e)
+{
+       qDebug("finished req %i",i);
+       if(i!=waitid)return;
+       finid=i;finerr=e;fin=true;
+       emit requestFinInt();
+}
+
+void MWebRequest::setProxy(QString h,quint16 p,QString u,QString pw)
+{
+       proxyname=h;proxyport=p;
+       proxyuser=u;proxypass=pw;
+}
+
+static inline QString calcAuth(QString algo,QString cha,QString pwd)
+{
+       QString ret;
+       if(algo=="md5")
+               return QCryptographicHash::hash(cha.toUtf8()+pwd.toUtf8(),QCryptographicHash::Md5).toHex();
+       if(algo=="sha1")
+               return QCryptographicHash::hash(cha.toUtf8()+pwd.toUtf8(),QCryptographicHash::Sha1).toHex();
+       if(algo=="hmac-md5")
+               return SMHmac::hmac(cha.toUtf8(),pwd.toUtf8(),QCryptographicHash::Md5).toHex();
+       if(algo=="hmac-sha1")
+               return SMHmac::hmac(cha.toUtf8(),pwd.toUtf8(),QCryptographicHash::Sha1).toHex();
+       //unreachable
+       return QByteArray();
+}
+
+bool MWebRequest::login(QString usr,QString pwd,QString hostname)
+{
+       //get authentication algo
+       if(!request("serverinfo"))return false;
+       if(rspstatus.toLower()!="ok"){
+               errstr=tr("Unable to get server info.");
+               return false;
+       }
+       QString algo;
+       QDomDocument doc;
+       QString err;int ln,cl;
+       if(!doc.setContent(rspdata,&err,&ln,&cl)){
+               errstr=tr("Error while parsing server info (line %1 col %2): %3").arg(ln).arg(cl).arg(err);
+               return false;
+       }
+       QDomNodeList nl=doc.documentElement().elementsByTagName("AuthAlgorithm");
+       if(nl.size()!=1){
+               errstr=tr("Error in server info: missing authentication algorithm info.");
+               return false;
+       }
+       algo=nl.at(0).toElement().text().trimmed().toLower();
+       if(algo!="md5"&&algo!="sha1"&&algo!="hmac-md5"&&algo!="hmac-sha1"){
+               errstr=tr("The server requested an unsupported hash algorithm: %1.").arg(algo);
+               return false;
+       }
+       //get challenge
+       if(!request("startsession",getRandom(32).toHex()))return false;
+       if(rspstatus.toLower()!="ok"){
+               errstr=tr("Unable to get authentication challenge.");
+               return false;
+       }
+       if(!doc.setContent(rspdata,&err,&ln,&cl)){
+               errstr=tr("Error while parsing session challenge (line %1 col %2): %3").arg(ln).arg(cl).arg(err);
+               return false;
+       }
+       QDomElement del=doc.documentElement();
+       nl=del.elementsByTagName("ID");
+       if(nl.size()!=1){
+               errstr=tr("Error in session challenge: missing session ID.");
+               return false;
+       }
+       sessionid=nl.at(0).toElement().text().trimmed();
+       nl=del.elementsByTagName("HostChallenge");
+       if(nl.size()!=1){
+               errstr=tr("Error in session challenge: missing host challenge.");
+               return false;
+       }
+       QString hcha=nl.at(0).toElement().text().trimmed();
+       nl=del.elementsByTagName("UserChallenge");
+       if(nl.size()!=1){
+               errstr=tr("Error in session challenge: missing user challenge.");
+               return false;
+       }
+       QString ucha=nl.at(0).toElement().text().trimmed();
+       //calculate auth
+       hcha=calcAuth(algo,hcha,QSettings().value("hostkey").toString());
+       ucha=calcAuth(algo,ucha,pwd);
+       //create auth msg
+       QDomDocument adoc("Authenticate");
+       del=adoc.createElement("SessionAuth");
+       QDomElement el=adoc.createElement("HostName");
+       el.appendChild(adoc.createTextNode(hostname));
+       del.appendChild(el);
+       el=adoc.createElement("HostAuth");
+       el.appendChild(adoc.createTextNode(hcha));
+       del.appendChild(el);
+       el=adoc.createElement("UserName");
+       el.appendChild(adoc.createTextNode(usr));
+       del.appendChild(el);
+       el=adoc.createElement("UserAuth");
+       el.appendChild(adoc.createTextNode(ucha));
+       del.appendChild(el);
+       adoc.appendChild(del);
+       //authenticate
+       if(!request("sessionauth",adoc.toByteArray()))return false;
+       if(rspstatus.toLower()=="unauthenticated"){
+               errstr=tr("Failed to log in: user/password mismatch, non-allowed host key, or challenge timed out.");
+               return false;
+       }
+       if(rspstatus.toLower()!="ok"){
+               errstr=tr("Unable to authenticate.");
+               return false;
+       }
+       //TODO: store session timeout and do something useful with it
+       //all ok
+       return true;
+}
+
+QString MWebRequest::errorString()
+{
+       return errstr;
+}
diff --git a/src/webrequest.h b/src/webrequest.h
new file mode 100644 (file)
index 0000000..1418b3d
--- /dev/null
@@ -0,0 +1,64 @@
+//
+// C++ Interface: webrequest
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+#ifndef MAGICSMOKE_WEBREQUEST_H
+#define MAGICSMOKE_WEBREQUEST_H
+
+#include <QObject>
+#include <QString>
+#include <QByteArray>
+#include <QPointer>
+#include <QHttp>
+#include <QUrl>
+
+class MWebRequest:public QObject
+{
+       Q_OBJECT
+       public:
+               MWebRequest();
+               ~MWebRequest();
+               
+               bool request(QString,QByteArray a=QByteArray());
+               
+               QString errorString();
+               
+       public slots:
+               void setTimeout(int);
+               bool setUrl(QUrl);
+               void setProxy(QString,quint16,QString u=QString(),QString p=QString());
+               
+               bool login(QString user,QString passwd,QString hostname);
+               
+       private slots:
+               void httpFin(int,bool);
+               
+       signals:
+               void requestFinInt();
+               
+       private:
+               //central request object
+               QHttp req;
+               QString proxyname,proxyuser,proxypass;
+               QUrl url;
+               int webtimeout,proxyport;
+               //login data
+               QString user,passwd,sessionid,hostname;
+               //error data
+               QString errstr;
+               bool finerr,fin;
+               //response data
+               QString rspstatus;
+               QByteArray rspdata;
+               int finid,waitid;
+};
+
+#endif
diff --git a/www/inc/cauth_hash.php b/www/inc/cauth_hash.php
new file mode 100644 (file)
index 0000000..a6eedbb
--- /dev/null
@@ -0,0 +1,28 @@
+<?
+//
+// PHP Implementation: cauth_hash
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+function calcAuth($cha,$tok)
+{
+       global $ClientAuthAlgo;
+       switch($ClientAuthAlgo){
+               case "md5":
+               case "sha1":
+               case "sha256":return hash($ClientAuthAlgo,$cha.$tok);
+               case "hmac-md5":return hash_hmac("md5",$cha,$tok);
+               case "hmac-sha1":return hash_hmac("sha1",$cha,$tok);
+               case "hmac-sha256":return hash_hmac("sha256",$cha,$tok);
+               default:trigger_error("Internal error: unknown hash algorithm",E_USER_ERROR);
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/www/inc/cauth_mhash.php b/www/inc/cauth_mhash.php
new file mode 100644 (file)
index 0000000..f20dcf2
--- /dev/null
@@ -0,0 +1,28 @@
+<?
+//
+// PHP Implementation: cauth_mhash
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+function calcAuth($cha,$tok)
+{
+       global $ClientAuthAlgo;
+       switch($ClientAuthAlgo){
+               case "md5":return mhash(MHASH_MD5,$cha.$tok);
+               case "sha1":return mhash(MHASH_SHA1,$cha.$tok);
+               case "sha256":return mhash(MHASH_SHA256,$cha.$tok);
+               case "hmac-md5":return mhash(MHASH_MD5,$cha,$tok);
+               case "hmac-sha1":return mhash(MHASH_SHA1,$cha,$tok);
+               case "hmac-sha256":return mhash(MHASH_SHA256,$cha,$tok);
+               default:trigger_error("Internal error: unknown hash algorithm",E_USER_ERROR);
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/www/inc/cauth_string.php b/www/inc/cauth_string.php
new file mode 100644 (file)
index 0000000..bcb2fbc
--- /dev/null
@@ -0,0 +1,24 @@
+<?
+//
+// PHP Implementation: cauth_string
+//
+// Description: 
+//
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+function calcAuth($key,$tok)
+{
+       global $ClientAuthAlgo;
+       switch($ClientAuthAlgo){
+               case "md5":return strtolower(md5($key.$tok));
+               case "sha1":return strtolower(sha1($key.$tok));
+               default:trigger_error("Internal error: unknown hash algorithm",E_USER_ERROR);
+       }
+}
+
+?>
\ No newline at end of file
index 00b4495..453b46e 100644 (file)
@@ -59,6 +59,9 @@ abstract class DbEngine
        /**update database values*/
        public abstract function update($table,array $values,$where);
        
+       /**delete database values*/
+       public abstract function deleteRows($table,$where);
+       
        /**creates a table; the argument is an array of the form "col-name" => array("col-type", "flags"...); use sqlCreateTable() etc. to create the actual statement*/
        protected abstract function createTable($tablename,$table);
        
@@ -153,6 +156,12 @@ abstract class DbEngine
                return $ret;
        }
        
+       /**creates a SQL92 statement for deletes*/
+       protected function sqlDelete($table,$where)
+       {
+               return "DELETE FROM ".$this->tableName($table)." WHERE ".$where;
+       }
+       
        /**creates a SQL92 statement for updates*/
        protected function sqlUpdate($table,array $values,$where)
        {
@@ -179,13 +188,13 @@ abstract class DbEngine
        }
        
        /**escapes integers; the default implementation just makes sure it is an int*/
-       protected function escapeInt($i)
+       public function escapeInt($i)
        {
                return $i + 0;
        }
        
        /**escapes strings; the default uses addslashes and encloses the value in ''*/
-       protected function escapeString($s)
+       public function escapeString($s)
        {
                return "'".addslashes($s)."'";
        }
index 1ed42f4..a2553ef 100644 (file)
@@ -125,6 +125,11 @@ class MysqlEngine extends DbEngine
                mysql_query($this->sqlUpdate($table,$values,$where));
        }
        
+       public function deleteRows($table,$where)
+       {
+               mysql_query($this->sqlDelete($table,$where));
+       }
+       
        public function lastError()
        {
                return mysql_error();
diff --git a/www/inc/random.php b/www/inc/random.php
new file mode 100644 (file)
index 0000000..2e13e02
--- /dev/null
@@ -0,0 +1,45 @@
+<?
+//
+// PHP Implementation: random
+//
+// Description: internal Random Number Generator
+//
+// The algorithm used here should be safe enough for the purpose, but definitely could be better.
+//
+// Author: Konrad Rosenbaum <konrad@silmor.de>, (C) 2007
+//
+// Copyright: See README/COPYING files that come with this distribution
+//
+//
+
+//get current random seed
+$RANDSEED=$db->getConfig("randseed");
+
+/**add some seed into the random function*/
+function randseed($rand)
+{
+       global $RANDSEED;
+       $RANDSEED.=$rand;
+}
+
+/**return $bits bits of random data*/
+function getRandom($bits)
+{
+       //number of digits...
+       $bits/=4;
+       //init
+       global $RANDSEED,$db;
+       $ret="";$ctr=0;
+       //get string
+       while(strlen($ret)<$bits){
+               $ctr++;
+               $ret.=sha1($RANDSEED.microtime().$ctr);
+       }
+       //rewrite seed to DB
+       $RANDSEED=sha1($RANDSEED.microtime().$ret);
+       $db->setConfig("randseed",$RANDSEED);
+       //return
+       return substr($ret,0,$bits);
+}
+
+?>
\ No newline at end of file
index 6297a28..3311ce3 100644 (file)
 // Copyright: See README/COPYING files that come with this distribution
 //
 //
+
+//prune session cache
+$db->deleteRows("session","timeout<=".time());
+
+/**initiate new session*/
+function newSession($rand)
+{
+       global $db,$ClientAuthTimeout;
+       //get random bits
+       randseed($rand);
+       $sid=getRandom(128);
+       $ucha=getRandom(128);
+       $hcha=getRandom(128);
+       //try to create entry
+       $db->beginTransaction();
+       while(1){
+               //check for existence
+               $res=$db->select("session","sessionid","sessionid='".$sid."'");
+               if(count($res)==0)break;
+               //create new SID and repeat
+               $sid=getRandom(128);
+       }
+       $ret=array("sessionid"=>$sid,"uchallenge"=>$ucha,"hchallenge"=>$hcha,"user"=>"","timeout"=>time()+$ClientAuthTimeout);
+       $db->insert("session",$ret);
+       $db->commitTransaction();
+       return $ret;
+}
+
+/**delete current session*/
+function deleteSession()
+{
+       global $_SERVER,$db;
+       if(isset($_SERVER["HTTP_X_MAGICSMOKE_SESSION"]))
+               $db->deleteRows("session","sessionid=".$db->escapeString($_SERVER["HTTP_X_MAGICSMOKE_SESSION"]));
+}
+
+/**The session class*/
+class Session
+{
+       private $sessid="";
+       private $user="";
+       
+       public function __construct()
+       {
+               global $_SERVER,$db;
+               if(isset($_SERVER["HTTP_X_MAGICSMOKE_SESSION"])){
+                       $res=$db->select("session","sessionid,user","sessionid=".$db->escapeString($_SERVER["HTTP_X_MAGICSMOKE_SESSION"]));
+                       if(count($res)>0){
+                               $this->sessid=$_SERVER["HTTP_X_MAGICSMOKE_SESSION"];
+                               $this->user=$res[0]["user"];
+                       }
+               }
+       }
+       
+       public function isValid()
+       {
+               return $this->sessid!="";
+       }
+       
+       public function isAuthenticated()
+       {
+               return $this->user!="";
+       }
+       
+       public function authenticate()
+       {
+               global $db,$REQUESTDATA;
+               //already authenticated?
+               if($this->isAuthenticated()){
+                       header("X-MagicSmoke-Status: Error");
+                       die("Protocol violation: already authenticated.");
+               }
+               //get DB record:session
+               $sres=$db->select("session","*","sessionid=".$db->escapeString($this->sessid));
+               if(count($sres)<1){
+                       header("X-MagicSmoke-Status: Unauthenticated");
+                       deleteSession();
+                       die("No such session");
+               }
+               //parse request
+               $auth=new DOMDocument;
+               if(!$auth->loadXML($REQUESTDATA)){
+                       header("X-MagicSmoke-Status: SyntaxError");
+                       deleteSession();
+                       die("unable to parse XML data");
+               }
+               $hostname="";$hostauth="";$username="";$userauth="";
+               foreach($auth->getElementsByTagName("HostName") as $el)
+                       foreach($el->childNodes as $cn)
+                               if($cn->nodeType==XML_TEXT_NODE)
+                                       $hostname=$cn->wholeText;
+               foreach($auth->getElementsByTagName("HostAuth") as $el)
+                       foreach($el->childNodes as $cn)
+                               if($cn->nodeType==XML_TEXT_NODE)
+                                       $hostauth=$cn->wholeText;
+               foreach($auth->getElementsByTagName("UserName") as $el)
+                       foreach($el->childNodes as $cn)
+                               if($cn->nodeType==XML_TEXT_NODE)
+                                       $username=$cn->wholeText;
+               foreach($auth->getElementsByTagName("UserAuth") as $el)
+                       foreach($el->childNodes as $cn)
+                               if($cn->nodeType==XML_TEXT_NODE)
+                                       $userauth=$cn->wholeText;
+               if($hostname=="" || $hostauth=="" || $username=="" || $userauth==""){
+                       header("X-MagicSmoke-Status: SyntaxError");
+                       deleteSession();
+                       die("missing some authentication data");
+               }
+               //get user data
+               $ures=$db->select("users","*","uname=".$db->escapeString($username));
+               if(count($ures)<1){
+                       header("X-MagicSmoke-Status: Unauthenticated");
+                       deleteSession();
+                       die("No such user");
+               }
+               //get allowed hosts
+               $uhres=$db->select("userhosts","host","uname=".$db->escapeString($username));
+               $hres=$db->select("host","*","hostname=".$db->escapeString($hostname));
+               $hosts=array();
+               foreach($uhres as $hst)
+                       $hosts[]=$hst["host"];
+               //check that host is allowed
+               $needhostauth=true;
+               if(in_array("_anon",$hosts)){
+                       //anonymous hosts allowed, ignore host auth
+                       $needhostauth=false;
+               }else
+               if(in_array("_any",$hosts)){
+                       //any host allowed, check it exists
+                       if(count($hres)<1){
+                               header("X-MagicSmoke-Status: Unauthenticated");
+                               deleteSession();
+                               die("unknown host");
+                       }
+               }else{
+                       //check whether allowed
+                       if(!in_array($hostname,$hosts)){
+                               //host name not in allowed list
+                               header("X-MagicSmoke-Status: Unauthenticated");
+                               deleteSession();
+                               die("host not allowed");
+                       }
+                       //check whether exists
+                       if(count($hres)<1){
+                               header("X-MagicSmoke-Status: Unauthenticated");
+                               deleteSession();
+                               die("No such host");
+                       }
+               }
+               //compare
+               $ua=calcAuth($sres[0]["uchallenge"],$ures[0]["passwd"]);
+               if($ua!=$userauth){
+                       header("X-MagicSmoke-Status: Unauthenticated");
+                       deleteSession();
+                       die("Challenge failed $ua vs $userauth");
+               }
+               if($needhostauth){
+                       $ha=calcAuth($sres[0]["hchallenge"],$hres[0]["hostkey"]);
+                       if($ha!=$hostauth){
+                               header("X-MagicSmoke-Status: Unauthenticated");
+                               deleteSession();
+                               die("challenge failed");
+                       }
+               }
+               //success
+               header("X-MagicSmoke-Status: Ok");
+               global $ClientSessionTimeout;
+               $tout=time()+$ClientSessionTimeout;
+               $db->update("session",array("user"=>$username,"timeout"=>$tout),"sessionid=".$db->escapeString($this->sessid));
+               echo $tout;
+       }
+       
+};
+
+include("cauth_".$HashLib.".php");
+
 ?>
\ No newline at end of file
index 5dbf19d..06e1474 100644 (file)
@@ -1,10 +1,12 @@
 <?
 //check the HTTP-request type
 if($_SERVER["REQUEST_METHOD"] != "POST" || !isset($_SERVER["HTTP_X_MAGICSMOKE_REQUEST"])){
-       header("X-MagicSmoke-Status","NonPost");
+       header("X-MagicSmoke-Status: NonPost");
        print("<html><title>Error</title><body>This is the machine interface of Magic Smoke. Other clients and browsers are not allowed.</html>");
        exit();
 }
+//fix content-type to something that is not manipulated by proxies
+header("Content-Type: application/x-MagicSmoke");
 
 //check whether the request is known
 // all valid requests must be listed here (in lower case)
@@ -16,7 +18,7 @@ $ALLOWEDREQUESTS=array(
 $SMOKEREQUEST=strtolower($_SERVER["HTTP_X_MAGICSMOKE_REQUEST"]);
 if(!in_array($SMOKEREQUEST,$ALLOWEDREQUESTS)){
        header("X-MagicSmoke-Status: InvalidRequest");
-       exit();
+       die("Invalid Request, please use the MagicSmoke Client with this page.");
 }
 $REQUESTDATA="";
 if(isset($HTTP_RAW_POST_DATA)){
@@ -29,7 +31,7 @@ include("loader.php");
 // server info can be answered without performing any more initialization
 if($SMOKEREQUEST=="serverinfo"){
        header("X-MagicSmoke-Status: Ok");
-       print("<Info>\n <ServerVersion proto=\"0000 0000\">$MAGICSMOKEVERSION</ServerVersion\n <AuthAlgorithm>$ClientAuthAlgo</AuthAlgorithm>\n</Info>");
+       print("<Info>\n <ServerVersion proto=\"0000 0000\">$MAGICSMOKEVERSION</ServerVersion>\n <AuthAlgorithm>$ClientAuthAlgo</AuthAlgorithm>\n</Info>");
        exit();
 }
 
@@ -37,26 +39,47 @@ if($SMOKEREQUEST=="serverinfo"){
 include("loader_nonadmin.php");
 
 //load machine interface
+include("inc/random.php");
 include("inc/session.php");
 
 // request to start a session
 if($SMOKEREQUEST=="startsession"){
-       //TODO: start session
-       echo "blah session";
+       //start session
+       $sess=newSession($REQUESTDATA);
+       header("X-MagicSmoke-Status: Ok");
+       print("<SessionStart><ID>".$sess["sessionid"]."</ID><HostChallenge>".$sess["hchallenge"].
+       "</HostChallenge><UserChallenge>".$sess["uchallenge"]."</UserChallenge><Timeout>".
+       $sess["timeout"]."</Timeout></SessionStart>");
        exit();
 }
 //request to close a session
 if($SMOKEREQUEST=="closesession"){
-       //TODO: close session
+       //close session
+       deleteSession();
+       //return
        header("X-MagicSmoke-Status: Ok");
        exit();
 }
 
 //all others need a valid session, check it
-//TODO: check session
+//check session
+$session=new Session;
+if(!$session->isValid()){
+       header("X-MagicSmoke-Status: Unauthenticated");
+       die("Invalid or missing sessionid, or session timed out.");
+}
 
 //request session authentication
+if($SMOKEREQUEST=="sessionauth"){
+       $session->authenticate();
+       exit();
+}
 
+//remainder must be authenticated
+if(!$session->isAuthenticated()){
+       header("X-MagicSmoke-Status: Unauthenticated");
+       die("Session not yet authenticated.");
+}
 
 //EOF
 die("Internal Error");