Tuesday, November 18, 2014

Authentication using simple Active Directory bind with Spring

Recently I wrote a web application, which had to be made secure.  I wanted to allow the users to use their normal Active Directory passwords and not to have to create one especially for the application. The application was also meant to be available to only to a selected group of users and not everyone with a valid AD account.

Rather than trying to get an Active Directory "application" account, which would have been nice as it would have allowed me to search for roles but which would have taken forever to get because of bureaucratic reasons, I decided to use a simple bind to the AD with the entered password and username to authenticate the users.

The problem was that just binding would have allowed everyone with a valid AD account to access the web application, which is what I didn't want. To limited the users, I needed a different way to verify whether or not they were authorized to use the application and this I did by storing their roles in the application's database.

To test whether or not this approach would work and to determine the connection details, I used Apache Directory Studio as shown below.





To add this approach into the web application, I used Spring Security, which I setup using the name space configuration (XML with specific Spring tags).

    <s:authentication-manager> 

        <s:authentication-provider ref="ldapAuthenticationProvider"/>   

    </s:authentication-manager>

    <bean id="ldapAuthenticationProvider"    class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">

        <constructor-arg>

            <bean class="eu.ecb.csdb.gateway.security.UsernameBindAuthenticator">

                <constructor-arg ref="contextSource" /> 

                <!-- just keep the base class happy -->

                <property name="userDnPatterns">

                     <list>

                        <value>sAMAccountName={0},OU=Standard User,OU=Users and Groups</value>

                    </list>

                </property>     

            </bean>

        </constructor-arg>

        <constructor-arg>

            <bean class="eu.ecb.csdb.gateway.security.DatabaseAuthoritiesPopulator">  

            </bean>

        </constructor-arg>

    </bean>


First, I configured my own authentication provider (UsernameBindAuthenticator) , which binds to AD using the entered password and username as shown here:


public class UsernameBindAuthenticator extends AbstractLdapAuthenticator {
/*...*/

    public DirContextOperations authenticate(Authentication authentication) {
  
        DirContextAdapter result = null;

        String username = authentication.getName();
        String password = (String)authentication.getCredentials();
  
        String domainUsername = domain + "\\" + username;

        DirContext ctx = null;

        try {

            ctx = getContextSource().getContext(domainUsername, password);

            // Check for password policy control

            PasswordPolicyControl ppolicy = PasswordPolicyControlExtractor.extractControl(ctx); 

            //bind was successful if we got here.

            result = new DirContextAdapter();

        } finally {
            LdapUtils.closeContext(ctx);
        }

        return result;              

    }
/*...*/

}


Then, I added my own LdapAuthoritiesPopulator (DatabaseAuthoritiesPopulator) , which queries the database to see if the user has any roles.

public class DatabaseAuthoritiesPopulator implements LdapAuthoritiesPopulator {

/*...*/

    public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {

  
        logger.debug(String.format("Getting roles for %s", username));

        List<GrantedAuthority> result = new ArrayList<GrantedAuthority>();

        try {

            final String query = String.format("select roles from USERS where USERNAME = '%s'", username);     

            final String roles = this.jdbcTemplate.queryForObject(query, String.class);
    
            String splitRoles[] = null;
            if(roles != null && (splitRoles = roles.split(";")).length > 0){       
                for(String strRole : splitRoles){
                    result.add(new CsdbAuthority(strRole));
                }
            }

        }catch(Exception ex){
            logger.debug(ex);
        }

        logger.debug(String.format("%s roles found for %s", (result == null ? 0 : result.size()), username));
    
        return result;           

    }

/*...*/

}


The CsdbAuthority is nothing but an implementation of the GrantedAuthority interface.

So if the bind was successful, then I knew that the user was a legitimate AD user and then I looked in the application's database to see if the user was authorized to use the application.

At this point, you might be asking yourself, wouldn't it have been easier just to use the FilterBasedLdapUserSearch and DefaultLdapAuthoritiesPopulator classes from Spring?  First of all no because I didn't have any roles in LDAP to search for. As mentioned I wanted a quick solution and not one where I would need to do months of paper work to get the roles added to AD; this all took place within a very bureaucratic government agency. Secondly no, because I found the implementation of the FilterBasedLdapUserSearch  confusing, quirky and poorly documented and I figured that rather than messing around for hours trying to configure the class it would be easier just to write my own.


Friday, November 7, 2014

