Login   Register  
PHP Classes
elePHPant
Icontem

File: multiotp.server.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of André Liechti  >  multiOTP PHP class  >  multiotp.server.php  >  Download  
File: multiotp.server.php
Role: Application script
Content type: text/plain
Description: server component of the client/server feature
Class: multiOTP PHP class
Authenticate and manage OTP strong user tokens
Author: By
Last change: 4.1.0 release
Date: 8 months ago
Size: 21,528 bytes
 

Contents

Class file image Download
<?php
/**
 * @file  multiotp.server.php
 * @brief web service for the multiOTP class.
 *
 * multiOTP web service - Strong two-factor authentication PHP class
 * http://www.multiotp.net
 *
 * Donation are always welcome! Please check http://www.multiotp.net
 * and you will find the magic button ;-)
 *
 * The multiOTP web service is simply merged with the multiOTP PHP class
 * in order to provide the server part of the client/server solution.
 *
 * This file can be used with any web server supporting PHP as
 * script language, like the following web servers:
 *  - nginx is a light one under Linux
 *    (http://nginx.org/)
 *  - Mongoose is a light one under Windows
 *    (http://code.google.com/p/mongoose/)
 *  - The Apache HTTP server is a very well known web server running under Linux and Windows
 *    (http://httpd.apache.org/)
 *
 *
 * LICENCE
 *
 *   Copyright (c) 2013, SysCo systemes de communication sa
 *   SysCo (tm) is a trademark of SysCo systemes de communication sa
 *   (http://www.sysco.ch)
 *   All rights reserved.
 * 
 *   This file is part of the multiOTP PHP class
 *
 *   multiOTP PHP class is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser General Public License as
 *   published by the Free Software Foundation, either version 3 of the License,
 *   or (at your option) any later version.
 * 
 *   multiOTP PHP class is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 * 
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with MultiOTP PHP class.
 *   If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * PHP 5.3.0 or higher is supported.
 *
 * @author    Andre Liechti, SysCo systemes de communication sa, <developer@sysco.ch>
 * @version   4.1.0
 * @date      2013-12-23
 * @since     2013-08-06
 * @copyright (c) 2013 by SysCo systemes de communication sa
 * @copyright GNU Lesser General Public License
 *
 *
 * Usage
 *
 *   You will have to send an XML formatted content in the field named data
 *    in a POSTed form.
 *
 *
 * Special issues
 *
 *   If you need specific developements concerning strong authentication,
 *   do not hesistate to contact us per email at developer@sysco.ch.
 *
 *
 * Users feedbacks and comments
 *
 * 2013-07-25 Dominik Pretzsch from Last Squirrel IT
 *   After some discussions with Dominik, integration of the
 *    client/server support in the basic library
 *
 *
 * Change Log
 *
 *   2013-12-23 4.1.0  SysCo/al Adding basic web functionalities
 *   2013-08-30 4.0.7  SysCo/al Version synchronization
 *   2013-08-25 4.0.6  SysCo/al Enhanced default page
 *   2013-08-20 4.0.4  SysCo/al Initial release
 *
 *********************************************************************/

///////////////////////////////////////////////////////////////////////////
// For your convenience, the class file is directly integrated in this file
///////////////////////////////////////////////////////////////////////////
require_once('multiotp.class.php');

$multiotp = new Multiotp('DefaultCliEncryptionKey', FALSE);

$multiotp_etc_dir  = '/etc/multiotp';
if (file_exists($multiotp_etc_dir))
{
    // Let says that the new created files have this linux mode
    $multiotp->SetLinuxFileMode('0666');
    
    $multiotp->SetConfigFolder   ($multiotp_etc_dir. '/config/');
    $multiotp->SetDevicesFolder  ($multiotp_etc_dir. '/devices/');
    $multiotp->SetGroupsFolder   ($multiotp_etc_dir. '/groups/');
    $multiotp->SetTokensFolder   ($multiotp_etc_dir. '/tokens/');
    $multiotp->SetUsersFolder    ($multiotp_etc_dir. '/users/');
}

$multiotp_log_dir  = '/var/log/multiotp';
if (file_exists($multiotp_log_dir))
{
    $multiotp->SetLogFolder($multiotp_log_dir. '/');
}

$multiotp->ReadConfigData();

$data = isset($_POST['data'])?$_POST['data']:'';
$method = substr(isset($_GET['method'])?$_GET['method']:(isset($_POST['method'])?$_POST['method']:''),0,255);
$options = isset($_GET['options'])?$_GET['options']:(isset($_POST['options'])?$_POST['options']:'');
$postdata = file_get_contents("php://input");

