diff --git a/CASA-auth-token/java/configure.in b/CASA-auth-token/java/configure.in index e6b77b61..ebaf0e9e 100644 --- a/CASA-auth-token/java/configure.in +++ b/CASA-auth-token/java/configure.in @@ -265,6 +265,7 @@ package/linux/Makefile package/linux/CASA_auth_token_svc.spec server/Makefile server/Svc/Makefile +server/Svc/external/Makefile server/Svc/src/Makefile server/Svc/src/com/Makefile server/Svc/src/com/novell/Makefile diff --git a/CASA-auth-token/java/server/Svc/Makefile.am b/CASA-auth-token/java/server/Svc/Makefile.am index 96b44cec..dbced513 100644 --- a/CASA-auth-token/java/server/Svc/Makefile.am +++ b/CASA-auth-token/java/server/Svc/Makefile.am @@ -20,7 +20,7 @@ ####################################################################### SUBDIRS = src -DIST_SUBDIRS = src +DIST_SUBDIRS = src external EXTRA_DIST = authtoken.settings \ identoken.settings \ @@ -34,6 +34,7 @@ ROOT = ../.. LIBDIR = $(ROOT)/$(LIB) IDENT_ABSTRACTION_DIR = /usr/share/java/identity-abstraction +AXIS_JARS_DIR = external JAVAROOT = . JAVAC= javac @@ -50,6 +51,8 @@ JAVAFILES = src/com/novell/casa/authtoksvc/ProtoDefs.java \ src/com/novell/casa/authtoksvc/AuthTokenConfig.java \ src/com/novell/casa/authtoksvc/EnabledSvcsConfig.java \ src/com/novell/casa/authtoksvc/AuthMechanism.java \ + src/com/novell/casa/authtoksvc/WSSecurity.java \ + src/com/novell/casa/authtoksvc/SessionToken.java \ src/com/novell/casa/authtoksvc/Authenticate.java \ src/com/novell/casa/authtoksvc/RpcMethod.java \ src/com/novell/casa/authtoksvc/Rpc.java \ @@ -66,8 +69,7 @@ JAVAFILES = src/com/novell/casa/authtoksvc/ProtoDefs.java \ src/com/novell/casa/authtoksvc/GetAuthTokReqMsg.java \ src/com/novell/casa/authtoksvc/GetAuthTokRespMsg.java \ src/com/novell/casa/authtoksvc/Krb5Authenticate.java \ - src/com/novell/casa/authtoksvc/PwdAuthenticate.java \ - src/com/novell/casa/authtoksvc/SessionToken.java + src/com/novell/casa/authtoksvc/PwdAuthenticate.java BUILDDIR = build @@ -77,8 +79,12 @@ AUTHTOKEN_FILES = -C $(BUILDDIR)/webapp/WEB-INF/classes com CLASSES = $(addprefix $(BUILDDIR)/, $(JAVAFILES:%.java=%.class)) +#AXIS_LIBS = $(AXIS_JARS_DIR)/axis.jar:$(AXIS_JARS_DIR)/axis-ant.jar:$(AXIS_JARS_DIR)/commons-discovery-0.2.jar:$(AXIS_JARS_DIR)/commons-logging-1.0.4.jar:$(AXIS_JARS_DIR)/commons-logging-api.jar:$(AXIS_JARS_DIR)/jaxrpc.jar:$(AXIS_JARS_DIR)/log4j-1.2.8.jar:$(AXIS_JARS_DIR)/saaj.jar:$(AXIS_JARS_DIR)/wsdl4j-1.5.1.jar:$(AXIS_JARS_DIR)/wss4j-1.5.0.jar:$(AXIS_JARS_DIR)/xalan.jar:$(AXIS_JARS_DIR)/xercesImpl.jar:$(AXIS_JARS_DIR)/xml-apis.jar:$(AXIS_JARS_DIR)/xmlsec-1.2.1.jar +AXIS_LIBS = $(AXIS_JARS_DIR)/axis.jar:$(AXIS_JARS_DIR)/saaj.jar:$(AXIS_JARS_DIR)/wss4j-1.5.0.jar:$(AXIS_JARS_DIR)/xmlsec-1.2.1.jar +#AXIS_LIBS = $(AXIS_JARS_DIR)/wss4j-1.5.0.jar + LIBS = /usr/share/java/servletapi5.jar -CLASSPATH = $(IDENT_ABSTRACTION_DIR)/identity-abstraction.jar:$(LIBS) +CLASSPATH = $(AXIS_LIBS):$(IDENT_ABSTRACTION_DIR)/identity-abstraction.jar:$(LIBS) CUR_DIR := $(shell pwd) @@ -98,6 +104,7 @@ $(BUILDDIR)/$(WEBAPP): $(BUILDDIR) $(CLASSES) cp src/com/novell/casa/authtoksvc/Pwd_mechanism.settings $(BUILDDIR)/webapp/WEB-INF/conf/installed_auth_mechanisms/PwdAuthenticate/mechanism.settings cp $(IDENT_ABSTRACTION_DIR)/*.jar $(BUILDDIR)/webapp/WEB-INF/lib/ rm $(BUILDDIR)/webapp/WEB-INF/lib/identity-abstraction.jar + cp $(AXIS_JARS_DIR)/*.jar $(BUILDDIR)/webapp/WEB-INF/lib/ ls $(BUILDDIR)/webapp/WEB-INF/lib/ jar cvf $(BUILDDIR)/$(WEBAPP) -C $(BUILDDIR)/webapp . cp $(BUILDDIR)/$(WEBAPP) $(LIBDIR)/java/ diff --git a/CASA-auth-token/java/server/Svc/README b/CASA-auth-token/java/server/Svc/README index 6b83201b..72d3625f 100644 --- a/CASA-auth-token/java/server/Svc/README +++ b/CASA-auth-token/java/server/Svc/README @@ -69,6 +69,8 @@ Thhe following is an example svc.settings file: /home/jluciani/jakarta-tomcat-5.0.28/webapps/CasaAuthTokenSvc/WEB-INF/conf/iaRealms.xml 60 o=novell + privKey + foobar Note the following about the sample svc.settings file: @@ -102,6 +104,12 @@ Note the following about the sample svc.settings file: from the root of the tree. This setting or an equivalent setting will be moved to the identity abstraction configuration file where it belongs. Once this is done, the setting will no longer be recognized within the svc.settings file. + +- The KeyStoreUses setting specifies the user's alias name in the keystore that identifies + the private key that is to be used to sign tokens. + +- The KeyStorePwd setting specifies the password of the user specified by KeyStoreUser to get + the private signing key from the keystore. CONFIGURING SERVICES TO CONSUME CASA AUTHENTICATION TOKENS diff --git a/CASA-auth-token/java/server/Svc/external/Makefile.am b/CASA-auth-token/java/server/Svc/external/Makefile.am new file mode 100644 index 00000000..0c3e2c8f --- /dev/null +++ b/CASA-auth-token/java/server/Svc/external/Makefile.am @@ -0,0 +1,52 @@ +####################################################################### +# +# Copyright (C) 2006 Novell, Inc. +# +# This program 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 program 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 program; if not, write to the Free +# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# Author: Juan Carlos Luciani +# +####################################################################### + +SUBDIRS = + +DIST_SUBDIRS = + +CFILES = + +EXTRA_DIST = axis.jar \ + axis-ant.jar \ + commons-discovery-0.2.jar \ + commons-logging-1.0.4.jar \ + commons-logging-api.jar \ + jaxrpc.jar \ + log4j.properties \ + log4j-1.2.8.jar \ + README \ + saaj.jar \ + wsdl4j-1.5.1.jar \ + wss4j-1.5.0.jar \ + xalan.jar \ + xercesImpl.jar \ + xml-apis.jar \ + xmlsec-1.2.1.jar + +.PHONY: package package-clean package-install package-uninstall +package package-clean package-install package-uninstall: + $(MAKE) -C $(TARGET_OS) $@ + +maintainer-clean-local: + rm -f Makefile.in + diff --git a/CASA-auth-token/java/server/Svc/external/README b/CASA-auth-token/java/server/Svc/external/README new file mode 100644 index 00000000..459f8a25 --- /dev/null +++ b/CASA-auth-token/java/server/Svc/external/README @@ -0,0 +1,25 @@ +The following describes the source of the files present in this folder. + +axis-1_4 ----> axis-ant.jar +axis-1_4 ----> axis.jar +axis-1_4 ----> commons-discovery-0.2.jar +axis-1_4 ----> commons-logging-1.0.4.jar +xml-security-1_2_1 ----> commons-logging-api.jar +axis-1_4 ----> jaxrpc.jar +axis-1_4 ----> log4j-1.2.8.jar +axis-1_4 ----> log4j.properties +axis-1_4 ----> saaj.jar +axis-1_4 ----> wsdl4j-1.5.1.jar +wss4j-1.5 ----> wss4j-1.5.0.jar +xml-security-1_2_1 ----> xalan.jar +xml-security-1_2_1 ----> xercesImpl.jar +xml-security-1_2_1 ----> xml-apis.jar +xml-security-1_2_1 ----> xmlsec-1.2.1.jar + +xml-security-1_2_1 - URL: http://xml.apache.org/security/dist/java-library/ - File: xml-security-bin-1_2_1.zip + +axis-1_4 - URL: http://www.apache.org/dyn/closer.cgi/ws/axis/1_4 - File: axis-bin-1_4.tar.gz + +wss4j-1.5 - URL: http://www.apache.org/dyn/dyn/closer.cgi/ws/wss4j/ - File: wss4j-bin-1.5.0.zip + + diff --git a/CASA-auth-token/java/server/Svc/external/axis-ant.jar b/CASA-auth-token/java/server/Svc/external/axis-ant.jar new file mode 100644 index 00000000..17527ffd Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/axis-ant.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/axis.jar b/CASA-auth-token/java/server/Svc/external/axis.jar new file mode 100644 index 00000000..20b09a59 Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/axis.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/commons-discovery-0.2.jar b/CASA-auth-token/java/server/Svc/external/commons-discovery-0.2.jar new file mode 100644 index 00000000..b8855484 Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/commons-discovery-0.2.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/commons-logging-1.0.4.jar b/CASA-auth-token/java/server/Svc/external/commons-logging-1.0.4.jar new file mode 100644 index 00000000..b73a80fa Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/commons-logging-1.0.4.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/commons-logging-api.jar b/CASA-auth-token/java/server/Svc/external/commons-logging-api.jar new file mode 100644 index 00000000..209bcdfd Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/commons-logging-api.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/jaxrpc.jar b/CASA-auth-token/java/server/Svc/external/jaxrpc.jar new file mode 100644 index 00000000..a2c13d9a Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/jaxrpc.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/log4j-1.2.8.jar b/CASA-auth-token/java/server/Svc/external/log4j-1.2.8.jar new file mode 100644 index 00000000..493a3ccc Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/log4j-1.2.8.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/log4j.properties b/CASA-auth-token/java/server/Svc/external/log4j.properties new file mode 100644 index 00000000..3ca86f40 --- /dev/null +++ b/CASA-auth-token/java/server/Svc/external/log4j.properties @@ -0,0 +1,20 @@ +# Set root category priority to INFO and its only appender to CONSOLE. +log4j.rootCategory=INFO, CONSOLE +#log4j.rootCategory=INFO, CONSOLE, LOGFILE + +# Set the enterprise logger category to FATAL and its only appender to CONSOLE. +log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE + +# CONSOLE is set to be a ConsoleAppender using a PatternLayout. +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.Threshold=INFO +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n + +# LOGFILE is set to be a File appender using a PatternLayout. +log4j.appender.LOGFILE=org.apache.log4j.FileAppender +log4j.appender.LOGFILE.File=axis.log +log4j.appender.LOGFILE.Append=true +log4j.appender.LOGFILE.Threshold=INFO +log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout +log4j.appender.LOGFILE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n diff --git a/CASA-auth-token/java/server/Svc/external/saaj.jar b/CASA-auth-token/java/server/Svc/external/saaj.jar new file mode 100644 index 00000000..4ea696e7 Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/saaj.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/wsdl4j-1.5.1.jar b/CASA-auth-token/java/server/Svc/external/wsdl4j-1.5.1.jar new file mode 100644 index 00000000..c6254ee6 Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/wsdl4j-1.5.1.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/wss4j-1.5.0.jar b/CASA-auth-token/java/server/Svc/external/wss4j-1.5.0.jar new file mode 100644 index 00000000..90ae8826 Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/wss4j-1.5.0.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/xalan.jar b/CASA-auth-token/java/server/Svc/external/xalan.jar new file mode 100644 index 00000000..73cf175f Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/xalan.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/xercesImpl.jar b/CASA-auth-token/java/server/Svc/external/xercesImpl.jar new file mode 100644 index 00000000..14c3162c Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/xercesImpl.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/xml-apis.jar b/CASA-auth-token/java/server/Svc/external/xml-apis.jar new file mode 100644 index 00000000..2dd83771 Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/xml-apis.jar differ diff --git a/CASA-auth-token/java/server/Svc/external/xmlsec-1.2.1.jar b/CASA-auth-token/java/server/Svc/external/xmlsec-1.2.1.jar new file mode 100644 index 00000000..512f93fd Binary files /dev/null and b/CASA-auth-token/java/server/Svc/external/xmlsec-1.2.1.jar differ diff --git a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/AuthReqMsg.java b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/AuthReqMsg.java index c220efd0..7b9a55ab 100644 --- a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/AuthReqMsg.java +++ b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/AuthReqMsg.java @@ -256,6 +256,11 @@ public class AuthReqMsg m_state = AWAITING_REALM_ELEMENT_END; break; + case AWAITING_REALM_ELEMENT_END: + // Consume the data + m_authReqMsg.m_realm = m_authReqMsg.m_realm.concat(new String(ch, start, length)); + break; + case AWAITING_MECH_DATA: // Consume the data m_authReqMsg.m_authMechanism = new String(ch, start, length); @@ -264,6 +269,11 @@ public class AuthReqMsg m_state = AWAITING_MECH_ELEMENT_END; break; + case AWAITING_MECH_ELEMENT_END: + // Consume the data + m_authReqMsg.m_authMechanism = m_authReqMsg.m_authMechanism.concat(new String(ch, start, length)); + break; + case AWAITING_AUTH_MECH_TOKEN_DATA: // Consume the data m_authReqMsg.m_authMechToken = new String(ch, start, length); @@ -272,6 +282,11 @@ public class AuthReqMsg m_state = AWAITING_AUTH_MECH_TOKEN_ELEMENT_END; break; + case AWAITING_AUTH_MECH_TOKEN_ELEMENT_END: + // Consume the data + m_authReqMsg.m_authMechToken = m_authReqMsg.m_authMechToken.concat(new String(ch, start, length)); + break; + default: // Do nothing break; diff --git a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/AuthToken.java b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/AuthToken.java index b081ea8a..b95abdb2 100644 --- a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/AuthToken.java +++ b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/AuthToken.java @@ -26,20 +26,26 @@ package com.novell.casa.authtoksvc; import java.io.ByteArrayInputStream; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; +import org.apache.axis.Message; +import org.apache.axis.MessageContext; +import org.apache.axis.client.AxisClient; +import org.apache.axis.configuration.NullProvider; +import org.apache.axis.message.SOAPEnvelope; +import org.apache.axis.message.SOAPBody; +import org.apache.axis.message.MessageElement; + +import javax.xml.namespace.QName; +import java.io.*; /* * AuthToken Class. * * This class constructs authentication tokens that clients can present - * to services for authentication. The format of the authentication token - * is as follows: - * - * + * to services for authentication. The authentication token consists of + * a SOAP message secured with WSSecurity with the appropriate elements signed + * and with a timestamp. The body of the SOAP message is as follows: + * * * signature value * lifetime value @@ -51,280 +57,23 @@ public class AuthToken { private String m_token; - private String m_lifetime; - private String m_lifetimeShorter; - private String m_identityTokenType; - private StringBuffer m_identityToken; - private String m_signature; + private String m_lifetime = ""; + private String m_lifetimeShorter = ""; + private String m_identityTokenType = null; + private String m_identityToken = null; - /* - * Class for handling parsing events. - */ - private class SAXHandler extends org.xml.sax.helpers.DefaultHandler - { - private final static int AWAITING_ROOT_ELEMENT_START = 0; - private final static int AWAITING_ROOT_ELEMENT_END = 1; - private final static int AWAITING_SIGNATURE_ELEMENT_START = 2; - private final static int AWAITING_SIGNATURE_ELEMENT_END = 3; - private final static int AWAITING_SIGNATURE_DATA = 4; - private final static int AWAITING_LIFETIME_ELEMENT_START = 5; - private final static int AWAITING_LIFETIME_ELEMENT_END = 6; - private final static int AWAITING_LIFETIME_DATA = 7; - private final static int AWAITING_IDENT_TOKEN_ELEMENT_START = 8; - private final static int AWAITING_IDENT_TOKEN_ELEMENT_END = 9; - private final static int AWAITING_IDENT_TOKEN_DATA = 10; - private final static int AWAITING_TYPE_ELEMENT_START = 11; - private final static int AWAITING_TYPE_ELEMENT_END = 12; - private final static int AWAITING_TYPE_DATA = 13; - private final static int DONE_PARSING = 14; + static final String authTokenSoapMsg = + "" + + "" + + " " + + " " + + " " + + ""; - private AuthToken m_AuthToken; - private int m_state; - - /* - * Constructor - */ - public SAXHandler (AuthToken AuthToken) - { - super(); - - // Initialize our members - m_AuthToken = AuthToken; - m_state = AWAITING_ROOT_ELEMENT_START; - } - - /* - * endDocument() implementation. - */ - public void endDocument () throws SAXException - { - // Verify that we obtained all of the required elements - if (m_state != DONE_PARSING) - { - System.err.println("AuthToken SAXHandler.endDocument()- Missing element"); - throw new SAXException("Missing element"); - } - } - - /* - * startElement() implementation. - */ - public void startElement (String uri, String name, String qName, org.xml.sax.Attributes atts) throws SAXException - { - // Proceed based on our state - switch (m_state) - { - - case AWAITING_ROOT_ELEMENT_START: - // Verify that we are processing the expected tag - if (ProtoDefs.authTokenElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_SIGNATURE_ELEMENT_START; - } - else - { - System.err.println("AuthToken SAXHandler.startElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_SIGNATURE_ELEMENT_START: - // Verify that we are processing the expected tag - if (ProtoDefs.signatureElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_SIGNATURE_DATA; - } - else - { - System.err.println("AuthToken SAXHandler.startElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_LIFETIME_ELEMENT_START: - // Verify that we are processing the expected tag - if (ProtoDefs.lifetimeElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_LIFETIME_DATA; - } - else - { - System.err.println("AuthToken SAXHandler.startElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - - break; - - case AWAITING_IDENT_TOKEN_ELEMENT_START: - // Verify that we are processing the expected tag - if (ProtoDefs.identTokenElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_TYPE_ELEMENT_START; - } - else - { - System.err.println("AuthToken SAXHandler.startElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_TYPE_ELEMENT_START: - // Verify that we are processing the expected tag - if (ProtoDefs.typeElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_TYPE_DATA; - } - else - { - System.err.println("AuthToken SAXHandler.startElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - default: - System.err.println("AuthToken SAXHandler.startElement()- State error"); - throw new SAXException("State error"); - } - } - - /* - * endElement() immplementation. - */ - public void endElement (String uri, String name, String qName) throws SAXException - { - // Proceed based on our state - switch (m_state) - { - - case AWAITING_ROOT_ELEMENT_END: - // Verify that we are processing the expected tag - if (ProtoDefs.authTokenElementName.equals(qName)) - { - // Advance to the next state - m_state = DONE_PARSING; - } - else - { - System.err.println("AuthToken SAXHandler.endElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_SIGNATURE_ELEMENT_END: - // Verify that we are processing the expected tag - if (ProtoDefs.signatureElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_LIFETIME_ELEMENT_START; - } - else - { - System.err.println("AuthToken SAXHandler.endElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_LIFETIME_ELEMENT_END: - // Verify that we are processing the expected tag - if (ProtoDefs.lifetimeElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_IDENT_TOKEN_ELEMENT_START; - } - else - { - System.err.println("AuthToken SAXHandler.endElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_TYPE_ELEMENT_END: - // Verify that we are processing the expected tag - if (ProtoDefs.typeElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_IDENT_TOKEN_DATA; - } - else - { - System.err.println("AuthToken SAXHandler.endElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_IDENT_TOKEN_ELEMENT_END: - // Verify that we are processing the expected tag - if (ProtoDefs.identTokenElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_ROOT_ELEMENT_END; - } - else - { - System.err.println("AuthToken SAXHandler.endElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - default: - System.err.println("AuthToken SAXHandler.startElement()- State error"); - throw new SAXException("State error"); - } - } - - /* - * character() implementation. - */ - public void characters (char ch[], int start, int length) throws SAXException - { - // Proceed based on our state - switch (m_state) - { - - case AWAITING_SIGNATURE_DATA: - // Consume the data - m_AuthToken.m_signature = new String(ch, start, length); - - // Advance to the next state - m_state = AWAITING_SIGNATURE_ELEMENT_END; - break; - - case AWAITING_LIFETIME_DATA: - // Consume the data - m_AuthToken.m_lifetime = new String(ch, start, length); - - // Advance to the next state - m_state = AWAITING_LIFETIME_ELEMENT_END; - break; - - case AWAITING_TYPE_DATA: - // Consume the data - m_AuthToken.m_identityTokenType = new String(ch, start, length); - - // Advance to the next state - m_state = AWAITING_TYPE_ELEMENT_END; - break; - - case AWAITING_IDENT_TOKEN_DATA: - case AWAITING_IDENT_TOKEN_ELEMENT_END: - // Consume the data - m_AuthToken.m_identityToken.append(ch, start, length); - - // Advance to the next state - m_state = AWAITING_IDENT_TOKEN_ELEMENT_END; - break; - - default: - // Do nothing - break; - } - } - } + static final private MessageContext axisMsgContext = new MessageContext(new AxisClient(new NullProvider())); /* * Constructor. @@ -354,37 +103,28 @@ public class AuthToken targetHost, svcConfig); - m_identityToken = new StringBuffer(); - m_identityToken.append(identityToken.getEncodedToken()); + m_identityToken = identityToken.getEncodedToken(); m_identityTokenType = identityToken.getProviderType(); m_lifetime = authTokenConfig.getSetting(AuthTokenConfig.TokenLifetime); m_lifetimeShorter = authTokenConfig.getSetting(AuthTokenConfig.LifetimeShorter); - // Generate a signature - // tbd - Over identToken, identToken type, and lifetime data. - m_signature = "tbd"; + // Create AuthTokenMessage + Message authTokenMessage = getMessage(identityToken.getEncodedToken(), + identityToken.getProviderType(), + Integer.valueOf(m_lifetime).intValue(), + svcConfig); - // Get a StringBuffer to help us with the construction of the token - StringBuffer sb = new StringBuffer(); - - // Start building the message - sb.append(ProtoDefs.xmlDeclaration + "\r\n"); - sb.append("<" + ProtoDefs.authTokenElementName + ">" + "\r\n"); - sb.append("<" + ProtoDefs.signatureElementName + ">" + m_signature + "" + "\r\n"); - sb.append("<" + ProtoDefs.lifetimeElementName + ">" + m_lifetime + "" + "\r\n"); - sb.append("<" + ProtoDefs.identTokenElementName + ">" - + "<" + ProtoDefs.typeElementName + ">" + m_identityTokenType + "" - + m_identityToken + "" + "\r\n"); - sb.append("" + "\r\n"); - - // Save the token - m_token = sb.toString(); + // Now save the message as a string + OutputStream outStream = new ByteArrayOutputStream(); + authTokenMessage.writeTo(outStream); + m_token = outStream.toString(); + outStream.close(); } catch (Exception e) { // tbd - System.err.println("AuthToken()- Exception: " + e.toString()); + System.err.println("AuthToken()- Exception: " + e.toString()); } } else @@ -402,33 +142,102 @@ public class AuthToken // Decode the token string m_token = Base64Coder.decode(token); - // Instantiate string buffer for the identity token - m_identityToken = new StringBuffer(); + // Now instantiate a SOAP message with the string + InputStream inStream = new ByteArrayInputStream(m_token.getBytes()); + Message message = new Message(inStream); + + // Get access to the SOAP Envelope + SOAPEnvelope envelope = message.getSOAPEnvelope(); + + // Verify the message + if (WSSecurity.verifyMessage(envelope)) + { + // Message verification succeded, now obtain the identity token + // and its type from the message body. + SOAPBody body = (SOAPBody) envelope.getBody(); + QName authTokenElementName = new QName("auth_token"); + MessageElement authTokenElement = body.getChildElement(authTokenElementName); + QName identTokenElementName = new QName("ident_token"); + MessageElement identTokenElement = authTokenElement.getChildElement(identTokenElementName); + if (identTokenElement != null) + { + QName identTokenTypeElementName = new QName("type"); + MessageElement identTokenTypeElement = identTokenElement.getChildElement(identTokenTypeElementName); + if (identTokenTypeElement != null) + { + m_identityToken = identTokenElement.getChildNodes().item(1).getNodeValue(); + m_identityTokenType = identTokenTypeElement.getValue(); + } + } + + if (m_identityToken == null || m_identityTokenType == null) + { + System.out.println("AuthToken()- Required data missing from authentication token"); + throw new Exception("Error: Required data missing from Authentication Token"); + } + } + else + { + // Message verification failed + System.err.println("AuthToken()- Invalid Authentication Token"); + throw new Exception("Invalid Authentication Token"); + } + } + + /** + * Get AuthToken SOAP Message + * + * @param identityToken String containing the identity token that should be part of the message + * @param identityTokenType String containing the identity token type + * @param lifetime Lifetime that should be specified in the message timestamp (seconds) + * @param svcConfig Service configuratio object + * @return Message AuthToken message, null if the method fails. + */ + private Message getMessage(String identityToken, + String identityTokenType, + int lifetime, + SvcConfig svcConfig) + { + Message secureMessage; - // Now parse the token into its elements try { - // Parse the AuthToken - XMLReader xr = XMLReaderFactory.createXMLReader(); - SAXHandler handler = new SAXHandler(this); - xr.setContentHandler(handler); - xr.setErrorHandler(handler); + // Build SOAP Message with an identity token in the body + // + // First create a message and obtain its body + InputStream inStream = new ByteArrayInputStream(authTokenSoapMsg.getBytes()); + Message message = new Message(inStream); + message.setMessageContext(axisMsgContext); + SOAPBody body = (SOAPBody) message.getSOAPBody(); - ByteArrayInputStream inStream = new ByteArrayInputStream(m_token.getBytes()); - InputSource source = new InputSource(inStream); - xr.parse(source); + // Get access to the auth_token element + QName authTokenElementName = new QName("auth_token"); + MessageElement authTokenElement = body.getChildElement(authTokenElementName); - // Verify the signature - // tbd + // Get access to the ident_token element and set its value + QName identTokenElementName = new QName("ident_token"); + MessageElement identTokenElement = authTokenElement.getChildElement(identTokenElementName); + identTokenElement.addTextNode(identityToken); - // Verify that the token has not expired - // tbd + // Get access to the identity token type element element and set its value + QName identTokenTypeElementName = new QName("type"); + MessageElement identTokenTypeElement = identTokenElement.getChildElement(identTokenTypeElementName); + identTokenTypeElement.setValue(identityTokenType); + + // Now we need to secure the SOAP message that we created, we are doing to + // do so by adding a timestamp and signing the timestamp as well as the body. + // To do this we are going to leverage WS-Security. + secureMessage = WSSecurity.secureSOAPEnvelope(message.getSOAPEnvelope(), + lifetime, + svcConfig); } - catch (SAXException e) + catch (Exception e) { - System.err.println("AuthToken()- Parse exception: " + e.toString()); - throw new Exception("Protocol error"); + System.out.println("AuthToken.getMessage() - Exception caught building message, error: " + e.getMessage()); + secureMessage = null; } + + return secureMessage; } /* @@ -441,11 +250,20 @@ public class AuthToken /* * Returns the lifetime of the token. + * + * Note: It is only valid to execute this procedure if its called on an object + * instantiated via the constructor which takes a lifetime parameter. */ - public String getLifetime() + public String getLifetime() throws Exception { - // tbd - Convert to tokenLifetime and lifetimeShorter to ints, substractand then convert result to string - return "60"; + // Throw exeption if the lifetime parameter is not set + if (m_lifetime.length() == 0) + { + System.out.println("AuthToken.getLifetime() - Called when lifetime is not set"); + throw new Exception("Error: Called getLifetime while not set"); + } + + return Integer.toString(Integer.valueOf(m_lifetime).intValue() - Integer.valueOf(m_lifetimeShorter).intValue()); } /* @@ -453,7 +271,7 @@ public class AuthToken */ public String getIdentityToken() { - return m_identityToken.toString(); + return m_identityToken; } /* diff --git a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/Authenticate.java b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/Authenticate.java index fb4a24da..4e5115a3 100644 --- a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/Authenticate.java +++ b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/Authenticate.java @@ -281,16 +281,16 @@ public class Authenticate implements RpcMethod // An identity was resolved, get a SessionToken for it. SessionToken sessionToken = new SessionToken(identId, authReqMsg.getRealm(), - m_svcConfig.getSetting(SvcConfig.SessionTokenLifetime)); + m_svcConfig.getSetting(SvcConfig.SessionTokenLifetime), + m_svcConfig); // Write out the response + String respLifetime = Integer.toString(Integer.valueOf(m_svcConfig.getSetting(SvcConfig.SessionTokenLifetime)).intValue() + - Integer.valueOf(m_svcConfig.getSetting(SvcConfig.LifetimeShorter)).intValue()); AuthRespMsg authRespMsg = new AuthRespMsg(ProtoDefs.httpOkStatusMsg, ProtoDefs.httpOkStatusCode, sessionToken.toString(), - m_svcConfig.getSetting(SvcConfig.SessionTokenLifetime)); - // tbd - Convert to ints, perform calculation, and then convert result to string - //m_svcConfig.getSetting(SvcConfig.SessionTokenLifetime) - //- m_svcConfig.getSetting(SvcConfig.LifetimeShorter)); + respLifetime); out.println(authRespMsg.toString()); } else diff --git a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/GetAuthPolicyReqMsg.java b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/GetAuthPolicyReqMsg.java index 73815f00..b19a3d47 100644 --- a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/GetAuthPolicyReqMsg.java +++ b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/GetAuthPolicyReqMsg.java @@ -102,7 +102,6 @@ public class GetAuthPolicyReqMsg // Proceed based on our state switch (m_state) { - case AWAITING_ROOT_ELEMENT_START: // Verify that we are processing the expected tag if (ProtoDefs.getAuthPolicyRequestElementName.equals(qName)) @@ -160,7 +159,6 @@ public class GetAuthPolicyReqMsg // Proceed based on our state switch (m_state) { - case AWAITING_ROOT_ELEMENT_END: // Verify that we are processing the expected tag if (ProtoDefs.getAuthPolicyRequestElementName.equals(qName)) @@ -217,7 +215,6 @@ public class GetAuthPolicyReqMsg // Proceed based on our state switch (m_state) { - case AWAITING_SERVICE_DATA: // Consume the data m_GetAuthPolicyReqMsg.m_serviceName = new String(ch, start, length); @@ -226,6 +223,11 @@ public class GetAuthPolicyReqMsg m_state = AWAITING_SERVICE_ELEMENT_END; break; + case AWAITING_SERVICE_ELEMENT_END: + // Consume the data + m_GetAuthPolicyReqMsg.m_serviceName = m_GetAuthPolicyReqMsg.m_serviceName.concat(new String(ch, start, length)); + break; + case AWAITING_HOST_DATA: // Consume the data m_GetAuthPolicyReqMsg.m_hostName = new String(ch, start, length); @@ -234,6 +236,11 @@ public class GetAuthPolicyReqMsg m_state = AWAITING_HOST_ELEMENT_END; break; + case AWAITING_HOST_ELEMENT_END: + // Consume the data + m_GetAuthPolicyReqMsg.m_hostName = m_GetAuthPolicyReqMsg.m_hostName.concat(new String(ch, start, length)); + break; + default: // Do nothing break; diff --git a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/GetAuthTokReqMsg.java b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/GetAuthTokReqMsg.java index 52ecee00..9a98dbd8 100644 --- a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/GetAuthTokReqMsg.java +++ b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/GetAuthTokReqMsg.java @@ -107,7 +107,6 @@ public class GetAuthTokReqMsg // Proceed based on our state switch (m_state) { - case AWAITING_ROOT_ELEMENT_START: // Verify that we are processing the expected tag if (ProtoDefs.getAuthTokRequestElementName.equals(qName)) @@ -179,7 +178,6 @@ public class GetAuthTokReqMsg // Proceed based on our state switch (m_state) { - case AWAITING_ROOT_ELEMENT_END: // Verify that we are processing the expected tag if (ProtoDefs.getAuthTokRequestElementName.equals(qName)) @@ -250,7 +248,6 @@ public class GetAuthTokReqMsg // Proceed based on our state switch (m_state) { - case AWAITING_SERVICE_DATA: // Consume the data m_GetAuthTokReqMsg.m_serviceName = new String(ch, start, length); @@ -259,6 +256,11 @@ public class GetAuthTokReqMsg m_state = AWAITING_SERVICE_ELEMENT_END; break; + case AWAITING_SERVICE_ELEMENT_END: + // Consume the data + m_GetAuthTokReqMsg.m_serviceName = m_GetAuthTokReqMsg.m_serviceName.concat(new String(ch, start, length)); + break; + case AWAITING_HOST_DATA: // Consume the data m_GetAuthTokReqMsg.m_hostName = new String(ch, start, length); @@ -267,6 +269,11 @@ public class GetAuthTokReqMsg m_state = AWAITING_HOST_ELEMENT_END; break; + case AWAITING_HOST_ELEMENT_END: + // Consume the data + m_GetAuthTokReqMsg.m_hostName = m_GetAuthTokReqMsg.m_hostName.concat(new String(ch, start, length)); + break; + case AWAITING_SESSION_TOKEN_DATA: // Consume the data m_GetAuthTokReqMsg.m_sessionToken = new String(ch, start, length); @@ -275,6 +282,11 @@ public class GetAuthTokReqMsg m_state = AWAITING_SESSION_TOKEN_ELEMENT_END; break; + case AWAITING_SESSION_TOKEN_ELEMENT_END: + // Consume the data + m_GetAuthTokReqMsg.m_sessionToken = m_GetAuthTokReqMsg.m_sessionToken.concat(new String(ch, start, length)); + break; + default: // Do nothing break; diff --git a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/Makefile.am b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/Makefile.am index 34a945e4..b9899c28 100644 --- a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/Makefile.am +++ b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/Makefile.am @@ -48,7 +48,8 @@ JAVAFILES = ProtoDefs.java \ GetAuthTokRespMsg.java \ Krb5Authenticate.java \ PwdAuthenticate.java \ - SessionToken.java + SessionToken.java \ + WSSecurity.java EXTRA_DIST = $(JAVAFILES) \ Krb5_mechanism.settings \ diff --git a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/SessionToken.java b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/SessionToken.java index 7f9fc29a..3067e5d9 100644 --- a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/SessionToken.java +++ b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/SessionToken.java @@ -26,20 +26,26 @@ package com.novell.casa.authtoksvc; import java.io.ByteArrayInputStream; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; +import org.apache.axis.Message; +import org.apache.axis.MessageContext; +import org.apache.axis.client.AxisClient; +import org.apache.axis.configuration.NullProvider; +import org.apache.axis.message.SOAPEnvelope; +import org.apache.axis.message.SOAPBody; +import org.apache.axis.message.MessageElement; +import javax.xml.namespace.QName; +import java.io.*; /* * SessionToken class. * -* This class constructs session tokens that Casa clients can present to -* the Casa server to prove that an entity has been authenticated to -* a particular realm. The format of the session token is as follows: -* -* +* This class constructs sessions tokens that clients can present to an ATS +* to prove that an entity has been authenticated to a particular realm. +* The session token consists of a SOAP message secured with WSSecurity +* with the appropriate elements signed and with a timestamp. The body of +* the SOAP message is as follows: +* * * signature value * lifetime value @@ -51,308 +57,47 @@ import org.xml.sax.helpers.XMLReaderFactory; public class SessionToken { - private String m_id; - private String m_realm; - private String m_lifetime; - private String m_signature; + private String m_id = null; + private String m_realm = null; private String m_token; - /* - * Class for handling parsing events. - */ - private class SAXHandler extends org.xml.sax.helpers.DefaultHandler - { - private final static int AWAITING_ROOT_ELEMENT_START = 0; - private final static int AWAITING_ROOT_ELEMENT_END = 1; - private final static int AWAITING_SIGNATURE_ELEMENT_START = 2; - private final static int AWAITING_SIGNATURE_ELEMENT_END = 3; - private final static int AWAITING_SIGNATURE_DATA = 4; - private final static int AWAITING_LIFETIME_ELEMENT_START = 5; - private final static int AWAITING_LIFETIME_ELEMENT_END = 6; - private final static int AWAITING_LIFETIME_DATA = 7; - private final static int AWAITING_REALM_ELEMENT_START = 8; - private final static int AWAITING_REALM_ELEMENT_END = 9; - private final static int AWAITING_REALM_DATA = 10; - private final static int AWAITING_IDENT_ID_ELEMENT_START = 11; - private final static int AWAITING_IDENT_ID_ELEMENT_END = 12; - private final static int AWAITING_IDENT_ID_DATA = 13; - private final static int DONE_PARSING = 14; + static final String sessionTokenSoapMsg = + "" + + "" + + " " + + " " + + " " + + ""; - private SessionToken m_SessionToken; - private int m_state; + static final private MessageContext axisMsgContext = new MessageContext(new AxisClient(new NullProvider())); - /* - * Constructor - */ - public SAXHandler (SessionToken SessionToken) - { - super(); - - // Initialize our members - m_SessionToken = SessionToken; - m_state = AWAITING_ROOT_ELEMENT_START; - } - - /* - * endDocument() implementation. - */ - public void endDocument () throws SAXException - { - // Verify that we obtained all of the required elements - if (m_state != DONE_PARSING) - { - System.err.println("SessionToken SAXHandler.endDocument()- Missing element"); - throw new SAXException("Missing element"); - } - } - - /* - * startElement() implementation. - */ - public void startElement (String uri, String name, String qName, org.xml.sax.Attributes atts) throws SAXException - { - // Proceed based on our state - switch (m_state) - { - - case AWAITING_ROOT_ELEMENT_START: - // Verify that we are processing the expected tag - if (ProtoDefs.sessionTokenElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_SIGNATURE_ELEMENT_START; - } - else - { - System.err.println("SessionToken SAXHandler.startElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_SIGNATURE_ELEMENT_START: - // Verify that we are processing the expected tag - if (ProtoDefs.signatureElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_SIGNATURE_DATA; - } - else - { - System.err.println("SessionToken SAXHandler.startElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_LIFETIME_ELEMENT_START: - // Verify that we are processing the expected tag - if (ProtoDefs.lifetimeElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_LIFETIME_DATA; - } - else - { - System.err.println("SessionToken SAXHandler.startElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - - break; - - case AWAITING_REALM_ELEMENT_START: - // Verify that we are processing the expected tag - if (ProtoDefs.realmElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_REALM_DATA; - } - else - { - System.err.println("SessionToken SAXHandler.startElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_IDENT_ID_ELEMENT_START: - // Verify that we are processing the expected tag - if (ProtoDefs.identIdElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_IDENT_ID_DATA; - } - else - { - System.err.println("SessionToken SAXHandler.startElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - default: - System.err.println("SessionToken SAXHandler.startElement()- State error"); - throw new SAXException("State error"); - } - } - - /* - * endElement() immplementation. - */ - public void endElement (String uri, String name, String qName) throws SAXException - { - // Proceed based on our state - switch (m_state) - { - - case AWAITING_ROOT_ELEMENT_END: - // Verify that we are processing the expected tag - if (ProtoDefs.sessionTokenElementName.equals(qName)) - { - // Advance to the next state - m_state = DONE_PARSING; - } - else - { - System.err.println("SessionToken SAXHandler.endElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_SIGNATURE_ELEMENT_END: - // Verify that we are processing the expected tag - if (ProtoDefs.signatureElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_LIFETIME_ELEMENT_START; - } - else - { - System.err.println("SessionToken SAXHandler.endElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_LIFETIME_ELEMENT_END: - // Verify that we are processing the expected tag - if (ProtoDefs.lifetimeElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_REALM_ELEMENT_START; - } - else - { - System.err.println("SessionToken SAXHandler.endElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_REALM_ELEMENT_END: - // Verify that we are processing the expected tag - if (ProtoDefs.realmElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_IDENT_ID_ELEMENT_START; - } - else - { - System.err.println("SessionToken SAXHandler.endElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - case AWAITING_IDENT_ID_ELEMENT_END: - // Verify that we are processing the expected tag - if (ProtoDefs.identIdElementName.equals(qName)) - { - // Advance to the next state - m_state = AWAITING_ROOT_ELEMENT_END; - } - else - { - System.err.println("SessionToken SAXHandler.endElement()- Un-expected element"); - throw new SAXException("Un-expected element"); - } - break; - - default: - System.err.println("SessionToken SAXHandler.startElement()- State error"); - throw new SAXException("State error"); - } - } - - /* - * character() implementation. - */ - public void characters (char ch[], int start, int length) throws SAXException - { - // Proceed based on our state - switch (m_state) - { - - case AWAITING_SIGNATURE_DATA: - // Consume the data - m_SessionToken.m_signature = new String(ch, start, length); - - // Advance to the next state - m_state = AWAITING_SIGNATURE_ELEMENT_END; - break; - - case AWAITING_LIFETIME_DATA: - // Consume the data - m_SessionToken.m_lifetime = new String(ch, start, length); - - // Advance to the next state - m_state = AWAITING_LIFETIME_ELEMENT_END; - break; - - case AWAITING_REALM_DATA: - // Consume the data - m_SessionToken.m_realm = new String(ch, start, length); - - // Advance to the next state - m_state = AWAITING_REALM_ELEMENT_END; - break; - - case AWAITING_IDENT_ID_DATA: - // Consume the data - m_SessionToken.m_id = new String(ch, start, length); - - // Advance to the next state - m_state = AWAITING_IDENT_ID_ELEMENT_END; - break; - - default: - // Do nothing - break; - } - } - } /* * Constructor */ - public SessionToken(String id, String realm, String lifetime) throws Exception + public SessionToken(String id, + String realm, + String lifetime, + SvcConfig svcConfig) throws Exception { // Save copies of the input parameters m_id = id; m_realm = realm; - m_lifetime = lifetime; - // Generate a signature - // tbd - Over id, realm, and lifetime data. - m_signature = "tbd"; + // Create SessionTokenMessage + Message sessionTokenMessage = getMessage(realm, + id, + Integer.valueOf(lifetime).intValue(), + svcConfig); - // Get a StringBuffer to help us with the construction of the token - StringBuffer sb = new StringBuffer(); - - // Start building the message - sb.append(ProtoDefs.xmlDeclaration + "\r\n"); - sb.append("<" + ProtoDefs.sessionTokenElementName + ">" + "\r\n"); - sb.append("<" + ProtoDefs.signatureElementName + ">" + m_signature + "" + "\r\n"); - sb.append("<" + ProtoDefs.lifetimeElementName + ">" + m_lifetime + "" + "\r\n"); - sb.append("<" + ProtoDefs.realmElementName + ">" + m_realm + "" + "\r\n"); - sb.append("<" + ProtoDefs.identIdElementName + ">" + m_id + "" + "\r\n"); - sb.append("" + "\r\n"); - - // Save the token - m_token = sb.toString(); + // Now save the message as a string + OutputStream outStream = new ByteArrayOutputStream(); + sessionTokenMessage.writeTo(outStream); + m_token = outStream.toString(); + outStream.close(); } /* @@ -364,30 +109,102 @@ public class SessionToken // Decode the token string m_token = Base64Coder.decode(token); - // Now parse the token into its elements + // Now instantiate a SOAP message with the string + InputStream inStream = new ByteArrayInputStream(m_token.getBytes()); + Message message = new Message(inStream); + + // Get access to the SOAP Envelope + SOAPEnvelope envelope = message.getSOAPEnvelope(); + + // Verify the message + if (WSSecurity.verifyMessage(envelope)) + { + // Message verification succeded, now obtain the realm and identity id + // from the message body. + SOAPBody body = (SOAPBody) envelope.getBody(); + QName sessionTokenElementName = new QName("session_token"); + MessageElement sessionTokenElement = body.getChildElement(sessionTokenElementName); + QName realmElementName = new QName("realm"); + MessageElement realmElement = sessionTokenElement.getChildElement(realmElementName); + if (realmElement != null) + { + m_realm = realmElement.getChildNodes().item(0).getNodeValue(); + } + QName identIdElementName = new QName("ident_id"); + MessageElement identIdElement = sessionTokenElement.getChildElement(identIdElementName); + if (identIdElement != null) + { + m_id = identIdElement.getChildNodes().item(0).getNodeValue(); + } + + if (m_realm == null || m_id == null) + { + System.out.println("SessionToken()- Required data missing from session token"); + throw new Exception("Error: Required data missing from session Token"); + } + } + else + { + // Message verification failed + System.err.println("SessionToken()- Invalid Session Token"); + throw new Exception("Invalid Session Token"); + } + } + + /** + * Get SessionToken SOAP Message + * + * @param realm String containing the identity token that should be part of the message + * @param identityId String containing the identity token type + * @param lifetime Lifetime that should be specified in the message timestamp (seconds) + * @param svcConfig Service Config object + * @return Message SessionToken message, null if the method fails. + */ + private Message getMessage(String realm, + String identityId, + int lifetime, + SvcConfig svcConfig) + { + Message secureMessage; + try { - // Parse the SessionToken - XMLReader xr = XMLReaderFactory.createXMLReader(); - SAXHandler handler = new SAXHandler(this); - xr.setContentHandler(handler); - xr.setErrorHandler(handler); + // Build SOAP Message with an identity token in the body + // + // First create a message and obtain its body + InputStream inStream = new ByteArrayInputStream(sessionTokenSoapMsg.getBytes()); + Message message = new Message(inStream); + message.setMessageContext(axisMsgContext); + SOAPBody body = (SOAPBody) message.getSOAPBody(); - ByteArrayInputStream inStream = new ByteArrayInputStream(m_token.getBytes()); - InputSource source = new InputSource(inStream); - xr.parse(source); + // Get access to the session_token element + QName sessionTokenElementName = new QName("session_token"); + MessageElement sessionTokenElement = body.getChildElement(sessionTokenElementName); - // Verify the signature - // tbd + // Get access to the realm element and set its value + QName realmElementName = new QName("realm"); + MessageElement realmElement = sessionTokenElement.getChildElement(realmElementName); + realmElement.addTextNode(realm); - // Verify that the token has not expired - // tbd + // Get access to the ident_id element and set its value + QName identIdElementName = new QName("ident_id"); + MessageElement identIdElement = sessionTokenElement.getChildElement(identIdElementName); + identIdElement.addTextNode(identityId); + + // Now we need to secure the SOAP message that we created, we are doing to + // do so by adding a timestamp and signing the timestamp as well as the body. + // To do this we are going to leverage WS-Security. + secureMessage = WSSecurity.secureSOAPEnvelope(message.getSOAPEnvelope(), + lifetime, + svcConfig); } - catch (SAXException e) + catch (Exception e) { - System.err.println("SessionToken()- Parse exception: " + e.toString()); - throw new Exception("Protocol error"); + System.out.println("SessionToken.getMessage() - Exception caught building message, error: " + e.getMessage()); + secureMessage = null; } + + return secureMessage; } /* diff --git a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/SvcConfig.java b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/SvcConfig.java index a9c34e50..e5845f1a 100644 --- a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/SvcConfig.java +++ b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/SvcConfig.java @@ -53,6 +53,8 @@ public class SvcConfig public final static String ConfigFolderPath = "ConfigFolderPath"; public final static String AppRootPath = "AppRootPath"; public final static String ReconfigureInterval = "ReconfigureInterval"; + public final static String KeyStoreUser = "KeyStoreUser"; + public final static String KeyStorePwd = "KeyStorePwd"; // Default configuration values public final static String DefaultSessionTokenLifetimeValue = "43200"; // Seconds diff --git a/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/WSSecurity.java b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/WSSecurity.java new file mode 100644 index 00000000..b0699437 --- /dev/null +++ b/CASA-auth-token/java/server/Svc/src/com/novell/casa/authtoksvc/WSSecurity.java @@ -0,0 +1,274 @@ +/*********************************************************************** + * + * Copyright (C) 2006 Novell, Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 + * of the License. + * + * This library 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 + * Library Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, + * you may find current contact information at www.novell.com. + * + * Author: Juan Carlos Luciani + * + ***********************************************************************/ + +package com.novell.casa.authtoksvc; + +import java.io.ByteArrayInputStream; + +import org.apache.axis.Message; +import org.apache.axis.message.SOAPEnvelope; +import org.apache.ws.security.*; +import org.apache.ws.security.components.crypto.Crypto; +import org.apache.ws.security.components.crypto.CryptoFactory; +import org.apache.ws.security.message.WSSecHeader; +import org.apache.ws.security.message.WSSecSignature; +import org.apache.ws.security.message.WSSecTimestamp; +import org.apache.xml.security.c14n.Canonicalizer; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import javax.xml.soap.MessageFactory; +import java.util.Set; +import java.util.Vector; + + +/* + * WSSecurity Class. + * + * This class provides static methods for securing and verifying SOAP messages. SOAP messages + * are secured by adding a timestamp and signing the appropriate elements using methods and + * headers defined by WS* specifications. + * + */ +public class WSSecurity +{ + static final private WSSecurityEngine secEngine = new WSSecurityEngine(); + static final private Crypto crypto = CryptoFactory.getInstance(); + + /** + * Creates a SOAP message from a document. + * + */ + private static Message toSOAPMessage(Document doc) throws Exception + { + Canonicalizer c14n = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS); + byte[] canonicalMessage = c14n.canonicalizeSubtree(doc); + ByteArrayInputStream in = new ByteArrayInputStream(canonicalMessage); + MessageFactory factory = MessageFactory.newInstance(); + return (org.apache.axis.Message) factory.createMessage(null, in); + } + + /*** + * Returns the first element that containes an Id with value + * uri and namespace. + *

