Table of Contents

RADIUS Authentication Backend

Since the release 2013-05-10 “Weatherwax”
see AuthRadius plugin page

For releases 2012-10-13 “Adora Belle” and older
see info below

Radius is a authentication and accounting protocol for large departments, somehow similar to LDAP. If your department uses RADIUS to manage accounts, you can reuse that accounts for your dokuwiki.

Requirements

# pear install pecl/radius

Code

Save this under …/dokuwiki/inc/auth/radius.class.php:

radius.class.php
<?php
/**
 * RADIUS authentication backend
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Sebastian Menge <bastl@users.sf.net>
 */
 
define('DOKU_AUTH', dirname(__FILE__));
require_once(DOKU_AUTH.'/plain.class.php');
 
// FIXME: needed?
// we only accept page ids for auth_plain
//if(isset($_REQUEST['u']))
//  $_REQUEST['u'] = cleanID($_REQUEST['u']);
//if(isset($_REQUEST['acl_user']))
//  $_REQUEST['acl_user'] = cleanID($_REQUEST['acl_user']);
//// the same goes for password reset requests
//if(isset($_POST['login'])){
//  $_POST['login'] = cleanID($_POST['login']);
//}
 
// FIXME: better way to do this? must we support windows?
//load radius extension if required
if(!extension_loaded('radius')) {
	if (preg_match('/windows/i', getenv('OS'))) {
	    dl('php_radius.dll');
	} else {
	    dl('radius.so');
	}
}
 
class auth_radius extends auth_plain {
 
    /**
     * handle to the radius server
     */    
    var $radius = null;
 
 
    /**
     * radius config
     */
    var $cnf = null;
 
    /**
     * Constructor
     *
     * Carry out sanity checks to ensure the object is
     * able to operate. Sets no capabilities because we only auhtenticate via radius
     *
     * @author  Sebastian Menge <bastl@users.sf.net>
     */
    function auth_radius() {
 
      parent::auth_plain();
 
      global $conf;
      $this->cnf = $conf['auth']['radius'];
 
      // radius extension is needed
      if(!function_exists('radius_add_server')) {
        if ($this->cnf['debug'])
          msg("Radius err: PHP radius extension not found.",-1,__LINE__,__FILE__);
        $this->success = false;
        return;
      }
 
      // FIXME: defaults like this: (e.g. localhost:1812)
      // if(empty($this->cnf['groupkey'])) $this->cnf['groupkey'] = 'cn';
 
      // get parameters from config
      $host    = $this->cnf['host'];
      $port    = $this->cnf['port'];
      $secret  = $this->cnf['secret'];
      $timeout = $this->cnf['timeout'];
      $tries   = $this->cnf['tries'];
 
 
      //create handle and add server
      $this->radius = radius_auth_open();
 
      //try to connect
      if (!radius_add_server($this->radius,$host,$port,$secret,$timeout,$tries)){
        msg("Radius err: ". radius_strerror($this->radius),-1,__LINE__,__FILE__);
        $this->success = false;
        return;
      }
 
      // set additional capabilities
      $this->cando['modLogin'] = false;
      $this->cando['modPass'] = false;
//      $this->cando['external'] = true;  // does the module do external auth checking?
//      $this->cando['logoff']   = false; // has the module some special logoff method?
 
      return;
    }
 
    /**
     * Check user+password [required auth function]
     *
     * Checks if the given user exists and the given
     * plaintext password is correct
     *
     * @author  Sebastian Menge <bastl@users.sf.net>
     * @return  bool
     */
    function checkPass($user,$pass){
      if (! radius_create_request($this->radius,RADIUS_ACCESS_REQUEST)) {
        msg("Radius err: ". radius_strerror($this->radius),-1,__LINE__,__FILE__);
      }
 
      radius_put_attr($this->radius,RADIUS_USER_NAME,$user);
      radius_put_attr($this->radius,RADIUS_USER_PASSWORD,$pass);
 
      //send the actual request and return result
      switch (radius_send_request($this->radius)) {
        case RADIUS_ACCESS_ACCEPT:
          $data = $this->getUserData($user);
          return true;
          break;
        case RADIUS_ACCESS_REJECT:
          return false;
          break;
        case RADIUS_ACCESS_CHALLENGE:
          //msg("Radius: CHAP not supported by auth_radius.",-1);
          return false;
          break;
        default:
          msg('Radius Error: ('.$user.') ' . radius_strerror($this->radius),-1,__LINE__,__FILE__);
      }
      return false;
    }
 
