001 /**
002 *
003 * Copyright 2003-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.geronimo.security.realm.providers;
019
020 import java.io.IOException;
021 import java.security.cert.Certificate;
022 import java.security.cert.X509Certificate;
023 import java.util.Map;
024 import java.util.Set;
025 import javax.security.auth.Subject;
026 import javax.security.auth.callback.Callback;
027 import javax.security.auth.callback.CallbackHandler;
028 import javax.security.auth.callback.UnsupportedCallbackException;
029 import javax.security.auth.login.LoginException;
030 import javax.security.auth.spi.LoginModule;
031 import javax.security.auth.x500.X500Principal;
032
033 import org.apache.commons.logging.Log;
034 import org.apache.commons.logging.LogFactory;
035
036
037 /**
038 * An example LoginModule that reads a list of users and group from a file on disk.
039 * Authentication is provided by the SSL layer supplying the client certificate.
040 * All we check is that it is present. The
041 * file should be formatted using standard Java properties syntax. Expects
042 * to be run by a GenericSecurityRealm (doesn't work on its own).
043 *
044 * The usersURI property file should have lines of the form token=certificatename
045 * where certificate name is X509Certificate.getSubjectX500Principal().getName()
046 *
047 * The groupsURI property file should have lines of the form group=token1,token2,...
048 * where the tokens were associated to the certificate names in the usersURI properties file.
049 *
050 * @version $Rev: 355877 $ $Date: 2005-12-10 18:48:27 -0800 (Sat, 10 Dec 2005) $
051 */
052 public class CertificateChainLoginModule implements LoginModule {
053 private static Log log = LogFactory.getLog(CertificateChainLoginModule.class);
054
055 Subject subject;
056 CallbackHandler handler;
057 X500Principal principal;
058
059 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
060 this.subject = subject;
061 this.handler = callbackHandler;
062 // try {
063 // Kernel kernel = KernelRegistry.getKernel((String)options.get(JaasLoginModuleUse.KERNEL_LM_OPTION));
064 // ServerInfo serverInfo = (ServerInfo) options.get(JaasLoginModuleUse.SERVERINFO_LM_OPTION);
065 // URI usersURI = new URI((String)options.get(USERS_URI));
066 // URI groupsURI = new URI((String)options.get(GROUPS_URI));
067 // loadProperties(kernel, serverInfo, usersURI, groupsURI);
068 // } catch (Exception e) {
069 // log.error(e);
070 // throw new IllegalArgumentException("Unable to configure properties file login module: "+e);
071 // }
072 }
073
074
075
076 public boolean login() throws LoginException {
077 Callback[] callbacks = new Callback[1];
078
079 callbacks[0] = new CertificateChainCallback();
080 try {
081 handler.handle(callbacks);
082 } catch (IOException ioe) {
083 throw (LoginException) new LoginException().initCause(ioe);
084 } catch (UnsupportedCallbackException uce) {
085 throw (LoginException) new LoginException().initCause(uce);
086 }
087 assert callbacks.length == 1;
088 Certificate[] certificateChain = ((CertificateChainCallback)callbacks[0]).getCertificateChain();
089 if (certificateChain == null || certificateChain.length == 0) {
090 return false;
091 }
092 if (!(certificateChain[0] instanceof X509Certificate)) {
093 return false;
094 }
095 //TODO actually validate chain
096 principal = ((X509Certificate)certificateChain[0]).getSubjectX500Principal();
097
098 return true;
099 }
100
101 public boolean commit() throws LoginException {
102 Set principals = subject.getPrincipals();
103
104 principals.add(principal);
105 principals.add(new GeronimoUserPrincipal(principal.getName()));
106
107 return true;
108 }
109
110 public boolean abort() throws LoginException {
111 principal = null;
112
113 return true;
114 }
115
116 public boolean logout() throws LoginException {
117 principal = null;
118
119 return true;
120 }
121
122 /**
123 * Gets the names of all principal classes that may be populated into
124 * a Subject.
125 */
126 public String[] getPrincipalClassNames() {
127 return new String[]{GeronimoUserPrincipal.class.getName()};
128 }
129
130 }