+ * + * Copyright Note: The code for this function was copied from file + * WSSecurityUtil.java from package org.apache.ws.security.util. + * The Copyright notice on this file is as follows: + * + * Copyright 2003-2006 The Apache Software Foundation, or their licensors, as + * appropriate. + * + * 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. + * + * @param startNode Where to start the search + * @param value Value of the Id attribute + * @param namespace Namespace URI of the Id + * @return The found element or null + */ + private static Element findElementById(Node startNode, + String value, + String namespace) + { + // Just return null if startNode is set to null + if (startNode == null) + { + return null; + } + + Node startParent = startNode.getParentNode(); + Node processedNode; + while (startNode != null) + { + // start node processing at this point + if (startNode.getNodeType() == Node.ELEMENT_NODE) + { + Element se = (Element) startNode; + if (se.hasAttributeNS(namespace, "Id") + && value.equals(se.getAttributeNS(namespace, "Id"))) + { + return se; + } + } + + processedNode = startNode; + startNode = startNode.getFirstChild(); + + // no child, this node is done. + if (startNode == null) + { + // close node processing, get sibling + startNode = processedNode.getNextSibling(); + } + + // no more siblings, get parent, all children + // of parent are processed. + while (startNode == null) + { + processedNode = processedNode.getParentNode(); + if (processedNode == startParent) + { + return null; + } + + // close parent node processing (processed node now) + startNode = processedNode.getNextSibling(); + } + } + + return null; + } + + /** + * Verifies SOAP envelope timestamp and signatures. + * + * @param envelope SOAP envelope with timestamp + * @return boolean True if verification succeeds + * @throws Exception on error + */ + public static boolean verifyMessage(SOAPEnvelope envelope) throws Exception + { + boolean msgVerificationStatus = false; + + try + { + boolean timeStampProcessed = false; + boolean signatureProcessed = false; + Vector results; + Document signedDoc = envelope.getAsDocument(); + results = secEngine.processSecurityHeader(signedDoc, null, null, crypto); + if (results != null) + { + for (WSSecurityEngineResult result : results) + { + if (result.getAction() == WSConstants.TS) + { + timeStampProcessed = true; + } + else if (result.getAction() == WSConstants.SIGN) + { + // A signature was processed, verify that the signature was over the timestamp + // and the body. + boolean timeStampSigned = false; + boolean bodySigned = false; + Set signedElements = result.getSignedElements(); + for (Object signedElement : signedElements) + { + String elementId = (String) signedElement; + Element element = findElementById(signedDoc.getDocumentElement(), elementId, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); + if (element != null) + { + if ("wsu:Timestamp".equalsIgnoreCase(element.getNodeName())) + { + timeStampSigned = true; + } + else if ("SOAP-ENV:Body".equalsIgnoreCase(element.getNodeName())) + { + bodySigned = true; + } + } + } + + if (timeStampSigned && bodySigned) + { + signatureProcessed = true; + } + } + } + } + + if (timeStampProcessed && signatureProcessed) + { + System.out.println("WSSecurity.verifyMessage() - Validation succeded"); + msgVerificationStatus = true; + } + else + { + System.out.println("WSSecurity.verifyMessage() - validation failed"); + } + } + catch (WSSecurityException e) + { + System.out.println("WSSecurity.verifyMessage() - Verification failed with error:" + e.getMessage() + " code = " + e.getErrorCode()); + } + + return msgVerificationStatus; + } + + /** + * Add timestamp and sign SOAP message in compliance with WS-Security. + * + * @param envelope String containing a SOAP envelope + * @param timeToLive Value to set the timestamp timeToLive parameter in seconds + * @param svcConfig Service Config object + * @return Message Signed and timestamped SOAP message + * @throws Exception on error + */ + public static Message secureSOAPEnvelope(SOAPEnvelope envelope, + int timeToLive, + SvcConfig svcConfig) throws Exception + { + WSSecSignature signer = new WSSecSignature(); + signer.setUserInfo(svcConfig.getSetting(SvcConfig.KeyStoreUser), + svcConfig.getSetting(SvcConfig.KeyStorePwd)); + signer.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER); // Include X509 Cert in message + + Document doc = envelope.getAsDocument(); + + WSSecHeader secHeader = new WSSecHeader(); + secHeader.insertSecurityHeader(doc); + + WSSecTimestamp timeStamper = new WSSecTimestamp(); + timeStamper.setTimeToLive(timeToLive); + timeStamper.build(doc, secHeader); + + Vector parts = new Vector(); + + String soapNamespace = doc.getDocumentElement().getNamespaceURI(); + WSEncryptionPart bodyPart = new WSEncryptionPart("Body", soapNamespace, ""); + parts.add(bodyPart); + + WSEncryptionPart timeStampPart = new WSEncryptionPart(timeStamper.getId()); + parts.add(timeStampPart); + + signer.setParts(parts); + + Document signedDoc = signer.build(doc, crypto, secHeader); + + // Convert the signed document into a SOAP message and return it. + return toSOAPMessage(signedDoc); + } +}