--- html/includes/authentication/ldap.inc.php	2014-11-04 17:14:27.000000000 +0100
+++ html/includes/authentication/ldap.inc.php	2015-02-09 23:11:12.319931939 +0100
@@ -39,6 +39,41 @@
   }
 }
 
+function search_user($ldap_group, $userdn, $depth)
+{
+  global $ds, $config;
+  
+  $compare = ldap_compare($ds, $ldap_group, $config['auth_ldap_groupmemberattr'], $userdn);
+  
+  if ($compare === TRUE)
+  {
+    return TRUE;
+  } 
+  elseif (($config['auth_ldap_recursive'] === true) && ($depth < $config['auth_ldap_recursive_maxdepth']))
+  {
+    $depth = $depth + 1;
+    
+    $filter = "(&(objectCategory=group)(objectclass=group)(memberOf=". $ldap_group ."))";
+    $ldap_search = ldap_search($ds, trim($config['auth_ldap_suffix'], ', '), $filter, array("distinguishedname"));
+    $ldap_results = ldap_get_entries($ds, $ldap_search);
+    
+    array_shift($ldap_results);
+    
+    foreach($ldap_results as $element)
+    {
+      $result = search_user($element['distinguishedname'][0], $userdn, $depth); 
+      if ($result === TRUE)
+      {
+        return TRUE;
+      }
+    }
+  }
+  else
+  {
+    return FALSE;
+  }
+}
+
 // DOCME needs phpdoc block
 function ldap_init()
 {
@@ -108,8 +143,8 @@
           foreach ($config['auth_ldap_group'] as $ldap_group)
           {
             print_debug("LDAP[Authenticate][Comparing: " . $ldap_group . "][".$config['auth_ldap_groupmemberattr']."=$userdn]");
-            $compare = ldap_compare($ds, $ldap_group, $config['auth_ldap_groupmemberattr'], $userdn);
-
+            $compare = search_user($ldap_group, $userdn, 0);
+            
             if ($compare === -1)
             {
               print_debug("LDAP[Authenticate][Compare LDAP error: " . ldap_error($ds) . "]");
@@ -120,7 +155,7 @@
               // $compare === TRUE
               print_debug("LDAP[Authenticate][Processing group: $ldap_group][Matched]");
               return 1;
-            } // FIXME does not support nested groups
+            }
           }
         }
       }
@@ -216,8 +251,8 @@
     // So, we foreach our locally known groups instead.
     foreach ($config['auth_ldap_groups'] as $ldap_group => $ldap_group_info)
     {
-      $compare = ldap_compare($ds, 'cn=' . $ldap_group . ',' . $config['auth_ldap_groupbase'], $config['auth_ldap_groupmemberattr'], $userdn);
-
+      $compare = search_user('cn=' . $ldap_group . ',' . $config['auth_ldap_groupbase'], $userdn, 0);
+      
       if ($compare === -1)
       {
         print_debug("LDAP[UserLevel][Compare LDAP error: " . ldap_error($ds) . "]");
@@ -311,14 +346,14 @@
       $user_id  = ldap_internal_auth_user_id($entries[$i]);
 
       $userdn = ($config['auth_ldap_groupmembertype'] == 'fulldn' ? $entries[$i]['dn'] : $username);
-
       print_debug("LDAP[UserList][Compare: " . implode('|',$config['auth_ldap_group']) . "][".$config['auth_ldap_groupmemberattr']."][$userdn]");
 
       foreach ($config['auth_ldap_group'] as $ldap_group)
       {
         $authorized = 0;
-        $compare = ldap_compare($ds, $ldap_group, $config['auth_ldap_groupmemberattr'], $userdn);
-
+	
+        $compare = search_user($ldap_group, $userdn, 0);
+        
         if ($compare === -1)
         {
           print_debug("LDAP[UserList][Compare LDAP error: " . ldap_error($ds) . "]");
@@ -330,7 +365,7 @@
           print_debug("LDAP[UserList][Authorized: $userdn for group $ldap_group]");
           $authorized = 1;
           break;
-        } // FIXME does not support nested groups
+        }
       }
 
       if (!isset($config['auth_ldap_group']) || $authorized)
--- includes/defaults.inc.php	2014-11-21 13:33:07.000000000 +0100
+++ includes/defaults.inc.php	2015-02-09 22:48:21.459927476 +0100
@@ -542,6 +542,8 @@
 #$config['auth_ldap_ad_domain'] = "ad.yourcorp.com";   // AD domain name (fqdn form), used to determine DCs if server list is unset
 $config['auth_ldap_port']   = 389;                    // LDAP server port
 $config['auth_ldap_starttls'] = 'no';                 // Use STARTTLS ('no', 'optional' or 'require')
+$config['auth_ldap_recursive'] = TRUE;		      // Active Directory recursive lookup for nested groups
+$config['auth_ldap_recursive_maxdepth'] = 3;          // Max depth for recursive lookup
 $config['auth_ldap_prefix'] = "uid=";
 $config['auth_ldap_suffix'] = ",ou=People,dc=example,dc=com";
 #$config['auth_ldap_group']  = array("cn=observium,ou=groups,dc=example,dc=com");