Tuesday, December 11, 2007

Java sighting on a cup of milk


Looking at this cup of soy milk in Taipei, I thought at first that somebody stole Java logo. Then, looking closer, I realized it's a genuine ad for Java training. Something you won't see on a milk container in the US...

Friday, November 2, 2007

Form based login from a shell script

The following code uses only shell scripting and curl to log in to a website protected by a form based authentication and download contents of page "about.jspx". We pay special attention to returning reasonable error messages. These snippets were tested with Tomcat 5.5.

  1. Get user name, password, and the application's URL from the command line,
    eval set -- $(getopt "u:p:b:" "$@")
    while [ "$1" != "--" ]
    do
    case "$1" in
    -u) shift; WEB_UID="$1"; shift; ;;
    -p) shift; WEB_PWD="$1"; shift; ;;
    -b) shift; BASE_URL="${1%\/}"; shift; ;;
    esac
    done
  2. Store URLs in variables,
    typeset aboutUrl="$BASE_URL/about.jspx"
    typeset securityAction="$BASE_URL/j_security_check"
    typeset logoffUrl="$BASE_URL/logoff.jsp"
  3. Access the URL and look for session cookie JSESSIONID,
    headers="$(curl -s -S -f -L -D - -o /dev/null --url "$aboutUrl" 2>&1)" \
    || error "Error accessing $aboutUrl: $headers"
    if [[ "$headers" =~ 'Set-Cookie: JSESSIONID=([^;]*)' ]]
    then
    sid="${BASH_REMATCH[1]}"
    fi
  4. If JSESSIONID cookie is set, submit user name and password to the login form. On success, the application will redirect us to the original URL. Variable "about" will store the page source or the error message.
    about="$(curl -s -S -L -f -b "JSESSIONID=$sid" -o - \
    -d "
    j_username=$WEB_UID" \
    -d "
    j_password=$WEB_PWD" \
    --url "
    $securityAction" 2>&1)" \
    || error "Error submitting credentials to $securityAction: $about"
  5. Parse the page for some useful information (looking for <sysdate date="20071102">)
    if [[ "$about" =~ 'sysdate date=\"([^\"]*)\"' ]]
    then
    date="${BASH_REMATCH[1]}"
    fi
  6. Finally, hit the logoff URL so that the application may clean up.
    curl -s -f -b "JSESSIONID=$sid" --url "$logoffUrl" -o /dev/null
For more information, see cURL and libcurl

Tuesday, September 25, 2007

Sun NetBeans day

September 11th was a Sun NetBeans day in Boston. Here's what I learned:

  • NetBeans keeps improving. NetBeans 6 consolidates six independent Java parsers of version 5 into one. Syntax highlighting and refactoring can now use more semantic information; it is now faster and more powerful and accurate.
  • Profiler is now improved and built-in into NetBeans. It can be enabled or disabled at runtime using profiling points. More info at Netbeans profiler pages.
  • The most active site about NetBeans is NetBeans Wiki.
  • New England has an active Java User Group, which organizes nice talks. See NEJUG.org
  • A lot of introductory presentation about Java technology inluding RIA and REST is posted by one of the speakers at JavaPassion.com. The speaker himself was speaking way too fast with a Korean accent, but his presentations must be filled with information.

Friday, September 21, 2007

Upgraded to Slackware 12

Updated my Thinkpad T40 from Slackware 11 to Slackware 12. The 2 hour upgrade went without a hitch. I was pleasantly surprised to find out that Paul compiled KDE KMail with S/MIME support. Great, I don't need to roll my own packages any more.

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
#****

Thursday, March 22, 2007

I am glad I am not using Windows

Just read a scary and fascinating story about Gozi Windows trojan, which sees through SSL and steals bank passwords, security questions, and the newfangled security pictures. I am so glad my desktop machine runs Linux, in which malware will not be able to install itself as a layer between the browser and the SSL code. More details.

Returning login failure reason in a Tomcat Realm

When Tomcat Realm authenticates a user via its Realm, there is no way for the Realm to tell the application the exact reason of a failure. The realm either succeeds and returns a Principal object or fails and returns null. So, when I needed to implement account lockout after several unsuccessful logins, I couldn't show the user the difference between a wrong password and account being locked out.

Digging deeper into Tomcat code, I found that the Realm is being invoked from FormAuthenticator Valve. It is possible to extend the Valve so that in case of authentication failure, the Valve adds failure reason as an HttpServletRequest attribute. More details in ExtendedStatusFormAuthenticator code.