Tuesday, April 9, 2013

Modifying the "supported" attributes of embedded ldap users/groups

WLS Admin console provides limited access to modify the attributes of users and groups that are located in its embedded ldap. This is primarily because embedded ldap is not supposed to hold any application users/groups on a large scale and probably this is one of the reasons why WLS does not provide any dedicated GUI to to modify the user/group attributes inside embedded ldlap. Neverthless, we can store these users/groups and we can modify the attributes as well. There are two approaches:

1) Using an external LDAP browser utility. My favorite as of today is jxplorer . I use such a utility against WLS embedded ldap for various purposes - testing the connectivity, the bind operation, search queries, etc

Using such ldap browser utilities against WLS embedded ldap requires one to explicitly open embedded ldap for "outside WLS" access.This involves resetting the password of embedded ldap's super user "Admin" to a known value. Note that when WLS domain is created, the password for this user is set randomly.



Restart the Administration server for changes to take effect.

Next, we use jxplorer to access the embedded ldap externally:



Once a successful connection is made, open of the user/group of choice and just do it:


2. Using a piece of JMX code

package examples.wls.jmx;

import javax.management.Descriptor;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.MBeanException;
import javax.management.modelmbean.ModelMBeanInfo;
import javax.management.remote.JMXConnector;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Hashtable;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.naming.Context;


//supportedUserAttributes = { "displayname", "employeenumber", "employeetype", "givenname", "homephone", "mail", "title", "preferredlanguage", "departmentnumber", "facsimiletelephonenumber", "mobile", "pager", "telephonenumber", "postaladdress", "street", "l", "st", "postofficebox", "c", "homepostaladdress" }

public class UserUtil {
 
 public static void main(String args[]) throws Exception{
  (new UserUtil()).doWork();
 }
 
 public void doWork() throws Exception{
  JMXConnector jmxConnector = getJMXConnector("192.169.0.100","17001","weblogic","weblogic1");
  MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
  //createNewUser(mBeanServerConnection, "weblogic.security.providers.authentication.DefaultAuthenticatorMBean", "JavaUser","weblogic1","Created from JAVA via JMX");
  modifyUserAttribute(mBeanServerConnection, "weblogic.security.providers.authentication.DefaultAuthenticatorMBean", "JavaUser", "mail", "javauser@examples.com");
  jmxConnector.close();
 }
 
 private void modifyUserAttribute(MBeanServerConnection mBeanServerConnection, String atnClassName, String userName, String userAttributeName, String userAttributeNewValue) throws Exception {
  ObjectName atnProvider = getATNProvider(mBeanServerConnection, atnClassName);
  ObjectName MBTservice = new ObjectName("com.bea:Name=MBeanTypeService,Type=weblogic.management.mbeanservers.MBeanTypeService");  
  
  //check whether the atnProvider implements UserAttributeEditorMBean, otherwise we cannot modify the attributes for the user in this provider
  String[] mba =  (String[]) mBeanServerConnection.invoke( MBTservice,
       "getSubtypes", new Object[] {
       "weblogic.management.security.authentication.UserAttributeEditorMBean" }, 
       new String[] { "java.lang.String" });
  boolean isEditor = false;
  for (int i = 0; i < mba.length; i++) {
   if (mba[i].equals(atnClassName)){
    isEditor = true;
    break;
   }   
  }
  if (isEditor == false)
   throw new Exception("*** The Provider does not support attribute modification");
  else
    System.out.println("*** This provider supports user attribute modification");  
  
  String userAttributeOldValue = null;
  String userAttributeNewlySetValue = null;
  
  
  //check what value we already got for the attribute
  userAttributeOldValue = (String) mBeanServerConnection.invoke( atnProvider,
    "getUserAttributeValue", new Object[] {userName, userAttributeName}, new String[] {"java.lang.String", "java.lang.String"});
  System.out.println("*** userAttributeOldValue>>" + userAttributeOldValue);
  
  //set new value
  System.out.println("*** Attempting to modify user attribute>>"+userAttributeName + ", with new value as>>" +userAttributeNewValue);
  mBeanServerConnection.invoke( atnProvider,
    "setUserAttributeValue", new Object[] {userName, userAttributeName, userAttributeNewValue}, new String[] {"java.lang.String", "java.lang.String", "java.lang.Object"});
  
  //get newly set value
  userAttributeNewlySetValue = (String) mBeanServerConnection.invoke( atnProvider,
    "getUserAttributeValue", new Object[] {userName, userAttributeName}, new String[] {"java.lang.String", "java.lang.String"});
  
  if(userAttributeNewValue.equals(userAttributeNewlySetValue))
   System.out.println("*** value succesfully set to>>" + userAttributeNewlySetValue);
 }

