2013-04-17 17:24:50 +02:00
< ? php
2014-09-24 13:05:09 +02:00
# Copyright (c) 2009-2014 Yubico AB
2013-04-17 17:24:50 +02:00
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require_once 'ykval-common.php' ;
require_once 'ykval-config.php' ;
require_once 'ykval-synclib.php' ;
2015-07-15 15:38:22 +02:00
header ( 'content-type: text/plain' );
2013-04-17 17:24:50 +02:00
2015-07-15 15:38:22 +02:00
if ( empty ( $_SERVER [ 'QUERY_STRING' ])) {
2015-07-16 23:34:35 +02:00
sendResp ( S_MISSING_PARAMETER , $myLog );
2013-04-17 17:24:50 +02:00
}
2015-07-16 23:44:35 +02:00
2015-07-16 23:46:59 +02:00
$ipaddr = $_SERVER [ 'REMOTE_ADDR' ];
$allowed = $baseParams [ '__YKVAL_ALLOWED_SYNC_POOL__' ];
2015-07-16 23:44:35 +02:00
$myLog = new Log ( 'ykval-sync' );
$myLog -> addField ( 'ip' , $ipaddr );
2015-07-15 15:38:22 +02:00
$myLog -> log ( LOG_INFO , 'Request: ' . $_SERVER [ 'QUERY_STRING' ]);
2015-07-16 23:44:35 +02:00
$myLog -> log ( LOG_DEBUG , 'Received request from ' . $ipaddr );
2013-04-17 17:24:50 +02:00
2015-07-16 23:41:22 +02:00
// verify request sent by whitelisted address
2015-07-16 23:29:11 +02:00
if ( in_array ( $ipaddr , $allowed , TRUE ) === FALSE ) {
2015-07-16 23:00:29 +02:00
$myLog -> log ( LOG_NOTICE , 'Operation not allowed from IP ' . $ipaddr );
2015-07-16 23:29:11 +02:00
$myLog -> log ( LOG_DEBUG , 'Remote IP ' . $ipaddr . ' not listed in allowed sync pool : ' . implode ( ', ' , $allowed ));
2015-07-16 23:34:35 +02:00
sendResp ( S_OPERATION_NOT_ALLOWED , $myLog );
2013-04-17 17:24:50 +02:00
}
2015-07-16 23:41:22 +02:00
$sync = new SyncLib ( 'ykval-sync:synclib' );
$sync -> addField ( 'ip' , $ipaddr );
if ( ! $sync -> isConnected ()) {
sendResp ( S_BACKEND_ERROR , $myLog );
}
2013-04-17 17:24:50 +02:00
#
# Define requirements on protocol
#
2015-07-15 15:38:22 +02:00
$syncParams = array (
2015-07-16 22:54:31 +02:00
'modified' => NULL ,
'otp' => NULL ,
'nonce' => NULL ,
'yk_publicname' => NULL ,
'yk_counter' => NULL ,
'yk_use' => NULL ,
'yk_high' => NULL ,
'yk_low' => NULL
2015-07-15 15:38:22 +02:00
);
2013-04-17 17:24:50 +02:00
#
# Extract values from HTTP request
#
$tmp_log = " Received " ;
foreach ( $syncParams as $param => $value ) {
2015-07-16 22:54:31 +02:00
$value = getHttpVal ( $param , NULL );
if ( $value == NULL ) {
2013-04-17 17:24:50 +02:00
$myLog -> log ( LOG_NOTICE , " Received request with parameter[s] ( " . $param . " ) missing value " );
2015-07-16 23:34:35 +02:00
sendResp ( S_MISSING_PARAMETER , $myLog );
2013-04-17 17:24:50 +02:00
}
$syncParams [ $param ] = $value ;
$tmp_log .= " $param = $value " ;
}
$myLog -> log ( LOG_INFO , $tmp_log );
#
# At this point we should have the otp so let's add it to the logging module
#
$myLog -> addField ( 'otp' , $syncParams [ 'otp' ]);
$sync -> addField ( 'otp' , $syncParams [ 'otp' ]);
#
# Verify correctness of input parameters
#
2015-07-16 23:17:29 +02:00
foreach ( array ( 'modified' , 'yk_counter' , 'yk_use' , 'yk_high' , 'yk_low' ) as $param )
{
// -1 is valid except for modified
if ( $param !== 'modified' && $syncParams [ $param ] === '-1' )
2015-07-16 23:10:48 +02:00
continue ;
2015-07-16 23:17:29 +02:00
// [0-9]+
2015-07-16 23:10:48 +02:00
if ( $syncParams [ $param ] !== '' && ctype_digit ( $syncParams [ $param ]))
continue ;
$myLog -> log ( LOG_NOTICE , 'Input parameters ' . $param . ' not correct' );
2015-07-16 23:34:35 +02:00
sendResp ( S_MISSING_PARAMETER , $myLog );
2013-04-17 17:24:50 +02:00
}
#
# Get local counter data
#
$yk_publicname = $syncParams [ 'yk_publicname' ];
$localParams = $sync -> getLocalParams ( $yk_publicname );
if ( ! $localParams ) {
$myLog -> log ( LOG_NOTICE , 'Invalid Yubikey ' . $yk_publicname );
2015-07-16 23:34:35 +02:00
sendResp ( S_BACKEND_ERROR , $myLog );
2013-04-17 17:24:50 +02:00
}
/* Conditional update local database */
$sync -> updateDbCounters ( $syncParams );
2015-07-15 15:38:22 +02:00
$myLog -> log ( LOG_DEBUG , 'Local params ' , $localParams );
$myLog -> log ( LOG_DEBUG , 'Sync request params ' , $syncParams );
2013-04-17 17:24:50 +02:00
2015-07-16 23:28:20 +02:00
/**
* Compare sync and local counters and generate warnings according to
* https :// developers . yubico . com / yubikey - val / doc / ServerReplicationProtocol . html
*/
2013-04-17 17:24:50 +02:00
if ( $sync -> countersHigherThan ( $localParams , $syncParams )) {
$myLog -> log ( LOG_WARNING , 'Remote server out of sync.' );
}
if ( $sync -> countersEqual ( $localParams , $syncParams )) {
if ( $syncParams [ 'modified' ] == $localParams [ 'modified' ] &&
$syncParams [ 'nonce' ] == $localParams [ 'nonce' ]) {
/* This is not an error . When the remote server received an OTP to verify , it would
* have sent out sync requests immediately . When the required number of responses had
* been received , the current implementation discards all additional responses ( to
* return the result to the client as soon as possible ) . If our response sent last
* time was discarded , we will end up here when the background ykval - queue processes
* the sync request again .
*/
$myLog -> log ( LOG_INFO , 'Sync request unnecessarily sent' );
}
if ( $syncParams [ 'modified' ] != $localParams [ 'modified' ] &&
$syncParams [ 'nonce' ] == $localParams [ 'nonce' ]) {
$deltaModified = $syncParams [ 'modified' ] - $localParams [ 'modified' ];
if ( $deltaModified < - 1 || $deltaModified > 1 ) {
$myLog -> log ( LOG_WARNING , 'We might have a replay. 2 events at different times have generated the same counters. The time difference is ' . $deltaModified . ' seconds' );
}
}
if ( $syncParams [ 'nonce' ] != $localParams [ 'nonce' ]) {
$myLog -> log ( LOG_WARNING , 'Remote server has received a request to validate an already validated OTP ' );
}
}
if ( $localParams [ 'active' ] != 1 ) {
/* The remote server has accepted an OTP from a YubiKey which we would not .
* We still needed to update our counters with the counters from the OTP though .
*/
2015-07-16 23:28:20 +02:00
$myLog -> log ( LOG_WARNING , 'Received sync-request for de-activated Yubikey ' . $yk_publicname . ' - check database synchronization!!!' );
2015-07-16 23:34:35 +02:00
sendResp ( S_BAD_OTP , $myLog );
2013-04-17 17:24:50 +02:00
}
2015-07-15 15:38:22 +02:00
$extra = array (
2015-07-16 22:54:31 +02:00
'modified' => $localParams [ 'modified' ],
2015-07-15 15:38:22 +02:00
'nonce' => $localParams [ 'nonce' ],
'yk_publicname' => $yk_publicname ,
'yk_counter' => $localParams [ 'yk_counter' ],
'yk_use' => $localParams [ 'yk_use' ],
'yk_high' => $localParams [ 'yk_high' ],
'yk_low' => $localParams [ 'yk_low' ]
);
2013-04-17 17:24:50 +02:00
2015-07-16 23:34:35 +02:00
sendResp ( S_OK , $myLog , '' , $extra );