if (FALSE !== strpos($data,'<multiOTP'))
{
    $multiotp->XmlServer($data);
}
elseif (FALSE !== strpos($postdata,'<SOAP-ENV'))
{
    echo "ERROR: No SOAP server support.";
}
else
{
    session_start();
    $hash_salt     = 'AjaxH@shS@lt';
    $multiotp->SetHashSalt($hash_salt); // Shared secret

    /****************************************
     * WE REALLY DO NOT WANT TO BE CACHED !!!
     ****************************************/
    header("Expires: " . gmdate("D, d M Y H:i:s") . " GMT");
    header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    header("Cache-Control: no-store, no-cache, must-revalidate");
    header("Cache-Control: post-check=0, pre-check=0", false);
    header("Pragma: no-cache");

    if ('' == $method)
    {
        /*********************
         * Basic web server *
         *********************/
         
        $actual_date   = date('Y-m-d H:i:s');
        $class_name    = $multiotp->GetClassName();
        $class_version = $multiotp->GetVersion();
        $class_date    = $multiotp->GetDate();
        $rpi_serial    = $multiotp->GetRaspberryPiSerialNumber();
        $rpi_info      = (('' != $rpi_serial)?"<br />\n        Raspberry Pi serial number: ".$rpi_serial."\n        ":'');
        

        echo <<<EOWEBPAGE
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    <head>
        <title>
            multiOTP basic web server
        </title>
        <style>
            body {
                font-family: Verdana, Helvetica, Arial;
                color: black;
                font-size: 10pt;
                font-weight: normal;
                text-decoration: none;
            }
            h2 {
                font-size: 12pt;
                font-weight: bold;
                margin-top: 0em;
                margin-bottom: 0em;
            }
            h3 {
                font-size: 11pt;
                font-weight: bold;
                margin-top: 0.5em;
                margin-bottom: 0.1em;
            }
            p {
                margin-top: 0em;
                margin-bottom: 0em;
            }
        </style>
        <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
        <script type="text/javascript" src="md5.js"></script>
        <script>
            // Relative url page in order to execute the method remotely
            var url_page = location.protocol + '//' + location.host + location.pathname;

            // Shared secret
            var hash_salt = '$hash_salt';
            
            function ChangeAdminPassword()
            {
                var newpassword = document.getElementById('newpassword').value;
                var newpassword2 = document.getElementById('newpassword2').value;
                document.getElementById('newpassword').value = '';
                document.getElementById('newpassword2').value = '';
                
                if ('' == newpassword)
                {
                    alert('Password is empty!');
                }
                else if (newpassword != newpassword2)
                {
                    alert('Passwords are not equal!');
                }
                else
                {
                    var hash_password = md5(hash_salt+newpassword+hash_salt);
                    RemoteCall('SetAdminPasswordHash', hash_password);
                    Logout();
                }
            }

            function Create()
            {
                var newuser = document.getElementById('newuser').value;
                if (IsLoggedIn())
                {
                    RemoteCall('FastCreateUser', newuser);
                    UpdatePage();
                }
                document.getElementById('newuser').value = '';
            }

            function DeleteUser(one_user)
            {
                if ('' != one_user)
                {
                    RemoteCall('DeleteUser', one_user);
                    UpdatePage();
                }
            }

            function IsLoggedIn()
            {
                var logged_in = ('true' == eval(RemoteCall('UserLoggedIn')));
                return logged_in;
            }

            function Login()
            {
                var random_salt = eval(RemoteCall('GetRandomSalt'));

                var user = document.getElementById('user').value;
                var password = document.getElementById('password').value;
                document.getElementById('password').value = '';

                var hash_password = md5(random_salt+md5(hash_salt+password+hash_salt)+random_salt);
                RemoteCall('Login', user+"\t"+hash_password);
                UpdatePage();
            }

            function Logout()
            {
                RemoteCall("Login", "");
                UpdatePage();
            }

            function PrintQrCode(one_user)
            {
                if ('' != one_user)
                {
                    var http_params = "method=PrintQrCode"+"&options="+encodeURIComponent(one_user);
                    var full_url = url_page +'?'+http_params;
                    window.open(full_url,'_blank');
                }
            }

            function RebootDevice()
            {
                var result = GetHttp('RebootDevice','');
            }

            function RemoteCall(my_method, my_options, my_id, async, form_method)
            /******************************************************************************************
             * RemoteCall is a sample function that make an (a)synchronous GET or POST request to launch
             *   a remote command and return the result as an HTML content in the defined id if defined.
             *
             * @param   string   my_method   remote method to call
             * @param   string   my_options  options (separated by \t) for the called method
             * @param   string   my_id       id of the div/span to update asynchronously with the result
             * @param   boolean  async       define the asynchronous mode
             * @param   string   form_method method used to send the request (GET or POST)
             * @return  none
             ******************************************************************************************/
            {
                my_method = typeof my_method !== 'undefined' ? my_method : '';
                my_options = typeof my_options !== 'undefined' ? my_options : '';
                my_id = typeof my_id !== 'undefined' ? my_id : '';
                async = typeof async !== 'undefined' ? async : false;
                form_method = typeof form_method !== 'undefined' ? form_method : 'GET';

                async = false;

                var result = '';
                
                if ('POST' != form_method)
                {
                    form_method = 'GET';
                }

                var xmlhttp;
                if (window.XMLHttpRequest)
                { // code for IE7+, Firefox, Chrome, Opera, Safari
                    xmlhttp=new XMLHttpRequest();
                }
                else
                { // code for IE6, IE5
                    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
                }
                
                if (true == async)
                {
                    xmlhttp.onreadystatechange=function()
                    {
                        if (xmlhttp.readyState==4 && xmlhttp.status==200)
                        {
                            result = xmlhttp.responseText;
                            if ('' != my_id)
                            {
                                document.getElementById(my_id).innerHTML=eval(result);
                            }
                        }
                    }
                }

                var http_params = "method="+my_method+"&options="+encodeURIComponent(my_options);
                
                var full_url = url_page;
                var post_params = null;

                if ('GET' == form_method)
                {
                    full_url = url_page +'?'+http_params;
                }
                else
                {
                    post_params = http_params;
                }

                xmlhttp.open(form_method,full_url,false);
                if ('POST' == form_method)
                {
                    xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                    xmlhttp.setRequestHeader("Content-length", post_params.length);
                    xmlhttp.setRequestHeader("Connection", "close");
                }
                xmlhttp.send(post_params);

                if (false == async)
                {
                    if (xmlhttp.status === 200)
                    {
                        result = xmlhttp.responseText;
                        if ('' != my_id)
                        {
                            document.getElementById(my_id).innerHTML=eval(result);
                        }
                    }
                    return (result);
                }
            }
            
            function UpdatePage()
            {
                var logged_in = IsLoggedIn();
                document.getElementById('logged').innerHTML = (logged_in?'User authenticated':'User NOT authenticated');
                document.getElementById('logout').style.visibility=(logged_in?'visible':'hidden');
                document.getElementById('login').style.visibility=(logged_in?'hidden':'visible');
                document.getElementById('admin_div').style.visibility=(logged_in?'visible':'hidden');

                var remotecall = eval(RemoteCall('GetUsersList'));
                var userslist = typeof remotecall !== 'undefined' ? remotecall.trim() : '';

                if ('' != userslist)
                {
                    var usersarray = userslist.split("\t");

                    if ('false' == userslist)
                    {
                        userslist = 'not authorized';
                    }
                    else
                    {
                        userslist = '';
                        for (var i = 0; i < usersarray.length; i++)
                        {
                            userslist = userslist + '<button type="button" onclick="DeleteUser(\''+usersarray[i]+'\');">Delete</button>';
                            userslist = userslist + '<button type="button" onclick="PrintQrCode(\''+usersarray[i]+'\');">Print</button>';
                            userslist = userslist + ' ' + usersarray[i] + '<br />';
                        }
                    }
                }
                document.getElementById('userslist').innerHTML = userslist;
            }
        </script>
    </head>
    <body onload='UpdatePage();'>
        <h2>multi<i>OTP</i></h2>
        $class_name $class_version $class_date
        <br />
        Web service is ready $actual_date
        $rpi_info<hr />
        This package is the result of a *bunch* of work. If you find this package useful, <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B78FJAH6RBNZ2">[Donation]</a> are always welcome to support this project.
        <br />
        Please check <a target="_blank" href="http://www.multiOTP.net/">http://www.multiOTP.net/</a> and you will find the magic button ;-)
        <br />
        <br />

        <form>
            Username: <input type=text" onfocus="this.blur();" name="user" id="user" length="20" value="admin" /> (default is admin)
            <span id="log_info"></span><span id="logged"></span>
            <br />
            Password: <input type="password" onfocus="this.value='';" name="password" id="password" length="20" value="1234" /> (default is 1234)
            &nbsp;
            <span id="login"><button type="button" onclick="Login();" >Login</button></span>
            <span id="logout"><button type="button" onclick="Logout();">Logout</button></span>
        </form>
        <hr />
        <div id="admin_div">
            <form>
                New password: <input type=password" name="newpassword" id="newpassword" length="20" value="" />
                <br />
                Retype new password: <input type=password" name="newpassword2" id="newpassword2" length="20" value="" />
                <button type="button" onclick="ChangeAdminPassword();">Change admin password</button>
            </form>
            <hr />
            <form>
                Create a new user: <input type=text" name="newuser" id="newuser" length="20" value="" />
                <button type="button" onclick="Create();">Create</button>
            </form>
            <h3>List of users</h3>
            <div id="userslist">...</div>
        </div>
    </body>
</html>
EOWEBPAGE;
    }
    else
    {
        /*********************
         * Basic Ajax server *
         *********************/
        $method = preg_replace('/[^(x20-\x7F)]*/','', $method);
        $options = preg_replace('/[^(\x09\x20-\x7F)]*/','', $options);
        $options_array = explode("\t",$options);
    
        if ('' == $multiotp->GetConfigAttribute('admin_password_hash'))
        {
            $multiotp->SetAdminPassword('1234');
            $multiotp->WriteConfigData();
        }
    
        if (!isset($_SESSION['logged']))
        {
            $_SESSION['logged'] = FALSE;
        }

        if (!isset($_SESSION['random_salt']))
        {
            $random_salt = substr(md5(time()."@".rand(100000,999999)),0,12);
            $_SESSION['random_salt'] = $random_salt;
        }
        $multiotp->SetRandomSalt($_SESSION['random_salt']);
        
        $ajax_result = "false";

        switch (strtoupper($method))
        {
            case strtoupper("GetRandomSalt"):
                $ajax_result = $multiotp->GetRandomSalt();
                break;
            case strtoupper("Login"):
                $result = FALSE;
                $username = substr(isset($options_array[0])?$options_array[0]:'',0,255);
                $password = substr(isset($options_array[1])?$options_array[1]:'',0,255);
                if ('admin' == $username)
                {
                    $result = $multiotp->CheckAdminPasswordHashWithRandomSalt($password);
                }

                if ($result)
                {
                    $_SESSION['logged'] = TRUE;
                    /* And we change the random_salt to avoid a second login with the same previous hash */
                    $random_salt = substr(md5(time()."@".rand(100000,999999)),0,12);
                    $_SESSION['random_salt'] = $random_salt;
                    $multiotp->SetRandomSalt($random_salt);
                    $ajax_result = "true";
                }
                else
                {
                    $_SESSION = array();
                    $_SESSION['logged'] = FALSE;
                    $ajax_result = "false";
                }
                break;
            default:
                if ((isset($_SESSION['logged']) && $_SESSION['logged']))
                {
                    /*******************************************************
                     * The next methods are allowed only if we are logged in
                     *******************************************************/
                    switch (strtoupper($method))
                    {
                        case strtoupper("DeleteUser"):
                            $ajax_result = $multiotp->DeleteUser($options_array[0]);
                            break;
                        case strtoupper("FastCreateUser"):
                            $ajax_result = $multiotp->FastCreateUser($options_array[0]);
                            break;
                        case strtoupper("GetFullVersionInfo"):
                            $ajax_result = $multiotp->GetFullVersionInfo();
                            break;
                        case strtoupper("GetUsersList"):
                            $ajax_result = $multiotp->GetUsersList();
                            break;
                        case strtoupper("PrintQrCode"):
                            echo $multiotp->GenerateHtmlQrCode($options_array[0]);
                            $ajax_result = '';
                            break;
                        case strtoupper("SetAdminPasswordHash"):
                            $ajax_result = $multiotp->SetAdminPasswordHash($options_array[0]);
                            $multiotp->WriteConfigData();
                            break;
                        case strtoupper("UserLoggedIn"):
                            $ajax_result = "true"; //User is logged if code arrives here!
                            break;
                        default:
                            $ajax_result = "false";
                            break;
                    }
                }
                else
                {
                    $ajax_result = "false";
                }
                break;
        }
        if ('' != $ajax_result)
        {
            echo json_encode($ajax_result);
        }
        @ob_flush();
        flush(); 
    }
}
?>