BzzWare AS, Norway
This program is free software; 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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/***************************************************************************
* lwat_login - tries to log in user either given or stored admindn/adminpw
* lwat_key - Reads session key, or create a new one
* lwat_ldap - connects to the ldap-server, with proper options set
* reportLdapError - Reports error number and textual message of
* ldap error occured
* loadConfig - Loads configuration, either from config.php, or use
* predefined values
* loadLocale - Checks for browser language and tries to load locale
* xorstring - xors a string, to either crypt or decrypt data
* readKey - Decodes data posted through the web-browser
* debug - Displays data top debug through development
* pwgen - generates a random password
* cryptgen - generates a crypted password from a given password
* getnextid - fetch (and update) the next id to be used. This is
* stored in ldap under ou=variables
* get_sn - parses a full name, and fetches the surname
* chk_username - Checks if a given username is already taken
* get_username - generates a username from a fullname
* get_givenName - parses a fullname, and extract a givenName
* get_sambasid - looks up the sambasid to be used.
* ldapAddUser - Adds a user into ldap
* sambaAddInfo - Adds samba information for a user
* userAddGroup - Add a user into a group
***************************************************************************/
require_once ("createlm.php");
/**********************************************************************
* lwat_login - tries to log in user either given or stored
* admindn/adminpw
*****************
* Arguments
* ldap - ldap connection
* adminUser - the username to log in as
* admindn - password ?
* key - key used to xor the stored password
* Returns
* authenticated - True if logged in succesfully
*****************
* if AdminUser is given
* Loop while possible to look closer to base
* Try to look up the full dn of the admin user
* if found
* try to connect as the admin user
* if succesfull
* store admindn and admin pw
* return true
* look closer to the ldap base for a user
* return false
* if no stored admin dn
* return failure
* fetch stored password
* if able to bind using stored dn/pw
* update cookie
* return success
* return failure
**********************************************************************/
function lwat_login ($ldap, $adminUser, $adminpw, $key) {
global $base ;
if (!empty ($adminUser)) {
$loginbase = $base ;
$filter = '(|(&(objectClass=posixAccount)(uid=' . $adminUser . '))(&(objectClass=simpleSecurityObject)(cn=' . $adminUser . ')))' ;
$want = array ("dn") ;
while (!empty ($loginbase)) {
$result = @ldap_search($ldap, $loginbase, $filter, $want);
$entries = @ldap_get_entries ($ldap, $result);
$admindn = htmlspecialchars($entries[0]['dn']) ;
$bind = @ldap_bind ($ldap, $admindn, $adminpw);
if ($bind) {
$xorstring = xorstring ($key, $adminpw);
$packstring = unpack ("H*", $xorstring) ;
setcookie ('admindn', $admindn, time () + 1800) ;
setcookie ('xorstring', $packstring[1], time () + 1800) ;
return true ;
} else {
$loginbase = ldap_explode_dn ($loginbase,0) ;
array_splice ($loginbase, 0,2) ;
$loginbase = implode (",", $loginbase) ;
}
}
return false ;
}
$admindn = $_COOKIE['admindn'] ;
if (empty ($admindn))
return false ;
$packstring = $_COOKIE['xorstring'] ;
$xorstring = pack ("H*", $packstring);
$adminpw = xorstring ($key, $xorstring) ;
$bind = @ldap_bind ($ldap, $admindn, $adminpw);
if ($bind) {
setcookie ("admindn", $admindn, time () + 1800) ;
setcookie ("xorstring", $packstring, time () + 1800) ;
return true ;
}
return false ;
}
/**********************************************************************
* lwat_key - Reads session key, or create a new one
*****************
* Returns
* key - either read from session, or newlyt created
*****************
* Read key from session
* if no key found
* create new key
* store key in session
* return key value
**********************************************************************/
function lwat_key () {
if (isset($_SESSION['key']))
$key = $_SESSION['key'] ;
else {
$key = substr (crypt(sprintf (gettimeofday (true))),4) ;
$_SESSION['key'] = $key ;
}
return $key ;
}
/**********************************************************************
* lwat_ldap - connects to the ldap-server, with proper options set
*****************
* Returns
* ldap - ldap connection
*****************
* connect to ldap host
* set ldap options
* if not tls is available
* check error
* error 2: tls not available, ignore ?
* error 81: unable to contact ldap server
* other error:
* report ldaperror
*
* if error
* close conection
* return 0
* return ldap connection
**********************************************************************/
function lwat_ldap () {
global $ldaphost, $tlsRequire ;
$ldaperr = 0 ;
$ldap=ldap_connect ($ldaphost);
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
if ($tlsRequire) {
if (!@ldap_start_tls($ldap)) {
$ldaperr = ldap_errno ($ldap) ;
switch ($ldaperr) {
case 2:
printf (_("Unable to start encrypted connection with the ldapserver"), $ldaphost) ;
break ;
case 81:
printf (_("Sorry, but we are not able to contact your ldapserver running on the host %s"), $ldaphost) ;
break ;
default:
reportLdapError (ldap_errno ($ldap), "Tried to connect") ;
break ;
}
}
}
if ($ldaperr) {
ldap_close ($ldap) ;
die ("
" . _("Please check your configuration")) ;
}
return $ldap ;
}
/**********************************************************************
* reportLdapError - Reports error number and textual message of
* ldap error occured
*****************
* Arguments:
* ldaperr - Error code from ldap
* myAction - Description of what lead to the error from lwat
*****************
* Display an error message to the user
* Also display links to
* search lwat tickets for previously reported errors
* create a new ticket with some debug information
**********************************************************************/
function reportLdapError ($ldaperr, $myAction) {
printf (_("An Unknown error occured.") . "
") ;
printf (_("The error code from ldap was: %d") . "
", $ldaperr) ;
printf (_("The error string from ldap was: %s") ."
", ldap_err2str ($ldaperr));
printf (_("You may search for the error among the lwat tickets, or if not found, consider reporting it"),
sprintf ("http://lwat.org/cgi-bin/trac.cgi/search?ticket=on&q=%%22Error %d when %s%%22", $ldaperr, $myAction),
sprintf ("mailto:trac@lwat.org?subject=Error %d when %s&Body=The error message was:%%0A%s", $ldaperr, $myAction, ldap_err2str($ldaperr))) ;
}
/***************************************************************************
* loadConfig - Loads configuration, either from config.php, or use
* predefined values
***********************
* Load predefined values
* if config.php exists load settings
**************************************************************************/
function loadConfig () {
global $domain, $base, $ldaphost, $homelocation, $useLisGroup,
$groupbase, $authbase, $hostbase, $netgroupbase,
$automountbase, $variablesbase, $uname_maxlen,
$groupprefix, $authprefix, $hostprefix, $netgroupprefix,
$automountprefix, $variablesprefix, $uname_prefix,
$minPwLength, $minPwUpper, $minPwLower, $minPwNumber,
$allowPwSet, $tlsRequire, $smarty_templ, $smarty_compile ;
$groupprefix = "ou=Group" ;
$hostprefix = "ou=Hosts" ;
$netgroupprefix = "ou=Netgroup" ;
$variablesprefix = "ou=variables" ;
$automountprefix = "ou=Automount" ;
$homelocation = "/home" ;
$minPwLength = 5 ;
$minPwUpper = 1 ;
$minPwLower = 1 ;
$minPwNumber = 1 ;
$uname_maxlen = 8 ;
$allowPwSet = false ;
$smarty_templ = realpath ("../templates") ;
$smarty_compile = "/var/spool/lwat" ;
$domain = 'example.net' ;
$ldaphost = "ldap";
$useLisGroup = false ;
$tlsRequire = true ;
@include_once ("/etc/lwat/config.php");
if (!isset ($base)) {
$base_array = split ('.', $domain) ;
$base = 'dc=' . $base_array[0] ;
for ($i = 1 ; $i < count($base_array) ; $i++)
$base .= ",dc=" . $base_array[$i] ;
}
if (!is_dir ($smarty_compile))
$smarty_compile = "/var/tmp" ;
if (!is_file ($smarty_templ . "/login.tpl"))
$smarty_templ = realpath ("../templates") ;
if (isset ($groupbase)) {
$groupprefix = NULL ;
} elseif (isset($groupprefix))
$groupbase = $groupprefix . "," . $base ;
else
$groupbase = $base ;
if ($useLisGroup) {
$authbase = NULL ;
$authprefix = NULL ;
} elseif (isset ($authbase)) {
$useLisGroup = false ;
$authprefix = NULL ;
} elseif (isset($authprefix))
$authbase = $authprefix . "," . $base ;
else
$authbase = $base ;
if (isset ($hostbase))
$hostprefix = NULL ;
elseif (isset($hostprefix))
$hostbase = $hostprefix . "," . $base ;
else
$hostbase = $base ;
if (isset ($netgroupbase))
$netgroupprefix = NULL ;
elseif (isset($netgroupprefix))
$netgroupbase = $netgroupprefix . "," . $base ;
else
$netgroupbase = $base ;
if (isset ($variablesbase))
$variablesprefix = NULL ;
elseif (isset($variablesprefix))
$variablesbase = $variablesprefix . "," . $base ;
else
$variablesbase = $base ;
if (isset ($automountbase))
$automountprefix = NULL ;
elseif (isset($automountprefix))
$automountbase = $automountprefix . "," . $base ;
else
$automountbase = $base ;
}
/****************************************************************************
* loadLocale - Checks for browser language and tries to load locale
************
* try to load locale from cookie
* if no locale is found, and there is a locale subfolder
* create array of locales found under locales subfolder
* fetch the languages the browser accepts
* try to match exactly and mostly the locales for browser and application
* if exact locale is found use it
* else if match locale is found, use it
* if locale to use is found, set cookie
* set textdomain to use for locales
* set application output to be UTF8
****************************************************************************/
function loadLocale () {
if (isset ($_COOKIE['locale']))
$found_locale = $_COOKIE['locale'] ;
if (!isset($found_locale) && is_dir ('../locales')) {
$handle = @opendir ('../locales') ;
$all_locales = array () ;
while (false !== ($file = readdir ($handle))) {
if (is_dir ('../locales/' . $file . '/LC_MESSAGES')) {
$all_locales = array_merge ($all_locales, array ($file)) ;
}
}
closedir ($handle) ;
$lang_accept = array () ;
$lang_accept = explode (",", $_SERVER['HTTP_ACCEPT_LANGUAGE']);
for ($i = 0 ; $i < count ($lang_accept) ; $i++ ) {
$lang_accept[$i] = split(";", $lang_accept[$i]) ;
$lang_accept[$i] = $lang_accept[$i][0] ;
}
if (!empty($lang_accept[0]))
foreach ($lang_accept as $lang) {
if (!isset ($locale_exact)) {
foreach ($all_locales as $locales) {
if ($locales == $lang)
$locale_exact = $lang ;
elseif (!isset ($locale_match)) {
if (strpos ($locales, $lang) === 0)
$locale_match = $locales ;
}
}
}
}
if (isset ($locale_exact))
$found_locale=$locale_exact ;
elseif (isset ($locale_match))
$found_locale=$locale_match ;
if (isset ($found_locale))
setcookie ( 'locale', $found_locale) ;
unset ($all_locales, $lang_accept, $locale_match, $locale_exact, $lang, $locales) ;
}
if (isset ($found_locale)) {
$locale = setlocale (LC_ALL, $found_locale) ;
if (empty($locale))
setlocale (LC_ALL, $found_locale . ".UTF8");
bindtextdomain ("lwat", "../locales/");
bind_textdomain_codeset ("lwat", "UTF-8");
textdomain ("lwat");
}
header('Content-Type: text/html; charset=UTF-8') ;
}
/****************************************************************************
* xorstring - xors a string, to either crypt or decrypt data
*****************
* Arguments:
* key - key to use when encrypting or decrypting data
* xor - Data to xor, either clear text, or previously encrypted
* Returns:
* either encrypted or decrypted string
*****************
* if no key
* return nothing
* perform an xor with every character in string to be encrupted/decrypted
* with the correspondent charcater in key, repeat key if necesarry
* if at the end of a keyindex (assume decrypting)
* find first occurence of chr 0
* return string up to chr 0
* while not end of keyindex
* add with the rest of the key
* return encrypted string
****************************************************************************/
function xorstring ($key, $xor) {
$keylen=strlen ($key) ;
if (! $keylen) return "" ;
$newstr="" ;
$index=0 ;
$keyindex = $index ;
while ($index < strlen ($xor)) {
$newstr .= chr(ord($xor[$index]) ^ ord ($key[$keyindex])) ;
$index ++ ;
$keyindex= $index % $keylen ;
}
if ($keyindex == 0) {
$last=strpos ($newstr, chr(0));
if ($last === false) {
return $newstr ;
}
return(substr ($newstr, 0, $last)) ;
}
while ($keyindex != 0) {
$newstr .= $key[$keyindex] ;
$index ++ ;
$keyindex= $index % $keylen ;
}
return ($newstr) ;
}
/****************************************************************************
* readKey - Decodes data posted through the web-browser
*****************
* Arguments:
* key - which posted data to read
* Returns:
* the trimmed and stripped value of the data read
*****************
* Lookup data in posted array, trim, and remove clean data
****************************************************************************/
function readKey ($key) {
if (isset ($_POST[$key]))
return htmlspecialchars(trim($_POST[$key])) ;
return "" ;
}
/****************************************************************************
* debug - Displays data top debug through development
*****************
* Arguments:
* mixed - Data to display (array is preferred
*****************
* Display as preformatted code
****************************************************************************/
function debug ($mixed) {
echo "
\n" ; print_r ($mixed); echo "\n\n" ; } /**************************************************************************** * pwgen - generates a random password ***************** * Returns: * a password that should be easy to say, but hard to guess ***************** * First define consonants and vowels * select random characters, 2 set of three * Add a random number between 10 and 99 ****************************************************************************/ function pwgen(){ $pw = ''; $c = 'bcdfghjkmnprstvwzBCDFGHJKLMNPQRSTVW'; //consonants except hard to speak ones $v = 'aeiouAEU'; //vowels $a = $c.$v; //both //use two syllables... for($i=0;$i < 2; $i++){ $pw .= $c[rand(0, strlen($c)-1)]; $pw .= $v[rand(0, strlen($v)-1)]; $pw .= $a[rand(0, strlen($a)-1)]; } //... and add a nice number $pw .= rand(10,99); return $pw; } /**************************************************************************** * cryptgen - generates a crypted password from a given password, * ready to be stored in ldap ***************** * Arguments: * clear - the clear text password * Returns: * a crypted password ***************** * First prepare a salt using md5sum of random data * Return base64 encoded sha1 encrypted password ****************************************************************************/ function cryptgen($clear){ //prepare a salt $salt = md5(uniqid(rand(), true)); $salt=substr($salt,0,4); return '{SSHA}'.base64_encode(pack("H*", sha1($clear.$salt)).$salt); } /**************************************************************************** * getnextid - fetch (and update) the next id to be used. This is * stored in ldap under ou=variables ***************** * Arguments * connect - the ldap connection * Returns: * the next ID to be used ***************** * Search for a nextID under ou=Variables * Loop * minimum nextID should be 10000 * if nextID > 59999 * if already looped * Die with an error message * else * set looped * restart from 10000 * search ldap for found gidNumber or uidNumber * increase NextID if found * until free uidNumber and gidNumber is found * build new ID to be stored in ldap * if add newID to ldap with success * delete old nextID * rename newID nextID * return nextID * die with error message ****************************************************************************/ function getnextid ($connect) { global $variablesbase, $base ; $looped = false ; $nextID=0 ; $want= array ("gidNumber", "uidNumber"); $filter = "(&(gidNumber=*)(cn=nextID))" ; $result= @ldap_search ($connect, "ou=Variables," . $base, $filter, $want); if ($result) { $entries = ldap_get_entries ($connect, $result) ; if ($entries["count"]) $nextID=$entries[0]["gidnumber"][0] ; } do { if ($nextID < 10000) $nextID=10000 ; else if ($nextID > 59999) { if ($looped) { die ('Sorry, all user id\'s between 10000 and 59999 seems to have been taken') ; } else { $looped = true ; $nextID=10000 ; } } $filter = "(&(|(gidNumber=$nextID)(uidNumber=$nextID))(!(cn=nextID)))" ; $result = @ldap_search ($connect, $base, $filter, $want); $entries = @ldap_get_entries ($connect, $result) ; if ($entries["count"] > 0) $nextID++ ; } while ($entries["count"]) ; $info=array () ; $info["objectClass"] = array ("top", "posixGroup") ; $info["gidnumber"] = $nextID + 1 ; $info["cn"] = "newID" ; if (@ldap_add ($connect,"cn=newID," . $variablesbase,$info)) { @ldap_delete ($connect,"cn=nextID," . $variablesbase) ; @ldap_rename ($connect,"cn=newID," . $variablesbase, "cn=nextID", $variablesbase, true) ; return $nextID ; } $ldaperr = ldap_errno ($connect) ; switch ($ldaperr) { case 50: die (_("Sorry, you are not allowed to add users")) ; break ; default: reportLdapError ($ldaperr, "getnextid") ; break ; } die ("