File: multiotp.cli.proxy.php

Recommend this page to a friend!
  Classes of André Liechti  >  multiOTP PHP class  >  multiotp.cli.proxy.php  >  Download  
File: multiotp.cli.proxy.php
Role: Auxiliary script
Content type: text/plain
Description: Auxiliary script
Class: multiOTP PHP class
Authenticate and manage OTP strong user tokens
Author: By
Last change: New release
ENH: New QRcode library used (without external files dependency)
ENH: New Raspberry images support for Raspberry Pi 1B/1B+/2B/3B/3B+
New release
FIX: If any, clean specific NTP DHCP option at every reboot
ENH: Modifications for Debian 9.x (stretch) binary images support
ENH: Raspberry Pi 3B+ support
ENH: Import of PSKC definition files with binary decoding key file
ENH: Added Swisscom LA REST, Afilnet, Clickatell2, eCall, Nexmo, NowSMS, SMSEagle and custom SMS provider support
New release
FIX: Values of SetUserCacheLevel(), GetUserCacheLevel(), SetUserCacheLifetime() and GetUserCacheLifetime() are not correctly initialized
ENH: Enigma Virtual Box updated to version 9.10 (to create the special all-in-one-file)
ENH: PHP 7.1.22 used in the one single file (only PHP < 7.2 is still compatible with Windows 7/2008)
ENH: Compatibility mode to Windows 7 automatically added for radiusd.exe during radius service installation
ENH: PHP display error flag is now set to off by default in the webservice under Windows
New release
FIX: Better without2FA algorithm support
New release
FIX: Restore configuration has been fixed in the command line edition
ENH: Cache-level and cache-lifetime can be set separately for each user
ENH: In client/server mode, only unencrypted user attributes are sent back to a successful client request
ENH: Enhanced monitoring
New release
FIX: stream_timeout is no more pushed to 20 seconds in PostHttpDataXmlRequest if we are in Credential Provider mode
FIX: RemoveTokenFromUser() method corrected. Token administrative information corrected, new software token created for the user
ENH: Multiple semicolon separated "Users DN" supported for AD/LDAP synchronization
ENH: Additional debug messages for disabled users during synchronization
ENH: Enigma Virtual Box updated to version 9.00 (to create the special all-in-one-file)
ENH: PHP 7.2.8 used in the one single file
ENH: without2FA algorithm now available (useful to do 2FA only for some accounts and not for others)
New release
ENH: Enhanced AD/LDAP support for huge Microsoft Active Directory
ENH: Base DN and Users DN are now two different parameters (Users DN optional)
New release
FIX: typo in the source code of the command line option for ldap-pwd and prefix-pin
ENH: Dockerfile available
FIX: Enigma Virtual Box updated to version 8.10 (to create the special all-in-one-file)
FIX: [Receive an OTP by SMS] link is now fixed for Windows 10
ENH: Credential Provider registry entries are now always used when calling multiOTP.exe
FIX: To avoid virus false positive alert, multiOTP.exe is NO more packaged in one single file
using Enigma, a php folder is now included in the multiOTP folder
FIX: multiOTPOptions registry entry is now useless
ENH: Credential Provider registry entries are used if available
Expired AD/LDAP password support
multiOTP Credential Provider (for Windows) improvements ( UPN support, default domain name supported and displayed, SMS request link)
"force_no_prefix_pin" option for devices (for example if the device is a computer with multiOTP credential Provider and AD/LDAP synced password)
Better unicode handling, multibyte fonctions used when needed (mb_strtolower(), ...)
Better FreeRADIUS 3.x documentation
New radius tag prefix configuration option
New multiple groups device option
Some notice corrections (if the array element doesn't exist)
A user cannot be created with a leading backslash (fixed in FastCreateUser and CreateUserFromToken)
The proposed mOTP generator for Android/iOS is now OTP Authenticator
New QRCode provisioning format for mOTP (compatible with OTP Authenticator)
NirSoft nircmd.exe tool removed from the distribution (false virus detection)
Multiple URLs separator for client/server config is still ";", but [space] and "," are accepted
New developer mode for some specific detailed logs during development process only
New methods: SetLdapTlsReqcert, GetLdapTlsReqcert, SetLdapTlsCipherSuite, GetLdapTlsCipherSuite to change config parameters, instead of hard coded parameters (for SSL/TLS LDAP connection)
Fixed too much detailed information in the log when trying to detect a token serial number for self-registration
Fixed SSL/TLS LDAP failed connection for PHP 7.x (GnuTLS TLS1.2 restriction removed for PHP 7.x)
Fixed a typo in the ReadCacheData method for PostgreSQL support (thanks Frank for the feedback)
Fixed default folder detection for the multiotp.exe file
Important, under Linux, the config, devices, groups, tokens and users folders are now always located in /etc/multiotp/. Please be sure to make the move when you are upgrading
Cleaned some ugly PHP warnings when the backend is not initialized
Date: 1 year ago
Size: 6,748 bytes


Class file image Download
 * @file multiotp.cli.proxy.php
 * @brief Command line calling the proxy of the multiOTP PHP class.
 * multiOTP PHP CLI proxy - Strong two-factor authentication PHP class
 * Visit for additional support.
 * Donation are always welcome! Please check
 * and you will find the magic button ;-)
 * PHP 5.3.0 or higher is supported.
 * @author Andre Liechti, SysCo systemes de communication sa, <>
 * @version
 * @date 2019-01-30
 * @since 2010-06-08
 * @copyright (c) 2010-2019 SysCo systemes de communication sa
 * @copyright GNU Lesser General Public License
 * Copyright (c) 2014-2019 SysCo systemes de communication sa
 * SysCo (tm) is a trademark of SysCo systemes de communication sa
 * (
 * 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
 * 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 <>.
 * Command line usage
 * Same usage as multiotp.cli.header.php. It's just a proxy to call
 * the web version of multiotp.cli.header.php, which could be cached,
 * giving a massive speed improvement on "weak" machines like Raspberry Pi.
 * If the web version is not responding, it will fallback nicely and
 * will do a require_once of the local proxy file.

@set_time_limit(0); // It can take a lot of time...

global $argc;

// Define the stream context
if (function_exists("stream_context_set_default")) {
$default_ssl_context = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'disable_compression' => true,
$default_context = stream_context_set_default($default_ssl_context);

$proxy_full_url = isset($proxy_full_url) ? $proxy_full_url : "";
$timeout = isset($timeout)?intval($timeout):15;
$local_proxy_file = isset($local_proxy_file) ? $local_proxy_file : "multiotp.proxy.php";

// Clean quotes of the parameters if any
if (!function_exists('clean_quotes')) {
$cleaned = false;
$var = $value;
    if (((
'"' == substr($var,0,1)) || ("'" == substr($var,0,1))) && (('"' == substr($var,-1)) || ("'" == substr($var,-1)))) {
$var = substr($var, 1, strlen($var)-2);
$cleaned = true;
    if (
$cleaned) {
$var = clean_quotes($var);

$value_unencoded = '';
// $_SERVER["argv"][0] is useless, it's the name of the script

$argv = isset($_SERVER["argv"]) ? $_SERVER["argv"] : (isset($argv) ? $argv : "");
$argc = intval(isset($_SERVER["argc"]) ? $_SERVER["argc"] : (isset($argc) ? $argc : 0));

for (
$arg_loop=1; $arg_loop < $argc; $arg_loop++) {
$current_arg = clean_quotes($argv[$arg_loop]);
  if (
'' != $value_unencoded) {
$value_unencoded.= chr(0);
$value_unencoded.= $current_arg;

$key = 'argv';
$value = base64_encode($value_unencoded);

$multiotp_error_level_received = FALSE;
$multiotp_error_level = 99; // Unknown error

$post_url = $proxy_full_url;

$result = '';
$content_to_post = $key.'='.$value;

$pos = mb_strpos($post_url, '://');
if (
FALSE === $pos) {
$protocol = '';
} else {
  switch (
mb_strtolower(substr($post_url,0,$pos))) {
$protocol = 'ssl://';
$protocol = 'tls://';
$protocol = '';
$post_url = substr($post_url,$pos+3);

$pos = mb_strpos($post_url, '/');
if (
FALSE === $pos) {
$host = $post_url;
$url = '/';
} else {
$host = substr($post_url,0,$pos);
$url = substr($post_url,$pos); // And not +1 as we want the / at the beginning

$pos = mb_strpos($host, ':');
if (
FALSE === $pos) {
$port = 80;
} else {
$port = substr($host,$pos+1);
$host = substr($host,0,$pos);

$errno = 0;
$errdesc = 0;
$fp = @fsockopen($protocol.$host, $port, $errno, $errdesc, $timeout);
if (
FALSE !== $fp) {
$info['timed_out'] = FALSE;
fputs($fp, "POST ".$url." HTTP/1.0\r\n");
fputs($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-Length: ".strlen($content_to_post)."\r\n");
fputs($fp, "User-Agent: multiOTP proxy\r\n");
fputs($fp, "Host: ".$host."\r\n");
fputs($fp, "\r\n");
fputs($fp, $content_to_post);
fputs($fp, "\r\n");

stream_set_blocking($fp, TRUE);
stream_set_timeout($fp, 86400); // It can take a lot of time, if we are doing AD/LDAP sync for example
$info = stream_get_meta_data($fp);

$reply = '';
$last_length = 0;
  while ((!
feof($fp)) && ((!$info['timed_out']) || ($last_length != strlen($reply)))) {
$last_length = strlen($reply);
$reply.= fgets($fp, 1024);
$info = stream_get_meta_data($fp);
ob_flush(); // Avoid notice if any (if the buffer is empty and therefore cannot be flushed)

  if (
$info['timed_out']) {
// error_log("Warning: timeout after $timeout seconds for $protocol$host:$port$url with a result code of $errno ($errdesc)");
} else {
// error_log("CLI ok");
$pos = mb_strpos(mb_strtolower($reply), "\r\n\r\n");
$header = substr($reply, 0, $pos);
$header_array = explode("\r\n", $header);
$header_array as $one_header) {
$one_header_array = explode(":", $one_header, 2);
      if (isset(
$one_header_array[1])) {
        if (
'X-multiOTP-Error-Level' == trim($one_header_array[0])) {
$multiotp_error_level_received = TRUE;
$multiotp_error_level = intval(trim($one_header_array[1]));
$answer = substr($reply, $pos + 4);
$result = $answer;
    if (
$errno > 0) {
// Error ?

if (!
$multiotp_error_level_received) {
} else {

For more information send a message to info at phpclasses dot org.