From: konrad Date: Fri, 14 Sep 2007 14:28:29 +0000 (+0000) Subject: can login now X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=90f5c18408c358e8835b170eae154729d830ce9f;p=web%2Fkonrad%2Fsmoke.git can login now git-svn-id: https://silmor.de/svn/softmagic/smoke/trunk@22 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33 --- diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 49fc519..fa306ab 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -12,6 +12,8 @@ #include "mainwindow.h" #include "keygen.h" +#include "webrequest.h" +#include "overview.h" #include #include @@ -29,6 +31,7 @@ #include #include #include +#include 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); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 272f605..f4091ea 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -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 index 0000000..f410bed --- /dev/null +++ b/src/overview.h @@ -0,0 +1,22 @@ +// +// C++ Interface: overview +// +// Description: +// +// +// Author: Konrad Rosenbaum , (C) 2007 +// +// Copyright: See README/COPYING files that come with this distribution +// +// + +#ifndef MAGICSMOKE_OVERVIEW_H +#define MAGICSMOKE_OVERVIEW_H + +#include + +class MOverview:public QMainWindow +{ +}; + +#endif diff --git a/src/smoke.pro b/src/smoke.pro index 6fbdb6e..a76239b 100644 --- a/src/smoke.pro +++ b/src/smoke.pro @@ -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 index 0000000..16a96ad --- /dev/null +++ b/src/webrequest.cpp @@ -0,0 +1,226 @@ +// +// C++ Implementation: webrequest +// +// Description: +// +// +// Author: Konrad Rosenbaum , (C) 2007 +// +// Copyright: See README/COPYING files that come with this distribution +// +// + +#include "webrequest.h" +#include "keygen.h" +#include "hmac.h" + +#include +#include +#include +#include +#include +#include + +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 index 0000000..1418b3d --- /dev/null +++ b/src/webrequest.h @@ -0,0 +1,64 @@ +// +// C++ Interface: webrequest +// +// Description: +// +// +// Author: Konrad Rosenbaum , (C) 2007 +// +// Copyright: See README/COPYING files that come with this distribution +// +// + +#ifndef MAGICSMOKE_WEBREQUEST_H +#define MAGICSMOKE_WEBREQUEST_H + +#include +#include +#include +#include +#include +#include + +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 index 0000000..a6eedbb --- /dev/null +++ b/www/inc/cauth_hash.php @@ -0,0 +1,28 @@ +, (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 index 0000000..f20dcf2 --- /dev/null +++ b/www/inc/cauth_mhash.php @@ -0,0 +1,28 @@ +, (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 index 0000000..bcb2fbc --- /dev/null +++ b/www/inc/cauth_string.php @@ -0,0 +1,24 @@ +, (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 diff --git a/www/inc/db.php b/www/inc/db.php index 00b4495..453b46e 100644 --- a/www/inc/db.php +++ b/www/inc/db.php @@ -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)."'"; } diff --git a/www/inc/db_mysql.php b/www/inc/db_mysql.php index 1ed42f4..a2553ef 100644 --- a/www/inc/db_mysql.php +++ b/www/inc/db_mysql.php @@ -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 index 0000000..2e13e02 --- /dev/null +++ b/www/inc/random.php @@ -0,0 +1,45 @@ +, (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 diff --git a/www/inc/session.php b/www/inc/session.php index 6297a28..3311ce3 100644 --- a/www/inc/session.php +++ b/www/inc/session.php @@ -10,4 +10,180 @@ // 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 diff --git a/www/machine.php b/www/machine.php index 5dbf19d..06e1474 100644 --- a/www/machine.php +++ b/www/machine.php @@ -1,10 +1,12 @@ ErrorThis is the machine interface of Magic Smoke. Other clients and browsers are not allowed."); 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("\n $MAGICSMOKEVERSION$ClientAuthAlgo\n"); + print("\n $MAGICSMOKEVERSION\n $ClientAuthAlgo\n"); 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("".$sess["sessionid"]."".$sess["hchallenge"]. + "".$sess["uchallenge"]."". + $sess["timeout"].""); 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");