]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsSecurity.java
- Merge JAAS configs
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / kernel / CmsSecurity.java
1 package org.argeo.cms.internal.kernel;
2
3 import java.io.IOException;
4 import java.net.Inet6Address;
5 import java.net.InetAddress;
6 import java.net.URL;
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;
13
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;
23
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;
43
44 /** Low-level kernel security */
45 @Deprecated
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;
50 static {
51 try {
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);
55 }
56 }
57
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;
62
63 private String hostname;
64
65 private final int securityLevel;
66 private Subject nodeSubject;
67
68 // IPA
69 private String kerberosDomain;
70 private String service = null;
71 private GSSCredential acceptorCredentials;
72
73 private Path nodeKeyTab = KernelUtils.getOsgiInstancePath(KernelConstants.NODE_KEY_TAB_PATH);
74
75 CmsSecurity() {
76
77 if (!DeployConfig.isInitialized()) // first init
78 FirstInit.prepareInstanceArea();
79
80 securityLevel = evaluateSecurityLevel();
81
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);
93 }
94
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());
100 }
101 // explicitly load JAAS configuration
102 Configuration.getConfiguration();
103 nodeSubject = logInKernel();
104
105 // firstInit = !new File(getOsgiInstanceDir(), DIR_NODE).exists();
106
107 // this.keyStoreFile = new File(KernelUtils.getOsgiInstanceDir(),
108 // "node.p12");
109 // createKeyStoreIfNeeded();
110 }
111
112 private int evaluateSecurityLevel() {
113 int res = UNKNOWN;
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)) {
122 res = DEPLOYED;
123 } else {
124 res = STANDALONE;
125 kerberosDomain = null;
126 // FIXME make state more robust
127 }
128 } catch (UnknownHostException e) {
129 hostname = "localhost";
130 log.warn("Cannot determine hostname, using " + hostname + ":" + e.getMessage());
131 res = STANDALONE;
132 } catch (Exception e) {
133 log.warn("Exception when evaluating security level, setting it to DEV", e);
134 res = DEV;
135 }
136
137 if (res == UNKNOWN)
138 throw new CmsException("Undefined security level");
139 return res;
140 }
141
142 private Subject logInKernel() {
143 final Subject nodeSubject = new Subject();
144
145 CallbackHandler callbackHandler;
146 if (Files.exists(nodeKeyTab)) {
147 service = NodeHttp.DEFAULT_SERVICE;
148 // service = NodeConstants.NODE_SERVICE;
149 callbackHandler = new CallbackHandler() {
150
151 @Override
152 public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
153 for (Callback callback : callbacks)
154 if (callback instanceof NameCallback)
155 ((NameCallback) callback).setName(getKerberosServicePrincipal());
156
157 }
158 };
159 try {
160 LoginContext kernelLc = new LoginContext(NodeConstants.LOGIN_CONTEXT_NODE, nodeSubject,
161 callbackHandler);
162 kernelLc.login();
163 } catch (LoginException e) {
164 throw new CmsException("Cannot log in kernel", e);
165 }
166 } else {
167 callbackHandler = null;
168 // try {
169 // callbackHandler = (CallbackHandler)
170 // Class.forName("com.sun.security.auth.callback.TextCallbackHandler")
171 // .newInstance();
172 // } catch (ReflectiveOperationException e) {
173 // throw new CmsException("Cannot create text callback handler", e);
174 // }
175 try {
176 LoginContext kernelLc = new LoginContext(NodeConstants.LOGIN_CONTEXT_NODE, nodeSubject);
177 kernelLc.login();
178 } catch (LoginException e) {
179 throw new CmsException("Cannot log in kernel", e);
180 }
181 }
182
183 if (securityLevel >= DEPLOYED) {
184 acceptorCredentials = logInAsAcceptor(nodeSubject);
185 }
186 return nodeSubject;
187 }
188
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;
193 }
194
195 private GSSCredential logInAsAcceptor(Subject nodeSubject) {
196 // GSS
197 Iterator<KerberosPrincipal> krb5It = nodeSubject.getPrincipals(KerberosPrincipal.class).iterator();
198 if (!krb5It.hasNext())
199 return null;
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;
207 }
208
209 if (krb5Principal == null)
210 return null;
211
212 GSSManager manager = GSSManager.getInstance();
213 try {
214 GSSName gssName = manager.createName(krb5Principal.getName(), null);
215 GSSCredential serverCredentials = Subject.doAs(nodeSubject, new PrivilegedExceptionAction<GSSCredential>() {
216
217 @Override
218 public GSSCredential run() throws GSSException {
219 return manager.createCredential(gssName, GSSCredential.INDEFINITE_LIFETIME, KERBEROS_OID,
220 GSSCredential.ACCEPT_ONLY);
221
222 }
223
224 });
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);
230 }
231 }
232 //
233 // private Subject logInHardenedKernel() {
234 // final Subject kernelSubject = new Subject();
235 // createKeyStoreIfNeeded();
236 //
237 // CallbackHandler cbHandler = new CallbackHandler() {
238 //
239 // @Override
240 // public void handle(Callback[] callbacks) throws IOException,
241 // UnsupportedCallbackException {
242 // // alias
243 //// ((NameCallback) callbacks[1]).setName(AuthConstants.ROLE_KERNEL);
244 // // store pwd
245 // ((PasswordCallback) callbacks[2]).setPassword("changeit".toCharArray());
246 // // key pwd
247 // ((PasswordCallback) callbacks[3]).setPassword("changeit".toCharArray());
248 // }
249 // };
250 // try {
251 // LoginContext kernelLc = new
252 // LoginContext(KernelConstants.LOGIN_CONTEXT_HARDENED_KERNEL,
253 // kernelSubject,
254 // cbHandler);
255 // kernelLc.login();
256 // } catch (LoginException e) {
257 // throw new CmsException("Cannot log in kernel", e);
258 // }
259 // return kernelSubject;
260 // }
261
262 void destroy() {
263 // Logout kernel
264 try {
265 LoginContext kernelLc = new LoginContext(NodeConstants.LOGIN_CONTEXT_NODE, nodeSubject);
266 kernelLc.logout();
267 } catch (LoginException e) {
268 throw new CmsException("Cannot log out kernel", e);
269 }
270
271 // Security.removeProvider(SECURITY_PROVIDER);
272 }
273
274 public synchronized int getSecurityLevel() {
275 return securityLevel;
276 }
277
278 // public String getKerberosDomain() {
279 // return kerberosDomain;
280 // }
281
282 // public Subject getNodeSubject() {
283 // return nodeSubject;
284 // }
285
286 // public GSSCredential getServerCredentials() {
287 // return acceptorCredentials;
288 // }
289
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 " +
296 // newValue + ")");
297 // securityLevel = newValue;
298 // }
299
300 // private void createKeyStoreIfNeeded() {
301 // // for (Provider provider : Security.getProviders())
302 // // System.out.println(provider.getName());
303 //
304 // char[] ksPwd = "changeit".toCharArray();
305 // char[] keyPwd = Arrays.copyOf(ksPwd, ksPwd.length);
306 // if (!keyStoreFile.exists()) {
307 // try {
308 // keyStoreFile.getParentFile().mkdirs();
309 // KeyStore keyStore = PkiUtils.getKeyStore(keyStoreFile, ksPwd);
310 // // PkiUtils.generateSelfSignedCertificate(keyStore, new
311 // // X500Principal(AuthConstants.ROLE_KERNEL), 1024,
312 // // keyPwd);
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);
320 // }
321 // }
322 // }
323
324 // private final static String SECURITY_PROVIDER = "BC";// Bouncy Castle
325 // private final static Log log;
326 // static {
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);
338 // }
339 }