# HG changeset patch # User Dan Fuhry # Date 1357882374 18000 # Node ID a044870a9d3d9c8f5b7049334bf77844dabc382c # Parent 700d61d93b1bc6bc77c328d304cce10412786b4e Added password reset function diff -r 700d61d93b1b -r a044870a9d3d .hgignore --- a/.hgignore Tue Jan 08 23:21:25 2013 -0500 +++ b/.hgignore Fri Jan 11 00:32:54 2013 -0500 @@ -1,3 +1,4 @@ ^packages/.*/.*\.deb$ ^tarballs/ +^packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/compiled/ ~$ diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/.htaccess --- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/.htaccess Tue Jan 08 23:21:25 2013 -0500 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/.htaccess Fri Jan 11 00:32:54 2013 -0500 @@ -5,8 +5,10 @@ AuthDBMGroupFile /etc/apache2/ldap-groups Require group rtp - + Require valid-user + Satisfy Any + AuthType None RewriteEngine on diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/img/.htaccess --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/img/.htaccess Fri Jan 11 00:32:54 2013 -0500 @@ -0,0 +1,3 @@ +AuthType None +Satisfy Any +Require valid-user diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/functions.php --- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/functions.php Tue Jan 08 23:21:25 2013 -0500 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/functions.php Fri Jan 11 00:32:54 2013 -0500 @@ -29,7 +29,7 @@ function load_credentials() { $config = yaml_parse_file("/usr/local/etc/ssoinabox/webcreds.yml"); - $keys = array('LDAP_BASEDN', 'UID_MIN', 'GID_MIN', 'ldap_server', 'ldap_manager', 'ldap_user_basedn', 'ldap_group_basedn', 'kerberos_admin', 'PHONE_EXT_MIN'); + $keys = array('LDAP_BASEDN', 'UID_MIN', 'GID_MIN', 'ldap_server', 'ldap_manager', 'ldap_user_basedn', 'ldap_group_basedn', 'kerberos_admin', 'PHONE_EXT_MIN', 'hmac_secret'); foreach ( $keys as $key ) { @@ -42,3 +42,48 @@ $GLOBALS[$key] = $config[$key]; } } + +/** + * Test a password's policy compliance + * @param string password + * @return mixed true if compliant, otherwise a string describing why it isn't + */ + +function test_password($str) +{ + if ( strlen($str) < 8 ) + return 'must be at least 8 characters in length'; + + if ( countUniqueChars($str) < 6 ) + return 'must have at least 6 unique characters'; + + if ( strlen($str) <= 16 ) + { + if ( !preg_match('/[a-z]/', $str) ) + return 'must contain at least one lowercase letter'; + + if ( !preg_match('/[A-Z]/', $str) ) + return 'must contain at least one lowercase letter'; + + if ( !preg_match('/[0-9]/', $str) ) + return 'must contain at least one lowercase letter'; + + if ( !preg_match('/[^A-Za-z0-9]/', $str) ) + return 'must contain at least one lowercase letter'; + } + + return true; +} + +function countUniqueChars($str) +{ + $count = 0; + $uniq = ''; + for ( $i = 0; $i < strlen($str); $i++ ) + { + if ( strpos($uniq, $str{$i}) === false ) + $uniq .= $str{$i}; + } + + return strlen($uniq); +} diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/ldap.php --- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/ldap.php Tue Jan 08 23:21:25 2013 -0500 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/ldap.php Fri Jan 11 00:32:54 2013 -0500 @@ -353,3 +353,20 @@ return ldap_delete($_ldapconn, ldap_make_group_dn($cn)); } + +/** + * Is the given username in the specified LDAP group? + * @param string username + * @param string Group name + * @return bool + */ + +function ldap_test_group_membership($username, $group) +{ + global $_ldapconn, $ldap_group_basedn; + + $filter = sprintf('(&(memberUid=%s)(cn=%s)(objectClass=posixGroup))', ldap_escape($username), ldap_escape($group)); + + $result = ldap_search($_ldapconn, $ldap_group_basedn, $filter); + return ldap_count_entries($_ldapconn, $result) > 0; +} diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/smtp.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/smtp.php Fri Jan 11 00:32:54 2013 -0500 @@ -0,0 +1,111 @@ +\r\n"); + if ( _smtp_get_response($sock) !== 250 ) + throw new Exception("Expected 250"); + + // to + fputs($sock, "RCPT TO: <$to>\r\n"); + if ( _smtp_get_response($sock) !== 250 ) + throw new Exception("Expected 250"); + + // data + fputs($sock, "DATA\r\n"); + if ( !in_array(_smtp_get_response($sock), array(354, 250)) ) + throw new Exception("Expected 250 or 354"); + + // send headers + $full_headers = "Subject: $subject\r\n"; + $full_headers .= "From: <$from>\r\n"; + if ( !strstr($headers, "To: ") ) + $full_headers .= "To: <$to>\r\n"; + if ( !empty($headers) ) + $full_headers .= trim(str_replace("\n", "\r\n", str_replace("\r\n", "\n", $headers))) . "\r\n"; + + $full_headers .= "\r\n"; + fputs($sock, $full_headers); + + // send body + $body = str_replace("\n", "\r\n", str_replace("\r\n", "\n", $body)); + fputs($sock, $body); + + // send end marker + fputs($sock, "\r\n.\r\n"); + if ( _smtp_get_response($sock) !== 250 ) + throw new Exception("Expected 250"); + + // end session + fputs($sock, "QUIT\r\n"); + if ( _smtp_get_response($sock) !== 221 ) + throw new Exception("Expected 221"); + + fclose($sock); + } catch ( Exception $e ) + { + fputs($sock, "QUIT\r\n"); + _smtp_get_response($sock); + fclose($sock); + return false; + } + return true; +} + +function _smtp_get_response($sock) +{ + while ( !feof($sock) && ($line = fgets($sock, 8192)) ) + { + if ( preg_match('/^([0-9]+)(\s.+)?$/', $line, $match) ) + { + return intval($match[1]); + } + } + return false; +} + +// smtp_mail('plates@csh.rit.edu', 'plates@csh.rit.edu', 'Test e-mail', 'Testing', 'From: Plates '); + diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/starthere.php --- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/starthere.php Tue Jan 08 23:21:25 2013 -0500 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/starthere.php Fri Jan 11 00:32:54 2013 -0500 @@ -15,8 +15,12 @@ require_once(ACCOUNTS . 'includes/ldap.php'); require_once(ACCOUNTS . 'includes/kadm5.php'); require_once(ACCOUNTS . 'includes/users.php'); +require_once(ACCOUNTS . 'includes/smtp.php'); session_start(); +$adm = !empty($_SERVER['REMOTE_USER']) && ldap_test_group_membership($_SERVER['REMOTE_USER'], 'rtp'); +define('IS_ADMIN', $adm); + if ( !isset($_SESSION['messages']) ) $_SESSION['messages'] = array(); diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/template-wrapper.php --- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/template-wrapper.php Tue Jan 08 23:21:25 2013 -0500 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/template-wrapper.php Fri Jan 11 00:32:54 2013 -0500 @@ -15,6 +15,8 @@ if ( isset($_SERVER['REMOTE_USER']) ) $smarty->assign('userinfo', $ui = ldap_get_user($_SERVER['REMOTE_USER'])); + $smarty->assign('is_admin', IS_ADMIN); + if ( $ui === false ) redirect('/logout'); diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/emails/lostpw.tpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/emails/lostpw.tpl Fri Jan 11 00:32:54 2013 -0500 @@ -0,0 +1,9 @@ +Hello {$userinfo['cn']}, + +You're receiving this e-mail because someone (hopefully you) requested a password reset on your account. To reset your password, please click the link below: + +{$reset_link} + +This request was received from the IP address {$ip}. If you did not request this e-mail, you may safely ignore it as the link will expire 12 hours from this e-mail's issuance. + +If the name you were greeted with above does not match your real name, please report this e-mail to the administrators and do not click any links in it. diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/header.tpl --- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/header.tpl Tue Jan 08 23:21:25 2013 -0500 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/header.tpl Fri Jan 11 00:32:54 2013 -0500 @@ -15,10 +15,17 @@
diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/lostpw.tpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/lostpw.tpl Fri Jan 11 00:32:54 2013 -0500 @@ -0,0 +1,22 @@ +{assign var="title" value="Lost Password"} +{include file="header.tpl"} + +