    /**
     * Return user info
     *
     * Returns info about the given user needs to contain
     * at least these fields:
     *
     * name string  full name of the user, for now just $user
     * mail string  email address of the user, for now $user@$mailhost. 
     *     $mailhost is set in the config
     * grps array   list of groups the user is in, for now empty. 
     *     Could be calculated from the username regexes in the config.
     *
     * @author  Sebastian Menge <bastl@users.sf.net>
     */
    function getUserData($user) {
 
      if($this->users === null) parent::_loadUserData();
      $plain_data = isset($this->users[$user]) ? $this->users[$user] : false;
 
      if ($plain_data) {
 
        $data['grps'] = $plain_data['grps'];
        $data['mail'] = $plain_data['mail'];
        $data['name'] = $plain_data['name'];
 
      } else {
 
        $data['grps'] = array();
        $data['mail'] = $user.'@'.$this->cnf['mailhost'];
        $data['name'] = $user." (".implode(", ",$data['grps']).")";
 
      }
 
      //if no email address is set, use login@mailhost
      if (count ($data['mail']) == 0) {
        $data['mail'] = $user.'@'.$this->cnf['mailhost'];
      }
 
      //if no groups are found place the user in 'defaultgroup'
      if (count ($data['grps']) == 0) {
        $data['grps'][] = $this->cnf['defaultgroup'];
      }
 
      return $data;
    }
 
 
 
    /**
     * Return local user info
     *
     * Returns info about the given user from the local db. 
     * Needs to contain
     * at least these fields:
     *
     * name string  full name of the user
     * mail string  email addres of the user
     * grps array   list of groups the user is in
     *
     * @author  Andreas Gohr <andi@splitbrain.org>
     */
    function getLocalUserData($user){
 
      if($this->users === null) $this->_loadUserData();
      return isset($this->users[$user]) ? $this->users[$user] : false;
    }
 
    /**
     * Create a new User
     *
     * Returns false if the user already exists, null when an error
     * occured and true if everything went well.
     *
     * The new user will be added to the default group by this
     * function if grps are not specified (default behaviour).
     *
     * @author  Andreas Gohr <andi@splitbrain.org>
     * @author  Chris Smith <chris@jalakai.co.uk>
     */
    function createUser($user,$pwd,$name,$mail,$grps=null){
      global $conf;
 
      // user mustn't already exist
      if ($this->getLocalUserData($user) !== false) return false;
 
      $pass = "pass_set_by_radius";
 
      // set default group if no groups specified
      if (!is_array($grps)) $grps = array($conf['defaultgroup']);
 
      // prepare user line
      $groups = join(',',$grps);
      $userline = join(':',array($user,$pass,$name,$mail,$groups))."\n";
 
      if (io_saveFile(AUTH_USERFILE,$userline,true)) {
        $this->users[$user] = compact('pass','name','mail','grps');
        return $pwd;
      }
 
      msg('The '.AUTH_USERFILE.' file is not writable. Please inform the Wiki-Admin',-1);
      return null;
    }
 
 
 
 
}
 
//Setup VIM: ex: et ts=2 enc=utf-8 :

Configuration

local.protected.php
//enable radius auth
$conf['authtype']    = 'radius';
 
//radius Config
$conf['auth']['radius']['host'] = 'radius.mycompany.com';
$conf['auth']['radius']['port'] = '0'; //0 reuses data from /etc/services
$conf['auth']['radius']['secret'] = 'radiussecret';
$conf['auth']['radius']['timeout'] = '3';
$conf['auth']['radius']['tries'] = '3';
$conf['auth']['radius']['mailhost'] = 'mail.mycompany.com';

Put the following in …/dokuwiki/lib/plugins/config/settings/config.metadata.php to edit the main settings it via the config plugin.

config.metadata.php
//radius-settings
$meta['auth____radius____host'] = array('string');
$meta['auth____radius____port'] = array('string');
$meta['auth____radius____timeout'] = array('string');
$meta['auth____radius____tries'] = array('string');
$meta['auth____radius____secret'] = array('password');
$meta['auth____radius____mailhost'] = array('string');
$meta['auth____radius____defaultgroup'] = array('string');

Users can be managed via the user-manager plugin now. The password of users.auth.php is ignored. Just put a dummy value in there.

Users have to change the lines

      $this->cando['modLogin'] = false;
      $this->cando['modPass'] = false;

such that the right capabilities are set. (set modLogin to true.)