Friday, May 4, 2007

Fixed S/MIME interoperability between KDE KMail and Mozilla Thunderbird

Nelson Bolyard fixed interoperability problem between Thunderbird and KMail (or rather gpgsm). There was a bug in how gpgsm encoded cipher preferences, which Thunderbird could not understand and was falling back to RC2/40 cipher. In gpgsm, RC2/40 was not implemented for patently political
reasons
. Thunderbird crypto library was made more lenient in what it accepted. So, now, TB would correctly responds to KMail using 3DES instead of RC/40. More details are in Bugzilla and Usenet.

Still causing minor problems are the following bugs. Vote for or fix them!

Thursday, April 26, 2007

Does Google know math?


According to Google Calculator, 0^0=1. What else would you expect from a Stanford dropout son of a professor of mathematics... ;) At least Google has no opinion on 0/0

Tuesday, April 24, 2007

Encrypt your email

Everyone should be able to encrypt their email and assure others of their identity. Get your free Thawte email certificate and join Thawte web of trust. Do it even if your company gives you an email certificate. If you are in Central Massachusetts, Boston, or Manhattan, I may be able to assure your identity and help you join the web of trust.

Friday, April 20, 2007

Find all tables missing a primary key in an Oracle database

Typically all tables in a database have primary keys. For some reason in ours some primary keys have disappeared. It must've been a botched export re-import operation. Anyway, here is an Oracle data dictionary query returning all tables in the current schema without a primary key.

select table_name 
from user_tables
where table_name not in
(
select distinct table_name
from user_constraints
where constraint_type='P'
)
order by table_name;

Tuesday, April 17, 2007

Windows for Professional Users

Sam Gentile, a windows programmer wishes for a Windows Edition for Power Users. He's so good - he has been using Windows in Administrator mode for 25 years and is not going to change. My guess is that if he browsed with Internet Explorer in Administrator mode, he's been rooted so deeply, his antivirus does not know it. He also looked at Unix in the 80s, so he does not want to hear about Linux. Poor chap, he does not have much choice between grandmommy's Vista and geek's Linux.

Tuesday, March 27, 2007

Account Lockout Realm in Tomcat

(Quick links: SourceForge, CVS)

I am describing here a way to implement Account Lockout. We want to lock out those users who within a short period of time made multiple authentication attempts and failed. The Account Lockout feature is commonly used in Tomcat hardening and requested in security audit.

I derive my AccountLockoutDatasourceRealm from Tomcat's DataSourceRealm to include account lockout logic and return the reason for authentication failure. Finally, I modify login failure JSP to show a message specifying if the account is locked or password is merely incorrect.

To keep track of the password failures, I add two columns to the User table: LoginFailures number and LastLoginFailure date/time.

alter table User add (
LoginFailures number default 0,
LastLoginFailure date
);

comment on column User.LoginFailures is
'Number of consecutive login failures for the purposes of implementing Account Lockout';
comment on column User.LastLoginFailure is
'Date/time of the last login failure for the purposes of implementing Account Lockout';
The new AccountLockoutDatasourceRealm provides getters and setters for properties configuring account lockout, overrides authenticate() method using logic described below, and implements setExtendedStatus method of ExtendedStatusSetter interface.

New Realm Properties

failedAttemptsBeforeLockout
The maximum number of login failure attempts before the accounts is locked out or zero to disable lockout
lockoutDuration
The duration of lockout in minutes or zero for permanent lockout
getLoginStatsForUserStatement
SQL statement with one JDBC parameter (?) returning three values: the number of login failures, the date/time of the last login failure, and the current database date/time for the user whose id is specified by the parameter. For example,
 SELECT LoginFailures, LastLoginFailure, SYSDATE FROM User WHERE Id = ? 
resetAccountLockoutForUserStatement
SQL update statement with one JDBC parameter (?) resetting the number of login failures for the user whose id is specified by the parameter. For example,
 UPDATE User SET LoginFailures = 0 WHERE Id = ? 
recordFailureForUserStatement
SQL update statement with one JDBC parameter (?) incrementing the number of login failures and updating the date/time of the last login for the user whose id is specified by the parameter. For example,
 UPDATE User SET LoginFailures = NVL(LoginFailures, 0) + 1, LastLoginFailure = SYSDATE where Id = ? 

Account Lockout Logic

  1. Lockout feature is enabled if failedAttemptsBeforeLockout is greater than 0.
  2. If lockout is enabled, read login stats (LoginFailures, LastLoginFailure, and DatabaseDate) from the database.
  3. Lockout is considered expired, when
    1. lockout feature is not enabled, or
    2. lockout is not permanent and LastLoginFailure is more than lockoutDuration minutes before DatabaseDate.
  4. Account is considered locked, when the lockout is enabled and the LoginFailures is equal to or greater then failedAttemptsBeforeLockout.
  5. If lockout is enabled, account IS NOT locked, and lockout has expired, reset the lockout and reread the login stats.
  6. If lockout is enabled, account IS locked, lockout has expired, and the lockout is not permanent, reset the lockout and reread the login stats.
  7. If lockout is enabled and account IS locked, fail the login without checking the password and return.
  8. Otherwise (if lockout is not enabled or account is not locked), user check the password against the database.
  9. If password is not correct, increment LoginFailures and set LastLoginFailure to DatabaseDate in the database.
  10. Reread login stats. If account lockout enabled and account is locked according to the rules above, fail the login due to account lockout and return.
  11. If password is correct, reset the lockout and return login success.