 public JMXConnector getJMXConnector(String hostname, String portString, String username, String password) throws IOException, MalformedURLException {
  JMXConnector jmxConnector = null;
  String protocol = "t3";
  Integer portInteger = Integer.valueOf(portString);
  int port = portInteger.intValue();
  String jndiroot = "/jndi/";
  String mserver = "weblogic.management.mbeanservers.domainruntime";
  JMXServiceURL serviceURL = new JMXServiceURL(protocol, hostname,port, jndiroot + mserver);
  Hashtable h = new Hashtable();
  h.put(Context.SECURITY_PRINCIPAL, username);
  h.put(Context.SECURITY_CREDENTIALS, password);
  h.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES,"weblogic.management.remote");
  jmxConnector =  JMXConnectorFactory.connect(serviceURL, h);  
  System.out.println("*** Created JMXConnector instance and now returing from getJMXConnector");
  return jmxConnector;
 }
 
 
 public void createNewUser(MBeanServerConnection mBeanServerConnection, String atnClassName, String userName, String userPass, String userDescription) throws  Exception {
  ObjectName atnProvider = getATNProvider(mBeanServerConnection, atnClassName);
  ObjectName MBTservice = new ObjectName("com.bea:Name=MBeanTypeService,Type=weblogic.management.mbeanservers.MBeanTypeService");  
  
  //check whether the atnProvider implements UserEditorMBean, otherwise we cannot create the user for this provider
  String[] mba =  (String[]) mBeanServerConnection.invoke( MBTservice,
       "getSubtypes", new Object[] {
       "weblogic.management.security.authentication.UserEditorMBean" }, 
       new String[] { "java.lang.String" });
  boolean isEditor = false;
  for (int i = 0; i < mba.length; i++) {
   if (mba[i].equals(atnClassName)){
    isEditor = true;
    break;
   }   
  }
  if (isEditor == false)
   throw new Exception("*** The Provider does not support user creation");
  else
    System.out.println("*** This provider supports user creation");
  
  
  try {
   System.out.println("*** Attempting to create user");
   mBeanServerConnection.invoke(atnProvider, "createUser", new Object[] {userName, userPass, userDescription},
    new String[] {"java.lang.String", "java.lang.String", "java.lang.String"}
   );
     System.out.println("*** User created");
  }catch (MBeanException ex) {
   Exception e = ex.getTargetException();
   e.printStackTrace();
  }
  
  try {
   System.out.println("*** Attempting to add user to group"); 
   mBeanServerConnection.invoke(
     atnProvider, "addMemberToGroup",
    new Object[] {"Administrators", userName},
    new String [] {"java.lang.String", "java.lang.String"}
     );
   System.out.println("*** Added user to group"); 
  }catch (MBeanException ex) {
   Exception e = ex.getTargetException();
   e.printStackTrace();
  }
 }
 
 private ObjectName getATNProvider(MBeanServerConnection mBeanServerConnection, String atnClassName) throws Exception{
  ObjectName[] atnProviders = getAllATNProviders(mBeanServerConnection);
     System.out.println("*** Going to traverse through auth providers");
  for (int p = 0; atnProviders != null && p < atnProviders.length; p++) {
      System.out.println("*** Provider number>>"  +p);
   ModelMBeanInfo info = (ModelMBeanInfo) mBeanServerConnection.getMBeanInfo(atnProviders[p]);
   Descriptor desc = info.getMBeanDescriptor();
   String className = (String)desc.getFieldValue("interfaceClassName");
   if(atnClassName.equals(className)){
    System.out.println("*** Requested provider found, and will return. atnProvider>>"+atnProviders[p]);
    return atnProviders[p];
   }
   }
  System.out.println("*** Requested provider not found, and will return null");
  return null;
 }
 
 private ObjectName[] getAllATNProviders(MBeanServerConnection mBeanServerConnection) throws Exception{
  ObjectName service = new ObjectName("com.bea:Name=DomainRuntimeService,"+ "Type=weblogic.management.mbeanservers.domainruntime.DomainRuntimeServiceMBean");
  ObjectName domainMBean = (ObjectName) mBeanServerConnection.getAttribute(service, "DomainConfiguration");
  ObjectName securityConfiguration = (ObjectName) mBeanServerConnection.getAttribute(domainMBean, "SecurityConfiguration");
  ObjectName defaultRealm = (ObjectName) mBeanServerConnection.getAttribute(securityConfiguration, "DefaultRealm");
  return (ObjectName[]) mBeanServerConnection.getAttribute(defaultRealm, "AuthenticationProviders");
 }
 
}



