/*
* Copyright (C) 2005 Luca Veltri - University of Parma - Italy
*
* This source code 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 source code 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 source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author(s):
* Luca Veltri (luca.veltri@unipr.it)
*/
package local.server;
import org.zoolu.sip.address.*;
import org.zoolu.sip.header.WwwAuthenticateHeader;
import org.zoolu.sip.header.AuthorizationHeader;
import org.zoolu.sip.header.AuthenticationInfoHeader;
import org.zoolu.sip.header.ProxyAuthenticateHeader;
import org.zoolu.sip.header.ProxyAuthorizationHeader;
import org.zoolu.sip.message.*;
import org.zoolu.sip.provider.SipStack;
import org.zoolu.sip.authentication.DigestAuthentication;
import org.zoolu.tools.Log;
import org.zoolu.tools.LogLevel;
import org.zoolu.tools.Parser;
import org.zoolu.tools.MD5;
/** Class AuthenticationServerImpl implements an AuthenticationServer
* for HTTP Digest authentication.
*/
public class AuthenticationServerImpl implements AuthenticationServer
{
/** Server authentication. */
protected static final int SERVER_AUTHENTICATION=0;
/** Proxy authentication. */
protected static final int PROXY_AUTHENTICATION=1;
/** Event logger. */
protected Log log=null;
/** The repository of users's authentication data. */
protected AuthenticationService authentication_service;
/** The authentication realm. */
protected String realm;
/** The authentication scheme. */
protected String authentication_scheme="Digest";
/** The authentication qop-options. */
protected String qop_options="auth,auth-int";
/** The current random value. */
protected byte[] rand;
/** DIGEST */
//public static final String DIGEST="Digest";
/** AKA */
//public static final String AKA="AKA";
/** CHAP */
//public static final String CHAP="CHAP";
/** Costructs a new AuthenticationServerImpl. */
public AuthenticationServerImpl(String realm, AuthenticationService authentication_service, Log log)
{ init(realm,authentication_service,log);
}
/** Inits the AuthenticationServerImpl. */
private void init(String realm, AuthenticationService authentication_service, Log log)
{ this.log=log;
this.realm=realm;
this.authentication_service=authentication_service;
this.rand=pickRandBytes();
}
/** Gets the realm. */
/*public String getRealm()
{ return realm;
}*/
/** Gets the qop-options. */
/*public String getQopOptions()
{ return qop_options;
}*/
/** Gets the current rand value. */
/*public String getRand()
{ return HEX(rand);
}*/
/** Authenticates a SIP request.
* @param msg is the SIP request to be authenticated
* @return it returns the error Message in case of authentication failure,
* or null in case of authentication success. */
public Message authenticateRequest(Message msg)
{ return authenticateRequest(msg,SERVER_AUTHENTICATION);
}
/** Authenticates a SIP request.
* @param msg is the SIP request to be authenticated
* @return it returns the error Message in case of authentication failure,
* or null in case of authentication success. */
public Message authenticateProxyRequest(Message msg)
{ return authenticateRequest(msg,PROXY_AUTHENTICATION);
}
/** Authenticates a SIP request.
* @param msg is the SIP request to be authenticated
* @param proxy_authentication whether performing Proxy-Authentication or simple Authentication
* @return it returns the error Message in case of authentication failure,
* or null in case of authentication success. */
protected Message authenticateRequest(Message msg, int type)
{ Message err_resp=null;
//String username=msg.getFromHeader().getNameAddress().getAddress().getUserName();
//String user=username+"@"+realm;
AuthorizationHeader ah;
if (type==SERVER_AUTHENTICATION) ah=msg.getAuthorizationHeader();
else ah=msg.getProxyAuthorizationHeader();
if (ah!=null && ah.getNonceParam().equals(HEX(rand)))
{
//String username=ah.getUsernameParam();
String realm=ah.getRealmParam();
String nonce=ah.getNonceParam();
String username=ah.getUsernameParam();
String scheme=ah.getAuthScheme();
String user=username+"@"+realm;
if (authentication_service.hasUser(user))
{
if (authentication_scheme.equalsIgnoreCase(scheme))
{
DigestAuthentication auth=new DigestAuthentication(msg.getRequestLine().getMethod(),ah,null,keyToPasswd(authentication_service.getUserKey(user)));
// check user's authentication response
boolean is_authorized=auth.checkResponse();
rand=pickRandBytes();
if (!is_authorized)
{ // authentication/authorization failed
int result=403; // response code 403 ("Forbidden")
err_resp=MessageFactory.createResponse(msg,result,SipResponses.reasonOf(result),null);
printLog("LOGIN ERROR: Authentication of '"+user+"' failed",LogLevel.HIGH);
}
else
{ // authentication/authorization successed
printLog("Authentication of '"+user+"' successed",LogLevel.HIGH);
}
}
else
{ // authentication/authorization failed
int result=400; // response code 400 ("Bad request")
err_resp=MessageFactory.createResponse(msg,result,SipResponses.reasonOf(result),null);
printLog("Authentication method '"+scheme+"' not supported.",LogLevel.HIGH);
}
}
else
{ // no authentication credential found for this user
int result=404; // response code 404 ("Not Found")
err_resp=MessageFactory.createResponse(msg,result,SipResponses.reasonOf(result),null);
}
}
else
{ // no Authorization header found
int result;
if (type==SERVER_AUTHENTICATION) result=401; // response code 401 ("Unauthorized")
else result=407; // response code 407 ("Proxy Authentication Required")
err_resp=MessageFactory.createResponse(msg,result,SipResponses.reasonOf(result),null);
WwwAuthenticateHeader wah;
if (type==SERVER_AUTHENTICATION) wah=new WwwAuthenticateHeader("Digest");
else wah=new ProxyAuthenticateHeader("Digest");
wah.addRealmParam(realm);
wah.addQopOptionsParam(qop_options);
wah.addNonceParam(HEX(rand));
err_resp.setWwwAuthenticateHeader(wah);
}
return err_resp;
}
/** Gets AuthenticationInfoHeader. */
public AuthenticationInfoHeader getAuthenticationInfoHeader()
{ AuthenticationInfoHeader aih=new AuthenticationInfoHeader();
aih.addRealmParam(realm);
aih.addQopOptionsParam(qop_options);
aih.addNextnonceParam(HEX(rand));
return aih;
}
/** Picks a random array of 16 bytes. */
private static byte[] pickRandBytes()
{ return MD5(Long.toHexString(org.zoolu.tools.Random.nextLong()));
}
/** Converts the byte[] key in a String passwd. */
private static String keyToPasswd(byte[] key)
{ return new String(key);
}
/** Calculates the MD5 of a String. */
private static byte[] MD5(String str)
{ return MD5.digest(str);
}
/** Calculates the MD5 of an array of bytes. */
private static byte[] MD5(byte[] bb)
{ return MD5.digest(bb);
}
/** Calculates the HEX of an array of bytes. */
private static String HEX(byte[] bb)
{ return MD5.asHex(bb);
}
// ****************************** Logs *****************************
/** Adds a new string to the default Log */
protected void printLog(String str, int level)
{ if (log!=null) log.println("AuthenticationServer: "+str,level+SipStack.LOG_LEVEL_UA);
}
}