public static void authenticate( PGStream pgStream, String host, String user, String password, String jaasApplicationName, String kerberosServerName, Logger logger) throws IOException, SQLException { if (logger.logDebug()) logger.debug(" <=BE AuthenticationReqGSS"); Object result = null; if (jaasApplicationName == null) jaasApplicationName = "pgjdbc"; if (kerberosServerName == null) kerberosServerName = "postgres"; try { LoginContext lc = new LoginContext(jaasApplicationName, new GSSCallbackHandler(user, password)); lc.login(); Subject sub = lc.getSubject(); PrivilegedAction action = new GssAction(pgStream, host, user, password, kerberosServerName, logger); result = Subject.doAs(sub, action); } catch (Exception e) { throw new PSQLException(GT.tr("GSS Authentication failed"), PSQLState.CONNECTION_FAILURE, e); } if (result instanceof IOException) throw (IOException) result; else if (result instanceof SQLException) throw (SQLException) result; else if (result != null) throw new PSQLException( GT.tr("GSS Authentication failed"), PSQLState.CONNECTION_FAILURE, (Exception) result); }
/** * Preconditions: 1. xid is known to the RM or it's in prepared state * * <p>Implementation deficiency preconditions: 1. xid must be associated with this connection if * it's not in prepared state. * * <p>Postconditions: 1. Transaction is rolled back and disassociated from connection */ public void rollback(Xid xid) throws XAException { if (logger.logDebug()) debug("rolling back xid = " + xid); // We don't explicitly check precondition 1. try { if (currentXid != null && xid.equals(currentXid)) { state = STATE_IDLE; currentXid = null; conn.rollback(); conn.setAutoCommit(localAutoCommitMode); } else { String s = RecoveredXid.xidToString(xid); conn.setAutoCommit(true); Statement stmt = conn.createStatement(); try { stmt.executeUpdate("ROLLBACK PREPARED '" + s + "'"); } finally { stmt.close(); } } } catch (SQLException ex) { throw new PGXAException( GT.tr("Error rolling back prepared transaction"), ex, XAException.XAER_RMERR); } }
/** * Preconditions: 1. Flags is one of TMSUCCESS, TMFAIL, TMSUSPEND 2. xid != null 3. Connection is * associated with transaction xid * * <p>Implementation deficiency preconditions: 1. Flags is not TMSUSPEND * * <p>Postconditions: 1. connection is disassociated from the transaction. */ public void end(Xid xid, int flags) throws XAException { if (logger.logDebug()) debug("ending transaction xid = " + xid); // Check preconditions if (flags != XAResource.TMSUSPEND && flags != XAResource.TMFAIL && flags != XAResource.TMSUCCESS) throw new PGXAException(GT.tr("Invalid flags"), XAException.XAER_INVAL); if (xid == null) throw new PGXAException(GT.tr("xid must not be null"), XAException.XAER_INVAL); if (state != STATE_ACTIVE || !currentXid.equals(xid)) throw new PGXAException( GT.tr("tried to call end without corresponding start call"), XAException.XAER_PROTO); // Check implementation deficiency preconditions if (flags == XAResource.TMSUSPEND) throw new PGXAException(GT.tr("suspend/resume not implemented"), XAException.XAER_RMERR); // We ignore TMFAIL. It's just a hint to the RM. We could roll back immediately // if TMFAIL was given. // All clear. We don't have any real work to do. state = STATE_ENDED; }
public void commit(Xid xid, boolean onePhase) throws XAException { if (logger.logDebug()) debug("committing xid = " + xid + (onePhase ? " (one phase) " : " (two phase)")); if (xid == null) throw new PGXAException(GT.tr("xid must not be null"), XAException.XAER_INVAL); if (onePhase) commitOnePhase(xid); else commitPrepared(xid); }
private void sendStartupPacket(PGStream pgStream, String[][] params, Logger logger) throws IOException { if (logger.logDebug()) { String details = ""; for (int i = 0; i < params.length; ++i) { if (i != 0) details += ", "; details += params[i][0] + "=" + params[i][1]; } logger.debug(" FE=> StartupPacket(" + details + ")"); } /* * Precalculate message length and encode params. */ int length = 4 + 4; byte[][] encodedParams = new byte[params.length * 2][]; for (int i = 0; i < params.length; ++i) { encodedParams[i * 2] = params[i][0].getBytes("US-ASCII"); encodedParams[i * 2 + 1] = params[i][1].getBytes("US-ASCII"); length += encodedParams[i * 2].length + 1 + encodedParams[i * 2 + 1].length + 1; } length += 1; // Terminating \0 /* * Send the startup message. */ pgStream.SendInteger4(length); pgStream.SendInteger2(3); // protocol major pgStream.SendInteger2(0); // protocol minor for (int i = 0; i < encodedParams.length; ++i) { pgStream.Send(encodedParams[i]); pgStream.SendChar(0); } pgStream.SendChar(0); pgStream.flush(); }
private PGStream enableSSL(PGStream pgStream, boolean requireSSL, Properties info, Logger logger) throws IOException, SQLException { if (logger.logDebug()) logger.debug(" FE=> SSLRequest"); // Send SSL request packet pgStream.SendInteger4(8); pgStream.SendInteger2(1234); pgStream.SendInteger2(5679); pgStream.flush(); // Now get the response from the backend, one of N, E, S. int beresp = pgStream.ReceiveChar(); switch (beresp) { case 'E': if (logger.logDebug()) logger.debug(" <=BE SSLError"); // Server doesn't even know about the SSL handshake protocol if (requireSSL) throw new PSQLException( GT.tr("The server does not support SSL."), PSQLState.CONNECTION_FAILURE); // We have to reconnect to continue. pgStream.close(); return new PGStream(pgStream.getHost(), pgStream.getPort()); case 'N': if (logger.logDebug()) logger.debug(" <=BE SSLRefused"); // Server does not support ssl if (requireSSL) throw new PSQLException( GT.tr("The server does not support SSL."), PSQLState.CONNECTION_FAILURE); return pgStream; case 'S': if (logger.logDebug()) logger.debug(" <=BE SSLOk"); // Server supports ssl Driver.makeSSL(pgStream, info, logger); return pgStream; default: throw new PSQLException( GT.tr("An error occured while setting up the SSL connection."), PSQLState.CONNECTION_FAILURE); } }
/** * Preconditions: 1. flags must be one of TMNOFLAGS, TMRESUME or TMJOIN 2. xid != null 3. * connection must not be associated with a transaction 4. the TM hasn't seen the xid before * * <p>Implementation deficiency preconditions: 1. TMRESUME not supported. 2. if flags is TMJOIN, * we must be in ended state, and xid must be the current transaction 3. unless flags is TMJOIN, * previous transaction using the connection must be committed or prepared or rolled back * * <p>Postconditions: 1. Connection is associated with the transaction */ public void start(Xid xid, int flags) throws XAException { if (logger.logDebug()) debug("starting transaction xid = " + xid); // Check preconditions if (flags != XAResource.TMNOFLAGS && flags != XAResource.TMRESUME && flags != XAResource.TMJOIN) throw new PGXAException(GT.tr("Invalid flags"), XAException.XAER_INVAL); if (xid == null) throw new PGXAException(GT.tr("xid must not be null"), XAException.XAER_INVAL); if (state == STATE_ACTIVE) throw new PGXAException( GT.tr("Connection is busy with another transaction"), XAException.XAER_PROTO); // We can't check precondition 4 easily, so we don't. Duplicate xid will be catched in prepare // phase. // Check implementation deficiency preconditions if (flags == TMRESUME) throw new PGXAException(GT.tr("suspend/resume not implemented"), XAException.XAER_RMERR); // It's ok to join an ended transaction. WebLogic does that. if (flags == TMJOIN) { if (state != STATE_ENDED) throw new PGXAException( GT.tr("Transaction interleaving not implemented"), XAException.XAER_RMERR); if (!xid.equals(currentXid)) throw new PGXAException( GT.tr("Transaction interleaving not implemented"), XAException.XAER_RMERR); } else if (state == STATE_ENDED) throw new PGXAException( GT.tr("Transaction interleaving not implemented"), XAException.XAER_RMERR); try { localAutoCommitMode = conn.getAutoCommit(); conn.setAutoCommit(false); } catch (SQLException ex) { throw new PGXAException(GT.tr("Error disabling autocommit"), ex, XAException.XAER_RMERR); } // Preconditions are met, Associate connection with the transaction state = STATE_ACTIVE; currentXid = xid; }
/** ** XAConnection interface *** */ public Connection getConnection() throws SQLException { if (logger.logDebug()) debug("PGXAConnection.getConnection called"); Connection conn = super.getConnection(); // When we're outside an XA transaction, autocommit // is supposed to be true, per usual JDBC convention. // When an XA transaction is in progress, it should be // false. if (state == STATE_IDLE) conn.setAutoCommit(true); /* * Wrap the connection in a proxy to forbid application from * fiddling with transaction state directly during an XA transaction */ ConnectionHandler handler = new ConnectionHandler(conn); return (Connection) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {Connection.class, PGConnection.class}, handler); }
/** * Preconditions: 1. xid != null 2. xid is in ended state * * <p>Implementation deficiency preconditions: 1. xid was associated with this connection * * <p>Postconditions: 1. Transaction is prepared */ public int prepare(Xid xid) throws XAException { if (logger.logDebug()) debug("preparing transaction xid = " + xid); // Check preconditions if (!currentXid.equals(xid)) { throw new PGXAException( GT.tr( "Not implemented: Prepare must be issued using the same connection that started the transaction"), XAException.XAER_RMERR); } if (state != STATE_ENDED) throw new PGXAException(GT.tr("Prepare called before end"), XAException.XAER_INVAL); state = STATE_IDLE; currentXid = null; if (!conn.haveMinimumServerVersion("8.1")) throw new PGXAException( GT.tr("Server versions prior to 8.1 do not support two-phase commit."), XAException.XAER_RMERR); try { String s = RecoveredXid.xidToString(xid); Statement stmt = conn.createStatement(); try { stmt.executeUpdate("PREPARE TRANSACTION '" + s + "'"); } finally { stmt.close(); } conn.setAutoCommit(localAutoCommitMode); return XA_OK; } catch (SQLException ex) { throw new PGXAException(GT.tr("Error preparing transaction"), ex, XAException.XAER_RMERR); } }
public ProtocolConnection openConnectionImpl( String host, int port, String user, String database, Properties info, Logger logger) throws SQLException { // Extract interesting values from the info properties: // - the SSL setting boolean requireSSL = (info.getProperty("ssl") != null); boolean trySSL = requireSSL; // XXX temporary until we revisit the ssl property values // NOTE: To simplify this code, it is assumed that if we are // using the V3 protocol, then the database is at least 7.4. That // eliminates the need to check database versions and maintain // backward-compatible code here. // // Change by Chris Smith <*****@*****.**> if (logger.logDebug()) logger.debug("Trying to establish a protocol version 3 connection to " + host + ":" + port); if (!Driver.sslEnabled()) { if (requireSSL) throw new PSQLException( GT.tr("The driver does not support SSL."), PSQLState.CONNECTION_FAILURE); trySSL = false; } // // Establish a connection. // PGStream newStream = null; try { newStream = new PGStream(host, port); // Construct and send an ssl startup packet if requested. if (trySSL) newStream = enableSSL(newStream, requireSSL, info, logger); // Construct and send a startup packet. String[][] params = { {"user", user}, {"database", database}, {"client_encoding", "UNICODE"}, {"DateStyle", "ISO"}, {"extra_float_digits", "2"} }; sendStartupPacket(newStream, params, logger); // Do authentication (until AuthenticationOk). doAuthentication(newStream, user, info.getProperty("password"), logger); // Do final startup. ProtocolConnectionImpl protoConnection = new ProtocolConnectionImpl(newStream, user, database, info, logger); readStartupMessages(newStream, protoConnection, logger); // And we're done. return protoConnection; } catch (UnsupportedProtocolException upe) { // Swallow this and return null so ConnectionFactory tries the next protocol. if (logger.logDebug()) logger.debug("Protocol not supported, abandoning connection."); try { newStream.close(); } catch (IOException e) { } return null; } catch (ConnectException cex) { // Added by Peter Mount <*****@*****.**> // ConnectException is thrown when the connection cannot be made. // we trap this an return a more meaningful message for the end user throw new PSQLException( GT.tr( "Connection refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections."), PSQLState.CONNECTION_REJECTED, cex); } catch (IOException ioe) { if (newStream != null) { try { newStream.close(); } catch (IOException e) { } } throw new PSQLException( GT.tr("The connection attempt failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT, ioe); } catch (SQLException se) { if (newStream != null) { try { newStream.close(); } catch (IOException e) { } } throw se; } }
private void readStartupMessages( PGStream pgStream, ProtocolConnectionImpl protoConnection, Logger logger) throws IOException, SQLException { while (true) { int beresp = pgStream.ReceiveChar(); switch (beresp) { case 'Z': // Ready For Query; we're done. if (pgStream.ReceiveInteger4() != 5) throw new IOException("unexpected length of ReadyForQuery packet"); char tStatus = (char) pgStream.ReceiveChar(); if (logger.logDebug()) logger.debug(" <=BE ReadyForQuery(" + tStatus + ")"); // Update connection state. switch (tStatus) { case 'I': protoConnection.setTransactionState(ProtocolConnection.TRANSACTION_IDLE); break; case 'T': protoConnection.setTransactionState(ProtocolConnection.TRANSACTION_OPEN); break; case 'E': protoConnection.setTransactionState(ProtocolConnection.TRANSACTION_FAILED); break; default: // Huh? break; } return; case 'K': // BackendKeyData int l_msgLen = pgStream.ReceiveInteger4(); if (l_msgLen != 12) throw new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); int pid = pgStream.ReceiveInteger4(); int ckey = pgStream.ReceiveInteger4(); if (logger.logDebug()) logger.debug(" <=BE BackendKeyData(pid=" + pid + ",ckey=" + ckey + ")"); protoConnection.setBackendKeyData(pid, ckey); break; case 'E': // Error int l_elen = pgStream.ReceiveInteger4(); ServerErrorMessage l_errorMsg = new ServerErrorMessage(pgStream.ReceiveString(l_elen - 4), logger.getLogLevel()); if (logger.logDebug()) logger.debug(" <=BE ErrorMessage(" + l_errorMsg + ")"); throw new PSQLException(l_errorMsg); case 'N': // Warning int l_nlen = pgStream.ReceiveInteger4(); ServerErrorMessage l_warnMsg = new ServerErrorMessage(pgStream.ReceiveString(l_nlen - 4), logger.getLogLevel()); if (logger.logDebug()) logger.debug(" <=BE NoticeResponse(" + l_warnMsg + ")"); protoConnection.addWarning(new PSQLWarning(l_warnMsg)); break; case 'S': // ParameterStatus int l_len = pgStream.ReceiveInteger4(); String name = pgStream.ReceiveString(); String value = pgStream.ReceiveString(); if (logger.logDebug()) logger.debug(" <=BE ParameterStatus(" + name + " = " + value + ")"); if (name.equals("server_version")) protoConnection.setServerVersion(value); else if (name.equals("client_encoding")) { if (!value.equals("UNICODE")) throw new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); pgStream.setEncoding(Encoding.getDatabaseEncoding("UNICODE")); } else if (name.equals("standard_conforming_strings")) { if (value.equals("on")) protoConnection.setStandardConformingStrings(true); else if (value.equals("off")) protoConnection.setStandardConformingStrings(false); else throw new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); } break; default: if (logger.logDebug()) logger.debug("invalid message type=" + (char) beresp); throw new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); } } }
private void doAuthentication(PGStream pgStream, String user, String password, Logger logger) throws IOException, SQLException { // Now get the response from the backend, either an error message // or an authentication request while (true) { int beresp = pgStream.ReceiveChar(); switch (beresp) { case 'E': // An error occured, 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) { // if the error length is > than 30000 we assume this is really a v2 protocol // server, so trigger fallback. throw new UnsupportedProtocolException(); } ServerErrorMessage errorMsg = new ServerErrorMessage(pgStream.ReceiveString(l_elen - 4), logger.getLogLevel()); if (logger.logDebug()) logger.debug(" <=BE ErrorMessage(" + 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[] rst = new byte[2]; rst[0] = (byte) pgStream.ReceiveChar(); rst[1] = (byte) pgStream.ReceiveChar(); String salt = new String(rst, 0, 2, "US-ASCII"); if (logger.logDebug()) logger.debug(" <=BE AuthenticationReqCrypt(salt='" + salt + "')"); if (password == null) throw new PSQLException( GT.tr( "The server requested password-based authentication, but no password was provided."), PSQLState.CONNECTION_REJECTED); String result = UnixCrypt.crypt(salt, password); byte[] encodedResult = result.getBytes("US-ASCII"); if (logger.logDebug()) logger.debug(" FE=> Password(crypt='" + result + "')"); 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.logDebug()) { logger.debug( " <=BE AuthenticationReqMD5(salt=" + 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, password, md5Salt); if (logger.logDebug()) { logger.debug(" FE=> Password(md5digest=" + 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: { if (logger.logDebug()) { logger.debug(" <=BE AuthenticationReqPassword"); logger.debug(" 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("US-ASCII"); pgStream.SendChar('p'); pgStream.SendInteger4(4 + encodedPassword.length + 1); pgStream.Send(encodedPassword); pgStream.SendChar(0); pgStream.flush(); break; } case AUTH_REQ_OK: if (logger.logDebug()) logger.debug(" <=BE AuthenticationOk"); return; // We're done. default: if (logger.logDebug()) logger.debug(" <=BE AuthenticationReq (unsupported type " + ((int) 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.", new Integer(areq)), PSQLState.CONNECTION_REJECTED); } break; default: throw new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); } } }
private void debug(String s) { logger.debug("XAResource " + Integer.toHexString(this.hashCode()) + ": " + s); }
public Object run() { try { org.ietf.jgss.Oid desiredMechs[] = new org.ietf.jgss.Oid[1]; desiredMechs[0] = new org.ietf.jgss.Oid("1.2.840.113554.1.2.2"); GSSManager manager = GSSManager.getInstance(); GSSName clientName = manager.createName(user, GSSName.NT_USER_NAME); GSSCredential clientCreds = manager.createCredential(clientName, 8 * 3600, desiredMechs, GSSCredential.INITIATE_ONLY); GSSName serverName = manager.createName(kerberosServerName + "@" + host, GSSName.NT_HOSTBASED_SERVICE); GSSContext secContext = manager.createContext( serverName, desiredMechs[0], clientCreds, GSSContext.DEFAULT_LIFETIME); secContext.requestMutualAuth(true); byte inToken[] = new byte[0]; byte outToken[] = null; boolean established = false; while (!established) { outToken = secContext.initSecContext(inToken, 0, inToken.length); if (outToken != null) { if (logger.logDebug()) logger.debug(" FE=> Password(GSS Authentication Token)"); pgStream.SendChar('p'); pgStream.SendInteger4(4 + outToken.length); pgStream.Send(outToken); pgStream.flush(); } if (!secContext.isEstablished()) { int response = pgStream.ReceiveChar(); // Error if (response == 'E') { int l_elen = pgStream.ReceiveInteger4(); ServerErrorMessage l_errorMsg = new ServerErrorMessage(pgStream.ReceiveString(l_elen - 4), logger.getLogLevel()); if (logger.logDebug()) logger.debug(" <=BE ErrorMessage(" + l_errorMsg + ")"); return new PSQLException(l_errorMsg); } else if (response == 'R') { if (logger.logDebug()) logger.debug(" <=BE AuthenticationGSSContinue"); int len = pgStream.ReceiveInteger4(); int type = pgStream.ReceiveInteger4(); // should check type = 8 inToken = pgStream.Receive(len - 8); } else { // Unknown/unexpected message type. return new PSQLException( GT.tr("Protocol error. Session setup failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT); } } else { established = true; } } } catch (IOException e) { return e; } catch (GSSException gsse) { return new PSQLException( GT.tr("GSS Authentication failed"), PSQLState.CONNECTION_FAILURE, gsse); } return null; }