uPortal CVE-2014-5059 Workaround

In which I share instructions on how to fix your uPortal security.properties to immediately block the CVE-2014-5059 vulnerability.

Structure of this blog post

This blog post provides:

  • Why you should care
  • How to determine if your uPortal deployment is affected
  • How to compose a fix for your security.properties, and
  • How to apply the security.properties fix

This blog post is intended to be mechanically about fixing your portal. Given that there's this vulnerability, how do you quickly ascertain whether your portal is affected and fix it if you are.

I've resisted the urge to discuss how the vulnerability works, how the fix works, how Spring Security can help, and larger philosophical discussion. Those will be important conversations, but what I'm trying to communicate right here is just the facts on how you secure your portal.

Why you should care

CVE-2014-5059 is a nasty uPortal vulnerability whereby in certain configurations including the default out-of-the-box configuration, logging in users could log in as users other than themselves.

You should probably figure out if your uPortal is affected and, if affected, apply the workaround right now.

All uPortal versions are in principle vulnerable. Whether they're vulnerable in practice depends upon some specifics of how SecurityContexts in use interact with the framework security APIs. All uPortal versions should apply the workaround discussed herein.

uPortal 4.0.15 and 4.1.1 ship with a fix to block this vulnerability by making the involved SecurityContext class more careful. uPortal adopters should upgrade to those fixed uPortal versions or otherwise incorporate the code fix into their local uPortal environments. The code fix blocks the vulnerability for the affected SecurityContext that ships in uPortal, regardless of your security.properties configuration.

However. It is also possible to block this vulnerability through an adjustment to your security.properties configuration, even before you update any Java code. You should probably do that right now. This blog post tells you how. (uPortal 4.0.15 and 4.1.1 also ship with fixed default and example security.properties configurations.)

How to determine if your uPortal deployment is affected

Among the SecurityContext implementations shipping in modern uPortal, only the CasAssertionSecurityContext and its derivatives are vulnerable, and only in some (alas, typical) security.properties configurations.

You are affected by this vulnerability if

  • You are using the CasAssertionSecurityContext or its subclass, PasswordCachingCasAssertionSecurityContext, AND
  • Your security.properties is configured such that a principalName is declared on that context or on a parent context, AND
  • You are using uPortal prior to uPortal 4.0.15 / 4.1.1, since those versions fix this Context.

So, this security.properties configuration would be affected

root=org.jasig.portal.security.provider.UnionSecurityContextFactory  
root.cas=org.jasig.portal.security.provider.cas.CasAssertionSecurityContextFactory  
root.simple=org.jasig.portal.security.provider.SimpleSecurityContextFactory

principalToken.root=userName  
credentialToken.root=password  
credentialToken.root.cas=ticket  

. It would be affected because it declares the CasAssertionSecurityContext AND it declares a non-blank principalToken AND that principalToken applies to the CasAssertionSecurityContext (through inheritance down from root to root.cas).

and that is, alas, the default configuration shipping in uPortal prior to the fixed releases.

This configuration is also vulnerable

root=org.jasig.portal.security.provider.UnionSecurityContextFactory  
root.cas=org.jasig.portal.security.provider.cas.PasswordCachingCasAssertionSecurityContextFactory  
root.simple=org.jasig.portal.security.provider.SimpleSecurityContextFactory

principalToken.root=userName  
credentialToken.root=password  
credentialToken.root.cas=ticket  

because it is using the also-affected PasswordCachingCasAssertionSecurityContext which is a sub-class of CasAssertionSecurityContext and so inherits the same bug.

You might be affected by this vulnerability if you are using a custom-developed uPortal SecurityContext AND your security.properties declares a principalName on that context or on a parent context.

So, this security.properties configuration might be affected:

root=org.jasig.portal.security.provider.UnionSecurityContextFactory  
root.custom=edu.miskatonic.portal.TentacledSecurityContextFactory  
root.simple=org.jasig.portal.security.provider.SimpleSecurityContextFactory

principalToken.root=userName  
credentialToken.root=password  
credentialToken.root.custom=symbol  

Your portal might be affected by this vulnerability if you are using older CAS SecurityContexts or other SecurityContext implementations AND your security.properties declares a principalName on that context or on a parent context.

