Documentation
¶
Index ¶
- Constants
- Variables
- func GenerateO5LogonVerifier(password string) ([]byte, []byte, error)
- func IsExecSQL(ttcPayload []byte) bool
- func IsPiggybackClose(ttcPayload []byte) bool
- func IsPiggybackExecSQL(ttcPayload []byte) bool
- type ConnectDescriptor
- type O5LogonServer
- func (s *O5LogonServer) CloneForCandidate(salt, verifierKey []byte, encChallengeHex string) (*O5LogonServer, error)
- func (s *O5LogonServer) CustomHashEnabled() bool
- func (s *O5LogonServer) DecryptPassword(encClientSessKey, encPassword string) (string, error)
- func (s *O5LogonServer) DeriveCombinedKey(encClientSessKey string) error
- func (s *O5LogonServer) EnableCustomHash()
- func (s *O5LogonServer) GenerateChallenge() (string, string, error)
- func (s *O5LogonServer) PBKDF2ChkSalt() string
- func (s *O5LogonServer) PBKDF2SderCount() int
- func (s *O5LogonServer) PBKDF2VgenCount() int
- func (s *O5LogonServer) UseVerifier18453(salt, verifierKey []byte)
- func (s *O5LogonServer) VerifierType() int
- type OALL8Result
- type OFETCHResult
- type QueryResultV2
- type Server
- type TNSPacket
- type TNSPacketType
- type TTCFunctionCode
- type TTCResponse
Constants ¶
const ( ORA00028 uint16 = 28 // ORA-00028: your session has been killed (used for mid-stream grant-limit termination) ORA01017 uint16 = 1017 // ORA-01017: invalid username/password; logon denied ORA01045 uint16 = 1045 // ORA-01045: user lacks privilege; logon denied (used for "no active grant") ORA12505 uint16 = 12505 // TNS:listener does not currently know of SID ORA12514 uint16 = 12514 // TNS:listener does not currently know of service ORA12520 uint16 = 12520 // TNS:listener could not find available handler ORA12535 uint16 = 12535 // TNS:operation timed out ORA12541 uint16 = 12541 // TNS:no listener )
ORA error codes for TNS Refuse packets.
const ( PiggybackSubClose byte = 0x09 // Close cursor PiggybackSubExecSQL byte = 0x5e // Execute with SQL (OALL8 equivalent) PiggybackSubAuth1 byte = 0x76 // AUTH Phase 1 PiggybackSubAuth2 byte = 0x73 // AUTH Phase 2 )
Piggyback sub-operation codes (byte 1 when func=0x03).
const ( OracleTypeVARCHAR2 uint8 = 1 OracleTypeNUMBER uint8 = 2 OracleTypeDATE uint8 = 12 OracleTypeRAW uint8 = 23 OracleTypeCHAR uint8 = 96 OracleTypeBINFLOAT uint8 = 100 OracleTypeBINDOUBLE uint8 = 101 OracleTypeCLOB uint8 = 112 OracleTypeBLOB uint8 = 113 OracleTypeTIMESTAMP uint8 = 180 OracleTypeTIMESTAMPTZ uint8 = 181 OracleTypeTIMESTAMPLTZ uint8 = 231 )
Oracle data type codes.
const ( // VerifierType6949 selects the SHA-1 / MD5-XOR (or SHA-1 / PBKDF2 customHash) // derivation paths. VerifierType6949 = 6949 // VerifierType18453 selects the modern PBKDF2 / HMAC-SHA512 path. VerifierType18453 = 18453 )
Oracle verifier types observed in AUTH_VFR_DATA's flag field.
Variables ¶
var ( ErrAuthSvrResponseKeyNotFound = errors.New("AUTH_SVR_RESPONSE key not found in AUTH OK") ErrAuthSvrResponseTruncated = errors.New("AUTH_SVR_RESPONSE value truncated") ErrAuthSvrResponseNotHex = errors.New("AUTH_SVR_RESPONSE value not uppercase hex") ErrAuthSvrResponseBadLength = errors.New("AUTH_SVR_RESPONSE rebuilt with wrong length") )
AUTH_SVR_RESPONSE patcher errors.
var ( // ErrEmptyUsername indicates an AUTH message with no username. ErrEmptyUsername = errors.New("empty username in AUTH message") // ErrNoActiveGrant indicates no active grant exists for the user/database pair. ErrNoActiveGrant = errors.New("no active grant for this user/database") // ErrQueryLimitExceed indicates the grant's query count quota has been reached. ErrQueryLimitExceed = errors.New("query limit exceeded") // ErrDataLimitExceed indicates the grant's data transfer quota has been reached. ErrDataLimitExceed = errors.New("data transfer limit exceeded") // ErrServerNotFound indicates the requested database was not found in the store. ErrServerNotFound = errors.New("database not found") // ErrUserNotFound indicates the requested user was not found in the store. ErrUserNotFound = errors.New("user not found") // ErrAmbiguousServiceName indicates a service name shared by several dbbat // databases could not be narrowed to one (the user holds grants on more // than one of them, or they point at different upstream addresses). ErrAmbiguousServiceName = errors.New("service name matches multiple dbbat databases") // ErrTTCPayloadTooShort indicates a TTC payload is shorter than expected. ErrTTCPayloadTooShort = errors.New("TTC payload too short") // ErrNotDataPacket indicates a non-Data TNS packet was received where Data was expected. ErrNotDataPacket = errors.New("expected TNS Data packet") // ErrAuthFailed indicates upstream authentication did not succeed. ErrAuthFailed = errors.New("upstream authentication failed") // ErrExpectedConnectPacket indicates a non-Connect packet was received at session start. ErrExpectedConnectPacket = errors.New("expected TNS Connect packet") // ErrNoServiceName indicates the connect descriptor lacks a SERVICE_NAME. ErrNoServiceName = errors.New("no SERVICE_NAME in connect descriptor") // ErrUpstreamRefused indicates the upstream Oracle server refused the connection. ErrUpstreamRefused = errors.New("upstream refused connection") // ErrClientAuthFailed indicates client authentication to dbbat failed. ErrClientAuthFailed = errors.New("client authentication failed") // ErrNoO5LogonVerifier indicates no API key with O5LOGON verifier was found. ErrNoO5LogonVerifier = errors.New("no API key with O5LOGON verifier found") // ErrAPIKeyOwnerMismatch indicates the API key does not belong to the authenticated user. ErrAPIKeyOwnerMismatchOracle = errors.New("API key does not belong to user") // ErrAPIKeyVerification indicates no verifier candidate decrypted the // client's AUTH_PASSWORD to a valid API key (wrong password). ErrAPIKeyVerification = errors.New("API key verification failed") // ErrColumnDefTooShort indicates a column definition is shorter than expected. ErrColumnDefTooShort = errors.New("column definition too short") // ErrColumnNameTruncated indicates a column name extends beyond the payload. ErrColumnNameTruncated = errors.New("column name exceeds payload") // ErrNoTypeCode indicates a column definition is missing the type code byte. ErrNoTypeCode = errors.New("column definition missing type code") // ErrEmptyRowData indicates an empty row data payload. ErrEmptyRowData = errors.New("empty row data") // ErrRowValueTruncated indicates a row value extends beyond the payload. ErrRowValueTruncated = errors.New("row value exceeds payload") // ErrInvalidFloatLength indicates float data has an unexpected length. ErrInvalidFloatLength = errors.New("invalid float data length") // O5LOGON and TTC auth errors. ErrDecryptedPasswordTooShort = errors.New("decrypted password too short") ErrNoCombinedKeyCandidate = errors.New("no combined-key candidate available") ErrCiphertextNotAligned = errors.New("ciphertext is not a multiple of block size") ErrInvalidPadding = errors.New("invalid PKCS7 padding") ErrAuthPhase1TooShort = errors.New("AUTH Phase 1 payload too short") ErrAuthPhase1NoData = errors.New("AUTH Phase 1: no data after sub-op") ErrAuthPhase1BadUsername = errors.New("AUTH Phase 1: invalid username length") ErrAuthPhase2TooShort = errors.New("AUTH Phase 2 payload too short") ErrAuthPhase2MissingSessKey = errors.New("AUTH Phase 2: missing AUTH_SESSKEY") ErrAuthPhase2MissingPassword = errors.New("AUTH Phase 2: missing AUTH_PASSWORD") ErrUnexpectedPacketType = errors.New("unexpected TNS packet type") ErrMaxResendExceeded = errors.New("exceeded maximum resend attempts") ErrUpstreamTooManyRedirects = errors.New("upstream: too many redirects") ErrRedirectMissingHostPort = errors.New("redirect: could not extract HOST/PORT from descriptor") )
Oracle proxy errors.
var ( ErrTNSHeaderTooShort = errors.New("TNS header too short: need at least 8 bytes") ErrTNSPacketTooLarge = errors.New("TNS packet length exceeds maximum") )
TNS errors.
var ( ErrEmptySQL = errors.New("OALL8 message contains empty SQL") ErrOALL8TooShort = errors.New("OALL8 payload too short") ErrOFETCHTooShort = errors.New("OFETCH payload too short") ErrSQLLengthInvalid = errors.New("OALL8 SQL length exceeds payload") )
Decoding errors.
var ( ErrInvalidDateLength = errors.New("oracle DATE requires exactly 7 bytes") ErrInvalidTimestampLength = errors.New("oracle TIMESTAMP requires at least 11 bytes") ErrInvalidNumberData = errors.New("oracle NUMBER data is empty") )
Type decoding errors.
var ( ErrUpstreamConnNotSet = errors.New("upstream connection not set before AUTH") ErrUpstreamAuthNotBegun = errors.New("upstream AUTH Phase 2 requested before Phase 1") ErrPhase1MissingSessKey = errors.New("AUTH Phase 1 response: missing AUTH_SESSKEY") ErrPhase1MissingVerifierData = errors.New("AUTH Phase 1 response: missing AUTH_VFR_DATA") ErrInvalidHex = errors.New("invalid hex character") ErrInvalidHexLength = errors.New("invalid hex string length") ErrUpstreamAuthPhase1Rejected = errors.New("upstream AUTH Phase 1 rejected") ErrUpstreamAuthPhase2Rejected = errors.New("upstream AUTH Phase 2 rejected") )
errors specific to the upstream AUTH client.
var ErrAuthPhase1Rewrite = errors.New("AUTH Phase 1 rewrite failed")
ErrAuthPhase1Rewrite signals a Phase 1 body that does not match the expected piggyback/sub-op/userLen/mode/magic/username layout.
var ErrAuthPhase2Rewrite = errors.New("AUTH Phase 2 rewrite failed")
ErrAuthPhase2Rewrite signals a Phase 2 body that does not match the expected piggyback / has-username / user_id_len / mode / pair_count layout or whose KV pairs cannot be parsed.
var ErrUnsupportedVerifier = errors.New("unsupported Oracle verifier type")
ErrUnsupportedVerifier indicates an Oracle verifier type dbbat does not implement.
Functions ¶
func GenerateO5LogonVerifier ¶ added in v0.5.0
GenerateO5LogonVerifier creates salt + verifier key from a plaintext password. Called at API key creation time; the results are stored in the database. Delegates to the crypto package for the core computation.
func IsExecSQL ¶ added in v0.5.0
IsExecSQL checks if a func=0x11 payload is an execute-with-SQL message rather than a plain OFETCH. Different clients use different sub-ops.
func IsPiggybackClose ¶
IsPiggybackClose checks if a piggyback payload is a close cursor message.
func IsPiggybackExecSQL ¶
IsPiggybackExecSQL checks if a piggyback payload is an execute-with-SQL message.
Types ¶
type ConnectDescriptor ¶
type ConnectDescriptor struct {
ServiceName string
SID string
Host string
Port int
Program string // From CID
OSUser string // From CID
}
ConnectDescriptor holds metadata parsed from an Oracle connect descriptor.
type O5LogonServer ¶ added in v0.5.0
type O5LogonServer struct {
// CombinedKey is set by DecryptPassword to the negotiated combined key
// — either MD5/XOR (legacy) or the PBKDF2-customHash derivation. It is
// the AES key the client expects for AUTH_SVR_RESPONSE in the AUTH OK
// forwarded back.
CombinedKey []byte
// contains filtered or unexported fields
}
O5LogonServer implements server-side O5LOGON authentication. This is the mirror of the client-side implementation in go-ora.
func NewO5LogonServer ¶ added in v0.5.0
func NewO5LogonServer(salt, verifierKey []byte) *O5LogonServer
NewO5LogonServer creates a server from stored verifier data (legacy 6949).
func (*O5LogonServer) CloneForCandidate ¶ added in v0.15.4
func (s *O5LogonServer) CloneForCandidate(salt, verifierKey []byte, encChallengeHex string) (*O5LogonServer, error)
CloneForCandidate returns an O5LogonServer configured like s (verifier type, customHash mode, per-session PBKDF2 salt and iteration counts) but keyed with ANOTHER candidate API key's verifier material. Its server session key is recovered by decrypting the challenge ciphertext that was actually sent on the wire (encrypted under the primary candidate's verifier) with the new candidate's verifier — exactly what a client holding that candidate key computed client-side. This makes multi-key Oracle login possible when all of a user's keys share the user's salts: the challenge commits to one salt and one ciphertext, and Phase 2 tries each candidate's view of it.
func (*O5LogonServer) CustomHashEnabled ¶ added in v0.9.0
func (s *O5LogonServer) CustomHashEnabled() bool
CustomHashEnabled reports whether the server is in customHash mode. Used by the challenge builder to decide whether to include AUTH_PBKDF2_* fields.
func (*O5LogonServer) DecryptPassword ¶ added in v0.5.0
func (s *O5LogonServer) DecryptPassword(encClientSessKey, encPassword string) (string, error)
DecryptPassword extracts the plaintext password from the client's AUTH Phase 2. The client encrypts: random_prefix(16 bytes) + password using AES-192-CBC with a combined key derived from both session keys.
In customHash mode the server attempts both the customHash combined-key derivation (matching go-ora and JDBC, where the server-advertised customHash bit and the AUTH_PBKDF2_* fields are honored) and the legacy MD5/XOR path (matching python-oracledb thin, which always picks legacy when the session_key is 48 bytes regardless of caps[4]&0x20). The first candidate that yields a printable plaintext password wins. Out of customHash mode, only the legacy path is tried.
Side-effect: on success, the negotiated combined key is stored on the receiver as CombinedKey so callers can reuse it (e.g. to encrypt AUTH_SVR_RESPONSE in the AUTH OK payload forwarded to the client).
func (*O5LogonServer) DeriveCombinedKey ¶ added in v0.9.0
func (s *O5LogonServer) DeriveCombinedKey(encClientSessKey string) error
DeriveCombinedKey computes the combined-key the client used (the first candidate per combinedKeyCandidates) and stores it on the receiver, without requiring an AUTH_PASSWORD plaintext to verify against. Used by the empty-AUTH_PASSWORD branch (SQLcl / JDBC thin 23c+) where the proxy has no way to validate by password decryption — but still needs the combined key so it can re-encrypt AUTH_SVR_RESPONSE before forwarding the upstream's AUTH OK to the client.
In customHash mode the customHash PBKDF2 derivation is preferred (matches JDBC and go-ora). Otherwise the legacy MD5/XOR combined key is used.
func (*O5LogonServer) EnableCustomHash ¶ added in v0.9.0
func (s *O5LogonServer) EnableCustomHash()
EnableCustomHash switches the server into customHash mode. GenerateChallenge will then advertise per-session AUTH_PBKDF2_CSK_SALT / VGEN_COUNT / SDER_COUNT fields and DecryptPassword will use the PBKDF2-customHash combined-key derivation. Used when the upstream's Set Protocol response had caps[4]&0x20 set — keeping the bit on the client side avoids the verifier-6949 downgrade that real Oracle emits when the client signals "no customHash support".
func (*O5LogonServer) GenerateChallenge ¶ added in v0.5.0
func (s *O5LogonServer) GenerateChallenge() (string, string, error)
GenerateChallenge produces the AUTH_SESSKEY and AUTH_VFR_DATA for the client. Returns hex-encoded encrypted server session key and auth verifier data.
In customHash mode it also generates a fresh AUTH_PBKDF2_CSK_SALT (accessible via PBKDF2ChkSalt) so the caller can include the matching KV fields in the Phase 1 challenge sent on the wire.
func (*O5LogonServer) PBKDF2ChkSalt ¶ added in v0.9.0
func (s *O5LogonServer) PBKDF2ChkSalt() string
PBKDF2ChkSalt returns the per-session salt as an uppercase hex string. Empty until GenerateChallenge has run in customHash mode.
func (*O5LogonServer) PBKDF2SderCount ¶ added in v0.9.0
func (s *O5LogonServer) PBKDF2SderCount() int
PBKDF2SderCount returns the session-key-derivation iteration count advertised in the challenge as AUTH_PBKDF2_SDER_COUNT.
func (*O5LogonServer) PBKDF2VgenCount ¶ added in v0.9.0
func (s *O5LogonServer) PBKDF2VgenCount() int
PBKDF2VgenCount returns the verifier-generation iteration count advertised in the challenge as AUTH_PBKDF2_VGEN_COUNT.
func (*O5LogonServer) UseVerifier18453 ¶ added in v0.13.0
func (s *O5LogonServer) UseVerifier18453(salt, verifierKey []byte)
UseVerifier18453 switches the server to the modern verifier-18453 (12c PBKDF2 / HMAC-SHA512) using the supplied 16-byte salt and 32-byte verifier key. This implies customHash mode (the challenge carries AUTH_PBKDF2_* fields and the combined key uses the PBKDF2 derivation). Use it when the client negotiates customHash (Oracle 12c+/23ai) so python-oracledb thin, JDBC thin / SQLcl, and sqlplus — which reject the legacy 6949 challenge — can authenticate.
func (*O5LogonServer) VerifierType ¶ added in v0.13.0
func (s *O5LogonServer) VerifierType() int
VerifierType returns the O5LOGON verifier type the server is configured for (6949 or 18453), used by the challenge builder for the AUTH_VFR_DATA flag.
type OALL8Result ¶
OALL8Result contains the decoded fields from an OALL8 (parse+execute) message.
func (*OALL8Result) IsPLSQL ¶
func (r *OALL8Result) IsPLSQL() bool
IsPLSQL returns true if the SQL text is a PL/SQL block.
type OFETCHResult ¶
OFETCHResult contains the decoded fields from an OFETCH message.
type QueryResultV2 ¶
type QueryResultV2 struct {
Columns []string
// ColumnTypes holds the TTC type code per column when it is known from the
// describe records (nil otherwise — values are then decoded heuristically).
ColumnTypes []int
Rows [][]string
NoData bool // true if ORA-01403 (normal end-of-data)
}
QueryResultV2 contains parsed data from a v315+ TTC QueryResult (func=0x10).
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the Oracle proxy server.
func NewServer ¶
func NewServer( dataStore *store.Store, encryptionKey []byte, authCache *cache.AuthCache, queryStorage config.QueryStorageConfig, dumpConfig config.DumpConfig, logger *slog.Logger, ) *Server
NewServer creates a new Oracle proxy server.
type TNSPacket ¶
type TNSPacket struct {
Type TNSPacketType
Flags byte
Length uint16
Payload []byte
Raw []byte // Original raw bytes (for forwarding without re-encoding)
}
TNSPacket represents a single TNS protocol packet.
type TNSPacketType ¶
type TNSPacketType byte
TNSPacketType represents a TNS packet type code.
const ( TNSPacketTypeConnect TNSPacketType = 1 TNSPacketTypeAccept TNSPacketType = 2 TNSPacketTypeRefuse TNSPacketType = 3 TNSPacketTypeRedirect TNSPacketType = 4 TNSPacketTypeMarker TNSPacketType = 5 TNSPacketTypeData TNSPacketType = 6 TNSPacketTypeResend TNSPacketType = 11 TNSPacketTypeControl TNSPacketType = 12 )
TNS packet type codes.
func (TNSPacketType) String ¶
func (t TNSPacketType) String() string
String returns a human-readable name for the packet type.
type TTCFunctionCode ¶
type TTCFunctionCode byte
TTCFunctionCode represents a TTC function code inside a TNS Data packet. TTC (Two-Task Common) is Oracle's RPC protocol layered inside TNS Data packets.
Layout of a TNS Data packet payload:
Offset Size Field 0 2 Data flags (usually 0x0000) 2 1 TTC function code 3 ... Function-specific payload
const ( TTCFuncSetProtocol TTCFunctionCode = 0x01 // OSETPRO — session init TTCFuncSetDataTypes TTCFunctionCode = 0x02 // ODTYPES — session init TTCFuncPiggyback TTCFunctionCode = 0x03 // Generic piggyback (sub-op at byte 1) TTCFuncOERR TTCFunctionCode = 0x04 // OER — error/status message (server-side) TTCFuncOCLOSE TTCFunctionCode = 0x05 // OCLOSE — close cursor (legacy) TTCFuncContinuation TTCFunctionCode = 0x06 // Continuation data (multi-packet result rows) TTCFuncResponse TTCFunctionCode = 0x08 // Server response TTCFuncOClosev2 TTCFunctionCode = 0x09 // OCLOSE — close cursor (v315+) TTCFuncOVersion TTCFunctionCode = 0x0B // OVERSION — version request TTCFuncOALL8 TTCFunctionCode = 0x0E // OALL8 — parse+execute (legacy) TTCFuncQueryResult TTCFunctionCode = 0x10 // Query result with row data TTCFuncOFETCH TTCFunctionCode = 0x11 // OFETCH — fetch rows TTCFuncOCANCEL TTCFunctionCode = 0x14 // OCANCEL — cancel query )
TTC function codes for Oracle's Two-Task Common protocol. In modern Oracle (v315+), function 0x03 is a generic "piggyback" that carries sub-operations (auth, execute, close, etc.) identified by byte 1.
func (TTCFunctionCode) String ¶
func (fc TTCFunctionCode) String() string
String returns a human-readable name for the TTC function code.
Source Files
¶
- auth_svr_response.go
- connect_descriptor.go
- crypto_util.go
- describe.go
- errors.go
- intercept.go
- markers.go
- o5logon.go
- phase1_forward.go
- phase2_forward.go
- relay_preauth.go
- server.go
- session.go
- tns.go
- ttc.go
- ttc_auth.go
- ttc_auth_ok_response.go
- ttc_decode.go
- ttc_oer.go
- types.go
- upstream_auth.go
- upstream_auth_client.go
- upstream_auth_crypto.go