[pmwiki-users] LDAP / PAM Module

Patrick R. Michaud pmichaud at pobox.com
Thu Aug 4 08:42:30 CDT 2005

On Thu, Aug 04, 2005 at 08:21:03AM +0200, Gunnar Wagenknecht wrote:
> Has anyone achieved to get PmWiki running with LDAP or Linux PAM? I'd
> appreciate any code snippets if someone like to share them.

A couple of weeks ago I posted a version of authuser.php that would
allow authentication against LDAP, but AFAIK nobody's tried it out
on a "real system" yet.  I'm attaching it again here, and will go 
ahead and stick it in the distribution for the next release.  
Just copy it over the existing scripts/authuser.php file.

You can read more about the script at
If there are any questions or it doesn't seem to work, let me know
and we can work on it a bit.

-------------- next part --------------
<?php if (!defined('PmWiki')) exit();
/*  Copyright 2005 Patrick R. Michaud (pmichaud at pobox.com)
    This file is part of PmWiki; 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.  See pmwiki.php for full details.

    The APR compatible MD5 encryption algorithm in _crypt() below is 
    based on code Copyright 2005 by D. Faure and the File::Passwd
    PEAR library module by Mike Wallner <mike at php.net>.

    This script enables simple authentication based on username and 
    password combinations.  At present this script can authenticate
    from passwords held in arrays or in .htpasswd-formatted files,
    but eventually it will support authentication via sources such
    as LDAP and Active Directory.

    To configure a .htpasswd-formatted file for authentication, do
        $AuthUser['htpasswd'] = '/path/to/.htpasswd';
    prior to including this script.  

    Individual username/password combinations can also be placed
    directly in the $AuthUser array, such as:
        $AuthUser['pmichaud'] = crypt('secret');

    To authenticate against an LDAP server, put the url for
    the server in $AuthUser['ldap'], as in:
        $AuthUser['ldap'] = 'ldap://ldap.example.com/ou=People,o=example?uid';

# Let's set up an authorization prompt that includes usernames.
SDV($AuthPromptFmt, array(&$PageStartFmt,
  "<p><b>Password required</b></p>
    <form name='authform' action='{$_SERVER['REQUEST_URI']}' method='post'>
      Name: <input tabindex='1' type='text' name='authid' value='' /><br />
      Password: <input tabindex='2' type='password' name='authpw' value='' />
      <input type='submit' value='OK' />\$PostVars</form>
      <script language='javascript'><!--
        document.authform.authid.focus() //--></script>", &$PageEndFmt));

# This is a helper function called when someone meets the
# authentication credentials:
function AuthenticateUser($authid) {
  $GLOBALS['AuthId'] = $authid;
  @session_start(); $_SESSION['authid'] = $authid;

# If the admin hasn't configured any password entries, just return.
if (!$AuthUser) return;

# Now, let's get the $id and $pw to be checked -- we'll first take them 
# from a submitted form, if any; if not there then we'll check and see
# if they're available from HTTP basic authentication.  If we don't
# have any $id at all, we just exit since there's nothing to 
# authenticate here.
if (@$_POST['authid']) 
  { $id = $_POST['authid']; $pw = $_POST['authpw']; }
else if (@$_SERVER['PHP_AUTH_USER']) 
  { $id = $_SERVER['PHP_AUTH_USER']; $pw = $_SERVER['PHP_AUTH_PW']; }
else return;

# Okay, we have $id and $pw, now let's see if we can find any
# matching entries.  First, let's check the $AuthUser array directly:
if (@$AuthUser[$id]) 
  foreach((array)($AuthUser[$id]) as $c)
    if (crypt($pw, $c) == $c) { AuthenticateUser($id); return; }

# Now lets check any .htpasswd file equivalents
foreach((array)($AuthUser['htpasswd']) as $f) {
  $fp = fopen($f, "r"); if (!$fp) continue;
  while ($x = fgets($fp, 1024)) {
    $x = rtrim($x);
    list($i, $c, $r) = explode(':', $x, 3);
    if ($i == $id && _crypt($pw, $c) == $c) 
      { fclose($fp); AuthenticateUser($id); return; }

# LDAP authentication.  
if ($AuthUser['ldap'] &&
        $AuthUser['ldap'], $match)) {
  list($z, $server, $port, $path) = $match;
  list($basedn, $attr, $sub) = explode('?', $path);
  if (!$port) $port=389;
  if (!$attr) $attr = 'uid';
  if (!$sub) $attr = 'one';
  $ds = ldap_connect($server, $port);
  ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
  if (ldap_bind($ds)) {
    $fn = ($sub == 'sub') ? 'ldap_search' : 'ldap_list';
    $sr = $fn($ds, $basedn, "($attr=$id)", array($attr));
    $x = ldap_get_entries($ds, $sr);
    if ($x['count'] == 1) {
      $dn = $x[0]['dn'];
      if (ldap_bind($ds, $dn, $pw)) AuthenticateUser($id);

#  The _crypt function provides support for SHA1 encrypted passwords 
#  (keyed by '{SHA}') and Apache MD5 encrypted passwords (keyed by 
#  '$apr1$'); otherwise it just calls PHP's crypt() for the rest.
#  The APR MD5 encryption code was contributed by D. Faure.

function _crypt($plain, $salt=null) {
  if (strncmp($salt, '{SHA}', 5) == 0) 
    return '{SHA}'.base64_encode(pack('H*', sha1($plain)));
  if (strncmp($salt, '$apr1$', 6) == 0) {
    preg_match('/^\\$apr1\\$([^$]+)/', $salt, $match);
    $salt = $match[1];
    $length = strlen($plain);
    $context = $plain . '$apr1$' . $salt;
    $binary = pack('H32', md5($plain . $salt . $plain));
    for($i = $length; $i > 0; $i -= 16) 
      $context .= substr($binary, 0, min(16, $i));
    for($i = $length; $i > 0; $i >>= 1)
      $context .= ($i & 1) ? chr(0) : $plain{0};
    $binary = pack('H32', md5($context));
    for($i = 0; $i < 1000; $i++) {
      $new = ($i & 1) ? $plain : $binary;
      if ($i % 3) $new .= $salt;
      if ($i % 7) $new .= $plain;
      $new .= ($i & 1) ? $binary : $plain;
      $binary = pack('H32', md5($new));
    $q = '';
    for ($i = 0; $i < 5; $i++) {
      $k = $i + 6;
      $j = $i + 12;
      if ($j == 16) $j = 5;
      $q = $binary{$i}.$binary{$k}.$binary{$j} . $q;
    $q = chr(0).chr(0).$binary{11} . $q;
    $q = strtr(strrev(substr(base64_encode($q), 2)),
    return "\$apr1\$$salt\$$q";
  return crypt($plain, $salt);

More information about the pmwiki-users mailing list