You can also grab the source code of this sample JAVA class from here


!!!

Friday, January 11, 2013

Replacing expired certificates on SSL Server that uses JKS based keystore

Replacing an expired identity certificate in a JKS based keystore is pretty easy stuff, unless you have forgot to keep a backup of your private key.

This post discusses the use-case where we don't have a backup copy of the private key outside the JKS keystore, and we wish to replace the expired/going-to-expire identity certificate



There are two ways that I know of:
  • Portecle (easy) - this is a tool available out on internet
  • OpenSSL-Keytool combination (lengthy one)
I will discuss 2nd one, and would only provide commands (and not discuss each switch as you can always refer relevant product docs for it)

1. Backup the JKS keystore, suppose original is  "keystore.jks"

2. JKS -> PKCS12 conversion (pkcs12 obtained in this step would be run through OpenSSL in next step, to separate the private key from the expired certificates)

         keytool -importkeystore -srckeystore keystore.jks -destkeystore keystore.p12 -srcstoretype JKS -deststoretype PKCS12 -srcstorepass weblogic1 -deststorepass weblogic1 -srcalias server_pri_pub_cert -destalias server_pri_pub_cert -srckeypass weblogic1 -destkeypass weblogic1 -noprompt



3. Extract private and public keys (using OpenSSL):
         openssl pkcs12 -in keystore.p12 -out keystore.pem -passin pass:weblogic1 -passout pass:weblogic1


4. Take the private key obtained from above step, place it in a PEM file (say server_pri_key.pem). Use this PEM to generate a CSR if you want to!

Encrypted ---> plain (this is optional step, just in case you want to have  possession of decrypted private key )
         openssl rsa -in server_pri_key.pem -out server_pri_key_plain.pem


5. Get the newly generated public certificate from CA (this maybe a single public key or a chain, known as reply) and place its PEM format equivalent text in file "reply.pem"

 
6. Package private key and reply into pcks12:
          openssl pkcs12 -export -out newkeystore.p12 -in reply.pem -inkey server_pri_key.pem -name server_pri_pub_cert


7.  Delete existing expired key-pair from existing original JKS keystore:
         keytool -delete -alias server_pri_pub_cert -keystore keystore.jks


8. And finally import p12 into JKS:
         keytool -importkeystore -srckeystore newkeystore.p12 -destkeystore keystore.jks -srcstoretype PKCS12 -deststoretype JKS -srcstorepass weblogic1 -deststorepass weblogic1 -srcalias server_pri_pub_cert -destalias server_pri_pub_cert -srckeypass weblogic1 -destkeypass weblogic1 -noprompt

Tuesday, October 2, 2012

Dynamically enabling (or disabling) the JDBC driver level logging using debug version of Oracle JDBC thin driver (ie ojdbc6_g.jar) on app server

This post is about dynamically enabling (or disabling) the  JDBC driver level logging with debug version of Oracle JDBC thin driver "ojdbc6_g.jar" in place while app server is up and running.

Typically, when this jar is put in place in application server environment, we enable the logging using JAVA system property "-Doracle.jdbc.Trace=true". This approach is static in the sense that application server restart is needed each time we want to enable or disable the logging. This static approach is not at all feasible in a production system where we want to trace the jdbc calls at some particular instance of time.

A programmatic (dynamic) approach to enable/disable driver level logging is also discussed in doc "http://docs.oracle.com/cd/B28359_01/java.111/b31224/diagnose.htm". However this approach is suitable for enabling/disabling the logging from within the application. So this method would essentially only trace JDBC calls at places where application code interacts with DB which might not be sufficient to troubleshoot jdbc issue in application server environment where jdbc connections are actually maintained and managed by application server.

Extending this dynamic enabling/disabling logging idea to application server is a bit tricky. From application server perspective, we can toggle driver level logging at runtime using similar programmatic approach. This would affect the logging at global level (be it app server interactions with DB or any application that is deployed on the app server, all DB interactions would be logged). This can be accomplished on " sun.misc.Launcher$AppClassLoader" instance that lies at "com.oracle.jdbc"/"diagnosability" in MBean tree.

