]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsSecurity.java
168b199bac8b28e9cc3a85d695891b6a09a57ca6
[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.Iterator;
12
13 import javax.security.auth.Subject;
14 import javax.security.auth.callback.Callback;
15 import javax.security.auth.callback.CallbackHandler;
16 import javax.security.auth.callback.NameCallback;
17 import javax.security.auth.callback.UnsupportedCallbackException;
18 import javax.security.auth.kerberos.KerberosPrincipal;
19 import javax.security.auth.login.Configuration;
20 import javax.security.auth.login.LoginContext;
21 import javax.security.auth.login.LoginException;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.argeo.cms.CmsException;
26 import org.argeo.naming.DnsBrowser;
27 import org.argeo.node.NodeConstants;
28 import org.ietf.jgss.GSSCredential;
29 import org.ietf.jgss.GSSException;
30 import org.ietf.jgss.GSSManager;
31 import org.ietf.jgss.GSSName;
32 import org.ietf.jgss.Oid;
33
34 /** Low-level kernel security */
35 class CmsSecurity implements KernelConstants {
36 private final static Log log = LogFactory.getLog(CmsSecurity.class);
37 // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html
38 private final static Oid KERBEROS_OID;
39 static {
40 try {
41 KERBEROS_OID = new Oid("1.3.6.1.5.5.2");
42 } catch (GSSException e) {
43 throw new IllegalStateException("Cannot create Kerberos OID", e);
44 }
45 }
46
47 public final static int DEPLOYED = 30;
48 public final static int STANDALONE = 20;
49 public final static int DEV = 10;
50 public final static int UNKNOWN = 0;
51
52 private String hostname;
53
54 private final int securityLevel;
55 private Subject nodeSubject;
56
57 // IPA
58 private String kerberosDomain;
59 private String service = null;
60 private GSSCredential acceptorCredentials;
61
62 private Path nodeKeyTab = KernelUtils.getOsgiInstancePath("node/krb5.keytab");
63
64 public CmsSecurity() {
65 if (!DeployConfig.isInitialized()) // first init
66 FirstInit.prepareInstanceArea();
67
68 securityLevel = evaluateSecurityLevel();
69 // Configure JAAS first
70 if (System.getProperty(JAAS_CONFIG_PROP) == null) {
71 String jaasConfig = securityLevel < DEPLOYED ? JAAS_CONFIG : JAAS_CONFIG_IPA;
72 URL url = getClass().getClassLoader().getResource(jaasConfig);
73 System.setProperty(JAAS_CONFIG_PROP, url.toExternalForm());
74 }
75 // explicitly load JAAS configuration
76 Configuration.getConfiguration();
77 nodeSubject = logInKernel();
78
79 // firstInit = !new File(getOsgiInstanceDir(), DIR_NODE).exists();
80
81 // this.keyStoreFile = new File(KernelUtils.getOsgiInstanceDir(),
82 // "node.p12");
83 // createKeyStoreIfNeeded();
84 }
85
86 private int evaluateSecurityLevel() {
87 int res = UNKNOWN;
88 try (DnsBrowser dnsBrowser = new DnsBrowser()) {
89 InetAddress localhost = InetAddress.getLocalHost();
90 hostname = localhost.getHostName();
91 String dnsZone = hostname.substring(hostname.indexOf('.') + 1);
92 String ipfromDns = dnsBrowser.getRecord(hostname, localhost instanceof Inet6Address ? "AAAA" : "A");
93 boolean consistentIp = localhost.getHostAddress().equals(ipfromDns);
94 kerberosDomain = dnsBrowser.getRecord("_kerberos." + dnsZone, "TXT");
95 if (consistentIp && kerberosDomain != null && Files.exists(nodeKeyTab)) {
96 res = DEPLOYED;
97 } else {
98 res = STANDALONE;
99 kerberosDomain = null;
100 // FIXME make state more robust
101 }
102 } catch (UnknownHostException e) {
103 hostname = "localhost";
104 log.warn("Cannot determine hostname, using " + hostname + ":" + e.getMessage());
105 res = STANDALONE;
106 } catch (Exception e) {
107 log.warn("Exception when evaluating security level, setting it to DEV", e);
108 res = DEV;
109 }
110
111 if (res == UNKNOWN)
112 throw new CmsException("Undefined security level");
113 return res;
114 }
115
116 private Subject logInKernel() {
117 final Subject nodeSubject = new Subject();
118
119 CallbackHandler callbackHandler;
120 if (Files.exists(nodeKeyTab)) {
121 service = NodeConstants.NODE_SERVICE;
122 callbackHandler = new CallbackHandler() {
123
124 @Override
125 public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
126 for (Callback callback : callbacks)
127 if (callback instanceof NameCallback)
128 ((NameCallback) callback).setName(getKerberosServicePrincipal());
129
130 }
131 };
132 try {
133 LoginContext kernelLc = new LoginContext(NodeConstants.LOGIN_CONTEXT_NODE, nodeSubject,
134 callbackHandler);
135 kernelLc.login();
136 } catch (LoginException e) {
137 throw new CmsException("Cannot log in kernel", e);
138 }
139 } else {
140 callbackHandler = null;
141 // try {
142 // callbackHandler = (CallbackHandler)
143 // Class.forName("com.sun.security.auth.callback.TextCallbackHandler")
144 // .newInstance();
145 // } catch (ReflectiveOperationException e) {
146 // throw new CmsException("Cannot create text callback handler", e);
147 // }
148 try {
149 LoginContext kernelLc = new LoginContext(NodeConstants.LOGIN_CONTEXT_SINGLE_USER, nodeSubject);
150 kernelLc.login();
151 } catch (LoginException e) {
152 throw new CmsException("Cannot log in kernel", e);
153 }
154 }
155
156 if (securityLevel >= DEPLOYED) {
157 acceptorCredentials = logInAsAcceptor(nodeSubject);
158 }
159 return nodeSubject;
160 }
161
162 private String getKerberosServicePrincipal() {
163 if (hostname == null || "locahost".equals(hostname) || kerberosDomain == null || service == null)
164 throw new IllegalStateException("Cannot determine kerberos principal");
165 return service + "/" + hostname + "@" + kerberosDomain;
166 }
167
168 private GSSCredential logInAsAcceptor(Subject nodeSubject) {
169 // GSS
170 Iterator<KerberosPrincipal> krb5It = nodeSubject.getPrincipals(KerberosPrincipal.class).iterator();
171 if (!krb5It.hasNext())
172 return null;
173 KerberosPrincipal krb5Principal = null;
174 while (krb5It.hasNext()) {
175 KerberosPrincipal principal = krb5It.next();
176 if (service == null && krb5Principal == null)// first as default
177 krb5Principal = principal;
178 if (service != null && principal.getName().equals(getKerberosServicePrincipal()))
179 krb5Principal = principal;
180 }
181
182 if (krb5Principal == null)
183 return null;
184
185 GSSManager manager = GSSManager.getInstance();
186 try {
187 GSSName gssName = manager.createName(krb5Principal.getName(), null);
188 GSSCredential serverCredentials = Subject.doAs(nodeSubject, new PrivilegedExceptionAction<GSSCredential>() {
189
190 @Override
191 public GSSCredential run() throws GSSException {
192 return manager.createCredential(gssName, GSSCredential.INDEFINITE_LIFETIME, KERBEROS_OID,
193 GSSCredential.ACCEPT_ONLY);
194
195 }
196
197 });
198 if (log.isDebugEnabled())
199 log.debug("GSS acceptor configured for " + krb5Principal);
200 return serverCredentials;
201 } catch (Exception gsse) {
202 throw new CmsException("Cannot create acceptor credentials for " + krb5Principal, gsse);
203 }
204 }
205 //
206 // private Subject logInHardenedKernel() {
207 // final Subject kernelSubject = new Subject();
208 // createKeyStoreIfNeeded();
209 //
210 // CallbackHandler cbHandler = new CallbackHandler() {
211 //
212 // @Override
213 // public void handle(Callback[] callbacks) throws IOException,
214 // UnsupportedCallbackException {
215 // // alias
216 //// ((NameCallback) callbacks[1]).setName(AuthConstants.ROLE_KERNEL);
217 // // store pwd
218 // ((PasswordCallback) callbacks[2]).setPassword("changeit".toCharArray());
219 // // key pwd
220 // ((PasswordCallback) callbacks[3]).setPassword("changeit".toCharArray());
221 // }
222 // };
223 // try {
224 // LoginContext kernelLc = new
225 // LoginContext(KernelConstants.LOGIN_CONTEXT_HARDENED_KERNEL,
226 // kernelSubject,
227 // cbHandler);
228 // kernelLc.login();
229 // } catch (LoginException e) {
230 // throw new CmsException("Cannot log in kernel", e);
231 // }
232 // return kernelSubject;
233 // }
234
235 void destroy() {
236 // Logout kernel
237 try {
238 LoginContext kernelLc = new LoginContext(NodeConstants.LOGIN_CONTEXT_NODE, nodeSubject);
239 kernelLc.logout();
240 } catch (LoginException e) {
241 throw new CmsException("Cannot log out kernel", e);
242 }
243
244 // Security.removeProvider(SECURITY_PROVIDER);
245 }
246
247 public synchronized int getSecurityLevel() {
248 return securityLevel;
249 }
250
251 public String getKerberosDomain() {
252 return kerberosDomain;
253 }
254
255 public Subject getNodeSubject() {
256 return nodeSubject;
257 }
258
259 public GSSCredential getServerCredentials() {
260 return acceptorCredentials;
261 }
262
263 // public void setSecurityLevel(int newValue) {
264 // if (newValue != STANDALONE || newValue != DEV)
265 // throw new CmsException("Invalid value for security level " + newValue);
266 // if (newValue >= securityLevel)
267 // throw new CmsException(
268 // "Impossible to increase security level (from " + securityLevel + " to " +
269 // newValue + ")");
270 // securityLevel = newValue;
271 // }
272
273 // private void createKeyStoreIfNeeded() {
274 // // for (Provider provider : Security.getProviders())
275 // // System.out.println(provider.getName());
276 //
277 // char[] ksPwd = "changeit".toCharArray();
278 // char[] keyPwd = Arrays.copyOf(ksPwd, ksPwd.length);
279 // if (!keyStoreFile.exists()) {
280 // try {
281 // keyStoreFile.getParentFile().mkdirs();
282 // KeyStore keyStore = PkiUtils.getKeyStore(keyStoreFile, ksPwd);
283 // // PkiUtils.generateSelfSignedCertificate(keyStore, new
284 // // X500Principal(AuthConstants.ROLE_KERNEL), 1024,
285 // // keyPwd);
286 // PkiUtils.saveKeyStore(keyStoreFile, ksPwd, keyStore);
287 // if (log.isDebugEnabled())
288 // log.debug("Created keystore " + keyStoreFile);
289 // } catch (Exception e) {
290 // if (keyStoreFile.length() == 0)
291 // keyStoreFile.delete();
292 // log.error("Cannot create keystore " + keyStoreFile, e);
293 // }
294 // }
295 // }
296
297 // private final static String SECURITY_PROVIDER = "BC";// Bouncy Castle
298 // private final static Log log;
299 // static {
300 // log = LogFactory.getLog(NodeSecurity.class);
301 // // Make Bouncy Castle the default provider
302 // Provider provider = new BouncyCastleProvider();
303 // int position = Security.insertProviderAt(provider, 1);
304 // if (position == -1)
305 // log.error("Provider " + provider.getName()
306 // + " already installed and could not be set as default");
307 // Provider defaultProvider = Security.getProviders()[0];
308 // if (!defaultProvider.getName().equals(SECURITY_PROVIDER))
309 // log.error("Provider name is " + defaultProvider.getName()
310 // + " but it should be " + SECURITY_PROVIDER);
311 // }
312 }