Search in sources :

Example 1 with ISSPIClient

use of org.postgresql.sspi.ISSPIClient in project teiid by teiid.

the class ConnectionFactoryImpl method doAuthentication.

private void doAuthentication(PGStream pgStream, String host, String user, Properties info) throws IOException, SQLException {
    // Now get the response from the backend, either an error message
    // or an authentication request
    String password = PGProperty.PASSWORD.get(info);
    /* SSPI negotiation state, if used */
    ISSPIClient sspiClient = null;
    try {
        authloop: while (true) {
            int beresp = pgStream.receiveChar();
            switch(beresp) {
                case 'E':
                    // An error occurred, so pass the error message to the
                    // user.
                    // 
                    // The most common one to be thrown here is:
                    // "User authentication failed"
                    // 
                    int l_elen = pgStream.receiveInteger4();
                    if (l_elen > 30000) {
                        // server, so trigger fallback.
                        throw new UnsupportedProtocolException();
                    }
                    ServerErrorMessage errorMsg = new ServerErrorMessage(pgStream.receiveErrorString(l_elen - 4));
                    LOGGER.log(Level.FINEST, " <=BE ErrorMessage({0})", errorMsg);
                    throw new PSQLException(errorMsg);
                case 'R':
                    // Authentication request.
                    // Get the message length
                    int l_msgLen = pgStream.receiveInteger4();
                    // Get the type of request
                    int areq = pgStream.receiveInteger4();
                    // Process the request.
                    switch(areq) {
                        case AUTH_REQ_CRYPT:
                            {
                                byte[] salt = pgStream.receive(2);
                                if (LOGGER.isLoggable(Level.FINEST)) {
                                    LOGGER.log(Level.FINEST, " <=BE AuthenticationReqCrypt(salt=''{0}'')", new String(salt, "US-ASCII"));
                                }
                                if (password == null) {
                                    throw new PSQLException(GT.tr("The server requested password-based authentication, but no password was provided."), PSQLState.CONNECTION_REJECTED);
                                }
                                byte[] encodedResult = UnixCrypt.crypt(salt, password.getBytes("UTF-8"));
                                if (LOGGER.isLoggable(Level.FINEST)) {
                                    LOGGER.log(Level.FINEST, " FE=> Password(crypt=''{0}'')", new String(encodedResult, "US-ASCII"));
                                }
                                pgStream.sendChar('p');
                                pgStream.sendInteger4(4 + encodedResult.length + 1);
                                pgStream.send(encodedResult);
                                pgStream.sendChar(0);
                                pgStream.flush();
                                break;
                            }
                        case AUTH_REQ_MD5:
                            {
                                byte[] md5Salt = pgStream.receive(4);
                                if (LOGGER.isLoggable(Level.FINEST)) {
                                    LOGGER.log(Level.FINEST, " <=BE AuthenticationReqMD5(salt={0})", Utils.toHexString(md5Salt));
                                }
                                if (password == null) {
                                    throw new PSQLException(GT.tr("The server requested password-based authentication, but no password was provided."), PSQLState.CONNECTION_REJECTED);
                                }
                                byte[] digest = MD5Digest.encode(user.getBytes("UTF-8"), password.getBytes("UTF-8"), md5Salt);
                                if (LOGGER.isLoggable(Level.FINEST)) {
                                    LOGGER.log(Level.FINEST, " FE=> Password(md5digest={0})", new String(digest, "US-ASCII"));
                                }
                                pgStream.sendChar('p');
                                pgStream.sendInteger4(4 + digest.length + 1);
                                pgStream.send(digest);
                                pgStream.sendChar(0);
                                pgStream.flush();
                                break;
                            }
                        case AUTH_REQ_PASSWORD:
                            {
                                LOGGER.log(Level.FINEST, "<=BE AuthenticationReqPassword");
                                LOGGER.log(Level.FINEST, " FE=> Password(password=<not shown>)");
                                if (password == null) {
                                    throw new PSQLException(GT.tr("The server requested password-based authentication, but no password was provided."), PSQLState.CONNECTION_REJECTED);
                                }
                                byte[] encodedPassword = password.getBytes("UTF-8");
                                pgStream.sendChar('p');
                                pgStream.sendInteger4(4 + encodedPassword.length + 1);
                                pgStream.send(encodedPassword);
                                pgStream.sendChar(0);
                                pgStream.flush();
                                break;
                            }
                        case AUTH_REQ_GSS:
                        case AUTH_REQ_SSPI:
                            /*
                 * Use GSSAPI if requested on all platforms, via JSSE.
                 *
                 * For SSPI auth requests, if we're on Windows attempt native SSPI authentication if
                 * available, and if not disabled by setting a kerberosServerName. On other
                 * platforms, attempt JSSE GSSAPI negotiation with the SSPI server.
                 *
                 * Note that this is slightly different to libpq, which uses SSPI for GSSAPI where
                 * supported. We prefer to use the existing Java JSSE Kerberos support rather than
                 * going to native (via JNA) calls where possible, so that JSSE system properties
                 * etc continue to work normally.
                 *
                 * Note that while SSPI is often Kerberos-based there's no guarantee it will be; it
                 * may be NTLM or anything else. If the client responds to an SSPI request via
                 * GSSAPI and the other end isn't using Kerberos for SSPI then authentication will
                 * fail.
                 */
                            final String gsslib = PGProperty.GSS_LIB.get(info);
                            final boolean usespnego = PGProperty.USE_SPNEGO.getBoolean(info);
                            boolean useSSPI = false;
                            /*
                 * Use SSPI if we're in auto mode on windows and have a request for SSPI auth, or if
                 * it's forced. Otherwise use gssapi. If the user has specified a Kerberos server
                 * name we'll always use JSSE GSSAPI.
                 */
                            if (gsslib.equals("gssapi")) {
                                LOGGER.log(Level.FINE, "Using JSSE GSSAPI, param gsslib=gssapi");
                            } else if (areq == AUTH_REQ_GSS && !gsslib.equals("sspi")) {
                                LOGGER.log(Level.FINE, "Using JSSE GSSAPI, gssapi requested by server and gsslib=sspi not forced");
                            } else {
                                /* Determine if SSPI is supported by the client */
                                sspiClient = createSSPI(pgStream, PGProperty.SSPI_SERVICE_CLASS.get(info), /* Use negotiation for SSPI, or if explicitly requested for GSS */
                                areq == AUTH_REQ_SSPI || (areq == AUTH_REQ_GSS && usespnego));
                                useSSPI = sspiClient.isSSPISupported();
                                LOGGER.log(Level.FINE, "SSPI support detected: {0}", useSSPI);
                                if (!useSSPI) {
                                    /* No need to dispose() if no SSPI used */
                                    sspiClient = null;
                                    if (gsslib.equals("sspi")) {
                                        throw new PSQLException("SSPI forced with gsslib=sspi, but SSPI not available; set loglevel=2 for details", PSQLState.CONNECTION_UNABLE_TO_CONNECT);
                                    }
                                }
                                if (LOGGER.isLoggable(Level.FINE)) {
                                    LOGGER.log(Level.FINE, "Using SSPI: {0}, gsslib={1} and SSPI support detected", new Object[] { useSSPI, gsslib });
                                }
                            }
                            if (useSSPI) {
                                /* SSPI requested and detected as available */
                                sspiClient.startSSPI();
                            } else {
                                /* Use JGSS's GSSAPI for this request */
                                org.postgresql.gss.MakeGSS.authenticate(pgStream, host, user, password, PGProperty.JAAS_APPLICATION_NAME.get(info), PGProperty.KERBEROS_SERVER_NAME.get(info), usespnego);
                            }
                            break;
                        case AUTH_REQ_GSS_CONTINUE:
                            /*
                 * Only called for SSPI, as GSS is handled by an inner loop in MakeGSS.
                 */
                            sspiClient.continueSSPI(l_msgLen - 8);
                            break;
                        case AUTH_REQ_OK:
                            /* Cleanup after successful authentication */
                            LOGGER.log(Level.FINEST, " <=BE AuthenticationOk");
                            // We're done.
                            break authloop;
                        default:
                            LOGGER.log(Level.FINEST, " <=BE AuthenticationReq (unsupported type {0})", areq);
                            throw new PSQLException(GT.tr("The authentication type {0} is not supported. Check that you have configured the pg_hba.conf file to include the client''s IP address or subnet, and that it is using an authentication scheme supported by the driver.", areq), PSQLState.CONNECTION_REJECTED);
                    }
                    break;
                default:
                    throw new PSQLException(GT.tr("Protocol error.  Session setup failed."), PSQLState.PROTOCOL_VIOLATION);
            }
        }
    } finally {
        /* Cleanup after successful or failed authentication attempts */
        if (sspiClient != null) {
            try {
                sspiClient.dispose();
            } catch (RuntimeException ex) {
                LOGGER.log(Level.WARNING, "Unexpected error during SSPI context disposal", ex);
            }
        }
    }
}
Also used : ServerErrorMessage(org.postgresql.util.ServerErrorMessage) PSQLException(org.postgresql.util.PSQLException) ISSPIClient(org.postgresql.sspi.ISSPIClient)

Aggregations

ISSPIClient (org.postgresql.sspi.ISSPIClient)1 PSQLException (org.postgresql.util.PSQLException)1 ServerErrorMessage (org.postgresql.util.ServerErrorMessage)1