1<?php
2/* vim: set expandtab tabstop=4 shiftwidth=4: */
3/*
4Copyright (c) 2003, Michael Bretterklieber <michael@bretterklieber.com>
5All rights reserved.
6
7Redistribution and use in source and binary forms, with or without
8modification, are permitted provided that the following conditions
9are met:
10
111. Redistributions of source code must retain the above copyright
12   notice, this list of conditions and the following disclaimer.
132. Redistributions in binary form must reproduce the above copyright
14   notice, this list of conditions and the following disclaimer in the
15   documentation and/or other materials provided with the distribution.
163. The names of the authors may not be used to endorse or promote products
17   derived from this software without specific prior written permission.
18
19THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30This code cannot simply be copied and put under the GNU Public License or
31any other GPL-like (LGPL, GPL2) License.
32
33    $Id$
34*/
35
36/**
37* Client implementation of RADIUS. This are wrapper classes for
38* the RADIUS PECL.
39* Provides RADIUS Authentication (RFC2865) and RADIUS Accounting (RFC2866).
40*
41* @package Auth_RADIUS
42* @author  Michael Bretterklieber <michael@bretterklieber.com>
43* @access  public
44* @version $Revision$
45*/
46
47
48/**
49 * class Auth_RADIUS
50 *
51 * Abstract base class for RADIUS
52 *
53 * @package Auth_RADIUS
54 */
55class Auth_RADIUS {
56
57    /**
58     * List of RADIUS servers.
59     * @var  array
60     * @see  addServer(), putServer()
61     */
62    var $_servers  = array();
63
64    /**
65     * Path to the configuration-file.
66     * @var  string
67     * @see  setConfigFile()
68     */
69    var $_configfile = null;
70
71    /**
72     * Resource.
73     * @var  resource
74     * @see  open(), close()
75     */
76    var $res = null;
77
78    /**
79     * Username for authentication and accounting requests.
80     * @var  string
81     */
82    var $username = null;
83
84    /**
85     * Password for plaintext-authentication (PAP).
86     * @var  string
87     */
88    var $password = null;
89
90    /**
91     * List of known attributes.
92     * @var  array
93     * @see  dumpAttributes(), getAttributes()
94     */
95    var $attributes = array();
96
97    /**
98     * List of raw attributes.
99     * @var  array
100     * @see  dumpAttributes(), getAttributes()
101     */
102    var $rawAttributes = array();
103
104    /**
105     * List of raw vendor specific attributes.
106     * @var  array
107     * @see  dumpAttributes(), getAttributes()
108     */
109    var $rawVendorAttributes = array();
110
111    /**
112     * Switch whether we should put standard attributes or not
113     * @var  bool
114     * @see  putStandardAttributes()
115     */
116    var $useStandardAttributes = true;
117
118    /**
119     * Constructor
120     *
121     * Loads the RADIUS PECL/extension
122     *
123     * @return void
124     */
125    public function __construct()
126    {
127        $this->loadExtension('radius');
128    }
129
130    /**
131     */
132    protected function loadExtension($ext) {
133        if (extension_loaded($ext)) {
134            return true;
135        }
136        // if either returns true dl() will produce a FATAL error, stop that
137        if (
138            function_exists('dl') === false ||
139            ini_get('enable_dl') != 1 ||
140            ini_get('safe_mode') == 1
141        ) {
142            return false;
143        }
144        if (OS_WINDOWS) {
145            $suffix = '.dll';
146        } elseif (PHP_OS == 'HP-UX') {
147            $suffix = '.sl';
148        } elseif (PHP_OS == 'AIX') {
149            $suffix = '.a';
150        } elseif (PHP_OS == 'OSX') {
151            $suffix = '.bundle';
152        } else {
153            $suffix = '.so';
154        }
155        return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
156    }
157
158    /**
159     * Adds a RADIUS server to the list of servers for requests.
160     *
161     * At most 10 servers may be specified.	When multiple servers
162     * are given, they are tried in round-robin fashion until a
163     * valid response is received
164     *
165     * @param  string  $servername   Servername or IP-Address
166     * @param  integer $port         Portnumber
167     * @param  string  $sharedSecret Shared secret
168     * @param  integer $timeout      Timeout for each request
169     * @param  integer $maxtries     Max. retries for each request
170     * @return void
171     */
172    public function addServer($servername = 'localhost', $port = 0, $sharedSecret = 'testing123', $timeout = 3, $maxtries = 3)
173    {
174        $this->_servers[] = array($servername, $port, $sharedSecret, $timeout, $maxtries);
175    }
176
177    /**
178     * Returns an error message, if an error occurred.
179     *
180     * @return string
181     */
182    public function getError()
183    {
184        return radius_strerror($this->res);
185    }
186
187    /**
188     * Sets the configuration-file.
189     *
190     * @param  string  $file Path to the configuration file
191     * @return void
192     */
193    public function setConfigfile($file)
194    {
195        $this->_configfile = $file;
196    }
197
198    /**
199     * Puts an attribute.
200     *
201     * @param  integer $attrib       Attribute-number
202     * @param  mixed   $port         Attribute-value
203     * @param  type    $type         Attribute-type
204     * @return bool  true on success, false on error
205     */
206    public function putAttribute($attrib, $value, $type = null)
207    {
208        if ($type == null) {
209            $type = gettype($value);
210        }
211
212        switch ($type) {
213        case 'integer':
214        case 'double':
215            return radius_put_int($this->res, $attrib, $value);
216
217        case 'addr':
218            return radius_put_addr($this->res, $attrib, $value);
219
220        case 'string':
221        default:
222            return radius_put_attr($this->res, $attrib, $value);
223        }
224
225    }
226
227    /**
228     * Puts a vendor-specific attribute.
229     *
230     * @param  integer $vendor       Vendor (MSoft, Cisco, ...)
231     * @param  integer $attrib       Attribute-number
232     * @param  mixed   $port         Attribute-value
233     * @param  type    $type         Attribute-type
234     * @return bool  true on success, false on error
235     */
236    public function putVendorAttribute($vendor, $attrib, $value, $type = null)
237    {
238
239        if ($type == null) {
240            $type = gettype($value);
241        }
242
243        switch ($type) {
244        case 'integer':
245        case 'double':
246            return radius_put_vendor_int($this->res, $vendor, $attrib, $value);
247
248        case 'addr':
249            return radius_put_vendor_addr($this->res, $vendor,$attrib, $value);
250
251        case 'string':
252        default:
253            return radius_put_vendor_attr($this->res, $vendor, $attrib, $value);
254        }
255
256    }
257
258    /**
259     * Prints known attributes received from the server.
260     *
261     */
262    public function dumpAttributes()
263    {
264        foreach ($this->attributes as $name => $data) {
265            echo "$name:$data<br>\n";
266        }
267    }
268
269    /**
270     * Overwrite this.
271     */
272    public function open()
273    {
274    }
275
276    /**
277     * Overwrite this.
278     */
279    public function createRequest()
280    {
281    }
282
283    /**
284     * Puts standard attributes.
285     */
286    public function putStandardAttributes()
287    {
288        if (!$this->useStandardAttributes) {
289		    return;
290        }
291
292        if (isset($_SERVER)) {
293            $var = $_SERVER;
294        } else {
295            $var = $GLOBALS['HTTP_SERVER_VARS'];
296        }
297
298        $this->putAttribute(RADIUS_NAS_IDENTIFIER, isset($var['HTTP_HOST']) ? $var['HTTP_HOST'] : 'localhost');
299        $this->putAttribute(RADIUS_NAS_PORT_TYPE, RADIUS_VIRTUAL);
300        $this->putAttribute(RADIUS_SERVICE_TYPE, RADIUS_FRAMED);
301        $this->putAttribute(RADIUS_FRAMED_PROTOCOL, RADIUS_PPP);
302        $this->putAttribute(RADIUS_CALLING_STATION_ID, isset($var['REMOTE_HOST']) ? $var['REMOTE_HOST'] : '127.0.0.1');
303    }
304
305    /**
306     * Puts custom attributes.
307     */
308    public function putAuthAttributes()
309    {
310        if (isset($this->username)) {
311            $this->putAttribute(RADIUS_USER_NAME, $this->username);
312        }
313    }
314
315    /**
316     * Configures the radius library.
317     *
318     * @param  string  $servername   Servername or IP-Address
319     * @param  integer $port         Portnumber
320     * @param  string  $sharedSecret Shared secret
321     * @param  integer $timeout      Timeout for each request
322     * @param  integer $maxtries     Max. retries for each request
323     * @return bool  true on success, false on error
324     * @see addServer()
325     */
326    public function putServer($servername, $port = 0, $sharedsecret = 'testing123', $timeout = 3, $maxtries = 3)
327    {
328        if (!radius_add_server($this->res, $servername, $port, $sharedsecret, $timeout, $maxtries)) {
329            return false;
330        }
331        return true;
332    }
333
334    /**
335     * Configures the radius library via external configurationfile
336     *
337     * @param  string  $servername   Servername or IP-Address
338     * @return bool  true on success, false on error
339     */
340    public function putConfigfile($file)
341    {
342        if (!radius_config($this->res, $file)) {
343            return false;
344        }
345        return true;
346    }
347
348    /**
349     * Initiates a RADIUS request.
350     *
351     * @return bool  true on success, false on errors
352     */
353    public function start()
354    {
355        if (!$this->open()) {
356            return false;
357        }
358
359        foreach ($this->_servers as $s) {
360	        // Servername, port, sharedsecret, timeout, retries
361            if (!$this->putServer($s[0], $s[1], $s[2], $s[3], $s[4])) {
362                return false;
363            }
364        }
365
366        if (!empty($this->_configfile)) {
367            if (!$this->putConfigfile($this->_configfile)) {
368                return false;
369            }
370        }
371
372        $this->createRequest();
373        $this->putStandardAttributes();
374        $this->putAuthAttributes();
375        return true;
376    }
377
378    /**
379     * Sends a prepared RADIUS request and waits for a response
380     *
381     * @return mixed  true on success, false on reject, PEAR_Error on error
382     */
383    public function send()
384    {
385        $req = radius_send_request($this->res);
386        if (!$req) {
387            throw new Auth_RADIUS_Exception('Error sending request: ' . $this->getError());
388        }
389
390        switch($req) {
391        case RADIUS_ACCESS_ACCEPT:
392            if (is_subclass_of($this, 'auth_radius_acct')) {
393                throw new Auth_RADIUS_Exception('RADIUS_ACCESS_ACCEPT is unexpected for accounting');
394            }
395            return true;
396
397        case RADIUS_ACCESS_REJECT:
398            return false;
399
400        case RADIUS_ACCOUNTING_RESPONSE:
401            if (is_subclass_of($this, 'auth_radius_pap')) {
402                throw new Auth_RADIUS_Exception('RADIUS_ACCOUNTING_RESPONSE is unexpected for authentication');
403            }
404            return true;
405
406        default:
407            throw new Auth_RADIUS_Exception("Unexpected return value: $req");
408        }
409
410    }
411
412    /**
413     * Reads all received attributes after sending the request.
414     *
415     * This methods stores known attributes in the property attributes,
416     * all attributes (including known attibutes) are stored in rawAttributes
417     * or rawVendorAttributes.
418     * NOTE: call this function also even if the request was rejected, because the
419     * Server returns usualy an errormessage
420     *
421     * @return bool   true on success, false on error
422     */
423    public function getAttributes()
424    {
425
426        while ($attrib = radius_get_attr($this->res)) {
427
428            if (!is_array($attrib)) {
429                return false;
430            }
431
432            $attr = $attrib['attr'];
433            $data = $attrib['data'];
434
435            $this->rawAttributes[$attr] = $data;
436
437            switch ($attr) {
438            case RADIUS_FRAMED_IP_ADDRESS:
439                $this->attributes['framed_ip'] = radius_cvt_addr($data);
440                break;
441
442            case RADIUS_FRAMED_IP_NETMASK:
443                $this->attributes['framed_mask'] = radius_cvt_addr($data);
444                break;
445
446            case RADIUS_FRAMED_MTU:
447                $this->attributes['framed_mtu'] = radius_cvt_int($data);
448                break;
449
450            case RADIUS_FRAMED_COMPRESSION:
451                $this->attributes['framed_compression'] = radius_cvt_int($data);
452                break;
453
454            case RADIUS_SESSION_TIMEOUT:
455                $this->attributes['session_timeout'] = radius_cvt_int($data);
456                break;
457
458            case RADIUS_IDLE_TIMEOUT:
459                $this->attributes['idle_timeout'] = radius_cvt_int($data);
460                break;
461
462            case RADIUS_SERVICE_TYPE:
463                $this->attributes['service_type'] = radius_cvt_int($data);
464                break;
465
466            case RADIUS_CLASS:
467                $this->attributes['class'] = radius_cvt_string($data);
468                break;
469
470            case RADIUS_FRAMED_PROTOCOL:
471                $this->attributes['framed_protocol'] = radius_cvt_int($data);
472                break;
473
474            case RADIUS_FRAMED_ROUTING:
475                $this->attributes['framed_routing'] = radius_cvt_int($data);
476                break;
477
478            case RADIUS_FILTER_ID:
479                $this->attributes['filter_id'] = radius_cvt_string($data);
480                break;
481
482            case RADIUS_REPLY_MESSAGE:
483                $this->attributes['reply_message'] = radius_cvt_string($data);
484                break;
485
486            case RADIUS_VENDOR_SPECIFIC:
487                $attribv = radius_get_vendor_attr($data);
488                if (!is_array($attribv)) {
489                    return false;
490                }
491
492                $vendor = $attribv['vendor'];
493                $attrv = $attribv['attr'];
494                $datav = $attribv['data'];
495
496                $this->rawVendorAttributes[$vendor][$attrv] = $datav;
497
498                if ($vendor == RADIUS_VENDOR_MICROSOFT) {
499
500                    switch ($attrv) {
501                    case RADIUS_MICROSOFT_MS_CHAP2_SUCCESS:
502                        $this->attributes['ms_chap2_success'] = radius_cvt_string($datav);
503                        break;
504
505                    case RADIUS_MICROSOFT_MS_CHAP_ERROR:
506                        $this->attributes['ms_chap_error'] = radius_cvt_string(substr($datav,1));
507                        break;
508
509                    case RADIUS_MICROSOFT_MS_CHAP_DOMAIN:
510                        $this->attributes['ms_chap_domain'] = radius_cvt_string($datav);
511                        break;
512
513                    case RADIUS_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY:
514                        $this->attributes['ms_mppe_encryption_policy'] = radius_cvt_int($datav);
515                        break;
516
517                    case RADIUS_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES:
518                        $this->attributes['ms_mppe_encryption_types'] = radius_cvt_int($datav);
519                        break;
520
521                    case RADIUS_MICROSOFT_MS_CHAP_MPPE_KEYS:
522                        $demangled = radius_demangle($this->res, $datav);
523                        $this->attributes['ms_chap_mppe_lm_key'] = substr($demangled, 0, 8);
524                        $this->attributes['ms_chap_mppe_nt_key'] = substr($demangled, 8, RADIUS_MPPE_KEY_LEN);
525                        break;
526
527                    case RADIUS_MICROSOFT_MS_MPPE_SEND_KEY:
528                        $this->attributes['ms_chap_mppe_send_key'] = radius_demangle_mppe_key($this->res, $datav);
529                        break;
530
531                    case RADIUS_MICROSOFT_MS_MPPE_RECV_KEY:
532                        $this->attributes['ms_chap_mppe_recv_key'] = radius_demangle_mppe_key($this->res, $datav);
533                        break;
534
535                    case RADIUS_MICROSOFT_MS_PRIMARY_DNS_SERVER:
536                        $this->attributes['ms_primary_dns_server'] = radius_cvt_string($datav);
537                        break;
538                    }
539                }
540                break;
541
542            }
543        }
544
545        return true;
546    }
547
548    /**
549     * Frees resources.
550     *
551     * Calling this method is always a good idea, because all security relevant
552     * attributes are filled with Nullbytes to leave nothing in the mem.
553     *
554     */
555    public function close()
556    {
557        if ($this->res != null) {
558            radius_close($this->res);
559            $this->res = null;
560        }
561        $this->username = str_repeat("\0", strlen($this->username));
562        $this->password = str_repeat("\0", strlen($this->password));
563    }
564
565}
566
567/**
568 * class Auth_RADIUS_PAP
569 *
570 * Class for authenticating using PAP (Plaintext)
571 *
572 * @package Auth_RADIUS
573 */
574class Auth_RADIUS_PAP extends Auth_RADIUS
575{
576
577    /**
578     * Constructor
579     *
580     * @param  string  $username   Username
581     * @param  string  $password   Password
582     * @return void
583     */
584    public function __construct($username = null, $password = null)
585    {
586        parent::__construct();
587        $this->username = $username;
588        $this->password = $password;
589    }
590
591    /**
592     * Creates a RADIUS resource
593     *
594     * Creates a RADIUS resource for authentication. This should be the first
595     * call before you make any other things with the library.
596     *
597     * @return bool   true on success, false on error
598     */
599    function open()
600    {
601        $this->res = radius_auth_open();
602        if (!$this->res) {
603            return false;
604        }
605        return true;
606    }
607
608    /**
609     * Creates an authentication request
610     *
611     * Creates an authentication request.
612     * You MUST call this method before you can put any attribute
613     *
614     * @return bool   true on success, false on error
615     */
616    function createRequest()
617    {
618        if (!radius_create_request($this->res, RADIUS_ACCESS_REQUEST)) {
619            return false;
620        }
621        return true;
622    }
623
624    /**
625     * Put authentication specific attributes
626     *
627     * @return void
628     */
629    function putAuthAttributes()
630    {
631        if (isset($this->username)) {
632            $this->putAttribute(RADIUS_USER_NAME, $this->username);
633        }
634        if (isset($this->password)) {
635            $this->putAttribute(RADIUS_USER_PASSWORD, $this->password);
636        }
637    }
638
639}
640
641/**
642 * class Auth_RADIUS_CHAP_MD5
643 *
644 * Class for authenticating using CHAP-MD5 see RFC1994.
645 * Instead og the plaintext password the challenge and
646 * the response are needed.
647 *
648 * @package Auth_RADIUS
649 */
650class Auth_RADIUS_CHAP_MD5 extends Auth_RADIUS_PAP
651{
652    /**
653     * 8 Bytes binary challenge
654     * @var  string
655     */
656    var $challenge = null;
657
658    /**
659     * 16 Bytes MD5 response binary
660     * @var  string
661     */
662    var $response = null;
663
664    /**
665     * Id of the authentication request. Should incremented after every request.
666     * @var  integer
667     */
668    var $chapid = 1;
669
670    /**
671     * Constructor
672     *
673     * @param  string  $username   Username
674     * @param  string  $challenge  8 Bytes Challenge (binary)
675     * @param  integer $chapid     Requestnumber
676     * @return void
677     */
678    function __construct($username = null, $challenge = null, $chapid = 1)
679    {
680        parent::__construct();
681        $this->username = $username;
682        $this->challenge = $challenge;
683        $this->chapid = $chapid;
684    }
685
686    /**
687     * Put CHAP-MD5 specific attributes
688     *
689     * For authenticating using CHAP-MD5 via RADIUS you have to put the challenge
690     * and the response. The chapid is inserted in the first byte of the response.
691     *
692     * @return void
693     */
694    function putAuthAttributes()
695    {
696        if (isset($this->username)) {
697            $this->putAttribute(RADIUS_USER_NAME, $this->username);
698        }
699        if (isset($this->response)) {
700            $response = pack('C', $this->chapid) . $this->response;
701            $this->putAttribute(RADIUS_CHAP_PASSWORD, $response);
702        }
703        if (isset($this->challenge)) {
704            $this->putAttribute(RADIUS_CHAP_CHALLENGE, $this->challenge);
705        }
706    }
707
708    /**
709     * Frees resources.
710     *
711     * Calling this method is always a good idea, because all security relevant
712     * attributes are filled with Nullbytes to leave nothing in the mem.
713     */
714    public function close()
715    {
716        parent::close();
717        $this->challenge =  str_repeat("\0", strlen($this->challenge));
718        $this->response =  str_repeat("\0", strlen($this->response));
719    }
720
721}
722
723/**
724 * class Auth_RADIUS_MSCHAPv1
725 *
726 * Class for authenticating using MS-CHAPv1 see RFC2433
727 *
728 * @package Auth_RADIUS
729 */
730class Auth_RADIUS_MSCHAPv1 extends Auth_RADIUS_CHAP_MD5
731{
732    /**
733     * LAN-Manager-Response
734     * @var  string
735     */
736    var $lmResponse = null;
737
738    /**
739     * Wether using deprecated LM-Responses or not.
740     * 0 = use LM-Response, 1 = use NT-Response
741     * @var  bool
742     */
743    var $flags = 1;
744
745    /**
746     * Put MS-CHAPv1 specific attributes
747     *
748     * For authenticating using MS-CHAPv1 via RADIUS you have to put the challenge
749     * and the response. The response has this structure:
750     * struct rad_mschapvalue {
751     *   u_char ident;
752     *   u_char flags;
753     *   u_char lm_response[24];
754     *   u_char response[24];
755     * };
756     *
757     * @return void
758     */
759    function putAuthAttributes()
760    {
761        if (isset($this->username)) {
762            $this->putAttribute(RADIUS_USER_NAME, $this->username);
763        }
764        if (isset($this->response) || isset($this->lmResponse)) {
765            $lmResp = isset($this->lmResponse) ? $this->lmResponse : str_repeat ("\0", 24);
766            $ntResp = isset($this->response)   ? $this->response :   str_repeat ("\0", 24);
767            $resp = pack('CC', $this->chapid, $this->flags) . $lmResp . $ntResp;
768            $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_RESPONSE, $resp);
769        }
770        if (isset($this->challenge)) {
771            $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_CHALLENGE, $this->challenge);
772        }
773    }
774}
775
776/**
777 * class Auth_RADIUS_MSCHAPv2
778 *
779 * Class for authenticating using MS-CHAPv2 see RFC2759
780 *
781 * @package Auth_RADIUS
782 */
783class Auth_RADIUS_MSCHAPv2 extends Auth_RADIUS_MSCHAPv1
784{
785    /**
786     * 16 Bytes binary challenge
787     * @var  string
788     */
789    var $challenge = null;
790
791    /**
792     * 16 Bytes binary Peer Challenge
793     * @var  string
794     */
795    var $peerChallenge = null;
796
797    /**
798     * Put MS-CHAPv2 specific attributes
799     *
800     * For authenticating using MS-CHAPv1 via RADIUS you have to put the challenge
801     * and the response. The response has this structure:
802     * struct rad_mschapv2value {
803     *   u_char ident;
804     *   u_char flags;
805     *   u_char pchallenge[16];
806     *   u_char reserved[8];
807     *   u_char response[24];
808     * };
809     * where pchallenge is the peer challenge. Like for MS-CHAPv1 we set the flags field to 1.
810     * @return void
811     */
812    function putAuthAttributes()
813    {
814        if (isset($this->username)) {
815            $this->putAttribute(RADIUS_USER_NAME, $this->username);
816        }
817        if (isset($this->response) && isset($this->peerChallenge)) {
818            // Response: chapid, flags (1 = use NT Response), Peer challenge, reserved, Response
819            $resp = pack('CCa16a8a24',$this->chapid , 1, $this->peerChallenge, str_repeat("\0", 8), $this->response);
820            $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP2_RESPONSE, $resp);
821        }
822        if (isset($this->challenge)) {
823            $this->putVendorAttribute(RADIUS_VENDOR_MICROSOFT, RADIUS_MICROSOFT_MS_CHAP_CHALLENGE, $this->challenge);
824        }
825    }
826
827    /**
828     * Frees resources.
829     *
830     * Calling this method is always a good idea, because all security relevant
831     * attributes are filled with Nullbytes to leave nothing in the mem.
832     *
833     * @access public
834     */
835    function close()
836    {
837        parent::close();
838        $this->peerChallenge = str_repeat("\0", strlen($this->peerChallenge));
839    }
840}
841
842/**
843 * class Auth_RADIUS_Acct
844 *
845 * Class for RADIUS accounting
846 *
847 * @package Auth_RADIUS
848 */
849class Auth_RADIUS_Acct extends Auth_RADIUS
850{
851    /**
852     * Defines where the Authentication was made, possible values are:
853     * RADIUS_AUTH_RADIUS, RADIUS_AUTH_LOCAL, RADIUS_AUTH_REMOTE
854     * @var  integer
855     */
856    var $authentic = null;
857
858   /**
859     * Defines the type of the accounting request, on of:
860     * RADIUS_START, RADIUS_STOP, RADIUS_ACCOUNTING_ON, RADIUS_ACCOUNTING_OFF
861     * @var  integer
862     */
863    var $status_type = null;
864
865   /**
866     * The time the user was logged in in seconds
867     * @var  integer
868     */
869    var $session_time = null;
870
871   /**
872     * A uniq identifier for the session of the user, maybe the PHP-Session-Id
873     * @var  string
874     */
875    var $session_id = null;
876
877    /**
878     * Constructor
879     *
880     * Generates a predefined session_id. We use the Remote-Address, the PID, and the Current user.
881     * @return void
882     */
883    function __construct()
884    {
885        parent::__construct();
886
887        if (isset($_SERVER)) {
888            $var = $_SERVER;
889        } else {
890            $var = $GLOBALS['HTTP_SERVER_VARS'];
891        }
892
893        $this->session_id = sprintf("%s:%d-%s", isset($var['REMOTE_ADDR']) ? $var['REMOTE_ADDR'] : '127.0.0.1' , getmypid(), get_current_user());
894    }
895
896    /**
897     * Creates a RADIUS resource
898     *
899     * Creates a RADIUS resource for accounting. This should be the first
900     * call before you make any other things with the library.
901     *
902     * @return bool   true on success, false on error
903     */
904    function open()
905    {
906        $this->res = radius_acct_open();
907        if (!$this->res) {
908            return false;
909        }
910        return true;
911    }
912
913   /**
914     * Creates an accounting request
915     *
916     * Creates an accounting request.
917     * You MUST call this method before you can put any attribute.
918     *
919     * @return bool   true on success, false on error
920     */
921    function createRequest()
922    {
923        if (!radius_create_request($this->res, RADIUS_ACCOUNTING_REQUEST)) {
924            return false;
925        }
926        return true;
927    }
928
929  /**
930     * Put attributes for accounting.
931     *
932     * Here we put some accounting values. There many more attributes for accounting,
933     * but for web-applications only certain attributes make sense.
934     * @return void
935     */
936    function putAuthAttributes()
937    {
938        $this->putAttribute(RADIUS_ACCT_SESSION_ID, $this->session_id);
939        $this->putAttribute(RADIUS_ACCT_STATUS_TYPE, $this->status_type);
940        if (isset($this->session_time) && $this->status_type == RADIUS_STOP) {
941            $this->putAttribute(RADIUS_ACCT_SESSION_TIME, $this->session_time);
942        }
943        if (isset($this->authentic)) {
944            $this->putAttribute(RADIUS_ACCT_AUTHENTIC, $this->authentic);
945        }
946
947    }
948
949}
950
951/**
952 * class Auth_RADIUS_Acct_Start
953 *
954 * Class for RADIUS accounting. Its usualy used, after the user has logged in.
955 *
956 * @package Auth_RADIUS
957 */
958class Auth_RADIUS_Acct_Start extends Auth_RADIUS_Acct
959{
960   /**
961     * Defines the type of the accounting request.
962     * It is set to RADIUS_START by default in this class.
963     * @var  integer
964     */
965    var $status_type = RADIUS_START;
966}
967
968/**
969 * class Auth_RADIUS_Acct_Start
970 *
971 * Class for RADIUS accounting. Its usualy used, after the user has logged out.
972 *
973 * @package Auth_RADIUS
974 */
975class Auth_RADIUS_Acct_Stop extends Auth_RADIUS_Acct
976{
977   /**
978     * Defines the type of the accounting request.
979     * It is set to RADIUS_STOP by default in this class.
980     * @var  integer
981     */
982    var $status_type = RADIUS_STOP;
983}
984
985if (!defined('RADIUS_UPDATE')) {
986    define('RADIUS_UPDATE', 3);
987}
988
989/**
990 * class Auth_RADIUS_Acct_Update
991 *
992 * Class for interim RADIUS accounting updates.
993 *
994 * @package Auth_RADIUS
995 */
996class Auth_RADIUS_Acct_Update extends Auth_RADIUS_Acct
997{
998   /**
999     * Defines the type of the accounting request.
1000     * It is set to RADIUS_UPDATE by default in this class.
1001     * @var  integer
1002     */
1003    var $status_type = RADIUS_UPDATE;
1004}
1005
1006class Auth_RADIUS_Exception extends Exception {}