1 Reply Latest reply: Nov 21, 2013 12:06 PM by user4450806 RSS

    Kerberos and Oracle JDBC6: Got minus one from read call

    user4450806

      This discussion was archived, but I wonder if anyone can shed any light on the issue: 11G Thin driver Kerberos auth error: Got minus one from a read call

       

      NOTE That I had actually had successful Kerberos authentication with the same Oracle Database tested below using SQLPlus but anything that appears to use the OJDBC thin driver fails. Here's an example of a successful authentication from client to server using a Kerberos ticket.

      client$ klist

      Ticket cache: FILE:/tmp/krb5cc_500

      Default principal: jriffle@DEVEL.APU.EDU

       

      Valid starting     Expires            Service principal

      11/18/13 08:56:25  11/18/13 18:56:27  krbtgt/DEVEL.APU.EDU@DEVEL.APU.EDU

          renew until 11/25/13 08:56:25

       

      client$ sqlplus /@testdw01

      SQL*Plus: Release 11.2.0.3.0 Production on Mon Nov 18 08:57:09 2013

       

      Copyright (c) 1982, 2011, Oracle.  All rights reserved.

       

       

      Connected to:

      Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

      With the Partitioning, OLAP, Data Mining and Real Application Testing options

       

      SQL>

       

       

       

      I have nearly exactly the same problem attempting to perform Kerberos authentication with the OJDBC6 thin driver. Here's the error I have using the documented Kereberos authentication Java code:

      # java -version

      java version "1.6.0_45"

      Java(TM) SE Runtime Environment (build 1.6.0_45-b06)

      Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)

      # java -cp .:ojdbc6.jar KereberosJdbcDemo

      (Removed the first part which errors out because it tries to use Kerberos ticket cache and the last part which gives a NULL pointer error because it fails to authenticated and then tries to print out the non-existent user principal).

      Entering login

      set password to '****'

              [Krb5LoginModule] user entered username: jriffle@DEVEL.APU.EDU

       

      Acquire TGT using AS Exchange

      principal is jriffle@DEVEL.APU.EDU

      EncryptionKey: keyType=3 keyBytes (hex dump)=0000: B0 8F F8 BF A7 AB CE A4

      EncryptionKey: keyType=1 keyBytes (hex dump)=0000: B0 8F F8 BF A7 AB CE A4

      EncryptionKey: keyType=23 keyBytes (hex dump)=0000: 0C 1F DD 82 6E 69 E3 1B   77 22 4B D0 AC A8 00 E9  ....ni..w"K.....

       

      EncryptionKey: keyType=16 keyBytes (hex dump)=0000: 9B AB 83 7C C8 52 D5 DC   B6 83 5D 76 73 1C F7 B3  .....R....]vs...

      0010: 23 2C 31 6B A1 4A CE 19

      EncryptionKey: keyType=17 keyBytes (hex dump)=0000: BC 4C DF 2C 13 D4 36 63   44 90 AC F9 B3 DA 66 32  .L.,..6cD.....f2

       

      Entering commit

      Commit Succeeded

       

      Specific Subjects: [jriffle@DEVEL.APU.EDU]

      Entering driver.connect

      Entering OracleDriver

      Entering OracleDriver connect

      Exception caused by OracleDriver connect

      Message: IO Error: The service in process is not supported.

      java.sql.SQLRecoverableException: IO Error: The service in process is not supported.

          at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:475)

          at oracle.jdbc.driver.PhysicalConnection.<init>(PhysicalConnection.java:552)

          at oracle.jdbc.driver.T4CConnection.<init>(T4CConnection.java:253)

          at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)

          at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:526)

          at KerberosJdbcDemo$1.run(KerberosJdbcDemo.java:168)

          at java.security.AccessController.doPrivileged(Native Method)

          at javax.security.auth.Subject.doAs(Subject.java:396)

          at KerberosJdbcDemo.connectWithSpecificUser(KerberosJdbcDemo.java:154)

          at KerberosJdbcDemo.main(KerberosJdbcDemo.java:54)

      Caused by: oracle.net.ns.NetException: The service in process is not supported.

          at oracle.net.ano.AuthenticationService.h(Unknown Source)

          at oracle.net.ano.Ano.negotiation(Unknown Source)

          at oracle.net.ns.NSProtocol.connect(NSProtocol.java:439)

          at oracle.jdbc.driver.T4CConnection.connect(T4CConnection.java:1126)

          at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:337)

          ... 9 more

      Caused by: java.security.PrivilegedActionException: java.io.IOException: Got minus one from a read call

          at java.security.AccessController.doPrivileged(Native Method)

          at javax.security.auth.Subject.doAs(Subject.java:396)

          ... 14 more

      Caused by: java.io.IOException: Got minus one from a read call

          at oracle.net.ano.AnoComm.b(Unknown Source)

          at oracle.net.ano.AnoComm.n(Unknown Source)

          at oracle.net.ano.AnoComm.e(Unknown Source)

          at oracle.net.ano.AnoComm.e(Unknown Source)

          at oracle.net.ano.AuthenticationService.run(Unknown Source)

          ... 16 more

       

      Here's my test code (minus the sensitive bits). This code is referenced in Oracle documentation: JDBC Client-Side Security Features

      import com.sun.security.auth.module.Krb5LoginModule;

      import java.io.IOException;

       

      import java.security.PrivilegedExceptionAction;

      import java.sql.Connection;

      import java.sql.ResultSet;

      import java.sql.SQLException;

      import java.sql.Statement;

       

      import java.util.HashMap;

      import java.util.Properties;

      import javax.security.auth.Subject;

      import javax.security.auth.callback.Callback;

      import javax.security.auth.callback.CallbackHandler;

      import javax.security.auth.callback.PasswordCallback;

      import javax.security.auth.callback.UnsupportedCallbackException;

       

      import oracle.jdbc.OracleConnection;

      import oracle.jdbc.OracleDriver;

      import oracle.net.ano.AnoServices;

      public class KerberosJdbcDemo

      {

      /*

        String url ="jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)"+

          "(HOST=oracle-db-1.devel.apu.edu)(PORT=1521))(CONNECT_DATA=" +

          "(SERVICE_NAME=DEVDW01.apu.edu)))";

      */

        String url ="jdbc:oracle:thin:@127.0.0.1:1521:DEVDW01";

       

        public static void main(String[] arg)

        {

          /* If you see the following error message [Mechanism level: Could not load

           * configuration file c:\winnt\krb5.ini (The system cannot find the path

           * specified] it's because the JVM cannot locate your kerberos config file.

           * You have to provide the location of the file. For example, on Windows,

           * the MIT Kerberos client uses the config file: C\WINDOWS\krb5.ini:

           */

          // System.setProperty("java.security.krb5.conf","C:\\WINDOWS\\krb5.ini");

          System.setProperty("java.security.krb5.conf","/etc/krb5.conf");

       

          KerberosJdbcDemo kerberosDemo = new KerberosJdbcDemo();

          try

          {

            System.out.println("Attempt to connect with the default user:");

            kerberosDemo.connectWithDefaultUser();

          }

          catch (Exception e)

          {

            e.printStackTrace();

          }

          try

          {

            System.out.println("Attempt to connect with a specific user:");

            kerberosDemo.connectWithSpecificUser();

          }

          catch (Exception e)

          {

            e.printStackTrace();

          }

        }

       

       

        void connectWithDefaultUser() throws SQLException

        {

      OracleDriver driver = new OracleDriver();
      Properties prop = new Properties();

       

      prop.setProperty(OracleConnection.CONNECTION_PROPERTY_THIN_NET_AUTHENTICATION_SERVICES,
      "("+AnoServices.AUTHENTICATION_KERBEROS5+")");
      prop.setProperty(OracleConnection.CONNECTION_PROPERTY_THIN_NET_AUTHENTICATION_KRB5_MUTUAL,
      "true");

       

      /* If you get the following error [Unable to obtain Princpal Name for
      * authentication] although you know that you have the right TGT in your
      * credential cache, then it's probably because the JVM can't locate your
      * cache.
      *
      * Note that the default location on windows is "C:\Documents and Settings\krb5cc_username".
      * For Windows 7 default is "C:\Users\krb5cc_username"
      */

       

      // prop.setProperty(OracleConnection.CONNECTION_PROPERTY_THIN_NET_AUTHENTICATION_KRB5_CC_NAME,
      /*
      On linux:
      > which kinit
      /usr/kerberos/bin/kinit
      > ls -l /etc/krb5.conf
      lrwxrwxrwx 1 root  root   47 Jun 22 06:56 /etc/krb5.conf -> /home/Jdbc/Security/kerberos/krb5.conf

       

      > kinit client
      Password for client@US.ORACLE.COM:
      > klist
      Ticket cache: FILE:/tmp/krb5cc_5088
      Default principal: client@US.ORACLE.COM

       

      Valid starting Expires   Service principal
      11/02/06 09:25:11  11/02/06 19:25:11  krbtgt/US.ORACLE.COM@US.ORACLE.COM

       

       

      Kerberos 4 ticket cache: /tmp/tkt5088
      klist: You have no tickets cached
      */
      prop.setProperty(OracleConnection.CONNECTION_PROPERTY_THIN_NET_AUTHENTICATION_KRB5_CC_NAME,
      "/tmp/krb5cc_0");
      System.out.println("Entering Connection");
      Connection conn  = driver.connect(url,prop);
      System.out.println("Entering String auth");
      String auth = ((OracleConnection)conn).getAuthenticationAdaptorName();
      System.out.println("Entering auth toString");
      System.out.println("Authentication adaptor="+auth);
      System.out.println("Entering printUserName");
      printUserName(conn);
      conn.close();

        }

       

       

        void connectWithSpecificUser() throws Exception

        {

      Subject specificSubject = new Subject();

       

      // This first part isn't really meaningful to the sake of this demo. In
      // a real world scenario, you have a valid "specificSubject" Subject that
      // represents a web user that has valid Kerberos credentials.
      System.out.println("Entering Krb5LoginModule");
      Krb5LoginModule krb5Module = new Krb5LoginModule();
      HashMap sharedState = new HashMap();

      HashMap options = new HashMap();

      options.put("doNotPrompt","false");
      options.put("debug","true");
      options.put("useTicketCache","false");
      options.put("principal","jriffle@DEVEL.APU.EDU");

       

      System.out.println("Entering initialize");
      krb5Module.initialize(specificSubject,new KrbCallbackHandler(),sharedState,options);
      System.out.println("Entering login");
      boolean retLogin = krb5Module.login();
      System.out.println("Entering commit");
      krb5Module.commit();
      System.out.println("Specific Subjects: " + specificSubject.getPrincipals().toString());
      if(!retLogin)
      throw new Exception("Kerberos5 adaptor couldn't retrieve credentials (TGT) from the cache");

       

      // to use the TGT from the cache:
      // options.put("useTicketCache","true");
      // options.put("doNotPrompt","true");
      // options.put("ticketCache","C:\\Documents and Settings\\Jean de Lavarene\\krb5cc");
      // krb5Module.initialize(specificSubject,null,sharedState,options);

       

       

      // Now we have a valid Subject with Kerberos credentials. The second scenario
      // really starts here:
      // execute driver.connect(...) on behalf of the Subject 'specificSubject':
      System.out.println("Entering driver.connect");
      Connection conn =
      (Connection)Subject.doAs(specificSubject, new PrivilegedExceptionAction()
      {
      public Object run()
      {
      Connection con = null;
      Properties prop = new Properties();
      prop.setProperty(AnoServices.AUTHENTICATION_PROPERTY_SERVICES,
      "(" + AnoServices.AUTHENTICATION_KERBEROS5 + ")");
      try
      {
      System.out.println("Entering OracleDriver");
      OracleDriver driver = new OracleDriver();
      System.out.println("Entering OracleDriver connect");
      con = driver.connect(url, prop);

       

      } catch (Exception except)
      {
      System.out.println("Exception caused by OracleDriver connect");
      System.out.println("Message: " + except.getMessage());
      except.printStackTrace();
      }
      return con;
      }
      });

       

      System.out.println("Entering getAuthenticationAdaptorName");
      String auth = ((OracleConnection)conn).getAuthenticationAdaptorName();
      System.out.println("Authentication adaptor="+auth);
      printUserName(conn);
      conn.close();

        }

       

        void printUserName(Connection conn) throws SQLException

        {

      Statement stmt = null;
      try

       

      {
      stmt = conn.createStatement();
      ResultSet rs = stmt.executeQuery("select user from dual");
      while(rs.next())
      System.out.println("User is:"+rs.getString(1));
      rs.close();
      }
      finally
      {
      if(stmt != null)
      stmt.close();
      }

        }

      }

       

      class KrbCallbackHandler implements CallbackHandler

      {

      public void handle(Callback[] callbacks) throws IOException,

      UnsupportedCallbackException

      {

         for (int i = 0; i < callbacks.length; i++)

         {

      if (callbacks[i] instanceof PasswordCallback)
      {
      PasswordCallback pc = (PasswordCallback)callbacks[i];
      System.out.println("set password to '****'");
      pc.setPassword((new String("******")).toCharArray());
      } else
      {
      throw new UnsupportedCallbackException(callbacks[i],
      "Unrecognized Callback");
      }

         }

      }

      }

       

       

       

       

       

       

      NOTE The trace logs on the Oracle Sever 11gR2 indicate the following:

      Fatal NI connect error 12637, connecting to:

      (LOCAL=NO)

       

        VERSION INFORMATION:

              TNS for Linux: Version 11.2.0.3.0 - Production

              Oracle Bequeath NT Protocol Adapter for Linux: Version 11.2.0.3.0 - Production

              TCP/IP NT Protocol Adapter for Linux: Version 11.2.0.3.0 - Production

        Time: 18-NOV-2013 08:25:18

        Tracing to file: <E0>^^^W)<FF>^?

        Tns error struct:

          ns main err code: 12637

       

      TNS-12637: Packet receive failed

          ns secondary err code: 12532

          nt main err code: 0

          nt secondary err code: 0

          nt OS err code: 0

      opiodr aborting process unknown ospid (23251) as a result of ORA-609

      Mon Nov 18 08:25:20 2013

       

      and... ORA-609 resolves to some sort of network error:

      (I would be apt to agree that there is some sort of network error but this same error has been replicated on three different clients on different networks themselves)

      $ oerr ora 609

      00609, 00000, "could not attach to incoming connection"

      // *Cause:  Oracle process could not answer incoming connection

      // *Action: If the situation described in the next error on the stack

      //         can be corrected, do so; otherwise contact Oracle Support.

      $ oerr tns 12637

      12637, 00000, "Packet receive failed"

      // *Cause:  A process was unable to receive a packet from another process.

      //          Possible causes are:

      //          1. The other process was terminated.

      //          2. The machine on which the other process is running went down.

      //          3. Some other communications error occurred.

      // *Action: If the cause is not obvious, contact Oracle Customer Support.

       

       

      the ADMIN level trace for the PID show the following:

      ..... Shortened (I tried to cut out the part that looked like it mattered) ....

      2013-11-18 08:25:18.954910 : snauk5g_open_file:entry

      2013-11-18 08:25:18.955351 : snauk5k_lock_file:entry

      2013-11-18 08:25:18.955374 : snauk5k_lock_file:exit

      2013-11-18 08:25:18.955389 : snauk5g_open_file:exit

      2013-11-18 08:25:18.955427 : nauk5rz_validate:entry

      2013-11-18 08:25:18.955457 : nauk5rz_validate:exit

      2013-11-18 08:25:18.955485 : snauk5t_close_file:entry

      2013-11-18 08:25:18.955506 : snauk5k_lock_file:entry

      2013-11-18 08:25:18.955523 : snauk5k_lock_file:exit

      2013-11-18 08:25:18.955548 : snauk5t_close_file:exit

      2013-11-18 08:25:18.955567 : nauk5ra_rcinit:exit

      2013-11-18 08:25:18.955579 : nauk5lg_init_krb5:exit

      2013-11-18 08:25:18.955717 : nauk5lr_createpath:entry

      2013-11-18 08:25:18.955735 : snauk5h_gettmp:entry

      2013-11-18 08:25:18.955748 : snauk5h_gettmp:exit

      2013-11-18 08:25:18.955760 : nauk5lr_createpath:exit

      2013-11-18 08:25:18.955774 : nauk5ainit:CC pathname: /usr/tmp/98427.CC.2013-11-18 08:25:18.955787 : nauk5ainit:Service name: oracle.

      2013-11-18 08:25:18.955798 : nauk5ainit:exit

      2013-11-18 08:25:18.955813 : nau1saai_adapter_info:exit

      2013-11-18 08:25:18.955829 : nau_ctl:entry

      2013-11-18 08:25:18.955844 : nau_ctl:control function failed with error 12630

      2013-11-18 08:25:18.955858 : nau_ctl:exit

      2013-11-18 08:25:18.955872 : nau1saai_adapter_info:exit

      2013-11-18 08:25:18.955884 : nau_gse:exit

      2013-11-18 08:25:18.955896 : nau_sgl:exit

      2013-11-18 08:25:18.955908 : nau_gsai:entry

      2013-11-18 08:25:18.955922 : nauk5astatus:entry

      2013-11-18 08:25:18.955933 : nauk5astatus:exit

      2013-11-18 08:25:18.955950 : nacomsu:entry

      2013-11-18 08:25:18.955962 : nacomfsd:entry

      2013-11-18 08:25:18.955973 : nacomfsd:exit

      2013-11-18 08:25:18.955985 : nacomdp:entry

      2013-11-18 08:25:18.955997 : nacomdp:exit

      2013-11-18 08:25:18.956009 : nacomsu:exit

      2013-11-18 08:25:18.956021 : nacomsu:entry

      2013-11-18 08:25:18.956032 : nacomfsd:entry

      .... Continues onward ....

       

        • 1. Re: Kerberos and Oracle JDBC6: Got minus one from read call
          user4450806

          After a few more days of work I have some mixed success to report and more unanswered questions, but this may help anyone else who happens to be following along this thread.

           

          My goal was to get SQL Developer to perform the Kerberos protocol (Authentication from a KDC -- Active Directory in my case) on behalf of my user base. I am dealing with two servers that I have configured separately one of which I'm pretty sure is setup improperly and thus the "Got minus one from a read call" exception on the mis-configured server but I get "KrbException: EncryptedData is encrypted using keytype RC4 with HMAC but decryption key is of type NULL" when I tried against my "properly" configured server.

           

          For both Server & Client:

          1) Modify the /etc/krb5.conf file appropriately (C:\Windows\krb5.ini in Windows)

          Here's an example:

          [libdefaults]

              default_realm = MYREALM.COM

              forwardable  = true

              clockskew  = 300

           

          [realms]

              MYREALM.COM = {

                  kdc = dc1.myrealm.com

                  kdc = dc2.myrealm.com

                  kdc = dc3.myrealm.com

                  default_domain = myrealm.com

              }

          [domain_realm]

              myrealm.com = MYREALM.COM

              .myrealm.com = MYREALM.COM

           

           

          Server Configuration Looks like this (in brief).

          1) On Active Directory Domain Controller I create a user with any name that makes sense to me and is acceptable in AD.

          2) On the same DC I use the "ktpass" command-line tool to map the Kereberos principal "oracle/myserver.com@MYREALM" with the encryption type of RC4-HMAC (this one tends to be acceptably high encryption but it's preference-based) and simultaneously output the generated keytab to a file

          3) Securely copy the keytab file to the server and place it somewhere where the user that runs the Oracle Database service can access. Preferably where nobody else can access for security reasons.

          4) Create an appropriate $ORACLE_HOME/network/admin/sqlnet.ora file that looks kind of like this depending on your implementation:

          SQLNET.KERBEROS5_KEYTAB = /etc/ora.keytab

          SQLNET.AUTHENTICATION_SERVICES= (BEQ, TCPS, KERBEROS5)

          SQLNET.KERBEROS5_CONF = /etc/krb5.conf

          SQLNET.KERBEROS5_CONF_MIT = TRUE

          SQLNET.AUTHENTICATION_KERBEROS5_SERVICE = oracle

          ADR_BASE = /u01/app/oracle/

           

          Note regarding two of the settings:

          SQLNET.KERBEROS5_CONF_MIT is needed because without it it will be unable to read a Kerberos v5 configuration file (it assumes you're using krb.conf Kerberos v4)

          SQLNET.AUTHENTICATION_KERBEROS5_SERVICE must be set to the service name you used when you generated your Kerberos principal!

           

          Changes to sqlnet.ora are immediately applied to new connections. Although I have a sneaking suspicion a new keytab file may not take affect until some kind of listener or database restart.

          5) Update the Oracle Database's os_authent_prefix parameter to be blank (='') (you have to generate a pfile and install it as the new spfile which may require restarting the database -- I'm not a DBA so I can't speak for this process)

          By default os_authent_prefix = "OPS$" which means it tacks this onto the beginning of any principal that authenticates externally to the database. This can be annoying and possibly useless but you may choose to keep it.

           

          6) Create an externally identified schema with your realm name:

          CREATE USER user@MYREALM.COM IDENTIFIED EXTERNALLY;

          (Make sure to grant the user at least the ability to CONNECT and create a SESSION).

           

          NOTE if the user name is too long for Oracle supposedly (untested) this will let you map an external principal with a local user name that's shorter:

           

          CREATE USER user IDENTIFIED EXTERNALLY AS 'user@MYREALM.COM';

           

          SQL Developer Client Configuration Looks like this (in brief)

          1) Update the freaking OJDBC6 driver (apparently that has been my main problem!) Oracle Database 11g Release 2 JDBC Driver Downloads

          The version that ships with the current latest version of SQL Developer is:

          # java -cp .:ojdbc6-11.2.0.3.jar oracle.jdbc.OracleDriver

          Oracle 11.2.0.3.0 JDBC 4.0 compiled with JDK6 on Fri_Aug_26_08:19:15_PDT_2011

          #Default Connection Properties Resource

          #Thu Nov 21 09:10:41 PST 2013

           

          The version that I actually got to work was this:

          # java -cp .:ojdbc6-11.2.0.4.jar oracle.jdbc.OracleDriver

          Oracle 11.2.0.3.0 JDBC 4.0 compiled with JDK6 on Thu_Jul_11_15:43:23_PDT_2013

          #Default Connection Properties Resource

          #Thu Nov 21 09:11:28 PST 2013

           

          So copy the ojdbc6.jar file (11.2.0.4 or whatever is latest maybe?) and replace the file in sqldeveloper/jdbc/lib/ojdbc6.jar (just to be careful I also removed ojdbc6dms.jar or you could replace this one with the latest one as well).

           

          2) Change SQL Developer's settings in Tools > Preferences > Database > Advanced

          Config File (krb5.conf): /etc/krb5.conf (or C:\Windows\krb5.ini in Windows)

          Credential Cache: (I left this blank but there could be a use-case to point to /tmp/krb5cc_<User ID number> or C:\Users\<Username>\krb5cc_<Username> in order to retrieve the user's cached credentials)

          Tnsnames Directory: (I don't think this is required for Kerberos but it's sometimes helpful to have entries for your databases I use /etc/tnsnames.ora for a system)

           

          3) When you create a database connection check the "Kerberos Authentication" box and have users enter their realm's (Active Directory domain user name and password in my case) user name and password when they login to the database.

          NOTE: If you point at the Credential Cache in SQL Developer settings and the user's Kerberos credentials are cached when they login to their workstation you *should* be able to leave the user name and password blank and force SQL Developer to retrieve the user's principal when it negotiates with the database (I haven't gotten this to work yet-- I just get an unable to retrieve User Principal exception when attempting this even when settings are pointing at a valid cache).