package com.eucalyptus.auth.login;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;
import java.util.NavigableSet;
import java.util.TreeSet;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.net.URLCodec;
import org.apache.log4j.Logger;
import org.apache.xml.security.utils.Base64;
import com.eucalyptus.auth.Groups;
import com.eucalyptus.auth.Users;
import com.eucalyptus.auth.api.BaseLoginModule;
import com.eucalyptus.auth.crypto.Hmac;
import com.eucalyptus.auth.principal.User;
public class Hmacv2LoginModule extends BaseLoginModule<HmacCredentials> {
private static Logger LOG = Logger.getLogger( Hmacv2LoginModule.class );
public Hmacv2LoginModule() {}
@Override
public boolean accepts( ) {
return super.getCallbackHandler( ) instanceof HmacCredentials && ((HmacCredentials)super.getCallbackHandler( )).getSignatureVersion( ).equals( 2 );
}
@Override
public boolean authenticate( HmacCredentials credentials ) throws Exception {
String sig = credentials.getSignature( );
SecurityContext.enqueueSignature( sig );
User user = Users.lookupQueryId( credentials.getQueryId( ) );
String secretKey = user.getSecretKey( );
String canonicalString = this.makeSubjectString( credentials.getVerb( ), credentials.getHeaderHost( ), credentials.getServicePath( ), credentials.getParameters( ) );
String canonicalStringWithPort = this.makeSubjectString( credentials.getVerb( ), credentials.getHeaderHost( ) + ":" + credentials.getHeaderPort( ), credentials.getServicePath( ), credentials.getParameters( ) );
String computedSig = this.getSignature( secretKey, canonicalString, credentials.getSignatureMethod( ) );
String computedSigWithPort = this.getSignature( secretKey, canonicalStringWithPort, credentials.getSignatureMethod( ) );
if ( !computedSig.equals( sig ) && !computedSigWithPort.equals( sig ) ) {
sig = URLDecoder.decode( sig ).replaceAll("=","");
computedSig = this.getSignature( secretKey, canonicalString.replaceAll("\\+","%2B"), credentials.getSignatureMethod( ) ).replaceAll("\\+"," ");
computedSigWithPort = this.getSignature( secretKey, canonicalStringWithPort.replaceAll("\\+","%2B"), credentials.getSignatureMethod( ) ).replaceAll("\\+"," ");
if( !computedSig.equals( sig ) && !computedSigWithPort.equals( sig ) ) {
computedSig = this.getSignature( secretKey, canonicalString.replaceAll("\\+","%20"), credentials.getSignatureMethod( ) ).replaceAll("\\+"," ");
computedSigWithPort = this.getSignature( secretKey, canonicalStringWithPort.replaceAll("\\+","%20"), credentials.getSignatureMethod( ) ).replaceAll("\\+"," ");
if( !computedSig.equals( sig ) && !computedSigWithPort.equals( sig ) ) {
return false;
}
}
}
super.setCredential( credentials.getQueryId( ) );
super.setPrincipal( user );
super.getGroups( ).addAll( Groups.lookupUserGroups( super.getPrincipal( ) ) );
return true;
}
@Override
public void reset( ) {}
private String makeSubjectString( String httpMethod, String host, String path, final Map<String, String> parameters ) throws UnsupportedEncodingException {
URLCodec codec = new URLCodec();
parameters.remove("");
StringBuilder sb = new StringBuilder( );
sb.append( httpMethod );
sb.append( "\n" );
sb.append( host );
sb.append( "\n" );
sb.append( path );
sb.append( "\n" );
String prefix = sb.toString( );
sb = new StringBuilder( );
NavigableSet<String> sortedKeys = new TreeSet<String>( );
sortedKeys.addAll( parameters.keySet( ) );
String firstKey = sortedKeys.pollFirst( );
if( firstKey != null ) {
sb.append( codec.encode( firstKey ,"UTF-8" ) ).append( "=" ).append( codec.encode( parameters.get( firstKey ).replaceAll( "\\+", " " ), "UTF-8" ) );
}
while ( ( firstKey = sortedKeys.pollFirst( ) ) != null ) {
sb.append( "&" ).append( codec.encode( firstKey, "UTF-8" ) ).append( "=" ).append( codec.encode( parameters.get( firstKey ).replaceAll( "\\+", " " ), "UTF-8" ) );
}
String subject = prefix + sb.toString( );
LOG.trace( "VERSION2: " + subject );
return subject;
}
public String getSignature( final String queryKey, final String subject, final Hmac mac ) throws AuthenticationException {
SecretKeySpec signingKey = new SecretKeySpec( queryKey.getBytes( ), mac.toString( ) );
try {
Mac digest = mac.getInstance( );
digest.init( signingKey );
byte[] rawHmac = digest.doFinal( subject.getBytes( ) );
return Base64.encode( rawHmac ).replaceAll( "=", "" );
} catch ( Exception e ) {
LOG.error( e, e );
throw new AuthenticationException( "Failed to compute signature" );
}
}
}