/*
* Created on Jul 10, 2005
*
* Copyright 2005 CafeSip.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.cafesip.jiplet.realms;
import java.io.File;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import javax.sip.header.AuthorizationHeader;
import javax.sip.header.WWWAuthenticateHeader;
import javax.sip.message.Message;
import org.cafesip.jiplet.Jiplet;
import org.cafesip.jiplet.JipletException;
import org.cafesip.jiplet.JipletLogger;
import org.cafesip.jiplet.Pair;
import org.cafesip.jiplet.Realm;
/**
* This class is a base class for a realm implementation. An realm
* implementation can either implement the org.cafesip.jiplet.Realm interface or
* extend this class instead. This class implements many of the methods
* specified by the Realm interface making it easier to create realm
* implementation. If you extend this class, make sure that the realm-param
* specified in server.xml contains the following params: <br>
* <ul>
* <li>nonce-private-key</li>
* <li>domain-uri</li>
* </ul>
* The class extending this class must also call super.init() method from its
* init() method in case it overrides the init() method. In addition the class
* must also implement the abstract method getUserInformation().
*
* @author Amit Chatterjee
*
*/
public abstract class BaseRealm implements Realm
{
private String realmName;
private File confDir;
private String noncePrivateKey = "JIPLET";
private String domainUri;
private String opaque;
private boolean defaultRealm;
private HashMap params;
/**
* A constructor for this class.
*
*
*/
public BaseRealm()
{
super();
}
/*
* @see org.cafesip.jiplet.Realm#init(java.lang.String, java.io.File,
* java.util.HashMap, boolean)
*/
public void init(String name, File confDir, HashMap params, boolean defaultRealm)
throws JipletException
{
try
{
this.params = params;
this.defaultRealm = defaultRealm;
realmName = name;
this.confDir = confDir;
String key = (String) params.get("nonce-private-key");
if (key != null)
{
noncePrivateKey = key;
}
String uri = (String) params.get("domain-uri");
if (uri != null)
{
domainUri = uri;
}
opaque = computeNonce();
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger.debug("For the realm " + name + ", the opaque value is " + opaque);
}
}
catch (Exception e)
{
throw new JipletException(e);
}
}
private String computeNonce()
{
byte[] code = new byte[12];
long date = (new Date()).getTime();
int key = noncePrivateKey.hashCode();
int index = 0;
code[index++] = (byte)(date & 0xFF);
code[index++] = (byte)( (date >> 8) & 0xFF);
code[index++] = (byte)( (date >> 16) & 0xFF);
code[index++] = (byte)( (date >> 24) & 0xFF);
code[index++] = (byte)( (date >> 32) & 0xFF);
code[index++] = (byte)( (date >> 40) & 0xFF);
code[index++] = (byte)( (date >> 48) & 0xFF);
code[index++] = (byte)( (date >> 56) & 0xFF);
code[index++] = (byte)(key & 0xFF);
code[index++] = (byte)( (key >> 8) & 0xFF);
code[index++] = (byte)( (key >> 16) & 0xFF);
code[index++] = (byte)( (key >> 24) & 0xFF);
return Base64.encodeBytes(code);
}
/*
* @see org.cafesip.jiplet.Realm#getRealmName()
*/
public String getRealmName()
{
return realmName;
}
/*
* @see org.cafesip.jiplet.Realm#getAuthenticationHeader(org.cafesip.jiplet.Jiplet,
* javax.sip.message.Message message,
* java.lang.String, boolean)
*/
public WWWAuthenticateHeader getAuthenticationHeader(Jiplet jiplet,
Message message,
String headerType, boolean stale)
{
try
{
WWWAuthenticateHeader header = null;
if (headerType.equals(WWWAuthenticateHeader.NAME) == true)
{
header = jiplet.getHeaderFactory().createWWWAuthenticateHeader(
"Digest");
}
else
{
header = jiplet.getHeaderFactory()
.createProxyAuthenticateHeader("Digest");
}
String nonce = computeNonce();
header.setNonce(nonce);
header.setAlgorithm("MD5");
header.setRealm(realmName);
header.setDomain(domainUri);
header.setOpaque(opaque);
header.setStale(stale);
return header;
}
catch (ParseException e)
{
JipletLogger.fatal("Error creating authentication header: "
+ e.getMessage());
return null;
}
}
/*
* @see org.cafesip.jiplet.Realm#authenticate(javax.sip.header.AuthorizationHeader)
*/
public String[] authenticate(String method, AuthorizationHeader header)
{
try
{
if (header.getOpaque().equals(getOpaque()) == false)
{
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger
.debug("Received a SIP request with invalid OPAQUE value");
}
return null;
}
String name = header.getUsername();
Pair p = getUserInformation(name);
if (p == null)
{
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger
.debug("Received an authentication request from an unknown user : "
+ name);
}
return null;
}
// the user is found, verify if the response matches
String c_response = MessageDigestAlgorithm.calculateResponse(header
.getAlgorithm(), name, getRealmName(), (String) p
.getFirst(), header.getNonce(), header.getCNonce(), header
.getCNonce(), method, header.getURI().toString(), null,
header.getQop());
if (c_response.equals(header.getResponse()) == false)
{
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger.debug("Authentication failure for user "
+ name);
}
return null;
}
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger
.debug("User " + name + " has been authenticated. ");
}
return (String[]) p.getSecond();
}
catch (Exception e)
{
JipletLogger
.fatal("While authenticating an user, receved exception "
+ e.getClass().getName() + " : " + e.getMessage()
+ "\n" + JipletLogger.getStackTrace(e));
return null;
}
}
/**
* @return Returns the confDir.
*/
protected File getConfDir()
{
return confDir;
}
/**
* @return Returns the opaque.
*/
protected String getOpaque()
{
return opaque;
}
/**
* A class extending this class must implement this interface.
*
* @param user
* @return Returns a Pair object. The first item in the Pair is the password
* and the second item is a String[] containing the roles for the
* user.
*/
public abstract Pair getUserInformation(String user) throws Exception;
public boolean isDefaultRealm()
{
return defaultRealm;
}
public HashMap getParams()
{
return params;
}
}