Steps
======
A) Grab the instance name of MBean "com.oracle.jdbc"/"diagnosability" using JConsole, or any other JMX browser utility.

How:
1. Start JAVA_HOME/bin/jconsole or jvisualvm
2. Connect to app server process where logging needs to be enabled/disabled
3. Goto MBeans tab
4. Expand "com.oracle.jdbc"/"diagnosability"
5. Select the available MBean
6. Get the name portion from field "ObjectName" (this would be the loader string, as referenced in sample code here  .

For example, if we have:
  ObjectName="com.oracle.jdbc:type=diagnosability,name=sun.misc.Launcher$AppClassLoader@601bb1"

Then loader would be "sun.misc.Launcher$AppClassLoader@601bb1"


B) Target the sample application "JDBCDriverLogger.war" (download from here ) to the application server instance on which you want to enable/disable the driver logging.

C) Create a logging configuration, say "myConfig.properties"

######snippet from myConfig.properties - starts##########
.level=FINER
oracle.jdbc.level=FINER
oracle.jdbc.handlers=java.util.logging.FileHandler
java.util.logging.FileHandler.level=FINER
java.util.logging.FileHandler.pattern=d:\\jdbc.log
java.util.logging.FileHandler.count=1
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
######snippet from myConfig.properties - ends##########


D) set the JAVA System property to point to logging configuration file:
         set JAVA_OPTIONS=-Djava.util.logging.config.file=myConfig.properties


E) Restart the application server if necessary. Note this restart might be needed to pickup  file "myConfig.properties" and is one time activity. If this file is already available to the application server, then no need to restart.


F) hit the application on application server instance:
    http://soa_server1ip:port/JDBCDriverLogger/loggerControl.jsp


The IP and Port in above URL will correspond to the app server instance where logging needs to be enabled/disabled, and where the applicaton "JDBCDriverLogger.war" was targetted previously

G) Enter loader name (obtained from step A6) in the text field next to "Enter ClassLoader Object instance name:" on the JSP page, and hit "EnableLogging"

This would enable driver level logging on app server instance

H) to disable the logging, enter the same loader name in the text field next to "Enter ClassLoader Object instance name:" on the JSP page, and hit "DisableLogging"



Ref:
http://docs.oracle.com/cd/B28359_01/java.111/b31224/diagnose.htm

Tuesday, July 31, 2012

Configuring WebLogic GridLink DataSource with secure SCAN URL and secure ONS

Configuring SCAN and ONS on WLS GridLink datasource is already covered in Oracle whitepaper here

In this discussion today, I will list high level steps that are needed to configure secure SCAN and secure ONS on WLS GridLink Datasource instead of using their plain counterparts.

Here are the high level steps:

1) TCPS based listener configuration needs to be implemented on DB server side. Once it is done, we would get a secure SCAN URL. Based on this scan URL, we would get jdbc URL for use with our GridLink datasource, something like:

jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCPS)(HOST=sup.oracle.vm)(PORT=1523)))(CONNECT_DATA=(SERVICE_NAME=racdb.oracle.vm)))


2) ONS daemons running on RAC nodes need to be secured as well with user certificate (using wallet). Note that it is essential to have ditto wallet configuration in following(or equivalent) files to secure ONS daemon successfully:

node1:
ons.config
ons.config.node1

node2:
ons.config
ons.config.node2

Here is sample output of ons.config and ons.config.node1 from node1

[grid@sup1 conf]$ cat ons.config
localport=6100 # line added by Agent
allowgroup=true
usesharedinstall=true
remoteport=6200 # line added by Agent
nodes=node1:6200,node2:6200 # line added by Agent
debugcomp=ons[all]
walletfile=/u01/app/11.2.0/grid2/network/admin/cost
walletpassword=welcome1


[grid@sup1 conf]$ cat ons.config.node1
localport=6100 # line added by Agent
allowgroup=true
usesharedinstall=true
remoteport=6200 # line added by Agent
nodes=node1:6200,node2:6200 # line added by Agent
debugcomp=ons[all]
walletfile=/u01/app/11.2.0/grid2/network/admin/cost
walletpassword=welcome1



3) With above things in place, just follow the steps in doc:
http://www.oracle.com/technetwork/middleware/weblogic/wls-jdbc-gridlink-howto-333331.html