TortoiseCVS - ksh: cvs: not found

While trying to checkout a project from CVS using the ssh protocol with TortouseCVS, I kept getting the following error message:"ksh: cvs: not found".

Setting the environment variables CVS_RSH and CVS_SERVER didn't help. Neither did making PATH changes to my profile on the server side. Then  I found the TortioseCVS setting dialog by right clicking on Windows Explorer as shown below:


Then I found the proper setting shown below in red.


and changed it, which in my case was: "/usr/local/bin/cvs".

This fixed my problem and I could finally checkout my project from cvs.



Sunday, October 12, 2014

My first day with joomla!

Recently, I was asked if I could help someone with joomla. I didn't have any experience with it so I figured the best thing to do was to install it locally on my Windows laptop, which I did using the following instructions from joomla.

XAMMP ( in my case xampp-win32-1.8.3-5-VC11-installer.exe) is a complete, free, easy to install Apache distribution, which contains MySQL, PHP and Perl. Before installing joomla, it was necessary for me to install XAMMP.

Once XAMPP was installed, the control panel could be started from the Start Menu and the individual services need for joomla started by pushing the "Action" Start buttons; shown as Stop buttons below.  

t

Now that the necessary software had been installed and was running, it was time to install the joomla web application, which could be downloaded as a zip from here: http://www.joomla.org/download.html

Next, the downloaded zip file was unpacked into the XAMMP installation folder under the htdocs directory. Because the version of joomla was 334, I decided to install it into a folder called joomla334 under htdocs.



Now, using a browser, the joomla installation was continued by calling: http://localhost/joomla334. This started a wizard, which took me through the installation and ended up as follows:


If you haven't already done so, before starting the installation with the browser as described above, it will be necessary to set up a database. This can be done by using the myphpadmin web console, which should have already been started using the control panel.

http://localhost/phpmyadmin/


(Or just go to the control panel and press the "Admin" button for the MySQL installation.)

Over on the left, in the screen shot above, where is says "Neu", you can initiate the database creation. The name of the database, here "stebdb", and user "stebdba" and password will be needed for the joomla installation. 

It was also necessary to make sure that the joomla application user,  "stebdba", had the permissions to log into the database from a given host. In my case, the host name defaulted to "%", which I guess is a wildcard, but which produced problems. Only after I replaced the "%" with "localhost", was I able to configure the database properly with the joomla installation wizard.

At the end of the installation, I was asked to delete the "installation" folder in the %XAMMP_HOME%/htdocs/joomla334 directory. Only after doing, could I continue to either the joomla administrator application using:

http://localhost/joomla334/administrator/index.php



or to the application's front end:

http://localhost/joomla334/



To make back ups of an existing joomla installation, consider akeeba






Friday, October 10, 2014

Simple pgp encryption with gpg

Simple command line encryption and decryption with gpg

Secret and public key rings are in working directory.

Encrypt

>gpg.exe  -e -r mb-pets@bce.tni  --keyring pubring.gpg encryptme.txt
Use this key anyway?yes

//output called encryptme.txt.gpg

Including private key prevents question about trusting public key.

>gpg.exe -e -r mb-pets@bce.int --homedir . --keyring .\pubring.gpg --secret-keyring secring.gpg encryptme.txt

//output called encryptme.txt.gpg

Output encrpyte file as base64 text.

gpg.exe -e -a -r mb-pets@bce.int --homedir . --keyring pubring.gpg  --secret-keyring secring.gpg encryptme.txt

//output called encryptme.txt.asc

//With verbose
>gpg.exe -e -a -r mb-step@ecb.int --homedir . --verbose --keyring pubring.gpg  --secret-keyring secring.gpg encryptme.txt
gpg: using secondary key 57D35DF1 instead of primary key 23E858FE
gpg: This key belongs to us
gpg: reading from `.\encryptme.txt'
gpg: writing to `.\encryptme.txt.asc'
gpg: ELG-E/AES256 encrypted for: "57D35DF1 Statistics STEP Transfer (STEP Mail 1) <mb-step@ecb.int>"


It's also possbile base64 using openssl; I think.
/usr/sfw/bin/openssl enc -base64 -in signme.txt.gpg  -out signme.txt.b64

An attemp to base64 with powershall looked like this; however, beware because this wasn't tested properly.

[System.Convert]::ToBase64String(([System.Text.Encoding]::UTF8.GetBytes((get-content ".\signme.txt.gpg")))); set-content (".\signme.txt.asc" );