Forgot password

+ +

If you've forgotten your password but set an e-mail address for yourself, you have the option of recovering your account via a link sent to your e-mail address. To + get started, enter either your username or e-mail address in the form below.

+ +
+
+ +
+ +
+
+ +
+ +
+
+ +{include file="footer.tpl"} diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/resetpw.tpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/templates/resetpw.tpl Fri Jan 11 00:32:54 2013 -0500 @@ -0,0 +1,42 @@ +{assign var="title" value="Lost Password"} +{include file="header.tpl"} + +

Reset password

+ +

Almost done! Complete the form below to reset your password.

+ +
+ + +
+ +
+ +
+
+ + +
+ +
+ +

+ Must meet password security requirements. +

+
+
+ + +
+ +
+ +
+
+ +
+ +
+
+ +{include file="footer.tpl"} diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/logout.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/logout.php Fri Jan 11 00:32:54 2013 -0500 @@ -0,0 +1,3 @@ + (12 * 3600) ) + throw new Exception("This link has expired. Please request another password reset."); + + $userinfo = ldap_get_user($uid); + + // handle a submit? + $do_redirect = false; + if ( !empty($_POST['password']) ) + { + if ( ($result = test_password($_POST['password'])) !== true ) + throw new Exception("Your new password $result."); + + if ( $_POST['password'] !== $_POST['password_confirm'] ) + throw new Exception("The passwords you entered did not match."); + + if ( reset_password($uid, $_POST['password']) ) + { + queue_message(E_NOTICE, "Your password has been reset. You can now log into the control panel."); + redirect('/lostpw'); + } + else + { + throw new Exception("Internal error when performing password reset."); + } + } + } + catch ( Exception $e ) + { + queue_message(E_ERROR, $e->getMessage()); + if ( $do_redirect ) + redirect('/lostpw'); + } + + display_template('resetpw', array( + 'userinfo' => $userinfo + )); + + exit; +} + +if ( !empty($_POST['email_or_username']) ) +{ + try + { + global $_ldapconn, $ldap_user_basedn; + + $field = strstr($_POST['email_or_username'], '@') ? 'mail' : 'uid'; + if ( $field == 'uid' && !preg_match('/^[a-z0-9]{3,32}$/', $_POST['email_or_username']) ) + { + throw new Exception("Invalid username. Usernames can only contain 3-32 a-z and 0-9 (all lowercase) characters."); + } + + $search_filter = sprintf('(&(%s=%s)(objectClass=posixAccount))', $field, ldap_escape($_POST['email_or_username'])); + $search_result = ldap_search($_ldapconn, $ldap_user_basedn, $search_filter); + + if ( !$search_result ) + { + throw new Exception(ldap_error($_ldapconn)); + } + + if ( ldap_count_entries($_ldapconn, $search_result) == 0 ) + { + throw new Exception("Could not find any accounts that matched that $field."); + } + else if ( ldap_count_entries($_ldapconn, $search_result) > 1 ) + { + throw new Exception("LDAP search query erroneously returned multiple results."); + } + + $userinfo = ldap_array_cleanup(ldap_get_attributes($_ldapconn, ldap_first_entry($_ldapconn, $search_result))); + + if ( !isset($userinfo['mail']) ) + { + throw new Exception("No e-mail address is registered to your account. Please contact the administrator to request a password reset."); + } + + $mail_censored = $mail = is_array($userinfo['mail']) ? $userinfo['mail'][ count($userinfo['mail']) - 1 ] : $userinfo['mail']; + + // smtp_mail($from, $to, $subject, $body, $headers = '') + $reset_link = sprintf('http://%s/lostpw/%s/%08x/%s', gethostname(), $userinfo['uid'], time(), hash_hmac('sha1', "{$userinfo['uid']}%" . time(), $hmac_secret)); + $email_body = parse_template('emails/lostpw', array( + 'userinfo' => $userinfo + , 'reset_link' => $reset_link + , 'ip' => $_SERVER['REMOTE_ADDR'] + )); + + $domainname = strtolower(get_default_kerberos_realm()); + $mail_result = smtp_mail("accounts@$domainname", $mail, "$domainname password reset", $email_body, "From: $domainname accounts \r\nTo: {$userinfo['cn']} <$mail>"); + + // censor e-mail address to keep deviants from scraping for it + if ( $field == 'uid' && preg_match('/^(.)(.*?)(.)@([a-z\.]+)$/', $mail, $match) ) + $mail_censored = sprintf('%s%s%s@%s', $match[1], str_repeat('.', strlen($match[2])), $match[3], $match[4]); + + if ( $mail_result ) + { + queue_message(E_NOTICE, "Password reset instructions have been mailed to $mail_censored."); + } + else + { + throw new Exception("Failed to send e-mail."); + } + } + catch ( Exception $e ) + { + queue_message(E_ERROR, $e->getMessage()); + } +} + +display_template('lostpw'); diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/res/.htaccess --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/res/.htaccess Fri Jan 11 00:32:54 2013 -0500 @@ -0,0 +1,3 @@ +AuthType None +Satisfy Any +Require valid-user diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/res/ssoinabox.css --- a/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/res/ssoinabox.css Tue Jan 08 23:21:25 2013 -0500 +++ b/packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/res/ssoinabox.css Fri Jan 11 00:32:54 2013 -0500 @@ -25,7 +25,7 @@ div.navbar a.brand span.siab-logo { font-family: TransportDOTMedium, arial; - text-transform: lowercase; + text-transform: uppercase; letter-spacing: -1px; position: relative; top: 2px; diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/login.tmpl --- a/packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/login.tmpl Tue Jan 08 23:21:25 2013 -0500 +++ b/packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/login.tmpl Fri Jan 11 00:32:54 2013 -0500 @@ -99,7 +99,7 @@
diff -r 700d61d93b1b -r a044870a9d3d packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/logout.tmpl --- a/packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/logout.tmpl Tue Jan 08 23:21:25 2013 -0500 +++ b/packages/ssoinabox-webui/root/usr/local/share/weblogin/ssoinabox/templates/logout.tmpl Fri Jan 11 00:32:54 2013 -0500 @@ -33,7 +33,7 @@ [% END %]
diff -r 700d61d93b1b -r a044870a9d3d resources/functions --- a/resources/functions Tue Jan 08 23:21:25 2013 -0500 +++ b/resources/functions Fri Jan 11 00:32:54 2013 -0500 @@ -171,6 +171,7 @@ objectClass: posixGroup description: Default POSIX group for users gidNumber: 500 +memberUid: $username dn: cn=rtp,ou=Groups,$ldap_suffix cn: rtp @@ -352,6 +353,8 @@ PHONE_EXT_MIN: 500 +hmac_secret: `generate_password 40` + EOF chown root:www-data /usr/local/etc/ssoinabox/webcreds.yml