#include "mainwindow.h"
#include "keygen.h"
+#include "webrequest.h"
+#include "overview.h"
#include <QHttp>
#include <QFile>
#include <QPushButton>
#include <QSettings>
#include <QInputDialog>
+#include <QMessageBox>
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;
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);
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()
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("");
}
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());
}
{
//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);
}
class QCheckBox;
class QSpinBox;
+/**login and profile configuration window*/
class MMainWindow:public QMainWindow
{
Q_OBJECT
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;
void saveProfile();
void newProfile();
void startLogin();
- void loginStage2(int,bool);
-
- //request handling
- void startRequest(QString,QByteArray,char*);
};
#endif
--- /dev/null
+//
+// 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
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 \
--- /dev/null
+//
+// 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;
+}
--- /dev/null
+//
+// 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
--- /dev/null
+<?
+//
+// 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
--- /dev/null
+<?
+//
+// 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
--- /dev/null
+<?
+//
+// 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
/**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);
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)
{
}
/**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)."'";
}
mysql_query($this->sqlUpdate($table,$values,$where));
}
+ public function deleteRows($table,$where)
+ {
+ mysql_query($this->sqlDelete($table,$where));
+ }
+
public function lastError()
{
return mysql_error();
--- /dev/null
+<?
+//
+// 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
// 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
<?
//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)
$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)){
// 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();
}
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");