To unbase 64 it:
[System.Convert]::FromBase64String((get-content ".\signme.txt.asc")); set-content (".\out.gpg" );

Decrypt

>gpg.exe --homedir . --decrypt --secret-keyring .\secring.gpg --keyring .\pubring.gpg .\secret.txt.gpg
You need a passphrase to unlock the secret key for **********











Thursday, September 18, 2014

Spring externalized values not resolved in servlet context

If externalized values (@Value(${value.from.properties.file}) exist in a Spring servlet context, then the context will need its own PropertySourcesPlaceholderConfigurer. It's is not enough to have one defined in the parent context.

Values externalized with with the @Value annotation will not be substituted. Instead one will end up with the contents of the annotation's value attribute as the value of the class' String which you are attempting to externalize. In the example below, that would be: ${html.report}

/------------------------------ Spring managed bead ---------------------------------/
@Controller
@RequestMapping("/downloadreport")
public class ReportDownloader {

 @Value("${html.report}") 
 private String reportLocation;

}

Below is the web.xml entries.

/-------------------------------------   web.xml   ------------------------------------/
<!-- application context -->

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext.xml             
        </param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

<!-- serlvet context created with java config -->

  <servlet>
      <servlet-name>service</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
      <init-param>
       <param-name>contextClass</param-name>
       <param-value>
        org.springframework.web.context.support.AnnotationConfigWebApplicationContext
       </param-value>
      </init-param>
      <init-param>

       <param-name>contextConfigLocation</param-name>
       <param-value>
        eu.app.spring.mvc.config.WebMvcConfig
       </param-value>

      </init-param>

  </servlet>


Here is the corresponding java config code.

/-------------------------------------   java config file   ------------------------------------/
@Configuration
@EnableWebMvc
@ComponentScan("eu.app.spring.mvc.handler")
@PropertySource("classpath:web/euapp.properties")
public class WebMvcConfig {

 @Bean
 public PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
  return new PropertySourcesPlaceholderConfigurer();
 } 

  

 @Bean
 public InternalResourceViewResolver getInternalResourceViewResolver() {

  InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  resolver.setPrefix("/jsp/");
  resolver.setSuffix(".jsp");
  return resolver;   

 }

}



Monday, July 21, 2014

Creating Java libraries from DLLs for Concept2 ergometer

I have a Concept2 rower with a PM4, which has a SDK, which can be downloaded from the Concept2 website. The SDK provides some DLLs, which can be used to access the rower's PM4 computer. This blog describes how I generated some Java libraries using gluegen and Microsoft Visual Studio 2010 to access to DLLs with Java.

Because the SDK provides the C header files for its DLLs, I was able to give these as input to the gluegen program and automatically generate the Java and JNI code necessary to call Concept2 libraries. Below is an excerpt from the generated JNI code for the tkcmdsetCSAFE_command function. 


/*   Java->C glue code:
 *   Java package: org.wmmnpr.c2.csafe.PM3CsafeCP
 *    Java method: short tkcmdsetCSAFE_command(short unit_address, short cmd_data_size, java.nio.LongBuffer cmd_data, java.nio.ShortBuffer rsp_data_size, java.nio.LongBuffer rsp_data)
 *     C function: ERRCODE_T tkcmdsetCSAFE_command(UINT16_T unit_address, UINT16_T cmd_data_size, UINT32_T *  cmd_data, UINT16_T *  rsp_data_size, UINT32_T *  rsp_data);
 */
JNIEXPORT jshort JNICALL 

