server; $this->name = $row->name; $ldap_port = $row->port; $ldap_version = $row->version; $ldap_tls = $row->tls; $ldap_binddn = $row->general_user; $ldap_bindpw = $row->general_pass; } else { return false; } /* * Initiate the connection to the server */ if (!$this->handle = ldap_connect($ldap_server, $ldap_port)) { watchdog('user', 'LDAP Connect failure to %server with %port', array('%server' => $ldap_server, '%port' => $ldap_port)); return false; } /* * Set the options needed for the connection */ ldap_set_option($this->handle, LDAP_OPT_PROTOCOL_VERSION, $ldap_version); ldap_set_option($this->handle, LDAP_OPT_REFERRALS, 0); if (($ldap_version == '3') && ($ldap_tls == '1')) { if (!function_exists('ldap_start_tls')) { watchdog('user', 'Could not start TLS. It does not seem to be supported by your PHP installation'); return false; } if (!ldap_start_tls($this->handle)) { watchdog('user', "Could not start TLS to server %name. (Error %errno: %error).", array('%name' => $ldap_name, '%errno' => ldap_errno($this->handle), '%error' => ldap_error($this->handle))); return false; } } /* * Define bind failure, changed in case of bind success */ $bind_success = false; /* * Do we have a user stored in the session and attempt a bind process. */ $stored_user_credentials = manage_user_credentials('get', $sid); /* * Try binding with the saved credentials, if they were returned. */ if (is_array($stored_user_credentials)) { $bind_success = ldap_bind($this->handle, $stored_user_credentials['binddn'], $stored_user_credentials['bindpw']); if ($bind_success === false) { watchdog('user', 'LDAP Bind failure for user %user. Error %errno: %error', array('%user' => $stored_user_credentials['bindpw'], '%errno' => ldap_errno($this->handle), '%error' => ldap_error($this->handle))); } } /* * If the connection failed(or not yet attempted), try the master user (if it exists) */ if (($bind_success === false) && ($ldap_binddn != '')) { $bind_success = ldap_bind($this->handle, $ldap_binddn, $ldap_bindpw); } /* * If the connection has failed again do a last attempt at annonymous bind */ if ($bind_success === false) { $bind_success = ldap_bind($this->handle, '', ''); } /* * Check that we have a successful connection to the server */ if ($bind_sucess === false) { watchdog('user', 'All bind attempts to LDAP server %name has failed, server is unusable', array('%name' => $ldap_name)); return false; } else { return true; } } /* * Unbind (and disconnect) from the ldap server. LDAP connection will be closed * when class implementing variable is unset or scripting has finished, but it's * in good behaviour to close a connection when finished and then unset the variable. * * @param none * * @return none * */ function disconnect() { if ($this->handle) { ldap_unbind($this->handle); } } /** * Handle the safe temporary session storage of a users credentials * * @param $action * Save, Get or Clear user credentials * @param $sid * An ID of the LDAP server configuration * @param $dn * The users DN if action is store * @param $password * The associated password for the DN if the action is store * * @return * An array with users DN and credential if get or boolean in case of action success / failure */ function manage_user_credentials($action, $sid. $dn = '', $password = '') { /* Get or generate random vector for mcrypt salt */ if (array_key_exists('ldap_integration', $_SESSION) && is_array($_SESSION['ldap_integration']) && array_key_exists('iv', $_SESSION['ldap_integration']) ) { $iv = base64_decode($_SESSION['ldap_integration']['iv']); } else { $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_CFB)); $_SESSION['ldap_integration']['iv'] = base64_encode($iv); } /* Generate user encryption/decryption key from session static but not stored data */ $user_key = substr(md5($_SERVER['HTTP_USER_AGENT'] . session_id()),0,16); /* Performe action chosen by function call */ switch($action) { case 'save': $user_enc_password = base64_encode(mcrypt_encrypt(MCRYPT_3DES, $user_key, $save_password, MCRYPT_MODE_CFB, $iv)); $_SESSION['ldap_integration'][$sid]['dn'] = $dn; $_SESSION['ldap_integration'][$sid]['cred'] = $user_enc_password; return true; break; case 'get': $user_enc_password = base64_decode($_SESSION['ldap_integration'][$sid]['cred']); if($enc_password === false) { return false; } else { $user_password = mcrypt_decrypt(MCRYPT_3DES, $user_key, $enc_password, MCRYPT_MODE_CFB, $iv); return array('binddn' => $_SESSION['ldap_integration'][$sid]['dn'] ,'bindpw' => $user_password); } break; case 'clear': unset($_SESSION['ldap_integration'][$sid]); break; default: return false; } } /* * Encrypt the password accordning to chosen hashes for storage in a LDAPv3 server. * * @param $password * The clear text password string to be encoded. * @param $hash * One of the hashes defined in RFC2307. * * @return * Encoded password according to the chosen hash, or false if server does not support that hash method. */ function ldapPasswordCreate($password, $hash) { /* * Investigate the PHP installation posibilites for encryption * * FIXME */ /* * Create the password hash using the selected hash. */ switch ($hash) { case 'crypt': $enc_passwd = '{CRYPT}' . crypt($password, drupal_substr($password, 0, 2)); break; case 'md5': $enc_passwd = '{MD5}' . base64_encode(md5($password)); break; case 'smd5': // Not yet stable, return clear text $enc_passwd = $password; break; case 'sha': $enc_passwd = "{SHA}" . base64_encode(pack("H*", sha1($password))); break; case 'ssha': // Not yet stable, return clear text $enc_passwd = $password; break; case 'cleartext': default: $enc_passwd = $password; } return $enc_passwd; } /* * General search function. Handle all searches an formats the result according to * RFC2251 (LDAPv3 specifications) which states that attribute type names are not case * sensitive and as PHP is case sensitive, standard practice is to make all attribute * type names lowercase. * * @param $base_dn * The base DN where we should start the search * @param $filter * Full syntax ldap serarch filter * @param $scope * Search depth of action, defaults to a search of the entire subtree from the given DN * @param $attributes * What attributes do we want to fetch, all if not * * @return * Formated array according to RFC2251, of boolean fail in case of error. */ function search($base_dn, $filter = '(objectClass=*)', $scope = 'sub', $attributes = array()) { /* * Do the selected search. */ switch ($scope) { case 'one': $search = @ldap_list($this->handle, $base_dn, $filter, $attributes); break; case 'base': $search = @ldap_read($this->handle, $base_dn, $filter, $attributes); break; case 'sub': default: $search = @ldap_search($this->handle, $base_dn, $filter, $attributes); break; } /* * Handle the error if any from the LDAP server */ if ($search === false) { watchdog('user', 'Unable to do a search on %name using %basedn. The server returned the following error: %errstr', array('%name' => $this->name, '%basedn' => $base_dn, '%errstr' => ldap_err2str($this->handle))); return false; } /* * Fetch and format the result according to RFC2251 */ $entry_id = ldap_first_entry($this->handle, $search); $i = 0; while($entry_id) { $entry_result = ldap_get_attributes($this->handle, $entry_id); for ($j=0;$j<$entry_result['count'];++$j) { $returned_attrib_name = $entry_result[$j]; $result_array[$i][strtolower($returned_attrib_name)] = $entry_result[$returned_attrib_name]; } $entry_id = ldap_next_entry($this->handle, $entry_id); ++$i; } $result_array['count'] = ($i + 1); return $result_array; } // ??? // // All functions from this point and forward are probably not needed, as these actions // ldap_mod_*, ldap_add, ldap_rename, ldap_delete should be called from the module itself // and not useing a wrapper function. The open ldap server handle is accessable through // the generated variable ie: $my_instance_of_LDAPInterface->handle // // ??? /* * Create an entry in the LDAP database. Will need to add verification * for specific entries insted of just logging an error. * * @param $dn * The new full RDN that you want to add. * @param $attributes * An array with the LDAP entries needed. * * @return none * */ function create_entry($dn, $attributes) { $action_status = ldap_add($this->handle, $dn, $attributes); if ($action_status === false) { watchdog('user', 'Unable to add an entry to %name with the RDN %basedn. The server returned the following error: %errstr', array('%name' => $this->name, '%basedn' => $dn, '%errstr' => ldap_err2str($this->handle))); return false; } return $ret; } /* * Move an entry from an old RDN to a new RDN. Not sure that this is needed * at the moment, as it entails a quite a problems for DN's with children. * * @param $current_rdn * The current RDN that shall be moved * @param $new_rdn * The new RDN for this entry * @param $new_parent_dn * The new parent DN. Essentially the RDN minus the unique identifier for the entry * * @return boolean * True / False according to report from the command */ function rename_entry($dn, $newrdn, $newparent) { $ret = ldap_rename($this->connection, $dn, $newrdn, $newparent, $deleteoldrdn); return $ret; } function delete_entry($dn) { $ret = ldap_delete($this->connection, $dn); return $ret; } /* * Modify attributes of an entry. Handles adding, changing and removing attributes * * @param $action * Add, replace or delete action * @param $base_dn * The DN where the attributes should be modified * @param $attributes * The properly formated attributes according to ldap_mod_* syntax * * @return bool * True in case of success, false otherwise */ function attributeModification($action, $base_dn, $attributes) { } }