You are not affected by this vulnerability if you are exclusively using these security contexts

  • CacheLdapSecurityContext (needs principalName and uses it properly)
  • JAASSecurityContext (does not need principalName, but copes properly if presented)
  • RemoteUserSecurityContext (does not need principalName, but copes properly if presented)
  • SimpleLdapSecurityContext (needs principalName and uses it properly)
  • SimpleSecurityContext (needs principalName and uses it properly)
  • UnionSecurityContext

even if you are setting the principalName on those contexts, because in some cases they actually need the principalName token to know what user identity they should try to authenticate, and in the rest of the cases they happen to use the uPortal security APIs in a way to avoid this vulnerability.

The TrustSecurityContext isn't interestingly prey to this vulnerability because its behavior is to accept whatever user identity without verifying the identity in any way -- non-demo environments using it are very suspect regardless of this vulnerability.

How to compose a fix for your security.properties

If your portal is affected by this vulnerability, the good news is there's a trivial change to your security.properties you can make to block this vulnerability immediately.

The fix amounts to don't feed principalName to Contexts that don't need and expect it, and especially don't feed principalName to security Contexts that integrate with CAS.

The bad news is that the details of your security.properties depend on details of your local authentication integration choices. Still, your security.properties probably follows one of these patterns.

This section walks through some examples of security.properties for different scenarios and discusses for each whether the configuration is vulnerable and how to change it to no longer be vulnerable. The way you are intended to use this section is to find the example that matches yours and apply it.

Setting the principalName only appropriately

For a configuration that is exclusively doing username and password authentication against the uPortal-internal user store

root=org.jasig.portal.security.provider.SimpleSecurityContextFactory

principalToken.root=userName  
credentialToken.root=password  

no change is required -- that security context needs and expects principalName.

Likewise a configuration that is exclusively using LDAP authentication

root=org.jasig.portal.security.provider.SimpleLdapSecurityContextFactory

principalToken.root=userName  
credentialToken.root=password  

is not vulnerable and requires no change.

Likewise a configuration that is exclusively using REMOTE_USER authentication (in order to rely on a fronting Shibboleth SP, for example)

root=org.jasig.portal.security.provider.RemoteUserSecurityContextFactory

principalToken.root=remote_user  
credentialToken.root=password  

is not vulnerable and requires no change.

(The RemoteUserSecurityContext doesn't actually need that remote_user principalToken and would work fine if it were principalToken.root= -- the point here is that the RemoteUserSecurityContext implementation takes pains to handle the case where the uid it is trying to store into the session doesn't stick due to a different one having been previously bound. It's still a good idea not to feed this context a principalName token value it doesn't need.)

Not setting the principalName

For a configuration that is not setting principalName at all

## This is the factory that supplies the concrete authentication class
root=org.jasig.portal.security.provider.cas.CasAssertionSecurityContextFactory

principalToken.root=  
credentialToken.root=ticket  

no change is required - this configuration does not bind a request parameter as the username because the principalToken is blank.

Setting the principalName on a context that does not need it

This configuration is one character different from the previous one

## This is the factory that supplies the concrete authentication class
root=org.jasig.portal.security.provider.cas.CasAssertionSecurityContextFactory

principalToken.root=u  
credentialToken.root=ticket  

and that one character is all it takes to be vulnerable. Do you see it? It's telling the portal that the parameter u should be considered the principalToken for the root context which, here, is the CasAssertionSecurityContext, but that security context doesn't need a principalToken, and, in fact, is exactly the context that gets confused when you give it one.

This configuration is problematic in the same way:

## This is the factory that supplies the concrete authentication class
root=org.jasig.portal.security.provider.cas.CasAssertionSecurityContextFactory

principalToken.root=userName  
credentialToken.root=ticket  

except the principalName value here is the more plausible userName instead of just u.

The solution is to make the principalToken blank

## This is the factory that supplies the concrete authentication class
root=org.jasig.portal.security.provider.cas.CasAssertionSecurityContextFactory

principalToken.root=  
credentialToken.root=ticket  

This tells the portal that no request parameter ought to be bound as the principal on the CasAssertionSercurityContext, which is a good thing to tell the portal because that context doesn't need and prior to uPortal 4.0.15 / 4.1.1 doesn't properly cope with the parameter when it's set.

Setting the principalName on a parent context of some contexts that need it and some that do not