Java_org_wmmnpr_c2_csafe_PM3CsafeCP_tkcmdsetCSAFE_1command1__SSLjava_lang_Object_2IZLjava_lang_Object_2IZLjava_lang_Object_2IZ(JNIEnv *env, jclass _unused, jshort unit_address, jshort cmd_data_size, jobject cmd_data, jint cmd_data_byte_offset, jboolean cmd_data_is_nio, jobject rsp_data_size, jint rsp_data_size_byte_offset, jboolean rsp_data_size_is_nio, jobject rsp_data, jint rsp_data_byte_offset, jboolean rsp_data_is_nio) {
  UINT32_T * _cmd_data_ptr = NULL;
  UINT16_T * _rsp_data_size_ptr = NULL;
  UINT32_T * _rsp_data_ptr = NULL;

  ERRCODE_T _res;

  if ( NULL != cmd_data ) {
    _cmd_data_ptr = (UINT32_T *) ( JNI_TRUE == cmd_data_is_nio ?  (*env)->GetDirectBufferAddress(env, cmd_data) :  (*env)->GetPrimitiveArrayCritical(env, cmd_data, NULL) );  }
  if ( NULL != rsp_data_size ) {
    _rsp_data_size_ptr = (UINT16_T *) ( JNI_TRUE == rsp_data_size_is_nio ?  (*env)->GetDirectBufferAddress(env, rsp_data_size) :  (*env)->GetPrimitiveArrayCritical(env, rsp_data_size, NULL) );  }
  if ( NULL != rsp_data ) {
    _rsp_data_ptr = (UINT32_T *) ( JNI_TRUE == rsp_data_is_nio ?  (*env)->GetDirectBufferAddress(env, rsp_data) :  (*env)->GetPrimitiveArrayCritical(env, rsp_data, NULL) );  }
  _res = tkcmdsetCSAFE_command((UINT16_T) unit_address, (UINT16_T) cmd_data_size, (UINT32_T *) (((char *) _cmd_data_ptr) + cmd_data_byte_offset), (UINT16_T *) (((char *) _rsp_data_size_ptr) + rsp_data_size_byte_offset), (UINT32_T *) (((char *) _rsp_data_ptr) + rsp_data_byte_offset));

  if ( JNI_FALSE == cmd_data_is_nio && NULL != cmd_data ) {
    (*env)->ReleasePrimitiveArrayCritical(env, cmd_data, _cmd_data_ptr, 0);  }
  if ( JNI_FALSE == rsp_data_size_is_nio && NULL != rsp_data_size ) {
    (*env)->ReleasePrimitiveArrayCritical(env, rsp_data_size, _rsp_data_size_ptr, 0);  }
  if ( JNI_FALSE == rsp_data_is_nio && NULL != rsp_data ) {
    (*env)->ReleasePrimitiveArrayCritical(env, rsp_data, _rsp_data_ptr, 0);  }
  return _res;

}

The first difficulty experienced was that the DLLS, even though they were written in C, were compiled as C++ code, which meant that when the JNI generated code was compiled as C files, it could not be linked against the C++ static libraries (PM3CsafeCP.lib, PM3DDICP.lib and PM3USBCP.lib); the external references, namely the functions I wanted to call in the DLLs, were mangled according to C++ rules rather than C ones, which my C object files expected. The link error was as follows:

error LNK2019: unresolved external symbol "__imp__tkcmdsetCSAFE_async_command" ...

The only option was to compile the gluegen generated JNI C files are C++ files; however, that wouldn't work because the "__cplusplus" macros in the jni.h header file lead to compile errors; namely:

error C2819: type 'JNIEnv_' does not have an overloaded member 'operator ->'

I fixed this by commenting out the "__cplusplus" macros and their corresponding code in a local copy of the jni.h file so that the struct JNIEnv_ definition would stay the same and not change for C++ compilation modules. The changes allowed me to compile my C code with the C++ compiler without any errors and thereafter link against the PM4 C++ DLLs; which were really C ones. 

The next error occurred during runtime. Loading the DLLs made from the gluegen code with "System.loadLibrary
" was no problem given that the DLLs created with the code from gluegen and the ones from the SDK (PM3CsafeCP.dll, PM3DDICP.dll and PM3USBCP.dll) were located in the in the JVM's search path. My solution was to set the VM argument "java.library.path" to the proper location of the DLLs, which I had placed in one directory.

The first indication that something was wrong with my DLLs occurred when trying to access one of the exported functions; in which case, the following error occurred:

Exception in thread "main" java.lang.UnsatisfiedLinkError: org.wmmnpr.c2.ddi.PM3DDICP.tkcmdsetDDI_init()S
at org.wmmnpr.c2.ddi.PM3DDICP.tkcmdsetDDI_init(Native Method)

Again a problem with the name mangling. Because I had compiled my generated gluegen C source files as C++ source files into DLLs, the Java runtime could find them under the signatures generated by gluegen. The solution was the to make sure the the exported functions in the C modules were mangled using C mangling rules. This I did by enclosing my source code in 

extern "C" {
/* code from gluegen */
}

Enclosing the header file declarations would have been better but gluegen didn't produce any header files; only source ones.


With those three errors solved I was able to write a Java program to access my PM4.



Friday, June 20, 2014

