Table des matières

ownCloud sur ActiveDiretory

L'objectif étant de permettre à ownCloud de monter automatiquement les disques réseaux paramétrer par les “Group Policy”

But

Le but étant de considérer que la personne déployant ownCloud a correctement configuré son domaine. Se faisant, aucune configuration ne devrait être nécessaire. Afin de supporter des installations multi-domaines, l'authentification est gérée par le même plugin et prend en compte le nom d'utilisateur 'username@domain'.

Informations

LDAP

Sur un domaine “test.artnum.ch”, les GPO sont indiquées sur l'object dc=test,dc=artnum,dc=ch, attribut gPLink :

$ ldapsearch -s base -b "dc=test,dc=artnum,dc=ch"
 
# ... <snip>
gPLink: [LDAP://cn={5EE8813F-3AFE-483C-AC76-CB06FCFFDF0D},cn=policies,cn=syste
 m,DC=test,DC=artnum,DC=ch;2][LDAP://cn={0071FA96-BA06-4033-AB98-5FE59F5C0565}
 ,cn=policies,cn=system,DC=test,DC=artnum,DC=ch;2][LDAP://CN={31B2F340-016D-11
 D2-945F-00C04FB984F9},CN=Policies,CN=System,DC=test,DC=artnum,DC=ch;0]
# ...

Une requête subséquente sur les éléments indiqué permet de trouver le chemin effectif des GPO, attribut gPCFileSysPath :

$ ldapsearch -s base -b "cn={5EE8813F-3AFE-483C-AC76-B06FCFFDF0D},cn=policies,cn=system,dc=test,dc=artnum,dc=ch"
# ... <snip>
gPCFileSysPath: \\test.artnum.ch\SysVol\test.artnum.ch\Policies\{5EE8813F-3AFE-483C-AC76-CB06FCFFDF0D}
# ...

Sysvol

À partir du chemin sysvol, les disques réseaux se trouvent suivant le chemin 'User/Preferences/Drives' dans lequel se trouve un fichier Drives.xml

<?xml version="1.0" encoding="utf-8"?>
<Drives clsid="{8FDDCC1A-0C3C-43cd-A6B4-71A6DF20DA8C}"><Drive clsid="{935D1B74-9CB8-4e3c-9914-7DD559B7A417}" name="F:" status="F:" image="2" changed="2019-02-27 16:23:02" uid="{D1796459-45F2-495A-953D-CB34DE5CF4BB}" userContext="1" bypassErrors="1"><Properties action="U" thisDrive="NOCHANGE" allDrives="NOCHANGE" userName="" path="\\samba.test.artnum.ch\finance" label="Finance" persistent="1" useLetter="1" letter="F"/></Drive>
</Drives>

Implémentation

DNS

Pour trouver le serveur LDAP d'un domaine AD, il suffit de questionner DNS pour les enregistrement SRV, pour se faire :

function get_ldap ($domain) {
  foreach (array('_ldap._tcp.pdc._msdcs', '_ldap._tcp.dc._msdcs') as $i) {
    $dns_result = dns_get_record($i . '.' . $domain, DNS_SRV);
    if ($dns_result && !empty($dns_result) && !empty($dns_result[0]['target'])) {
      return $dns_result[0]['target'];
    }  
  }
  return false;
}

LDAP

Connexion

La première phase est de trouver le “naming context” de l'annuaire. Pour se faire une requête anonyme sur l'annuaire permet d'obtenir cette information. Comme cette requête permet d'obtenir des informations de configuration, nous pouvons établir la connexion à l'annuaire durant cette opération

function connect_ldap ($server) {
  $root = null;
  $lc = ldap_connect($server);
  $result = ldap_read($lc, '', '(objectclass=*)', array('defaultnamingcontext', 'supportedldapversion'));
  if ($result && ($entry = ldap_first_entry($lc, $result))) {
    $nctx = ldap_get_values($lc, $entry, 'defaultnamingcontext');
    /* first one should be the naming context, but do some checking */
    for ($i = 0; $i < $nctx['count']; $i++) {
      if (is_null($root)) { $root = $nctx[$i]; continue; }
      if (strpos(strtolower($nctx[$i]), strtolower($root)) === FALSE) {
        $root = $nctx[$i];
      }
    }
 
    $opt_version = 0;
    $version = ldap_get_values($lc, $entry, 'supportedldapversion');
    for ($i = 0; $i < $version['count']; $i++) {
      if ($opt_version < intval($version[$i])) {
        $opt_version = intval($version[$i]);
      }
    }
    ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $opt_version);
  }
  return array($lc, $root);
}

Authentification

Une fois la connexion établie, nous pouvons directement nous authentifier. ActiveDirectory nécessite une authentification avec TLS activé. Il est donc nécessaire de démarrer TLS. L'administrateur a pris la peine d'établir l'autorité de confiance sur le serveur Web afin qu'il n'y ait besoin d'aucune configuration particulière.

function auth_ldap ($lc, $user, $pass) {
  if (ldap_start_tls($lc)) {
    if (ldap_bind($lc, $user, $pass)) {
      return true;			
    }
  }
 
  return false;
}

Obtention des chemins GPO

Une fois authentifié, nous avons un utilisateur valide, donc pouvant se connecter sur les partages réseaux, et nous pouvons donc obtenir les chemins afin d'en extraire les informations.

function get_gpo_path($lc, $rootdn) {
  $gpo_paths = array();
  $result = ldap_read($lc, $rootdn, '(objectclass=*)', array('gplink'));
  if ($result && ($entry = ldap_first_entry($lc, $result))) {
    $gplinks = ldap_get_values($lc, $entry, 'gplink');
    for ($i = 0; $i < $gplinks['count']; $i++) {
      if(preg_match_all('/LDAP:\/\/([^;]*);([0-9]+)/', $gplinks[$i], $matches)) {
        foreach ($matches[1] as $dn) {
          $res = ldap_read($lc, $dn, '(objectclass=*)', array('gpcfilesyspath'));
          if ($res &&  ($e = ldap_first_entry($lc, $res))) {
            $paths = ldap_get_values($lc, $e, 'gpcfilesyspath');
            for ($j = 0; $j < $paths['count']; $j++) {
              $gpo_paths[] = $paths[$j];
            }
          }
        }
      }
    }
  }
  return $gpo_paths;
}

Test

En mettant tout ça ensemble, avec un nom d'utilisateur et un mot de passe, nous devrions pouvoir nous authentifier et obtenir les chemins des GPO pour le domaine :

$user = "pierre@test.artnum.ch";
$password = "test1234";
 
$server = get_ldap(explode($user, '@', 2)[0]);
list ($lc, $rootdn) = connect_ldap($server);
if (auth_ldap($lc, $user, $password)) {
	print_r(get_gpo_path($lc, $rootdn));
}
 
/* result on my test domain 
Array
(
    [0] => \\test.artnum.ch\SysVol\test.artnum.ch\Policies\{5EE8813F-3AFE-483C-AC76-CB06FCFFDF0D}
    [1] => \\test.artnum.ch\SysVol\test.artnum.ch\Policies\{0071FA96-BA06-4033-AB98-5FE59F5C0565}
    [2] => \\test.artnum.ch\sysvol\test.artnum.ch\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}
)
*/