This is the configuration that was default in uPortal up to this point. There's a parent Union context that combines two child contexts, one that uses CAS and one that uses the simple security context to do username and password validation against uPortal's internal user store. This configuration was common because even in environments externalizing real user logins to CAS, the portal often retains some non-human portal template and administrative user accounts that it locally manages, and this configuration allows authenticating in either way.

root=org.jasig.portal.security.provider.UnionSecurityContextFactory  
root.cas=org.jasig.portal.security.provider.cas.CasAssertionSecurityContextFactory  
root.simple=org.jasig.portal.security.provider.SimpleSecurityContextFactory

principalToken.root=userName  
credentialToken.root=password  
credentialToken.root.cas=ticket  

What's wrong with this configuration is that the principalToken value is applied to the root, and then the CAS context , as root.cas, inherits it. The way to fix it is to specify the blank token at root and then specify the userName value only on the specific context that needs it, root.simple. So you end up with:

root=org.jasig.portal.security.provider.UnionSecurityContextFactory  
root.cas=org.jasig.portal.security.provider.cas.CasAssertionSecurityContextFactory  
root.simple=org.jasig.portal.security.provider.SimpleSecurityContextFactory

principalToken.root=  
credentialToken.root=  
principalToken.root.simple=userName  
principalToken.root.simple=password  
credentialToken.root.cas=ticket  

and this way root.cas doesn't inherit a principalToken value.

The general case

In general, here's how security.properties works relevant to this issue.

  • You declare Security context factories that build SecurityContext instances.

  • Those factories are named, and their naming makes up a potential hierarchy of instances that get wired up to model the authentication of any given user. So, root is the parent of root.simple and root.cas.

  • The configuration flows down the hierarchy, so if you set a principalToken at a node, it applies to the children of that node.

  • You really don't want principalTokens applying to contexts that don't expect and understand them.

  • Therefore in general move the configuration down the tree to the leaves so it's applying tightly just to the specific context that expects it. Do not apply any non-blank configuration to parent nodes in the tree. So, principalToken.root.simple=userName , not principalToken.root=userName

  • However, alas, you have to declare the property name even though it will have a blank value, so principalToken.root=.

Something else? Get help.

Is your security.properties different from these examples such that you're not sure whether you're vulnerable or what needs done to fix it? Get help!

If you have a commercial support provider for your uPortal implementation, you might contact them for help.

Regardless of whether you have a commercial support provider, you can also get help on the uPortal email lists. Isn't open source great? Since this will be a discussion about using uPortal rather than a discussion of developing changes for inclusion in the uPortal product, it is most appropriately undertaken on the uportal-user@ email list.

How to apply the security.properties fix

This is, mechanically, how to apply a security.properties fix. What content change you ought to make to your security.properties is discussed above.

A. Fixing your uPortal as deployed

Modifying your deployed uPortal before fixing your source code, re-building, and re-deploying might or might not be the right move for you to make. Only you can decide how to wrangle your uPortal deployment.

A.0. Make a backup of your deployed security.properties

You always save a copy of a file you're about to edit live in a deployed environment, right?

A.1. Fix your security.properties

The file is WEB-INF/classes/properties/security.properties in your deployed uPortal web application, which is probably a directory named uPortal in the webapps directory of your Tomcat servlet container.

Apply the change to no longer bind a principalName to security contexts that should not have a principalName bound onto them, as discussed above.

A.2. Restart Tomcat

Restart your servlet container to make uPortal re-load security.properties and apply the new configuration.

B. Fixing your source code

Whether or not you fix in place, you should also fix your source code so on future builds and deploys you keep the fix.

B.1. Fix your security.properties

The security.properties is in

/uportal-war/src/main/resources/properties/

in your source code.

What change you need to make to fix it is discussed thoroughly above.

B.2. Build

Build your changed portal however you normally build it. That might be mvn package or maybe you combine building and deploying in your deploy step (the next step!)

B.3. Deploy

You probably stop Tomcat before you do this.

Deploy your changed portal however you normally do that. That might be ant deploy-war.

B.4. You restarted Tomcat, right?

If you stopped Tomcat, you need to start it again, of course.

If you didn't stop Tomcat, you probably need to restart it.

Acknowledgements

Cover photo: Stop look and listen :