maven jaxws plugin simple configuration wsimport

Below is a simple configuration for the jaxws-maven-plugin. The trick is to get the jaxws plugin to generate the java sources at the right time and in the right place so that the maven-compiler-plugin with its default settings can find the files when it needs them.

Configuring the jaxws plugging so that it executes the wsimport goal at the right time is done by the phase entry.

Making sure that the plugin puts the files in the right location is accomplished by the sourceDestDir element.

The only the that needs to be modified is the wsdlUrl entry and the finalName elements.

..

<build>

   <finalName>web-app</finalName>

   <plugins>
      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>jaxws-maven-plugin</artifactId>
         <version>1.12</version>
            <executions>
               <execution>               
                  <phase>generate-sources</phase>
                  <goals><goal>wsimport</goal></goals>
                  <configuration>
                     <wsdlUrls>
                        <wsdlUrl>http://hostname/context/SDMXQuery?wsdl</wsdlUrl>
                     </wsdlUrls>
                     <sourceDestDir>${project.build.directory}/generated-sources</sourceDestDir>
                  </configuration>
            </execution>
         </executions>
      </plugin>
   </plugins>

</build>

..





Tuesday, June 3, 2014

kerberos - Invalid option setting in ticket request. (101)

I'm using the Krb5LoginModule in a java application to test a web application that I'm trying to kerberized. Before testing, I authorized myself again the AS (in this case ApacheDS) by calling kinit as follows:

kinit.exe  wmmnpr@EXAMPLE.COM
Password for wmmnpr@EXAMPLE.COM:
New ticket is stored in cache file C:\Users\wnpr\krb5cc_wnpr

Then I ran my test and as one can see from the log output of Krb5LoginModule below, the login module finds my credential's cache and retrieves a TGT from it.

Acquire TGT from Cache
>>>KinitOptions cache name is C:\Users\wnpr\krb5cc_wnpr
>>>DEBUG <CCacheInputStream>  client principal is wmmnpr@EXAMPLE.COM
>>>DEBUG <CCacheInputStream> server principal is krbtgt/EXAMPLE.COM@EXAMPLE.COM
>>>DEBUG <CCacheInputStream> key type: 17
>>>DEBUG <CCacheInputStream> auth time: Tue Jun 03 10:03:36 CEST 2014
>>>DEBUG <CCacheInputStream> start time: null
>>>DEBUG <CCacheInputStream> end time: Wed Jun 04 10:03:36 CEST 2014
>>>DEBUG <CCacheInputStream> renew_till time: null
>>> CCacheInputStream: readFlags()  INITIAL; PRE_AUTH;
Host address is /172.28.10.201
Host address is /fe80:0:0:0:e11d:3dff:8612:e63b
>>> KrbCreds found the default ticket granting ticket in credential cache.
Config name: C:\Users\wnpr\krb5.conf
>>> Obtained TGT from LSA: Credentials:
client=wmmnpr@EXAMPLE.COM
server=krbtgt/EXAMPLE.COM@EXAMPLE.COM
authTime=20140603080336Z
endTime=20140604080336Z
renewTill=null
flags: INITIAL;PRE-AUTHENT
EType (int): 17
Principal is wmmnpr@EXAMPLE.COM

Commit Succeeded 

However, before trying to access the web application, the login module tries to go to the KDC (again ApacheDS) to get a ticket; however, before it gets there a KrbException is thrown (see below).

Found ticket for wmmnpr@EXAMPLE.COM to go to krbtgt/EXAMPLE.COM@EXAMPLE.COM expiring on Wed Jun 04 10:03:36 CEST 2014
Entered Krb5Context.initSecContext with state=STATE_NEW
Service ticket not found in the subject
>>> Credentials acquireServiceCreds: same realm
KrbException: Invalid option setting in ticket request. (101)
at sun.security.krb5.KrbTgsReq.<init>(KrbTgsReq.java:98)

at sun.security.krb5.KrbTgsReq.<init>(KrbTgsReq.java:62)

After looking at the sun.security.krb5.KrbTgsReq code at line 98 it occurred to me that it was the "forwardable" option that was causing problems. I then ran kinit as follows, with -f:  

>kinit.exe -f wmmnpr@EXAMPLE.COM
Password for wmmnpr@EXAMPLE.COM:
New ticket is stored in cache file C:\Users\wnpr\krb5cc_wnpr


And then check it with klist.

