javajdbcsingle-sign-onhanakerberos-delegation

Connect to SAP HANA DB using jdbc and Kerberos Delegation


Is it possible to connect to SAP HANA DB from my java application using jdbc and Kerberos Delegation?

Now I can create jdbc connection to SAP HANA DB without input db login and password, using only windows login. For this I set Kerberos External ID for db user in SAP HANA Administration Console (user1@domain_name) and use property "NativeAuthentification=true" when I create jdbc connection. Then I login to Windows by user1 and run my application, and I can connect to SAP HANA DB and select data.

But I need login to Windows on client computer, run my client java application, connect to my application server, application server must connect to SAP HANA DB with permissions of connected user and select data, granted to this user.

In client java application I got kerberos token using waffle-jna library, then I use it to connect to my application server using Spring Security (it works), but I can not create jdbc connection to SAP HANA DB using this token. I can not use Kerberos Delegation.

Any one know something about Kerberos Delegation in SAP HANA DB via jdbc? Thanks.


Solution

  • I solved this problem using GSS API. Now i briefly describe settings and code, which works in my environment

    1. Environment

    Client java application run on Windows 7 (java SE version 8, swing), application server run on Windows 2012 r2 (java 8, tomcat or jetty, on glassfish did not work), sap hana db run on linux. Windows user, which will connect to hana db and Windows user, which run application server, is in one windows domain (active directory).

    2. Configuration windows domain

    Was created spn and .keytab file in windows domain controller

    3. Configuration hana

    There is good Guide "HowTo_HANA_SSO_Kerberos_v1.7.1.pdf" Was created db user in hana with external ID user@windowsdomain (account name from windows active directory) There is good python script for configure hana for kerberos and check configuration. We got this script via sap hana support site.

    4. Client configuration

    windows registry key allowtgtsessionkey must be set to true

    On client were files login.conf

    com.sun.security.jgss.login {
    com.sun.security.auth.module.Krb5LoginModule required
    useTicketCache=true
    doNotPrompt=true
    debug=true;
    };
    
    com.sun.security.jgss.initiate {
    com.sun.security.auth.module.Krb5LoginModule required
    useTicketCache=true 
    principal="saphanauser"
    debug=true;
    };
    

    and krb5.conf

    [libdefaults]
        default_realm = DOMAINNAME.NEW
        forwardable = true
        clockskew = 3000
        default_tkt_enctypes = aes256-cts aes128-cts rc4-hmac
        default_tgs_enctypes = aes256-cts aes128-cts rc4-hmac
        permitted_enctypes  = aes256-cts aes128-cts rc4-hmac
    [realms]
            DOMAINNAME.NEW = {
                    kdc = KDCSERVERNAME
            default_domain = DOMAINNAME.NEW
            }
    [domain_realm]
            .domainname.new = DOMAINNAME.NEW
            domainname.new = DOMAINNAME.NEW
    

    5. Server configuration

    On server were files

    login.conf

    com.sun.security.jgss.login {
    com.sun.security.auth.module.Krb5LoginModule required
    useTicketCache=true
    doNotPrompt=true
    debug=true;
    };
    
    com.sun.security.jgss.initiate {
    com.sun.security.auth.module.Krb5LoginModule required
    useTicketCache=true 
    debug=true;
    };
    
    com.sun.security.jgss.accept {
    com.sun.security.auth.module.Krb5LoginModule required
    storeKey=true
    keyTab="C:/krb/keytab/krb5_hdb.keytab"
    useKeyTab=true
    realm="DOMAINNAME.NEW"
    principal="HDB/LINUX.DOMAINNAME.NEW"
    isInitiator=false
    debug=true;
    };
    

    and krb5.conf

    [libdefaults]
        default_realm = DOMAINNAME.NEW
        forwardable = true
        default_tkt_enctypes = aes256-cts aes128-cts rc4-hmac
        default_tgs_enctypes = aes256-cts aes128-cts rc4-hmac
        permitted_enctypes  = aes256-cts aes128-cts rc4-hmac
    [realms]
            DOMAINNAME.NEW = {
                    kdc = KDCSERVERNAME
            default_domain = DOMAINNAME.NEW
            }
    
    [domain_realm]
            .domainname.new = DOMAINNAME.NEW
            domainname.new = DOMAINNAME.NEW
    

    6. Client code

    Getting token for send it to application server

    public static byte[] getTokenGss() throws GSSException {
    
            String spnName = "spn_name";
    
            String oidValue= "1.2.840.113554.1.2.2"; // KERBEROS_MECH_OID
            String userLogin = System.getProperty("user.name");
            Oid mechOid = new Oid(oidValue);
    
            System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
    
            Path directoryConf = "C:\\client\\krb";
            String pathToGssConfigFile = directoryConf.resolve("login.conf").toString();
            System.setProperty("java.security.auth.login.config", pathToGssConfigFile);
            String pathToKrb5ConfigFile = directoryConf.resolve("krb5.conf").toString();
            System.setProperty("java.security.krb5.conf", pathToKrb5ConfigFile);
    
            System.setProperty("sun.security.krb5.debug", "true");
    
            GSSManager manager = GSSManager.getInstance();
            GSSName gssUserName = manager.createName(userLogin, GSSName.NT_USER_NAME, mechOid);
    
            logger.debug("before createCredential");
            GSSCredential clientGssCreds =
                    manager.createCredential(gssUserName.canonicalize(mechOid), GSSCredential.INDEFINITE_LIFETIME, mechOid,
                                             GSSCredential.INITIATE_ONLY);
    
            byte[] token = new byte[0];
    
            // create target server SPN
            GSSName gssServerName = manager.createName(spnName, GSSName.NT_USER_NAME);
            logger.debug("before createContext");
    
            GSSContext clientContext = manager.createContext(gssServerName.canonicalize(mechOid), mechOid, clientGssCreds,
                                                             GSSContext.DEFAULT_LIFETIME);
    
            // optional enable GSS credential delegation
            clientContext.requestCredDeleg(true);
            token = clientContext.initSecContext(token, 0, token.length);
            return token;
    
        }
    

    7. Server code

    Creating hibernate EntityManagerFactory using token from client

    private EntityManagerFactory createEntNamagerFactoryViaKerberos(byte[] inToken)
                throws Exception {
    
            System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
            System.setProperty("java.security.auth.login.config", "C:\\krb\\gsslogin\\login.conf");
            System.setProperty("java.security.krb5.conf", "C:\\krb\\krb5.conf");
    
            Oid mechOid = new Oid("1.2.840.113554.1.2.2");
            GSSManager manager = GSSManager.getInstance();
    
            //first obtain it's own credentials...
            GSSCredential myCred =
                    manager.createCredential(null, GSSCredential.DEFAULT_LIFETIME, mechOid, GSSCredential.ACCEPT_ONLY);
    
            //...and create a context for this credentials...
            GSSContext context = manager.createContext(myCred);
    
            //...then use that context to authenticate the calling peer by reading his token
            byte[] tokenForPeer = context.acceptSecContext(inToken, 0, inToken.length);
    
            if (!context.isEstablished()) {
                throw new Exception("Context not established!");
            }
    
            //...then obtain information from the context
            logger.debug("Clientcipal is " + context.getSrcName());
            logger.debug("Servercipal is " + context.getTargName());
    
            if (context.getCredDelegState()) {
                logger.debug("Then is delegatable.");
            } else {
                logger.debug("Then is NOT delegatable");
            }
    
            GSSCredential clientCr = context.getDelegCred();
            Subject s = GSSUtil.createSubject(clientCr.getName(), clientCr);
            KerberosActionCreateEmf kerberosAction = new KerberosActionCreateEmf();
            kerberosAction.unicalEntFactoryName = "kerb" + System.currentTimeMillis();
            Subject.doAs(s, kerberosAction);
            EntityManagerFactory emf = kerberosAction.emf;
            return emf;
    
        }
    
    
    
        class KerberosActionCreateEmf implements PrivilegedExceptionAction {
    
            public EntityManagerFactory emf;
    
            public String modelName;
            public String unicalEntFactoryName;
    
            @Override
            public Object run() throws Exception {
    
                Properties properties = new Properties();
                properties.setProperty("javax.persistence.jdbc.driver", "com.sap.db.jdbc.Driver");
                properties.setProperty("hibernate.connection.url", "jdbc:sap://10.0.0.121:31015");
                properties.setProperty("hibernate.dialect", "org.hibernate.dialect.HANAColumnStoreDialect");
    
                // do not use login and pass, use kerberos delegation (token)
                //properties.setProperty("hibernate.connection.username", login);
                //properties.setProperty("hibernate.connection.password", pass);
    
                properties.setProperty("hibernate.default_schema", "default_schema");
                properties.setProperty("hibernate.show_sql", model_manager_hibernate_show_sql);
                properties.setProperty("hibernate.ejb.entitymanager_factory_name", unicalEntFactoryName);
                properties.setProperty("hibernate.cache.use_query_cache", "false");
                properties.setProperty("hibernate.query.plan_cache_max_soft_references", "1");
                properties.setProperty("hibernate.query.plan_cache_max_strong_references", "1");
    
                properties.setProperty("hibernate.hikari.minimumIdle", "3");
                properties.setProperty("hibernate.hikari.maximumPoolSize", "20");
                properties.setProperty("hibernate.hikari.idleTimeout", "600000");
                properties.setProperty("hibernate.hikari.AutoCommit", "false");
                properties.setProperty("hibernate.hikari.poolName", unicalEntFactoryName);
                properties.setProperty("hibernate.hikari.connectionTimeout", "1800000");
    
                EntityManagerFactory newEntityManagerFactory =
                        Persistence.createEntityManagerFactory("persistenceUnitName", properties);
    
                emf = newEntityManagerFactory;
                return null;
            }
        }
    

    8. useful links

    http://thejavamonkey.blogspot.com/2008/04/clientserver-hello-world-in-kerberos.html https://dmdaa.wordpress.com/category/java/jgss/ https://dmdaa.wordpress.com/2010/03/13/kerberos-setup-and-jaas-configuration-for-running-sun-jgss-tutorial-against-ad/ http://cr.openjdk.java.net/~weijun/special/krb5winguide-2/raw_files/new/kwin