Description
php から Radius サーバにアクセスするための関数です。
Radius についての詳細は RFC2138を参照して下さい。
ただし、RFC2138 に完全に準拠した実装ではありません。自分が必要とした機能のみを実装しています。
How to use
php の関数として実装してありますので、radius.php を require して radiusauth() をコールして下さい。
radiusauth() には以下のパラメタを指定します。 詳しくはソースコードのコメントを参照して下さい
変数名 | 内容 |
---|---|
radius_server | radius サーバ |
radius_port | radius サーバのポート |
radius_id | ユーザID |
radius_passwd | パスワード |
radius_key | 共有鍵 |
radius_retry | 再送回数 |
radius_timeout | タイムアウト秒数 |
ipaddr | 自分自身の IPアドレス |
Source
radius.php
<?php
/*
* Radius 認証関数
* Radius についての詳細は RFC2138 を参照
* 但し RFC2138 を完全に実装はしていない
* 認証許可パケットの属性値は Session-Timeout(27) 以外は無視する
*
* 引数配列には以下の構成要素が必須
* "radius_server" => radius サーバのホスト名/IPアドレス
* "radius_port" => radius サーバのポート
* "radius_id" => ユーザID
* "radius_passwd" => パスワード
* "radius_key" => 共有鍵
* "radius_retry" => 再送回数
* "radius_timeout" => タイムアウト
* "ipaddr" => IP アドレス
*
* 以下の値をリターンする
* >= 0 : 認証成功(有効期間秒)
* -1 : 認証失敗
* -2 : タイムアウト
* -3 : ネットワークエラー
* -4 : パラメタ不正
*/
define("ACCESS_REJECT", 3);
define("HEADER_LEN", 20);
define("ATTRIB_SESSION_TIMEOUT", 27);
function radiusauth($values)
{
/* パラメタチェック */
foreach(array(
"radius_server",
"radius_port",
"radius_id",
"radius_passwd",
"radius_key",
"radius_retry",
"radius_timeout",
"ipaddr") as $key)
if(ereg("^[[:space:]]*$", $values[$key]))
return(-4);
/* NAS アドレス */
$nas = explode(".", $values["ipaddr"]);
/* Radius server */
if(!$server = gethostbyname($values["radius_server"]))
return(-3);
/* socket 作成 */
if(!$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP))
return(-3);
/* Authenticator */
srand(microtime() * 1000000);
for($i=0; $i<16; $i++)
$code .= pack("C", rand(1, 255));
/* password 暗号化 */
$pass = encrypt($values["radius_passwd"], $values["radius_key"], $code);
/* packet 長セット */
$len =
4 + /* header */
strlen($code) + /* authcode */
6 + /* service type */
2 + strlen($values["radius_id"]) + /* username */
2 + strlen($pass) + /* password */
6 + /* NAS IP */
6; /* NAS Port */
/* packet 作成 */
$id = rand(1, 255);
/* H A S I P IP PORT
v v v v v v v */
$data = pack("CCna*CCNCCa*CCa*CCCCCCCCCCCC",
1, $id, $len, /* header */
$code, /* authcode */
6, 6, 1, /* service type */
1, 2 + strlen($values["radius_id"]), $values["radius_id"],
2, 2 + strlen($pass), $pass, /* password */
4, 6, $nas[0], $nas[1], $nas[2], $nas[3], /* NAS IP */
5, 6, 0, 0, 0, 0 /* NAS Port */
);
/*
* packet 送信処理
*/
$count = 0;
while(1){
/*
* socket_sendto() は EXPERIMENTAL ステータスであるが
* socket_select() が socket_write() だと正常に動作しないので使用
*/
socket_sendto($sock, $data, $len, 0,
$server, $values["radius_port"]);
/* timeout 処理 */
if(($enable = socket_select($set = array($sock), $w=null, $e=null,
$values["radius_timeout"])) === false){
socket_close($sock);
return(-3);
}
else if($enable > 0)
break;
else
if($count++ >= $values["radius_retry"]){
socket_close($sock);
return(-2);
}
}
/* 認証応答受信 */
socket_recvfrom($sock, $data, 4096, 0, $server, $values["radius_port"]);
socket_close($sock);
/* packet 解析 */
$val = unpack("Ccode/Cid/nlen/a16auth/A*data", $data);
/* Access Reject */
if($val["code"] == ACCESS_REJECT)
return(-1);
/* Attribute 解析 */
$packetlen = $val["len"] - HEADER_LEN;
while($packetlen > 0){
/* type、length 取得 */
$val = unpack("Ctype/Clen/A*data", $val["data"]);
$packetlen -= $val["len"];
if(($type = $val["type"]) == ATTRIB_SESSION_TIMEOUT){
/* Session timeout の場合は u_long で秒を取得 */
$val = unpack("Nvalue/A*data", $val["data"]);
return($val[value]);
}
else{
/* それ以外の属性はスキップ */
$len = $val["len"] - 2;
$val = unpack("A{$len}value/A*data", $val["data"]);
}
}
return(0);
}
function encrypt($password, $key, $code)
{
$output = "";
/* MD5 は 32bit 固定 */
$sum = md5($key . $code);
/* パスワードが 16 bytes 以下の場合は NULL で埋める */
$pass = pack("a16", $password);
/* 暗号化アルゴリズムは RFC2138 P.22 参照 */
for($i=0; $i<16; $i++)
$output .= chr((hexdec(substr($sum, $i * 2, 2))) ^
(ord(substr($pass, $i, 1))));
return($output);
}
?>
この関数は 2005年頃の php 4 で作成したものなので現状の php とは仕様などが一部異なっている可能性がありますが、動作する事だけは確認できています。