klist" -f -e  -c c:\Users\wnpr\krb5cc_wnpr

Credentials cache: c:\Users\wnpr\krb5cc_wnpr

Default principal: wmmnpr@EXAMPLE.COM, 1 entry found.

[1]  Service Principal:  krbtgt/EXAMPLE.COM@EXAMPLE.COM
     Valid starting:  Jun 03,  2014 11:18
     Expires:         Jun 04,  2014 11:18
     Encryption type: AES128 CTS mode with HMAC SHA1-96
     Flags:           FORWARDABLE;INITIAL;PRE-AUTHENT







Sunday, June 1, 2014

IllegalBlockSizeException kinit kerberos ApacheDS

While trying to kerberize a web application, I ran into the following error on the KDC server, which happened to be ApacheDS.

IllegalBlockSizeException: Input length not multiple of 8 bytes.

Eventually, I was able to isolate the problem in the krb5.conf file of the client. When the default_tgs_enctype and the default_tkt_enctype were not explicitly specified, as shown below, the problem went away and the KDC could successfully generate either TGS-REP or  AS-REP responses.

#/------------------------------- start of krb5.conf --------------------------/
[libdefaults]
default_realm = EXAMPLE.COM
# default_tgs_enctypes = des-cbc-md5,des3-cbc-sha1-kd
# default_tkt_enctypes = des-cbc-md5,des3-cbc-sha1-kd
kdc_timeout = 5000
dns_lookup_realm = false
dns_lookup_kdc = false
allow_weak_crypto = yes
forwardable = true
ap_req_checksum_type = 12

[realms]
EXAMPLE.COM = {
kdc = 127.0.0.1:60088
}

[login]
krb4_convert = true
krb4_get_tickets = false
#/------------------------------- end of krb5.conf ----------------------------/

ApacheDS log output after a TGS_REQ:

INFO   | jvm 1    | 2014/06/01 17:50:49 | [17:50:49] DEBUG [org.apache.directory.server.KERBEROS_LOG] - /127.0.0.1:54210 SENT:  
INFO   | jvm 1    | 2014/06/01 17:50:49 | >-------------------------------------------------------------------------------
INFO   | jvm 1    | 2014/06/01 17:50:49 | KdcRep : TGS-REP
INFO   | jvm 1    | 2014/06/01 17:50:49 | pvno : 5
INFO   | jvm 1    | 2014/06/01 17:50:49 | msg-type : TGS_REP
INFO   | jvm 1    | 2014/06/01 17:50:49 | crealm : EXAMPLE.COM
INFO   | jvm 1    | 2014/06/01 17:50:49 | cname : { name-type: KRB_NT_PRINCIPAL, name-string : <'wmmnpr'> }
INFO   | jvm 1    | 2014/06/01 17:50:49 | Ticket :
INFO   | jvm 1    | 2014/06/01 17:50:49 |   tkt-vno : 5
INFO   | jvm 1    | 2014/06/01 17:50:49 |   realm : EXAMPLE.COM
INFO   | jvm 1    | 2014/06/01 17:50:49 |   sname : { name-type: KRB_NT_UNKNOWN, name-string : <'HTTP', 'example.com'> }
INFO   | jvm 1    | 2014/06/01 17:50:49 |   enc-part : EncryptedData : {
INFO   | jvm 1    | 2014/06/01 17:50:49 |     etype: aes128-cts-hmac-sha1-96 (17)
INFO   | jvm 1    | 2014/06/01 17:50:49 |     cipher: 0x0D 0x56 0xC4 0x0C 0x01 0x9E 0x62 0x6E 0x28 0x45 ....
INFO   | jvm 1    | 2014/06/01 17:50:49 | }
INFO   | jvm 1    | 2014/06/01 17:50:49 | 
INFO   | jvm 1    | 2014/06/01 17:50:49 | EncryptedData : {
INFO   | jvm 1    | 2014/06/01 17:50:49 |     etype: aes128-cts-hmac-sha1-96 (17)
INFO   | jvm 1    | 2014/06/01 17:50:49 |     cipher: 0x94 0x75 0xE9 0x4C 0xDA 0x6D 0x79 0xDC 0x49 0x0E ...
INFO   | jvm 1    | 2014/06/01 17:50:49 | }
INFO   | jvm 1    | 2014/06/01 17:50:49 | 
INFO   | jvm 1    | 2014/06/01 17:50:49 | -------------------------------------------------------------------------------<
INFO   | jvm 1    | 2014/06/01 17:50:49 | 

