Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / derby-10.3.2.1 / java / engine / org / apache / derby / impl / jdbc / authentication / AuthenticationServiceBase.java
diff --git a/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java b/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java
new file mode 100644 (file)
index 0000000..34fc52c
--- /dev/null
@@ -0,0 +1,542 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.jdbc.authentication.AuthenticationServiceBase\r
+\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to you under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+      http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.jdbc.authentication;\r
+\r
+import org.apache.derby.authentication.UserAuthenticator;\r
+import org.apache.derby.iapi.reference.Property;\r
+import org.apache.derby.iapi.jdbc.AuthenticationService;\r
+\r
+import org.apache.derby.iapi.reference.Limits;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+\r
+import org.apache.derby.iapi.services.context.ContextService;\r
+import org.apache.derby.iapi.services.daemon.Serviceable;\r
+\r
+import org.apache.derby.iapi.services.monitor.ModuleSupportable;\r
+import org.apache.derby.iapi.services.monitor.ModuleControl;\r
+import org.apache.derby.iapi.services.monitor.Monitor;\r
+import org.apache.derby.iapi.store.access.AccessFactory;\r
+import org.apache.derby.iapi.services.property.PropertyFactory;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.services.property.PropertySetCallback;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.reference.Attribute;\r
+\r
+import org.apache.derby.iapi.services.property.PropertyUtil;\r
+import org.apache.derby.iapi.util.StringUtil;\r
+\r
+import java.security.MessageDigest;\r
+import java.security.NoSuchAlgorithmException;\r
+\r
+import java.io.Serializable;\r
+import java.util.Dictionary;\r
+import java.util.Properties;\r
+import java.util.Date;\r
+\r
+/**\r
+ * This is the authentication service base class.\r
+ * <p>\r
+ * There can be 1 Authentication Service for the whole Derby\r
+ * system and/or 1 authentication per database.\r
+ * In a near future, we intend to allow multiple authentication services\r
+ * per system and/or per database.\r
+ * <p>\r
+ * It should be extended by the specialized authentication services.\r
+ *\r
+ * IMPORTANT NOTE:\r
+ * --------------\r
+ * User passwords are encrypted using SHA-1 message digest algorithm\r
+ * if they're stored in the database; otherwise they are not encrypted\r
+ * if they were defined at the system level.\r
+ * SHA-1 digest is single hash (one way) digest and is considered very\r
+ * secure (160 bits).\r
+ *\r
+ */\r
+public abstract class AuthenticationServiceBase\r
+       implements AuthenticationService, ModuleControl, ModuleSupportable, PropertySetCallback {\r
+\r
+       protected UserAuthenticator authenticationScheme; \r
+\r
+       // required to retrieve service properties\r
+       private AccessFactory store;\r
+\r
+       /**\r
+               Trace flag to trace authentication operations\r
+       */\r
+       public static final String AuthenticationTrace =\r
+                                               SanityManager.DEBUG ? "AuthenticationTrace" : null;\r
+       /**\r
+               Pattern that is prefixed to the stored password in the new authentication scheme\r
+       */\r
+       public static final String ID_PATTERN_NEW_SCHEME = "3b60";\r
+\r
+    /**\r
+        Userid with Strong password substitute DRDA security mechanism\r
+    */\r
+    protected static final int SECMEC_USRSSBPWD = 8;\r
+\r
+       /**\r
+               Length of the encrypted password in the new authentication scheme\r
+               See Beetle4601\r
+       */\r
+       public static final int MAGICLEN_NEWENCRYPT_SCHEME=44;\r
+\r
+       //\r
+       // constructor\r
+       //\r
+       public AuthenticationServiceBase() {\r
+       }\r
+\r
+       protected void setAuthenticationService(UserAuthenticator aScheme) {\r
+               // specialized class is the principal caller.\r
+               this.authenticationScheme = aScheme;\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       SanityManager.ASSERT(this.authenticationScheme != null, \r
+                               "There is no authentication scheme for that service!");\r
+               \r
+                       if (SanityManager.DEBUG_ON(AuthenticationTrace)) {\r
+\r
+                               java.io.PrintWriter iDbgStream =\r
+                                       SanityManager.GET_DEBUG_STREAM();\r
+\r
+                               iDbgStream.println("Authentication Service: [" +\r
+                                                               this.toString() + "]");\r
+                               iDbgStream.println("Authentication Scheme : [" +\r
+                                                               this.authenticationScheme.toString() + "]");\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+       /*\r
+       ** Methods of module control - To be overriden\r
+       */\r
+\r
+       /**\r
+               Start this module.  In this case, nothing needs to be done.\r
+               @see org.apache.derby.iapi.services.monitor.ModuleControl#boot\r
+\r
+               @exception StandardException upon failure to load/boot\r
+               the expected authentication service.\r
+        */\r
+        public void boot(boolean create, Properties properties)\r
+         throws StandardException\r
+        {\r
+                       //\r
+                       // we expect the Access factory to be available since we're\r
+                       // at boot stage.\r
+                       //\r
+                       store = (AccessFactory)\r
+                               Monitor.getServiceModule(this, AccessFactory.MODULE);\r
+                       // register to be notified upon db properties changes\r
+                       // _only_ if we're on a database context of course :)\r
+\r
+                       PropertyFactory pf = (PropertyFactory)\r
+                               Monitor.getServiceModule(this, org.apache.derby.iapi.reference.Module.PropertyFactory);\r
+                       if (pf != null)\r
+                               pf.addPropertySetNotification(this);\r
+\r
+        }\r
+\r
+       /**\r
+        * @see org.apache.derby.iapi.services.monitor.ModuleControl#stop\r
+        */\r
+       public void stop() {\r
+\r
+               // nothing special to be done yet.\r
+       }\r
+       /*\r
+       ** Methods of AuthenticationService\r
+       */\r
+\r
+       /**\r
+        * Authenticate a User inside JBMS.T his is an overload method.\r
+        *\r
+        * We're passed-in a Properties object containing user credentials information\r
+        * (as well as database name if user needs to be validated for a certain\r
+        * database access).\r
+        *\r
+        * @see\r
+        * org.apache.derby.iapi.jdbc.AuthenticationService#authenticate\r
+        *\r
+        *\r
+        */\r
+       public boolean authenticate(String databaseName, Properties userInfo) throws java.sql.SQLException\r
+       {\r
+               if (userInfo == (Properties) null)\r
+                       return false;\r
+\r
+               String userName = userInfo.getProperty(Attribute.USERNAME_ATTR);\r
+               if ((userName != null) && userName.length() > Limits.DB2_MAX_USERID_LENGTH) {\r
+               // DB2 has limits on length of the user id, so we enforce the same.\r
+               // This used to be error 28000 "Invalid authorization ID", but with v82,\r
+               // DB2 changed the behavior to return a normal "authorization failure\r
+               // occurred" error; so that means just return "false" and the correct\r
+               // exception will be thrown as usual.\r
+                       return false;\r
+               }\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (SanityManager.DEBUG_ON(AuthenticationTrace)) {\r
+\r
+                               java.io.PrintWriter iDbgStream =\r
+                                       SanityManager.GET_DEBUG_STREAM();\r
+\r
+                               iDbgStream.println(\r
+                                                               " - Authentication request: user [" +\r
+                                                           userName + "]"+ ", database [" +\r
+                                                           databaseName + "]");\r
+                               // The following will print the stack trace of the\r
+                               // authentication request to the log.  \r
+                               //Throwable t = new Throwable();\r
+                               //istream.println("Authentication Request Stack trace:");\r
+                               //t.printStackTrace(istream.getPrintWriter());\r
+                       }\r
+               }\r
+               return this.authenticationScheme.authenticateUser(userName,\r
+                                                 userInfo.getProperty(Attribute.PASSWORD_ATTR),\r
+                                                 databaseName,\r
+                                                 userInfo\r
+                                                );\r
+       }\r
+\r
+       /**\r
+        * Returns a property if it was set at the database or\r
+        * system level. Treated as SERVICE property by default.\r
+        *\r
+        * @return a property string value.\r
+        **/\r
+       public String getProperty(String key) {\r
+\r
+               String propertyValue = null;\r
+               TransactionController tc = null;\r
+\r
+               try {\r
+\r
+                 if (store != null)\r
+          {\r
+            tc = store.getTransaction(\r
+                ContextService.getFactory().getCurrentContextManager());\r
+          }\r
+\r
+                 propertyValue =\r
+                       PropertyUtil.getServiceProperty(tc,\r
+                                                                                       key,\r
+                                                                                       (String) null);\r
+                 if (tc != null) {\r
+                       tc.commit();\r
+                       tc = null;\r
+                 }\r
+\r
+               } catch (StandardException se) {\r
+                       // Do nothing and just return\r
+               }\r
+\r
+               return propertyValue;\r
+       }\r
+\r
+       public String getDatabaseProperty(String key) {\r
+\r
+               String propertyValue = null;\r
+               TransactionController tc = null;\r
+\r
+               try {\r
+\r
+                 if (store != null)\r
+                       tc = store.getTransaction(\r
+                ContextService.getFactory().getCurrentContextManager());\r
+\r
+                 propertyValue =\r
+                       PropertyUtil.getDatabaseProperty(tc, key);\r
+\r
+                 if (tc != null) {\r
+                       tc.commit();\r
+                       tc = null;\r
+                 }\r
+\r
+               } catch (StandardException se) {\r
+                       // Do nothing and just return\r
+               }\r
+\r
+               return propertyValue;\r
+       }\r
+\r
+       public String getSystemProperty(String key) {\r
+\r
+               boolean dbOnly = false;\r
+               dbOnly = Boolean.valueOf(\r
+                                       this.getDatabaseProperty(\r
+                                                       Property.DATABASE_PROPERTIES_ONLY)).booleanValue();\r
+\r
+               if (dbOnly)\r
+                       return null;\r
+\r
+               return PropertyUtil.getSystemProperty(key);\r
+       }\r
+\r
+       /*\r
+       ** Methods of PropertySetCallback\r
+       */\r
+       public void init(boolean dbOnly, Dictionary p) {\r
+               // not called yet ...\r
+       }\r
+\r
+       /**\r
+         @see PropertySetCallback#validate\r
+       */\r
+       public boolean validate(String key, Serializable value, Dictionary p)   {\r
+               return key.startsWith(org.apache.derby.iapi.reference.Property.USER_PROPERTY_PREFIX);\r
+       }\r
+       /**\r
+         @see PropertySetCallback#validate\r
+       */\r
+       public Serviceable apply(String key,Serializable value,Dictionary p)\r
+       {\r
+               return null;\r
+       }\r
+       /**\r
+         @see PropertySetCallback#map\r
+         @exception StandardException Thrown on error.\r
+       */\r
+       public Serializable map(String key, Serializable value, Dictionary p)\r
+               throws StandardException\r
+       {\r
+               // We only care for "derby.user." property changes\r
+               // at the moment.\r
+               if (!key.startsWith(org.apache.derby.iapi.reference.Property.USER_PROPERTY_PREFIX)) return null;\r
+               // We do not encrypt 'derby.user.<userName>' password if\r
+               // the configured authentication service is LDAP as the\r
+               // same property could be used to store LDAP user full DN (X500).\r
+               // In performing this check we only consider database properties\r
+               // not system, service or application properties.\r
+\r
+               String authService =\r
+                       (String)p.get(org.apache.derby.iapi.reference.Property.AUTHENTICATION_PROVIDER_PARAMETER);\r
+\r
+               if ((authService != null) &&\r
+                        (StringUtil.SQLEqualsIgnoreCase(authService, org.apache.derby.iapi.reference.Property.AUTHENTICATION_PROVIDER_LDAP)))\r
+                       return null;\r
+\r
+               // Ok, we can encrypt this password in the db\r
+               String userPassword = (String) value;\r
+\r
+               if (userPassword != null) {\r
+                       // encrypt (digest) the password\r
+                       // the caller will retrieve the new value\r
+                       userPassword = encryptPassword(userPassword);\r
+               }\r
+\r
+               return userPassword;\r
+       }\r
+\r
+\r
+       // Class implementation\r
+\r
+       protected final boolean requireAuthentication(Properties properties) {\r
+\r
+               //\r
+               // we check if derby.connection.requireAuthentication system\r
+               // property is set to true, otherwise we are the authentication\r
+               // service that should be run.\r
+               //\r
+               String requireAuthentication = PropertyUtil.getPropertyFromSet(\r
+                                       properties,\r
+                                       org.apache.derby.iapi.reference.Property.REQUIRE_AUTHENTICATION_PARAMETER\r
+                                                                                                               );\r
+               return Boolean.valueOf(requireAuthentication).booleanValue();\r
+       }\r
+\r
+       /**\r
+        * This method encrypts a clear user password using a\r
+        * Single Hash algorithm such as SHA-1 (SHA equivalent)\r
+        * (it is a 160 bits digest)\r
+        *\r
+        * The digest is returned as an object string.\r
+        *\r
+        * @param plainTxtUserPassword Plain text user password\r
+        *\r
+        * @return encrypted user password (digest) as a String object\r
+        */\r
+       protected String encryptPassword(String plainTxtUserPassword)\r
+       {\r
+               if (plainTxtUserPassword == null)\r
+                       return null;\r
+\r
+               MessageDigest algorithm = null;\r
+               try\r
+               {\r
+                       algorithm = MessageDigest.getInstance("SHA-1");\r
+               } catch (NoSuchAlgorithmException nsae)\r
+               {\r
+                                       // Ignore as we checked already during service boot-up\r
+               }\r
+\r
+               algorithm.reset();\r
+               byte[] bytePasswd = null;\r
+        bytePasswd = StringUtil.toHexByte(\r
+                plainTxtUserPassword,0,plainTxtUserPassword.length());\r
+               algorithm.update(bytePasswd);\r
+               byte[] encryptVal = algorithm.digest();\r
+        String hexString = ID_PATTERN_NEW_SCHEME +\r
+                StringUtil.toHexString(encryptVal,0,encryptVal.length);\r
+               return (hexString);\r
+\r
+       }\r
+\r
+    /**\r
+     * Strong Password Substitution (USRSSBPWD).\r
+     *\r
+     * This method generate a password subtitute to authenticate a client\r
+     * which is using a DRDA security mechanism such as SECMEC_USRSSBPWD.\r
+     *\r
+     * Depending how the user is defined in Derby and if BUILTIN\r
+     * is used, the stored password can be in clear-text (system level)\r
+     * or encrypted (hashed - *not decryptable*)) (database level) - If the\r
+     * user has authenticated at the network level via SECMEC_USRSSBPWD, it\r
+     * means we're presented with a password substitute and we need to\r
+     * generate a substitute password coming from the store to compare with\r
+     * the one passed-in.\r
+     *\r
+     * NOTE: A lot of this logic could be shared with the DRDA decryption\r
+     *       and client encryption managers - This will be done _once_\r
+     *       code sharing along with its rules are defined between the\r
+     *       Derby engine, client and network code (PENDING).\r
+     * \r
+     * Substitution algorithm works as follow:\r
+     *\r
+     * PW_TOKEN = SHA-1(PW, ID)\r
+     * The password (PW) and user name (ID) can be of any length greater\r
+     * than or equal to 1 byte.\r
+     * The client generates a 20-byte password substitute (PW_SUB) as follows:\r
+     * PW_SUB = SHA-1(PW_TOKEN, RDr, RDs, ID, PWSEQs)\r
+     * \r
+     * w/ (RDs) as the random client seed and (RDr) as the server one.\r
+     * \r
+     * See PWDSSB - Strong Password Substitution Security Mechanism\r
+     * (DRDA Vol.3 - P.650)\r
+     *\r
+        * @return a substituted password.\r
+     */\r
+    protected String substitutePassword(\r
+                String userName,\r
+                String password,\r
+                Properties info,\r
+                boolean databaseUser) {\r
+\r
+        MessageDigest messageDigest = null;\r
+\r
+        // Pattern that is prefixed to the BUILTIN encrypted password\r
+        String ID_PATTERN_NEW_SCHEME = "3b60";\r
+\r
+        // PWSEQs's 8-byte value constant - See DRDA Vol 3\r
+        byte SECMEC_USRSSBPWD_PWDSEQS[] = {\r
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,\r
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01\r
+                };\r
+        \r
+        // Generated password substitute\r
+        byte[] passwordSubstitute;\r
+\r
+        try\r
+        {\r
+            messageDigest = MessageDigest.getInstance("SHA-1");\r
+        } catch (NoSuchAlgorithmException nsae)\r
+        {\r
+            // Ignore as we checked already during service boot-up\r
+        }\r
+        // IMPORTANT NOTE: As the password is stored single-hashed in the\r
+        // database, it is impossible for us to decrypt the password and\r
+        // recompute a substitute to compare with one generated on the source\r
+        // side - Hence, we have to generate a password substitute.\r
+        // In other words, we cannot figure what the original password was -\r
+        // Strong Password Substitution (USRSSBPWD) cannot be supported for\r
+        // targets which can't access or decrypt passwords on their side.\r
+        //\r
+        messageDigest.reset();\r
+\r
+        byte[] bytePasswd = null;\r
+        byte[] userBytes = StringUtil.toHexByte(userName, 0, userName.length());\r
+\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            // We must have a source and target seed \r
+            SanityManager.ASSERT(\r
+              (((String) info.getProperty(Attribute.DRDA_SECTKN_IN) != null) &&\r
+              ((String) info.getProperty(Attribute.DRDA_SECTKN_OUT) != null)), \r
+                "Unexpected: Requester or server seed not available");\r
+        }\r
+\r
+        // Retrieve source (client)  and target 8-byte seeds\r
+        String sourceSeedstr = info.getProperty(Attribute.DRDA_SECTKN_IN);\r
+        String targetSeedstr = info.getProperty(Attribute.DRDA_SECTKN_OUT);\r
+\r
+        byte[] sourceSeed_ =\r
+            StringUtil.fromHexString(sourceSeedstr, 0, sourceSeedstr.length());\r
+        byte[] targetSeed_ =\r
+            StringUtil.fromHexString(targetSeedstr, 0, targetSeedstr.length());\r
+\r
+        String hexString = null;\r
+        // If user is at the database level, we don't encrypt the password\r
+        // as it is already encrypted (BUILTIN scheme) - we only do the\r
+        // BUILTIN encryption if the user is defined at the system level\r
+        // only - this is required beforehands so that we can do the password\r
+        // substitute generation right afterwards.\r
+        if (!databaseUser)\r
+        {\r
+            bytePasswd = StringUtil.toHexByte(password, 0, password.length());\r
+            messageDigest.update(bytePasswd);\r
+            byte[] encryptVal = messageDigest.digest();\r
+            hexString = ID_PATTERN_NEW_SCHEME +\r
+                StringUtil.toHexString(encryptVal, 0, encryptVal.length);\r
+        }\r
+        else\r
+            // Already encrypted from the database store\r
+            hexString = password;\r
+\r
+        // Generate the password substitute now\r
+\r
+        // Generate some 20-byte password token\r
+        messageDigest.update(userBytes);\r
+        messageDigest.update(\r
+                StringUtil.toHexByte(hexString, 0, hexString.length()));\r
+        byte[] passwordToken = messageDigest.digest();\r
+        \r
+        // Now we generate the 20-byte password substitute\r
+        messageDigest.update(passwordToken);\r
+        messageDigest.update(targetSeed_);\r
+        messageDigest.update(sourceSeed_);\r
+        messageDigest.update(userBytes);\r
+        messageDigest.update(SECMEC_USRSSBPWD_PWDSEQS);\r
+\r
+        passwordSubstitute = messageDigest.digest();\r
+\r
+        return StringUtil.toHexString(passwordSubstitute, 0,\r
+                                      passwordSubstitute.length);\r
+    }\r
+}\r