Now some security specific highlights are:
4) Following needs to added to DOMAIN_HOME/setDomainEnv.sh:


set WEBLOGIC_CLASSPATH=D:\oracle\wls1034\modules\com.oracle.osdt_cert_1.0.0.0.jar;D:\oracle\wls1034\modules\com.oracle.osdt_core_1.0.0.0.jar;D:\oracle\wls1034\modules\com.oracle.oraclepki_1.0.0.0.jar;%WEBLOGIC_CLASSPATH%

What this does is it pulls these jar files into classpath so that WebLogic can play with Oracle wallets.

5) GridLink datasource security related configuration is:

keystore Driver Properties:
---------------------------------------
javax.net.ssl.keyStore=D:\\apps\\rac_db_repro\\identity_against_db.jks
javax.net.ssl.keyStorePassword=welcome1
javax.net.ssl.keyStoreType=JKS
javax.net.ssl.trustStore=D:\\apps\\rac_db_repro\\db_trust.jks
javax.net.ssl.trustStorePassword=welcome1
javax.net.ssl.trustStoreType=JKS

Note:
a) "identity_against_db.jks" is nothing but Java Keystore that has user certificate (used as client identity)
b) "db_trust.jks" contains root certificate(s) of CA(s) who signed user certificate(s) used by DB process


6) ONS nodes in GridLink datasource used:
node1.oracle.vm:6200
node2.oracle.vm:6200

7) wallet in ONS configuration on GridLink datasource used:
ons-wallet-file --> D:\apps\rac_db_repro
ons-wallet-password --> welcome1

Note
a) the wallet file is "ewallet.p12", and is located at "D:\apps\rac_db_repro"
b) "ewallet.p12" contains root certificate(s) of CA(s) who signed user certificate(s) used by ONS process

Wednesday, May 9, 2012

Go HA

WebLogic provides many ways to automate lifecycle of cluster members to achieve server/service high availability without affecting service quality. For instance, we can configure whole server migration to make sure that in an event of physical  machine failure cluster members hosted on that machine are started on another machine.

There are alternative methods to manage server (or process migration, at OS level), or simply, to control the lifecycle activity of  a process. These alternative methods include inbuilt (or packaged) clustering software that comes with various flavors of Linux/Unix.

This post discusses the possible way to make such clustering software control WebLogic instances.

The central thing about this is the use of WebLogic Scripting Tool ( or WLST) script. This WLST script file would be invoked from service script (which is just a specific form of shell script used to execute lifecycle activities of a process) with appropriate argument, like start, stop, status. Script file has methods to interact with nodemnager to start, stop and check the server status. Also, the same WLST script contains logic for LSB compliance, and returns appropriate exit code to the calling service script. Calling service script has to return the same exit codes to the OS, and can also display further diagnostic messages if need be. This WLST script file would need environment variable to get hold of nodemnager and server details. Typically these environment variables would be set in service script, just before it invokes the WLST  script. These variables are discussed in this document towards the end.  If WebLogic domain is spanned over multiple physical servers, then we would have one instance of this WLST script file (in DOMAIN_HOME) per box. And of course, there has to be one instance of service script per WebLogic server instance on a physical box. (Note to wise: it is alright if you do not understand all this at this point, just follow the remaining  post in a hope to understand this).


Here is what needs to be done:
0) Associate all the WebLogic server instances (including Admin Server) with machines and then configure nodemanagers for these machines. Here are the background details on nodemanager:

1. Make sure that you have an entry for the concerned domain in file:
NODEMANAGER_HOME/nodemanager.domains

2. Generate "userConfigFile" and "userKeyFile" so that we do not need to use clear-text  username/password in the scripts responsible for starting/stopping/status-checking the WebLogic instances
Here are the steps:
a) First make sure that domain nodemanager credentials(username and password) for this domain are same as that of domain admin. This is so because if they are different then we would have to generate two config and two key files (one for connecting to Admin Server, and another for connecting to nodemanger – review the WLST script to see what I am talking about)
From, admin console, navigate to DOMAIN_NAME>>Security>>General>>Advanced.

















Change values for "NodeManager Username:", "NodeManager Password:", and "Confirm NodeManager Password:". Set them to domain administrator credentials