Saturday, May 31, 2014

NoSuchMethodError SpnegoAuthenticationProcessingFilter AbstractAuthenticationToken

In a nutshell, the solution to this problem involved cloning the git project, building the sources and deploying the newly created artifact (jar) to my local maven repository; see below for me details.

As part of an attempt to kerberize an web application (see here for details), which uses Spring Security's LDAP, I added in the necessary dependencies shown here:
<dependency>
    <groupId>org.springframework.security.extensions</groupId>
    <artifactId>spring-security-kerberos-core</artifactId>
    <version>1.0.0.M1</version>
</dependency>
and which I found here:
https://spring.io/blog/2009/09/28/spring-security-kerberos-spnego-extension

Next I configured the Spring Security XML configuration file as follows (description can be found at the link above too)

<beans ... >

<sec:http entry-point-ref="spnegoEntryPoint">
    <sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
    <sec:custom-filter ref="spnegoAuthenticationProcessingFilter" position="BASIC_AUTH_FILTER" />
</sec:http>
    
    
<bean id="spnegoEntryPoint" class="org.springframework.security.extensions.kerberos.web.SpnegoEntryPoint" />

<bean id="spnegoAuthenticationProcessingFilter" class="org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter">
    <property name="authenticationManager" ref="authenticationManager" />
</bean>

<sec:authentication-manager alias="authenticationManager">
    <sec:authentication-provider ref="kerberosServiceAuthenticationProvider" />
</sec:authentication-manager>

<bean id="kerberosServiceAuthenticationProvider" class="org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider">
    <property name="ticketValidator">
        <bean class="org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator">
            <property name="servicePrincipal" value="HTTP/tomcat@EXAMPLE.COM" />
            <property name="keyTabLocation" value="classpath:krb5.keytab" />
            <property name="debug" value="true"/>
        </bean>
    </property>
    <property name="userDetailsService" ref="dummyUserDetailsService" />
</bean>

<bean id="dummyUserDetailsService" class="org.wmmnpr.mykrb.config.Krb5DumyUserService"/>



</beans>

And started the Maven based project using the maven-tomcat-plugin as follows:
>mvn tomcat7:run

When I tried to access a protected URL, I got the following error:

org.apache.catalina.core.StandardWrapperValve invoke
Schwerwiegend: Servlet.service() for servlet [jsp] in context with path [/mykrb5webapp] threw exception [Filter execution threw an exception] with root cause
java.lang.NoSuchMethodError: org.springframework.security.authentication.AbstractAuthenticationToken.<init>(Ljava/util/List;)V
at org.springframework.security.extensions.kerberos.KerberosServiceRequestToken.<init>

Obviously, the SpnegoAuthenticationProcessingFilter was incompatible with the spring-security-core library. I had no option but to clone the spring-security-security repository and see if I could fix the problem.

