001: /*
002:
003: Derby - Class org.apache.derby.impl.jdbc.authentication.BasicAuthenticationServiceImpl
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.jdbc.authentication;
023:
024: import org.apache.derby.iapi.reference.MessageId;
025: import org.apache.derby.iapi.reference.Attribute;
026: import org.apache.derby.authentication.UserAuthenticator;
027: import org.apache.derby.iapi.services.property.PropertyUtil;
028: import org.apache.derby.iapi.services.daemon.Serviceable;
029: import org.apache.derby.iapi.services.monitor.ModuleFactory;
030: import org.apache.derby.iapi.services.monitor.Monitor;
031: import org.apache.derby.iapi.services.sanity.SanityManager;
032: import org.apache.derby.iapi.error.StandardException;
033: import org.apache.derby.iapi.services.i18n.MessageService;
034: import org.apache.derby.iapi.store.access.TransactionController;
035: import org.apache.derby.iapi.jdbc.AuthenticationService;
036: import org.apache.derby.iapi.util.StringUtil;
037:
038: import java.util.Properties; // security imports - for SHA-1 digest
039: import java.security.MessageDigest;
040: import java.security.NoSuchAlgorithmException;
041: import java.io.Serializable;
042: import java.util.Dictionary;
043:
044: /**
045: * This authentication service is the basic Cloudscape User authentication
046: * level support.
047: *
048: * It is activated upon setting derby.authentication.provider database
049: * or system property to 'BUILTIN'.
050: * <p>
051: * It instantiates & calls the basic User authentication scheme at runtime.
052: * <p>
053: * In 2.0, users can now be defined as database properties.
054: * If derby.database.propertiesOnly is set to true, then in this
055: * case, only users defined as database properties for the current database
056: * will be considered.
057: *
058: * @author Francois
059: */
060: public final class BasicAuthenticationServiceImpl extends
061: AuthenticationServiceBase implements UserAuthenticator {
062:
063: //
064: // ModuleControl implementation (overriden)
065: //
066:
067: /**
068: * Check if we should activate this authentication service.
069: */
070: public boolean canSupport(Properties properties) {
071:
072: if (!requireAuthentication(properties))
073: return false;
074:
075: //
076: // We check 2 System/Database properties:
077: //
078: //
079: // - if derby.authentication.provider is set to 'BUILTIN'.
080: //
081: // and in that case we are the authentication service that should
082: // be run.
083: //
084:
085: String authenticationProvider = PropertyUtil
086: .getPropertyFromSet(
087: properties,
088: org.apache.derby.iapi.reference.Property.AUTHENTICATION_PROVIDER_PARAMETER);
089:
090: if ((authenticationProvider != null)
091: && (authenticationProvider.length() != 0)
092: && (!(StringUtil
093: .SQLEqualsIgnoreCase(
094: authenticationProvider,
095: org.apache.derby.iapi.reference.Property.AUTHENTICATION_PROVIDER_BUILTIN))))
096: return false;
097: else
098: return true; // Yep, we're on!
099: }
100:
101: /**
102: * @see org.apache.derby.iapi.services.monitor.ModuleControl#boot
103: * @exception StandardException upon failure to load/boot the expected
104: * authentication service.
105: */
106: public void boot(boolean create, Properties properties)
107: throws StandardException {
108:
109: // We need authentication
110: // setAuthentication(true);
111:
112: // we call the super in case there is anything to get initialized.
113: super .boot(create, properties);
114:
115: // Initialize the MessageDigest class engine here
116: // (we don't need to do that ideally, but there is some
117: // overhead the first time it is instantiated.
118: // SHA-1 is expected in jdk 1.1x and jdk1.2
119: // This is a standard name: check,
120: // http://java.sun.com/products/jdk/1.{1,2}
121: // /docs/guide/security/CryptoSpec.html#AppA
122: try {
123: MessageDigest digestAlgorithm = MessageDigest
124: .getInstance("SHA-1");
125: digestAlgorithm.reset();
126:
127: } catch (NoSuchAlgorithmException nsae) {
128: throw Monitor.exceptionStartingModule(nsae);
129: }
130:
131: // Set ourselves as being ready and loading the proper
132: // authentication scheme for this service
133: //
134: this .setAuthenticationService(this );
135: }
136:
137: /*
138: ** UserAuthenticator methods.
139: */
140:
141: /**
142: * Authenticate the passed-in user's credentials.
143: *
144: * @param userName The user's name used to connect to JBMS system
145: * @param userPassword The user's password used to connect to JBMS system
146: * @param databaseName The database which the user wants to connect to.
147: * @param info Additional jdbc connection info.
148: */
149: public boolean authenticateUser(String userName,
150: String userPassword, String databaseName, Properties info) {
151: // Client security mechanism if any specified
152: // Note: Right now it is only used to handle clients authenticating
153: // via DRDA SECMEC_USRSSBPWD mechanism
154: String clientSecurityMechanism = null;
155: // Client security mechanism (if any) short representation
156: // Default value is none.
157: int secMec = 0;
158:
159: // let's check if the user has been defined as a valid user of the
160: // JBMS system.
161: // We expect to find and match a System property corresponding to the
162: // credentials passed-in.
163: //
164: if (userName == null)
165: // We don't tolerate 'guest' user for now.
166: return false;
167:
168: String definedUserPassword = null, passedUserPassword = null;
169:
170: // If a security mechanism is specified as part of the connection
171: // properties, it indicates that we've to account as far as how the
172: // password is presented to us - in the case of SECMEC_USRSSBPWD
173: // (only expected one at the moment), the password is a substitute
174: // one which has already been hashed differently than what we store
175: // at the database level (for instance) - this will influence how we
176: // assess the substitute password to be legitimate for Derby's
177: // BUILTIN authentication scheme/provider.
178: if ((clientSecurityMechanism = info
179: .getProperty(Attribute.CLIENT_SECURITY_MECHANISM)) != null) {
180: secMec = Integer.parseInt(clientSecurityMechanism);
181: }
182:
183: //
184: // Check if user has been defined at the database or/and
185: // system level. The user (administrator) can configure it the
186: // way he/she wants (as well as forcing users properties to
187: // be retrieved at the datbase level only).
188: //
189: String userNameProperty = org.apache.derby.iapi.reference.Property.USER_PROPERTY_PREFIX
190: .concat(userName);
191:
192: // check if user defined at the database level
193: definedUserPassword = getDatabaseProperty(userNameProperty);
194:
195: if (definedUserPassword != null) {
196: if (secMec != SECMEC_USRSSBPWD) {
197: // encrypt passed-in password
198: passedUserPassword = encryptPassword(userPassword);
199: } else {
200: // Dealing with a client SECMEC - password checking is
201: // slightly different and we need to generate a
202: // password substitute to compare with the substitute
203: // generated one from the client.
204: definedUserPassword = substitutePassword(userName,
205: definedUserPassword, info, true);
206: // As SecMec is SECMEC_USRSSBPWD, expected passed-in password
207: // to be HexString'ified already
208: passedUserPassword = userPassword;
209: }
210: } else {
211: // check if user defined at the system level
212: definedUserPassword = getSystemProperty(userNameProperty);
213: passedUserPassword = userPassword;
214:
215: if ((definedUserPassword != null)
216: && (secMec == SECMEC_USRSSBPWD)) {
217: // Dealing with a client SECMEC - see above comments
218: definedUserPassword = substitutePassword(userName,
219: definedUserPassword, info, false);
220: }
221: }
222:
223: if (definedUserPassword == null)
224: // no such user found
225: return false;
226:
227: // check if the passwords match
228: if (!definedUserPassword.equals(passedUserPassword))
229: return false;
230:
231: // NOTE: We do not look at the passed-in database name value as
232: // we rely on the authorization service that was put in
233: // in 2.0 . (if a database name was passed-in)
234:
235: // We do have a valid user
236: return true;
237: }
238: }
|