Also, for each server consider setting:
- set Xms, Xmx, XX:MaxPermSize
This is just to make sure that we don’t use the default values (especially for permanent generation which is often less to effectively start the server in RUNNING mode).
b) start ssh terminal, connect to server machine and navigate to domain home. Once here, set the environment from “bin” directory:
/opt/Oracle/Middleware/user_projects/domains/TestDomain1/bin$ . ./setDomainEnv.sh

Now, start WLST using command:
java WebLogic.WLST
c) Once at WLST offline prompt, connect to the running AdminServer
   
d) Execute following command:
storeUserConfig()

 
For details on command “storeUserConfig “, refer: http://download.oracle.com/docs/cd/E17904_01/web.1111/e13813/reference.htm#i1064674
e) Also execute following command for each WebLogic instance that exists on this physical machine:
nmGenBootStartupProps(‘<SERVER_NAME>’)
f) execute following command to exit from WLST:
exit()
g) Move the two generated files (“root-WebLogicConfig.properties” and “root-WebLogicKey.properties”) into DOMAIN_HOME. Also, place the file “service_helper.py” into DOMAIN_HOME



3. Assuming we have one admin server (“AdminServer”)  and one managed server (“MS1”), duplicate the file “Server” as “AdminServer” and “MS1”  and edit both of them to set following properties:
export DOMAIN_HOME=/opt/Oracle/Middleware/user_projects/domains/TestDomain1
export DOMAIN_NAME=TestDomain1
export SERVER_NAME=AdminServer (or MS1)
export IS_ADMIN=TRUE (“FALSE” if SERVER_NAME is set to MS1)
export ADMIN_URL=t3://mac11.oracle.vm:7001
export NM_HOME=/opt/Oracle/Middleware/wlserver_10.3/common/nodemanager
export NM_HOST=mac11.oracle.vm
export NM_PORT=5556
export USER_CONFIG_FILE=$DOMAIN_HOME/root-WebLogicConfig.properties
export USER_KEY_FILE=$DOMAIN_HOME/root-WebLogicKey.properties

4. Now we simply install the files as service into “/etc/init.d”
5. use following commands to start the services initially:
service AdminSever start
service MS1 start
Note there is a dependence of MS1 on AdminServer. So start MS1 only when AdminServer starts up successfully
6. Repeat following steps for each physical machine where WebLogic domain is scattered:
2b-g, 3, 4, 5
7. Now configure the High Availability solution’s monitoring console (HA MC) to import these two services as LSB services. And from this point onwards, you should be good to control them using MC.

I tried this setup while installing WebLogic instances as service into /etc/init.d, and then working with these instances as LSB instances in DRBD Management Console Release: 0.9.9  on SLES 10, and things look good.
mac11:~ # uname -a
Linux mac11 2.6.16.60-0.54.5-default #1 Fri Sep 4 01:28:03 UTC 2009 x86_64 x86_64 x86_64 GNU/Linux
 





Since this setup involves checking the status, starting/stopping the servers using WLST and nodemanager these operations do take some time. So one piece of advice goes here; start, stop, status and monitor interval timouts need to be tuned to make things work. Also, if you are thinking of starting/shutting down the servers outside the HA MC, then you still would have to do this using methods that involve the use of nodemanager, otherwise the tracking of WebLogic instances (using nodemanager) would be lost, and things would break up.

Hope this helps you guys to go HA out there!

Tuesday, November 29, 2011

Why JAVA 1.6 HTTP client can authenticate (using SPNEGO) only against certain WebLogic versions?

JAVA 1.6 HTTP client's inherits support for SPNEGO via Java GSS. This is listed at:
    http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html

From WebLogic side, the answer(to the question why JAVA HTTP client only works with certianin versions) lies in simple test that is carried out using "supported" browser and JAVA fat client against same version of WebLogic and then analyzing the network dumps.

Network dumps show:

For JAVA fat client(not working against WebLogic 10.3.3)
GSS-API Generic Security Service Application Program Interface
              OID: 1.3.6.1.5.5.2 (SPNEGO - Simple Protected Negotiation)
              Simple Protected Negotiation
                              negTokenInit
                                              mechTypes: 1 item
                                                 MechType: 1.2.840.113554.1.2.2 (KRB5 - Kerberos 5)


For browser (working  against WebLogic 10.3.3)
GSS-API Generic Security Service Application Program Interface
              OID: 1.3.6.1.5.5.2 (SPNEGO - Simple Protected Negotiation)
              Simple Protected Negotiation
                              negTokenInit
                                              mechTypes: 3 items
                                                              MechType: 1.2.840.48018.1.2.2 (MS KRB5 - Microsoft Kerberos 5)
                                                              MechType: 1.2.840.113554.1.2.2 (KRB5 - Kerberos 5)
                                                              MechType: 1.3.6.1.4.1.311.2.2.10 (NTLMSSP - Microsoft NTLM Security Support Provider)

