Location: PHPKode > projects > Simple Way to Usenet > swun/libs/core/auth/chals/chal_digest.class.php
<?php
/**
 * 
 * @author  Benjamin Gillissen <hide@address.com>
 * 
 *	**************************************************************

	Copyright (C) 2009  Benjamin Gillissen
	
	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.
	
	This program 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 General Public License for more details at:
	http://www.gnu.org/copyleft/gpl.html

 *	**************************************************************
 */

class chal_digest extends realm {
	
	private $chal, $ticket, $nonce;
	
	public function __construct($realm, $chal){
		parent::__construct($realm);
		$this->chal = $chal;
		unset($realm, $chal);
	}

	public function get_chaldata(){ 
		$this->nonce = CORE::hash();
		return $this->nonce; 
	}
	
	public function seticket($ticket){ $this->ticket = $ticket; }
	
	public function geticket(){
		$info = $this->http_digest_parse($_SERVER['PHP_AUTH_DIGEST']);
		if ( FALSE === $info ){	return FALSE; }
		if ( !isset($info['opaque']) ){ return FALSE; }
		return $info['opaque'];
	}
	
	public function sendform($err=NULL){
		if ( NULL !== $err ){ die(langs::dico_translate($err, NULL, 'realm')); }
		if ( headers_sent($file, $line)) {
			errors::raise('Digest-HTTP Authentification failure : output already started at '.$file.':'.$line, CORE_LOG_ALERT, 'REALM');
		}
		header('WWW-Authenticate: Digest '.
					'realm="'.$this->realm.'", '.
					'domain="/'.browsing::basedir().'", '.
					'qop="auth", '.
					'algorithm=MD5, '.
					'nonce="'.$this->nonce.'", '.
					'opaque="'.$this->ticket.'"');
		header('HTTP/1.1 401 Unauthorized');
		exit;
	}
	
	public function istrying(){
		if ( FALSE === isset($_SERVER['PHP_AUTH_DIGEST']) ){ return FALSE; }
		$info = $this->http_digest_parse($_SERVER['PHP_AUTH_DIGEST']);
		if ( !isset($info['opaque']) ){ return FALSE; }
		return ( TRUE === $this->isticket($info['opaque']) AND FALSE === $this->ischallenged($info['opaque']) );
	}
	
	public function precheck($chaldata){
		$info = $this->http_digest_parse($_SERVER['PHP_AUTH_DIGEST']);
		if ( FALSE === $info ){	return 'realmsg_digestbadreply'; }
		if ( $info['nonce'] !== $chaldata ){ return 'realmsg_digestnoncenomatch'; }
		return TRUE;
	}
	
	public function authenticate(){
		//FIXME i took some freedom from the RFC, need rework so it really follow digest RFC, with qop stuff 
		$data = $this->http_digest_parse($_SERVER['PHP_AUTH_DIGEST']);
		$ruid = $this->uidbylogin($data['username']);
		if ( FALSE === $ruid ){ return FALSE; }
		$pass = $this->getpass($ruid);
		if ( FALSE === $pass ){ return FALSE; }
		$uid = realm_accounts::splitid($ruid);
		$hash = configs::get('account', $uid['acc'], 'hash');
		$scheme = configs::get('account', $uid['acc'], 'hashscheme');
		if ($hash == 'clear'){
			$a1 = md5($data['username'].':'.$this->realm.':'.$pass);
		} elseif ( $hash == 'MD5' AND '%login:%realm:%pass' == $scheme ){
			$a1 = $pass;
		} else {
			return FALSE;
		}
		$a2 = md5($_SERVER['REQUEST_METHOD'].':'.$_SERVER['REQUEST_URI']);
		if ( isset($data['cnonce']) AND isset($data['nc']) AND isset($data['qop']) ){
			$expected = md5($a1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$a2);
		} else {
			$expected = md5($a1.':'.$data['nonce'].':'.$a2);
		}
		if ($data['response'] === $expected ) { return $ruid; }
		return FALSE;
	}

	
	
	private function http_digest_parse($txt){
		$infos = Array('username', 'cnonce', 'nonce', 'response', 'opaque', 'uri', 'qop', 'nc');
		foreach($infos as $k => &$info){
			$buf = split($info.'=', $txt);
			if ( isset($buf[1]) ){
				$buf = split(',', $buf[1]);
				if ( !empty($buf[0]) ){
					$buf = ereg_replace("^\"", '', $buf[0]);
					$o[$info] = ereg_replace('"$', '', $buf);
				}
			}
		}
		if (isset($o) ){ return $o; }
		return FALSE;
	}

}
Return current item: Simple Way to Usenet