Using the eclipse git plugin, I cloned the remote repository (https://github.com/spring-projects/spring-security-kerberos.git) and then ran the gradle buiild. To import the code into eclipse I added "apply plugin: 'eclipse'" to the sources build.gradle file and run the "eclipse" target. This generated the proper eclipse project files, which allowed me to import the project into eclipse (see below)



To build and deploy the library, I ran the gradle.gui by calling:

gradlew.bat --gui




This made it very easy to install the jar into my local maven repository. All I had to do was click on the install target shown above.

Next, I updated the eclipse project of my web application to reflect the new version and ran the project again in the debugger. 

The problem was that the source code was the changed but the executable code seemed to be different. When I tried to step through my changed code, the debugger didn't seem to know about the changes. 

I spent lots of time trying to clean and rebuild the gradle project, I even deleted the artifacts in my maven repository, but it didn't help either. 

Then started I started the jvisualvm using the same user, tomcat, under which the web application as running (see here)



I could not see which jar file the class was being loaded from. To do that, I needed to start the server using the -verbose:class JVM option, which I passed to maven by setting in the environment the MAVEN_OPTS variable.

This showed the source jar of the class very nicely.


[Loaded org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter from file:/C:/Users/wnpr/.m2/repository/org/springframework/security/extensions/spring-security-kerberos-core/1.0.0.CI-SNAPSHOT/spring-security-kerberos-core-1.0.0.CI-SNAPSHOT.jar]

Despite all my efforts, I was still getting the NoSuchMethodError. Eventually, I had the idea to look into the publish-maven.gradle file. There I found references to some external resources.

scm {
     url = 'https://github.com/SpringSource/spring-security-kerberos'
     connection = 'scm:git:git://github.com/SpringSource/spring-security-kerberos'
     developerConnection = 'scm:git:git://github.com/SpringSource/spring-security-kerberos'

}

which I changed to something meaningless:

scm {
     url = 'https://githubxxx.com/SpringSource/spring-security-kerberos'
    connection = 'scm:git:git://githubxxx.com/SpringSource/spring-security-kerberos'
     developerConnection = 'scm:git:git://githubxxx.com/SpringSource/spring-security-kerberos'



I ran the gradle clean, the maven install and started the web application. This time java code shown in the debugger agreed what was being executed and the NoSuchMethodError was gone. Obviously, the jars which I had picked up from the recommended setting (shown below) were wrong; however, now it didn't matter as I was building my own using gradle and deploying to my local maven repository.


<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>http://repo.spring.io/snapshot</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>

   <repository>
<id>spring-milestone</id>
<name>Spring Portfolio Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
   </repository>
</repositories>


All that was left to do, was to comment out the above in my pom.xml file and change my dependency declaration of the kerberos artifact.

<dependency>
   <groupId>org.springframework.security.kerberos</groupId>
   <artifactId>spring-security-kerberos-core</artifactId>
   <version>1.0.0.CI-SNAPSHOT</version>
</dependency>



Windows 7 home directory Administrator runas

While trying to start various applications on Windows 7 using a local account, I kept getting:

"Could not create directory" ...

In the case below, I tried to start jvisualvm as a user called tomcat, whom I created as a local user with admin rights; however,  instead of using tomcat's own home directory, the application kept trying to use the one from the Administrator user.




The application was being started with runas:

>runas /user:tomcat /profile jvisualvm

Fixing this required opening a cmd window as tomcat

>runas /user:tomcat cmd
Geben Sie das Kennwort für "tomcat" ein:

Es wird versucht, cmd als Benutzer "GFT6841\tomcat" zu starten...

then starting regedit and changing all references to "Administrator" to appropriate user, in my case tomcat, in the the following keys:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\
Desktop
LocalAppData
AppData

There might be more but changing the above was sufficient for my purposes.


jvisualvm runas windows

On Windows 7, it may be necessary to profile a java application which is running under a user other than the one you're presently logged on as. 

To be able to see the process, it will also be necessary to run jvisualvm as that user. This can be done as follows:

>runas /user:tomcat jvisualm 

Where tomcat is the user name under which the program was running, namely Apache Tomcat, that I wanted to profile.

Make sure that the jvisualvm is in the path. 

If you encounter problems with the runas user not being able to access or create certain folders, like this:

"Could not create directory" ...

then see here.

Windows Maven runas

Rather than passing user.home to the JVM as shown below, a better fix, which requires a registry change, can be found here.

I created a new Windows' account called tomcat and wanted to start my web application using Maven's maven-tomcat-plugin for debugging purposes . 

From my "normal" Windows' account, I started a new command prompt window for the:
tomcat user as follows:

C:\Users\wnpr>runas /user:tomcat cmd
Geben Sie das Kennwort für "tomcat" ein:
Es wird versucht, cmd als Benutzer "GFT6841\tomcat" zu starten...

C:\Users\wnpr>

In the newly opened window, I tried to clean the project using Maven.

>mvn clean
Could not create local repository at C:\Users\Administrator\.m2\repository -> [Help 1]



My first attempt to solve this issue was taken from stackoverflow and involved creating a .m2 folder in the tomcat user's home directory and to copying a settings.xml file into the folder and, in the settings.xml file, setting the "localRepository" XML element to point to the actual repository's location, which existed in my normal user's directory; namely: "C:\Users\wnpr\.m2\repository"; however, this still didn't help.  

Then I tried to hard code it in the MAVEN_HOME\bin\m2.conf file, see below, but this didn't change anything either.

set maven.home default ${user.home}/.m2

Next, I tried to pass it to Maven as a run time parameter
>mvn  clean -Duser.home=c:\Users\wnpr 

This helped for clean and compiling but not exactly for running the application.

:Error creating bean with name org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator defined in class path resource [applicationContext-security.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Cannot locate default realm

Which is a Kerberos error and not a tomcat one. For the solution see here.