I have run multiple tests whose results I have summed up in section "takeaway"

In all, we see that three Mech types are involved at most:
1) 1.2.840.48018.1.2.2 (MS KRB5 - Microsoft Kerberos 5)
2) 1.2.840.113554.1.2.2 (KRB5 - Kerberos 5)
3) 1.3.6.1.4.1.311.2.2.10 (NTLMSSP - Microsoft NTLM Security Support Provider)


NTLM tokens have never been supported by WebLogic. The remaining two types are what should be of interest. The details about these can be found at:
       http://msdn.microsoft.com/en-us/library/ms995330.aspx


Takeaway:

MechType: 1.2.840.48018.1.2.2 (MS KRB5 - Microsoft Kerberos 5)
- does work with WebLogic 10.3.3 and pre.
- does work with WebLogic 10.3.4, 10.3.5

MechType: 1.2.840.113554.1.2.2 (KRB5 - Kerberos 5):
- does not work on WLS 10.3.3 and pre
- does work on WLS 10.3.4 and 10.3.5

Sunday, October 9, 2011

com.bea.security.saml2.service.SAML2Exception: [Security:096575]The URL for relay state too long

In IdP initiated SSO, you might have a jsp/html resource at IdP end where SP services are defined having  similar form snippet:

<input type="hidden" name="SPName" value="<%=spname%>"
<input type="hidden" name="RequestURL" value="<%=requestURL%>"
<input type="hidden" name="param1" value="<%=value1%>"
<input type="hidden" name="param2" value="<%=value2%>"
<input type="hidden" name="param3" value="<%=value3%>"


However you are getting following exception whenever SP service is invoked from above jsp/html:

####<Sep 29, 2011 2:01:14 PM IST> <Debug> <SecuritySAML2Service> <MyMac> <AdminServer> <[ACTIVE] ExecuteThread: '0' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1311113162898> <BEA-000000> <exception info
com.bea.security.saml2.service.SAML2Exception: [Security:096575]The URL for relay state too long.
at com.bea.security.saml2.service.sso.SSOServiceProcessor.doInitiator(SSOServiceProcessor.java:284)
at com.bea.security.saml2.service.sso.SSOServiceProcessor.process(SSOServiceProcessor.java:97)
at com.bea.security.saml2.service.sso.SingleSignOnServiceImpl.process(SingleSignOnServiceImpl.java:50)
at com.bea.security.saml2.cssservice.SAML2ServiceImpl.process(SAML2ServiceImpl.java:161)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.bea.common.security.utils.ThreadClassLoaderContextInvocationHandler.invoke(ThreadClassLoaderContextInvocationHandler.java:26)
at $Proxy28.process(Unknown Source)


Even after confirming the request URL is well within 80 bytes, you are puzzled what is going wrong? Well, RequestURL is well below 80 bytes. However, the RelayState is constructed such that:

RelayState=RequestURL+"?"
+"&"+param1+"="+value1
+"&"+param2+"="+value2
+"&"+param3+"="+value3
.....
+"&"+paramn+"="+valuen

This way, the RelayState might easily exceed 80 bytes.

Unfortunately, we cannot workaround this issue. This 80 byte constraint is put by SAML2 specs rather than WebLogic. An extract from SAML2 Bind specs(http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf):
=========================
3.4.3 RelayState
"RelayState data MAY be included with a SAML protocol message transmitted with this binding. The value
MUST NOT exceed 80 bytes in length and SHOULD be integrity protected by the entity creating the
message independent of any other protections that may or may not exist during message transmission.
Signing is not realistic given the space limitation, but because the value is exposed to third-party
tampering, the entity SHOULD ensure that the value has not been tampered with by using a checksum, a
pseudo-random value, or similar means."
=========================


The way forward would be to add lesser parameters that go from IdP to SP, or add these as attributes into the assertion. In this case, we have to write an attribute mapper(which is a kind of user plugin) to get attributes into the assertion. And if you are using WebLogic 10.3.4, it is possible to write this plugin as it is supported from 10.3.4 onwards. However there is nothing, I believe, that can be done on pre-wls 10.3.4