1 package org
.argeo
.cms
.internal
.kernel
;
3 import java
.io
.IOException
;
4 import java
.net
.Inet6Address
;
5 import java
.net
.InetAddress
;
7 import java
.net
.UnknownHostException
;
8 import java
.nio
.file
.Files
;
9 import java
.nio
.file
.Path
;
10 import java
.security
.PrivilegedExceptionAction
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Iterator
;
14 import javax
.security
.auth
.Subject
;
15 import javax
.security
.auth
.callback
.Callback
;
16 import javax
.security
.auth
.callback
.CallbackHandler
;
17 import javax
.security
.auth
.callback
.NameCallback
;
18 import javax
.security
.auth
.callback
.UnsupportedCallbackException
;
19 import javax
.security
.auth
.kerberos
.KerberosPrincipal
;
20 import javax
.security
.auth
.login
.Configuration
;
21 import javax
.security
.auth
.login
.LoginContext
;
22 import javax
.security
.auth
.login
.LoginException
;
24 import org
.apache
.commons
.httpclient
.auth
.AuthPolicy
;
25 import org
.apache
.commons
.httpclient
.auth
.CredentialsProvider
;
26 import org
.apache
.commons
.httpclient
.cookie
.CookiePolicy
;
27 import org
.apache
.commons
.httpclient
.params
.DefaultHttpParams
;
28 import org
.apache
.commons
.httpclient
.params
.HttpMethodParams
;
29 import org
.apache
.commons
.httpclient
.params
.HttpParams
;
30 import org
.apache
.commons
.logging
.Log
;
31 import org
.apache
.commons
.logging
.LogFactory
;
32 import org
.argeo
.cms
.CmsException
;
33 import org
.argeo
.cms
.internal
.http
.NodeHttp
;
34 import org
.argeo
.cms
.internal
.http
.client
.SpnegoAuthScheme
;
35 import org
.argeo
.cms
.internal
.http
.client
.HttpCredentialProvider
;
36 import org
.argeo
.naming
.DnsBrowser
;
37 import org
.argeo
.node
.NodeConstants
;
38 import org
.ietf
.jgss
.GSSCredential
;
39 import org
.ietf
.jgss
.GSSException
;
40 import org
.ietf
.jgss
.GSSManager
;
41 import org
.ietf
.jgss
.GSSName
;
42 import org
.ietf
.jgss
.Oid
;
44 /** Low-level kernel security */
46 public class CmsSecurity
implements KernelConstants
{
47 private final static Log log
= LogFactory
.getLog(CmsSecurity
.class);
48 // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
49 private final static Oid KERBEROS_OID
;
52 KERBEROS_OID
= new Oid("1.3.6.1.5.5.2");
53 } catch (GSSException e
) {
54 throw new IllegalStateException("Cannot create Kerberos OID", e
);
58 public final static int DEPLOYED
= 30;
59 public final static int STANDALONE
= 20;
60 public final static int DEV
= 10;
61 public final static int UNKNOWN
= 0;
63 private String hostname
;
65 private final int securityLevel
;
66 private Subject nodeSubject
;
69 private String kerberosDomain
;
70 private String service
= null;
71 private GSSCredential acceptorCredentials
;
73 private Path nodeKeyTab
= KernelUtils
.getOsgiInstancePath(KernelConstants
.NODE_KEY_TAB_PATH
);
77 if (!DeployConfig
.isInitialized()) // first init
78 FirstInit
.prepareInstanceArea();
80 securityLevel
= evaluateSecurityLevel();
82 if (securityLevel
== DEPLOYED
) {
83 // Register client-side SPNEGO auth scheme
84 AuthPolicy
.registerAuthScheme(SpnegoAuthScheme
.NAME
, SpnegoAuthScheme
.class);
85 HttpParams params
= DefaultHttpParams
.getDefaultParams();
86 ArrayList
<String
> schemes
= new ArrayList
<>();
87 schemes
.add(SpnegoAuthScheme
.NAME
);// SPNEGO preferred
88 // schemes.add(AuthPolicy.BASIC);// incompatible with Basic
89 params
.setParameter(AuthPolicy
.AUTH_SCHEME_PRIORITY
, schemes
);
90 params
.setParameter(CredentialsProvider
.PROVIDER
, new HttpCredentialProvider());
91 params
.setParameter(HttpMethodParams
.COOKIE_POLICY
, CookiePolicy
.BROWSER_COMPATIBILITY
);
92 // params.setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
95 // Configure JAAS first
96 if (System
.getProperty(JAAS_CONFIG_PROP
) == null) {
97 String jaasConfig
= securityLevel
< DEPLOYED ? JAAS_CONFIG
: JAAS_CONFIG_IPA
;
98 URL url
= getClass().getClassLoader().getResource(jaasConfig
);
99 System
.setProperty(JAAS_CONFIG_PROP
, url
.toExternalForm());
101 // explicitly load JAAS configuration
102 Configuration
.getConfiguration();
103 nodeSubject
= logInKernel();
105 // firstInit = !new File(getOsgiInstanceDir(), DIR_NODE).exists();
107 // this.keyStoreFile = new File(KernelUtils.getOsgiInstanceDir(),
109 // createKeyStoreIfNeeded();
112 private int evaluateSecurityLevel() {
114 try (DnsBrowser dnsBrowser
= new DnsBrowser()) {
115 InetAddress localhost
= InetAddress
.getLocalHost();
116 hostname
= localhost
.getHostName();
117 String dnsZone
= hostname
.substring(hostname
.indexOf('.') + 1);
118 String ipfromDns
= dnsBrowser
.getRecord(hostname
, localhost
instanceof Inet6Address ?
"AAAA" : "A");
119 boolean consistentIp
= localhost
.getHostAddress().equals(ipfromDns
);
120 kerberosDomain
= dnsBrowser
.getRecord("_kerberos." + dnsZone
, "TXT");
121 if (consistentIp
&& kerberosDomain
!= null && Files
.exists(nodeKeyTab
)) {
125 kerberosDomain
= null;
126 // FIXME make state more robust
128 } catch (UnknownHostException e
) {
129 hostname
= "localhost";
130 log
.warn("Cannot determine hostname, using " + hostname
+ ":" + e
.getMessage());
132 } catch (Exception e
) {
133 log
.warn("Exception when evaluating security level, setting it to DEV", e
);
138 throw new CmsException("Undefined security level");
142 private Subject
logInKernel() {
143 final Subject nodeSubject
= new Subject();
145 CallbackHandler callbackHandler
;
146 if (Files
.exists(nodeKeyTab
)) {
147 service
= NodeHttp
.DEFAULT_SERVICE
;
148 // service = NodeConstants.NODE_SERVICE;
149 callbackHandler
= new CallbackHandler() {
152 public void handle(Callback
[] callbacks
) throws IOException
, UnsupportedCallbackException
{
153 for (Callback callback
: callbacks
)
154 if (callback
instanceof NameCallback
)
155 ((NameCallback
) callback
).setName(getKerberosServicePrincipal());
160 LoginContext kernelLc
= new LoginContext(NodeConstants
.LOGIN_CONTEXT_NODE
, nodeSubject
,
163 } catch (LoginException e
) {
164 throw new CmsException("Cannot log in kernel", e
);
167 callbackHandler
= null;
169 // callbackHandler = (CallbackHandler)
170 // Class.forName("com.sun.security.auth.callback.TextCallbackHandler")
172 // } catch (ReflectiveOperationException e) {
173 // throw new CmsException("Cannot create text callback handler", e);
176 LoginContext kernelLc
= new LoginContext(NodeConstants
.LOGIN_CONTEXT_NODE
, nodeSubject
);
178 } catch (LoginException e
) {
179 throw new CmsException("Cannot log in kernel", e
);
183 if (securityLevel
>= DEPLOYED
) {
184 acceptorCredentials
= logInAsAcceptor(nodeSubject
);
189 private String
getKerberosServicePrincipal() {
190 if (hostname
== null || "locahost".equals(hostname
) || kerberosDomain
== null || service
== null)
191 throw new IllegalStateException("Cannot determine kerberos principal");
192 return service
+ "/" + hostname
+ "@" + kerberosDomain
;
195 private GSSCredential
logInAsAcceptor(Subject nodeSubject
) {
197 Iterator
<KerberosPrincipal
> krb5It
= nodeSubject
.getPrincipals(KerberosPrincipal
.class).iterator();
198 if (!krb5It
.hasNext())
200 KerberosPrincipal krb5Principal
= null;
201 while (krb5It
.hasNext()) {
202 KerberosPrincipal principal
= krb5It
.next();
203 if (service
== null && krb5Principal
== null)// first as default
204 krb5Principal
= principal
;
205 if (service
!= null && principal
.getName().equals(getKerberosServicePrincipal()))
206 krb5Principal
= principal
;
209 if (krb5Principal
== null)
212 GSSManager manager
= GSSManager
.getInstance();
214 GSSName gssName
= manager
.createName(krb5Principal
.getName(), null);
215 GSSCredential serverCredentials
= Subject
.doAs(nodeSubject
, new PrivilegedExceptionAction
<GSSCredential
>() {
218 public GSSCredential
run() throws GSSException
{
219 return manager
.createCredential(gssName
, GSSCredential
.INDEFINITE_LIFETIME
, KERBEROS_OID
,
220 GSSCredential
.ACCEPT_ONLY
);
225 if (log
.isDebugEnabled())
226 log
.debug("GSS acceptor configured for " + krb5Principal
);
227 return serverCredentials
;
228 } catch (Exception gsse
) {
229 throw new CmsException("Cannot create acceptor credentials for " + krb5Principal
, gsse
);
233 // private Subject logInHardenedKernel() {
234 // final Subject kernelSubject = new Subject();
235 // createKeyStoreIfNeeded();
237 // CallbackHandler cbHandler = new CallbackHandler() {
240 // public void handle(Callback[] callbacks) throws IOException,
241 // UnsupportedCallbackException {
243 //// ((NameCallback) callbacks[1]).setName(AuthConstants.ROLE_KERNEL);
245 // ((PasswordCallback) callbacks[2]).setPassword("changeit".toCharArray());
247 // ((PasswordCallback) callbacks[3]).setPassword("changeit".toCharArray());
251 // LoginContext kernelLc = new
252 // LoginContext(KernelConstants.LOGIN_CONTEXT_HARDENED_KERNEL,
256 // } catch (LoginException e) {
257 // throw new CmsException("Cannot log in kernel", e);
259 // return kernelSubject;
265 LoginContext kernelLc
= new LoginContext(NodeConstants
.LOGIN_CONTEXT_NODE
, nodeSubject
);
267 } catch (LoginException e
) {
268 throw new CmsException("Cannot log out kernel", e
);
271 // Security.removeProvider(SECURITY_PROVIDER);
274 public synchronized int getSecurityLevel() {
275 return securityLevel
;
278 // public String getKerberosDomain() {
279 // return kerberosDomain;
282 // public Subject getNodeSubject() {
283 // return nodeSubject;
286 // public GSSCredential getServerCredentials() {
287 // return acceptorCredentials;
290 // public void setSecurityLevel(int newValue) {
291 // if (newValue != STANDALONE || newValue != DEV)
292 // throw new CmsException("Invalid value for security level " + newValue);
293 // if (newValue >= securityLevel)
294 // throw new CmsException(
295 // "Impossible to increase security level (from " + securityLevel + " to " +
297 // securityLevel = newValue;
300 // private void createKeyStoreIfNeeded() {
301 // // for (Provider provider : Security.getProviders())
302 // // System.out.println(provider.getName());
304 // char[] ksPwd = "changeit".toCharArray();
305 // char[] keyPwd = Arrays.copyOf(ksPwd, ksPwd.length);
306 // if (!keyStoreFile.exists()) {
308 // keyStoreFile.getParentFile().mkdirs();
309 // KeyStore keyStore = PkiUtils.getKeyStore(keyStoreFile, ksPwd);
310 // // PkiUtils.generateSelfSignedCertificate(keyStore, new
311 // // X500Principal(AuthConstants.ROLE_KERNEL), 1024,
313 // PkiUtils.saveKeyStore(keyStoreFile, ksPwd, keyStore);
314 // if (log.isDebugEnabled())
315 // log.debug("Created keystore " + keyStoreFile);
316 // } catch (Exception e) {
317 // if (keyStoreFile.length() == 0)
318 // keyStoreFile.delete();
319 // log.error("Cannot create keystore " + keyStoreFile, e);
324 // private final static String SECURITY_PROVIDER = "BC";// Bouncy Castle
325 // private final static Log log;
327 // log = LogFactory.getLog(NodeSecurity.class);
328 // // Make Bouncy Castle the default provider
329 // Provider provider = new BouncyCastleProvider();
330 // int position = Security.insertProviderAt(provider, 1);
331 // if (position == -1)
332 // log.error("Provider " + provider.getName()
333 // + " already installed and could not be set as default");
334 // Provider defaultProvider = Security.getProviders()[0];
335 // if (!defaultProvider.getName().equals(SECURITY_PROVIDER))
336 // log.error("Provider name is " + defaultProvider.getName()
337 // + " but it should be " + SECURITY_PROVIDER);