/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the license at
* https://glassfish.dev.java.net/public/CDDLv1.0.html or
* glassfish/bootstrap/legal/CDDLv1.0.txt.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at glassfish/bootstrap/legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* you own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
*/
package org.jvnet.glassfish.comms.security.auth.impl;
import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.security.auth.LoginContextDriver;
import static com.sun.enterprise.security.auth.digest.api.Constants.A1;
import com.sun.enterprise.security.auth.digest.api.DigestAlgorithmParameter;
import com.sun.enterprise.security.auth.digest.api.Key;
import com.sun.enterprise.security.auth.login.DigestCredentials;
import com.sun.enterprise.security.auth.nonce.StringNonce;
import com.sun.web.security.WebPrincipal;
import java.security.MessageDigest;
import java.security.Principal;
import java.util.Properties;
import java.util.logging.Level;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
/**
*
* @author K.Venugopal@sun.com
*/
public class DigestAuthenticator extends Authenticator {
private String realmName;
private boolean proxy = false;
private String authInfoHeader = null;
public DigestAuthenticator(String realmName, boolean proxy) {
this.realmName = realmName;
this.proxy = proxy;
}
public Principal authenticate(SipServletRequest request) {
try {
SipDigestParamGenerator generator = new SipDigestParamGenerator();
//(SipDigestParamGenerator)DigestParamGenerator.getInstance("SIPDigest");
DigestAlgorithmParameter[] params = generator.generateParameters(new SipAlgorithmParamImpl(
request, realmName, proxy));
Key key = null;
if (params == null) {
return null;
}
Properties props = generator.getParsedValues();
String nonce = props.getProperty("nonce");
String nc = props.getProperty("nc");
String nextNonce = null;
StringNonce sn = new StringNonce(nonce);
StringNonce sn2 = new StringNonce(nonce+nc);
if (localNonceCache.hasNonce(sn) && !isValid(sn2)) {
return null;
}
for (int i = 0; i < params.length; i++) {
DigestAlgorithmParameter dap = params[i];
if (A1.equals(dap.getName()) && (dap instanceof Key)) {
key = (Key) dap;
break;
}
}
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Realm configured for this application is " + realmName +
" realm received in the message is " + key.getRealmName());
}
if (!(key.getRealmName().equals(realmName))) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Realm in the message does not match with configured realm");
}
return null;
}
DigestCredentials creds = new DigestCredentials(realmName,
key.getUsername(), params);
LoginContextDriver.login(creds);
if (nc != null && nc.length() > 0) {
nextNonce = "00000001";
}
authInfoHeader = createAuthInfoHeader(request, nextNonce);
SecurityContext secCtx = SecurityContext.getCurrent();
return new WebPrincipal(creds.getUserName(), null, secCtx);
} catch (Exception le) {
logger.log(Level.SEVERE, "Digest Authentication failed", le);
//TODO: Log
}
return null;
}
public boolean hasAuthorizationHeader(SipServletRequest request) {
if (!proxy) {
String header = request.getHeader("Authorization");
if (header != null && header.length() > 0) {
return true;
}
return false;
}
String pheader = request.getHeader("Proxy-Authorization");
if (pheader != null && pheader.length() > 0) {
return true;
}
return false;
}
private String incrementNonce(String nonce) {
long v = Long.parseLong(nonce);
v++;
String vs = String.valueOf(v);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 8 - vs.length(); i++) {
sb.append(0);
}
sb.append(vs);
return sb.toString();
}
//TODO: share btw appserver and sailfin
protected String generateNOnce(SipServletRequest request) {
try {
long currentTime = System.currentTimeMillis();
String nOnceValue = request.getRemoteAddr() + ":" + currentTime +
":" + "sipserver";
MessageDigest digester = MessageDigest.getInstance("MD5");
byte[] buffer = digester.digest(nOnceValue.getBytes());
nOnceValue = new MD5Encoder().encode(buffer);
// Updating the value in the no once hashtable
// nOnceTokens.put(nOnceValue, Long.valueOf(currentTime + nOnceTimeout));
return nOnceValue;
} catch (Exception ex) {
logger.log(Level.SEVERE, "Error occurred while creating nonce", ex);
//TODO: Log
}
return null;
}
public void setAuthenticateHeader(SipServletResponse response,
SipServletRequest request, boolean proxy, boolean stale) {
// Get the realm name
String nonce = generateNOnce(request);
String staleValue = "false";
if (stale) {
staleValue = "true";
}
String authenticateHeader = "Digest realm=\"" + realmName + "\", " +
"qop=\"auth\", nonce=\"" + nonce + "\", " +
"opaque=\"" + generateNOnce(request) + "\"," + "stale=" + staleValue+",algorithm=MD5";
localNonceCache.validateNonce(new StringNonce(nonce));
if (!proxy) {
response.setHeader("WWW-Authenticate", authenticateHeader);
} else {
response.setHeader("Proxy-Authenticate", authenticateHeader);
}
}
public String getAuthInfoHeader() {
return authInfoHeader;
}
public String createAuthInfoHeader(SipServletRequest request, String nonceCount) {
String nonce = generateNOnce(request);
localNonceCache.validateNonce(new StringNonce(nonce));
StringBuilder builder = new StringBuilder();
builder.append("nextnonce=\"");
builder.append(nonce);
builder.append("\",qop=\"auth\"");
builder.append(",nc=");
builder.append(nonceCount);
return builder.toString();
}
}