Returning Login Failure Reason


We use code described in the post returning reason from a Tomcat Realm. In setExtendedStatus method we check if account is locked and if it is, we pass the message using a request attribute as follows.
if (isLockoutEnabled() && isAccountLocked(failureStats))
{
setMessage(request, "Account locked");
}

protected void setMessage(HttpServletRequest request, String message)
{
containerLog.debug("Setting extended status message to " + message);
request.setAttribute(ExtendedStatusSetter.LOGIN_FAILURE_MESSAGE_ATTR, message);
}

Displaying Login Failure Reason


Tomcat form authentication configures an error JSP, which is displayed when login fails. To display extended login failure reason, we check for the existence of our request attribute with the following code:
   <p class="error">Access Denied:
<c:choose>
<c:when test="${!(empty requestScope['com.ofc.tomcat.LOGIN_FAILURE_MESSAGE'])}">
<c:out value="${requestScope['com.ofc.tomcat.LOGIN_FAILURE_MESSAGE']}"/>
</c:when>
<c:otherwise>Invalid username and/or password</c:otherwise>
</c:choose>
</p>

To build the code, compile AccountLockoutDatasourceRealm below and ExtendedStatusFormAuthenticator and ExtendedStatusSetter from dzone snippet. Package the code and the mbeans-descriptor.xml into a jar, place the jar into Tomcat's server/lib, and restart tomcat.

Configuration


To configure your application, add the following lines to your context.xml
  <!-- Override Pragma:no-cache to work around the IE bug when app is served via SSL.
See http://www.mail-archive.com/tomcat-user%40jakarta.apache.org/msg151294.html
-->
<Valve className="com.ofc.tomcat.ExtendedStatusFormAuthenticator"
disableProxyCaching="false" />
<Realm className="com.ofc.tomcat.AccountLockoutDatasourceRealm"
dataSourceName="realm.datasource"
userTable="User"
userRoleTable="UserRole"
userNameCol="ID"
userCredCol="PASSWORD"
roleNameCol="ROLEID"
failedAttemptsBeforeLockout="3"
lockoutDuration="30"
getLoginStatsForUserStatement=
"SELECT LoginFailures, LastLoginFailure, SYSDATE FROM User where Id = ?"
resetAccountLockoutForUserStatement=
"UPDATE User SET LoginFailures = 0 WHERE Id = ?"
recordFailureForUserStatement=
"UPDATE User SET LoginFailures = NVL(LoginFailures, 0) + 1, LastLoginFailure = SYSDATE where Id = ?"
/>

Documentation and Source


I published source code in sourceforge lockout-realm project. Instructions are forthcoming.

Credits


This work was sponsored by Open Finance - a leading enabler of data consolidation for the financial services industry and Wealth Information Exchange - A Consolidated View of Wealth

Friday, March 23, 2007

Shell script to locate a Java class

The following shell script will locate a Java class in CLASSPATH and additional jars, zip files, or directories specified on the command line. For example,

$ CLASSPATH= findClass DataSourceRealm /usr/local/tomcat/server/lib/*.jar
---- Searching for DataSourceRealm
---- Searching in /usr/local/tomcat/server/lib/catalina-ant.jar
---- Searching in /usr/local/tomcat/server/lib/catalina.jar
org/apache/catalina/realm/DataSourceRealm.class
---- Searching in /usr/local/tomcat/server/lib/commons-beanutils.jar
...
**** DataSourceRealm found in /usr/local/tomcat/server/lib/catalina.jar

Source

#!/bin/bash
#****h* bin/findClass
# NAME
# findClass - searches for a Java class in directories, jars, zip files, and CLASSPATH
# ARGUMENTS
# * 1 - Java class name or substring
# * ... - additional directories, jars, zip files
# RETURN VALUE
# * exit code 0, if the class is found
# * exit code 1, if the class is not found
# SOURCE
if [ $# -eq 0 ]; then
echo "Usage: findClass <CLASSNAME> [ FILE ... ]"
exit
fi
class=`echo $1| tr . /`
shift
allDirs="$(echo "$@" $CLASSPATH| perl -F: -ane 'print join(" ",@F);')"
printf -- "---- Searching for %s\n" "$class" >&2
foundIn=()
for dir in $allDirs; do
printf -- "---- Searching in %s\n" "$dir" >&2
case $dir in
*.zip)
unzip -v "$dir" | grep $class && foundIn[${#foundIn}]="$dir"
;;
*.jar)
jar -tf "$dir" | grep $class && foundIn[${#foundIn}]="$dir"
;;
*)
find "$dir" -print | grep $class && foundIn[${#foundIn}]="$dir"
;;
esac
done
if [ ${#foundIn} -eq 0 ]; then
printf -- "**** %s not found\n" "$class" >&2
exit 1
else
printf -- "**** %s found in %s\n" "$class" "${foundIn[*]}" >&2
fi
#****