oracle

package
v0.18.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: AGPL-3.0 Imports: 37 Imported by: 0

Documentation

Index

Constants

View Source
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.

View Source
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).

View Source
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.

View Source
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

View Source
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.

View Source
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.

View Source
var (
	ErrTNSHeaderTooShort = errors.New("TNS header too short: need at least 8 bytes")
	ErrTNSPacketTooLarge = errors.New("TNS packet length exceeds maximum")
)

TNS errors.

View Source
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.

View Source
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.

View Source
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.

View Source
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.

View Source
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.

View Source
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

func GenerateO5LogonVerifier(password string) ([]byte, []byte, error)

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

func IsExecSQL(ttcPayload []byte) bool

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

func IsPiggybackClose(ttcPayload []byte) bool

IsPiggybackClose checks if a piggyback payload is a close cursor message.

func IsPiggybackExecSQL

func IsPiggybackExecSQL(ttcPayload []byte) bool

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

type OALL8Result struct {
	SQL        string
	CursorID   uint16
	BindValues []string
}

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

type OFETCHResult struct {
	CursorID  uint16
	FetchSize uint32
}

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.

func (*Server) Addr

func (s *Server) Addr() net.Addr

Addr returns the listener address, useful for tests with ":0" port.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the server.

func (*Server) Start

func (s *Server) Start(addr string) error

Start starts the 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.

func (*TNSPacket) Encode

func (p *TNSPacket) Encode() []byte

Encode encodes the packet into a byte slice (convenience method).

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.

type TTCResponse

type TTCResponse struct {
	ReturnCode   uint16
	RowCount     uint32
	Columns      []columnDef
	Rows         [][]interface{}
	MoreData     bool
	IsError      bool
	ErrorCode    int
	ErrorMessage string
}

TTCResponse contains decoded fields from a TTC Response message.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL