mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
Oracle raw TNS 修复: - connect 阶段支持 Resend 包重试(Oracle 18c+ 需要) - ANO 请求补齐加密/完整性算法列表和 auth UB2 字段 - ANO length 字段修正为包含 magic 的完整长度 - Oracle 18c ANO 仍不兼容(字节级匹配 go-ora 但被拒绝),爆破标记 SKIP 新增集成测试协议: ActiveMQ, Zookeeper, Rsync, VNC, SNMP, Oracle(服务检测), Cassandra, Neo4j, Kafka, SMTP, LDAP VNC 修复:换用支持 RFB 3.8 的 debian-xfce-vnc 镜像
2022 lines
47 KiB
Go
2022 lines
47 KiB
Go
//go:build plugin_oracle || !plugin_selective
|
|
|
|
package services
|
|
|
|
/*
|
|
Minimal Oracle TNS authentication probe.
|
|
|
|
Parts of the packet layout and password verifier handling are derived from
|
|
github.com/sijms/go-ora/v2, which is licensed under the MIT License:
|
|
|
|
Copyright (c) 2020 Samy Sultan
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
*/
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/des"
|
|
"crypto/hmac"
|
|
"crypto/md5"
|
|
"crypto/rand"
|
|
"crypto/sha1"
|
|
"crypto/sha512"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
oraclePacketConnect = 1
|
|
oraclePacketAccept = 2
|
|
oraclePacketRefuse = 4
|
|
oraclePacketRedirect = 5
|
|
oraclePacketData = 6
|
|
oraclePacketResend = 11
|
|
|
|
oracleNoNewPass = 0x1
|
|
oracleUserAndPass = 0x100
|
|
|
|
oracleTypeRepNative int16 = 0
|
|
oracleTypeRepUniversal int16 = 1
|
|
oracleTypeRepOracle int16 = 10
|
|
)
|
|
|
|
var errOracleAuthFailed = errors.New("oracle authentication failed")
|
|
|
|
type oracleSession struct {
|
|
conn net.Conn
|
|
in []byte
|
|
out bytes.Buffer
|
|
index int
|
|
version uint16
|
|
negotiatedOptions uint16
|
|
sessionDataUnit uint32
|
|
transportDataUnit uint32
|
|
acfl0 uint8
|
|
acfl1 uint8
|
|
handshakeComplete bool
|
|
ttcVersion uint8
|
|
hasEOSCapability bool
|
|
hasFSAPCapability bool
|
|
useBigClrChunks bool
|
|
clrChunkSize int
|
|
timeout time.Duration
|
|
summary *oracleSummary
|
|
}
|
|
|
|
type oracleTCPNego struct {
|
|
serverCharset int
|
|
serverFlags uint8
|
|
serverNCharset int
|
|
serverCompileTimeCaps []byte
|
|
serverRuntimeCaps []byte
|
|
}
|
|
|
|
type oracleSummary struct {
|
|
retCode int
|
|
errorMessage []byte
|
|
}
|
|
|
|
func oracleRawAuth(ctx context.Context, host string, port int, serviceName, username, password string, timeout time.Duration) error {
|
|
addr := net.JoinHostPort(host, strconv.Itoa(port))
|
|
dialer := net.Dialer{Timeout: timeout}
|
|
conn, err := dialer.DialContext(ctx, "tcp", addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer conn.Close()
|
|
|
|
s := &oracleSession{
|
|
conn: conn,
|
|
version: 317,
|
|
sessionDataUnit: 0x200000,
|
|
transportDataUnit: 0x200000,
|
|
clrChunkSize: 0x40,
|
|
timeout: timeout,
|
|
}
|
|
if err := s.connect(ctx, host, port, serviceName); err != nil {
|
|
return err
|
|
}
|
|
if s.acfl0&1 != 0 && s.acfl0&4 == 0 && s.acfl1&8 == 0 {
|
|
if err := s.advancedNegotiation(); err != nil {
|
|
return fmt.Errorf("ANO: %w", err)
|
|
}
|
|
}
|
|
nego, err := s.protocolNegotiation()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := s.dataTypeNegotiation(nego); err != nil {
|
|
return err
|
|
}
|
|
return s.authenticate(nego, host, port, serviceName, username, password)
|
|
}
|
|
|
|
func (s *oracleSession) connect(ctx context.Context, host string, port int, serviceName string) error {
|
|
connectData := oracleConnectData(host, port, serviceName)
|
|
packetLen := 70 + len(connectData)
|
|
if len(connectData) > 230 {
|
|
packetLen = 70
|
|
}
|
|
buf := make([]byte, packetLen)
|
|
binary.BigEndian.PutUint16(buf[0:2], uint16(packetLen))
|
|
buf[4] = oraclePacketConnect
|
|
binary.BigEndian.PutUint16(buf[8:10], 317)
|
|
binary.BigEndian.PutUint16(buf[10:12], 300)
|
|
binary.BigEndian.PutUint16(buf[12:14], 1|2048)
|
|
binary.BigEndian.PutUint16(buf[14:16], 0xffff)
|
|
binary.BigEndian.PutUint16(buf[16:18], 0xffff)
|
|
buf[18] = 79
|
|
buf[19] = 152
|
|
binary.BigEndian.PutUint16(buf[22:24], 1)
|
|
binary.BigEndian.PutUint16(buf[24:26], uint16(len(connectData)))
|
|
binary.BigEndian.PutUint16(buf[26:28], 70)
|
|
buf[32] = 1
|
|
buf[33] = 1
|
|
binary.BigEndian.PutUint32(buf[58:62], s.sessionDataUnit)
|
|
binary.BigEndian.PutUint32(buf[62:66], s.transportDataUnit)
|
|
if len(connectData) <= 230 {
|
|
copy(buf[70:], connectData)
|
|
}
|
|
sendConnect := func() error {
|
|
if err := s.writeRaw(ctx, buf); err != nil {
|
|
return err
|
|
}
|
|
if len(connectData) > 230 {
|
|
s.reset()
|
|
s.putBytes([]byte(connectData)...)
|
|
return s.writeData()
|
|
}
|
|
return nil
|
|
}
|
|
if err := sendConnect(); err != nil {
|
|
return err
|
|
}
|
|
|
|
var p *oraclePacket
|
|
for resends := 0; resends < 3; resends++ {
|
|
var err error
|
|
p, err = s.readPacket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if p.typ != oraclePacketResend {
|
|
break
|
|
}
|
|
if err := sendConnect(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
switch p.typ {
|
|
case oraclePacketAccept:
|
|
if len(p.raw) < 40 {
|
|
return errors.New("short oracle accept packet")
|
|
}
|
|
s.version = binary.BigEndian.Uint16(p.raw[8:10])
|
|
s.negotiatedOptions = binary.BigEndian.Uint16(p.raw[10:12])
|
|
s.sessionDataUnit = uint32(binary.BigEndian.Uint16(p.raw[12:14]))
|
|
s.transportDataUnit = uint32(binary.BigEndian.Uint16(p.raw[14:16]))
|
|
if s.version >= 315 {
|
|
s.sessionDataUnit = binary.BigEndian.Uint32(p.raw[32:36])
|
|
s.transportDataUnit = binary.BigEndian.Uint32(p.raw[36:40])
|
|
}
|
|
if s.transportDataUnit < s.sessionDataUnit {
|
|
s.sessionDataUnit = s.transportDataUnit
|
|
}
|
|
s.acfl0 = p.raw[22]
|
|
s.acfl1 = p.raw[23]
|
|
if s.version >= 315 {
|
|
s.handshakeComplete = true
|
|
}
|
|
return nil
|
|
case oraclePacketRefuse:
|
|
return oracleRefuseError(p.raw)
|
|
case oraclePacketRedirect:
|
|
return errors.New("oracle redirect is not supported by lightweight auth")
|
|
default:
|
|
return fmt.Errorf("unexpected oracle packet type %d", p.typ)
|
|
}
|
|
}
|
|
|
|
func oracleConnectData(host string, port int, serviceName string) string {
|
|
address := fmt.Sprintf("(ADDRESS=(PROTOCOL=tcp)(HOST=%s)(PORT=%d))", host, port)
|
|
connectData := "(CONNECT_DATA=(SERVICE_NAME=" + serviceName + "))"
|
|
return "(DESCRIPTION=" + address + connectData + ")"
|
|
}
|
|
|
|
type oraclePacket struct {
|
|
typ uint8
|
|
flag uint8
|
|
raw []byte
|
|
data []byte
|
|
}
|
|
|
|
func (s *oracleSession) readPacket() (*oraclePacket, error) {
|
|
header := make([]byte, 8)
|
|
if err := s.readFull(header); err != nil {
|
|
return nil, err
|
|
}
|
|
var length uint32
|
|
if s.handshakeComplete && s.version >= 315 {
|
|
length = binary.BigEndian.Uint32(header[0:4])
|
|
} else {
|
|
length = uint32(binary.BigEndian.Uint16(header[0:2]))
|
|
}
|
|
if length < 8 || length > 16*1024*1024 {
|
|
return nil, fmt.Errorf("invalid oracle packet length %d", length)
|
|
}
|
|
raw := make([]byte, length)
|
|
copy(raw, header)
|
|
if err := s.readFull(raw[8:]); err != nil {
|
|
return nil, err
|
|
}
|
|
p := &oraclePacket{typ: raw[4], flag: raw[5], raw: raw}
|
|
if p.typ == oraclePacketData {
|
|
if len(raw) < 10 {
|
|
return nil, errors.New("short oracle data packet")
|
|
}
|
|
p.data = raw[10:]
|
|
s.in = append(s.in, p.data...)
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
func (s *oracleSession) readFull(buf []byte) error {
|
|
if s.timeout > 0 {
|
|
_ = s.conn.SetReadDeadline(time.Now().Add(s.timeout))
|
|
}
|
|
_, err := io.ReadFull(s.conn, buf)
|
|
return err
|
|
}
|
|
|
|
func (s *oracleSession) writeRaw(ctx context.Context, buf []byte) error {
|
|
if deadline, ok := ctx.Deadline(); ok {
|
|
_ = s.conn.SetWriteDeadline(deadline)
|
|
} else if s.timeout > 0 {
|
|
_ = s.conn.SetWriteDeadline(time.Now().Add(s.timeout))
|
|
}
|
|
_, err := s.conn.Write(buf)
|
|
return err
|
|
}
|
|
|
|
func (s *oracleSession) writeData() error {
|
|
payload := s.out.Bytes()
|
|
if len(payload) == 0 {
|
|
return s.writeDataPacket(nil, 0)
|
|
}
|
|
segmentLen := int(s.sessionDataUnit) - 64
|
|
if segmentLen <= 0 {
|
|
segmentLen = len(payload)
|
|
}
|
|
for len(payload) > segmentLen {
|
|
if err := s.writeDataPacket(payload[:segmentLen], 0); err != nil {
|
|
s.out.Reset()
|
|
return err
|
|
}
|
|
payload = payload[segmentLen:]
|
|
}
|
|
err := s.writeDataPacket(payload, 0)
|
|
s.out.Reset()
|
|
return err
|
|
}
|
|
|
|
func (s *oracleSession) writeDataPacket(payload []byte, dataFlag uint16) error {
|
|
length := uint32(len(payload) + 10)
|
|
buf := make([]byte, length)
|
|
if s.handshakeComplete && s.version >= 315 {
|
|
binary.BigEndian.PutUint32(buf[0:4], length)
|
|
} else {
|
|
binary.BigEndian.PutUint16(buf[0:2], uint16(length))
|
|
}
|
|
buf[4] = oraclePacketData
|
|
binary.BigEndian.PutUint16(buf[8:10], dataFlag)
|
|
copy(buf[10:], payload)
|
|
if s.timeout > 0 {
|
|
_ = s.conn.SetWriteDeadline(time.Now().Add(s.timeout))
|
|
}
|
|
_, err := s.conn.Write(buf)
|
|
return err
|
|
}
|
|
|
|
func (s *oracleSession) reset() {
|
|
s.in = nil
|
|
s.out.Reset()
|
|
s.index = 0
|
|
s.summary = nil
|
|
}
|
|
|
|
func (s *oracleSession) read(n int) ([]byte, error) {
|
|
for s.index+n > len(s.in) {
|
|
p, err := s.readPacket()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if p.typ == oraclePacketResend {
|
|
return nil, errors.New("oracle resend is not supported")
|
|
}
|
|
if p.typ != oraclePacketData {
|
|
return nil, fmt.Errorf("expected oracle data packet, got %d", p.typ)
|
|
}
|
|
}
|
|
ret := s.in[s.index : s.index+n]
|
|
s.index += n
|
|
return ret, nil
|
|
}
|
|
|
|
func (s *oracleSession) putBytes(data ...byte) {
|
|
s.out.Write(data)
|
|
}
|
|
|
|
func (s *oracleSession) putString(v string) {
|
|
s.putClr([]byte(v))
|
|
}
|
|
|
|
func (s *oracleSession) putInt(v interface{}, size uint8, bigEndian, compress bool) {
|
|
num := toInt64(v)
|
|
if compress {
|
|
neg := num < 0
|
|
encoded := uint64(num)
|
|
if neg {
|
|
encoded = uint64(-(num + 1)) + 1
|
|
}
|
|
temp := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(temp, encoded)
|
|
temp = bytes.TrimLeft(temp, "\x00")
|
|
if size > uint8(len(temp)) {
|
|
size = uint8(len(temp))
|
|
}
|
|
if size == 0 {
|
|
s.out.WriteByte(0)
|
|
return
|
|
}
|
|
if neg {
|
|
size |= 0x80
|
|
}
|
|
s.out.WriteByte(size)
|
|
s.out.Write(temp)
|
|
return
|
|
}
|
|
if size == 1 {
|
|
s.out.WriteByte(uint8(num))
|
|
return
|
|
}
|
|
temp := make([]byte, size)
|
|
if bigEndian {
|
|
switch size {
|
|
case 2:
|
|
binary.BigEndian.PutUint16(temp, uint16(num))
|
|
case 4:
|
|
binary.BigEndian.PutUint32(temp, uint32(num))
|
|
case 8:
|
|
binary.BigEndian.PutUint64(temp, uint64(num))
|
|
}
|
|
} else {
|
|
switch size {
|
|
case 2:
|
|
binary.LittleEndian.PutUint16(temp, uint16(num))
|
|
case 4:
|
|
binary.LittleEndian.PutUint32(temp, uint32(num))
|
|
case 8:
|
|
binary.LittleEndian.PutUint64(temp, uint64(num))
|
|
}
|
|
}
|
|
s.out.Write(temp)
|
|
}
|
|
|
|
func (s *oracleSession) putUint(v interface{}, size uint8, bigEndian, compress bool) {
|
|
num := toUint64(v)
|
|
if size == 1 {
|
|
s.out.WriteByte(uint8(num))
|
|
return
|
|
}
|
|
if compress {
|
|
temp := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(temp, num)
|
|
temp = bytes.TrimLeft(temp, "\x00")
|
|
if size > uint8(len(temp)) {
|
|
size = uint8(len(temp))
|
|
}
|
|
if size == 0 {
|
|
s.out.WriteByte(0)
|
|
return
|
|
}
|
|
s.out.WriteByte(size)
|
|
s.out.Write(temp)
|
|
return
|
|
}
|
|
temp := make([]byte, size)
|
|
if bigEndian {
|
|
switch size {
|
|
case 2:
|
|
binary.BigEndian.PutUint16(temp, uint16(num))
|
|
case 4:
|
|
binary.BigEndian.PutUint32(temp, uint32(num))
|
|
case 8:
|
|
binary.BigEndian.PutUint64(temp, num)
|
|
}
|
|
} else {
|
|
switch size {
|
|
case 2:
|
|
binary.LittleEndian.PutUint16(temp, uint16(num))
|
|
case 4:
|
|
binary.LittleEndian.PutUint32(temp, uint32(num))
|
|
case 8:
|
|
binary.LittleEndian.PutUint64(temp, num)
|
|
}
|
|
}
|
|
s.out.Write(temp)
|
|
}
|
|
|
|
func (s *oracleSession) putClr(data []byte) {
|
|
if len(data) > 0xfc {
|
|
s.out.WriteByte(0xfe)
|
|
for start := 0; start < len(data); start += s.clrChunkSize {
|
|
end := start + s.clrChunkSize
|
|
if end > len(data) {
|
|
end = len(data)
|
|
}
|
|
chunk := data[start:end]
|
|
if s.useBigClrChunks {
|
|
s.putInt(len(chunk), 4, true, true)
|
|
} else {
|
|
s.out.WriteByte(uint8(len(chunk)))
|
|
}
|
|
s.out.Write(chunk)
|
|
}
|
|
s.out.WriteByte(0)
|
|
return
|
|
}
|
|
if len(data) == 0 {
|
|
s.out.WriteByte(0)
|
|
return
|
|
}
|
|
s.out.WriteByte(uint8(len(data)))
|
|
s.out.Write(data)
|
|
}
|
|
|
|
func (s *oracleSession) putKeyValString(key, val string, num uint8) {
|
|
s.putKeyVal([]byte(key), []byte(val), num)
|
|
}
|
|
|
|
func (s *oracleSession) putKeyVal(key, val []byte, num uint8) {
|
|
if len(key) == 0 {
|
|
s.out.WriteByte(0)
|
|
} else {
|
|
s.putUint(len(key), 4, true, true)
|
|
s.putClr(key)
|
|
}
|
|
if len(val) == 0 {
|
|
s.out.WriteByte(0)
|
|
} else {
|
|
s.putUint(len(val), 4, true, true)
|
|
s.putClr(val)
|
|
}
|
|
s.putInt(num, 4, true, true)
|
|
}
|
|
|
|
func (s *oracleSession) getByte() (uint8, error) {
|
|
b, err := s.read(1)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return b[0], nil
|
|
}
|
|
|
|
func (s *oracleSession) getBytes(n int) ([]byte, error) {
|
|
return s.read(n)
|
|
}
|
|
|
|
func (s *oracleSession) getInt(size int, compress, bigEndian bool) (int, error) {
|
|
v, err := s.getInt64(size, compress, bigEndian)
|
|
return int(v), err
|
|
}
|
|
|
|
func (s *oracleSession) getInt64(size int, compress, bigEndian bool) (int64, error) {
|
|
neg := false
|
|
if compress {
|
|
b, err := s.read(1)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
size = int(b[0])
|
|
if size&0x80 != 0 {
|
|
neg = true
|
|
size &= 0x7f
|
|
}
|
|
bigEndian = true
|
|
}
|
|
if size == 0 {
|
|
return 0, nil
|
|
}
|
|
if size > 8 {
|
|
return 0, fmt.Errorf("invalid oracle integer size %d", size)
|
|
}
|
|
b, err := s.read(size)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
tmp := make([]byte, 8)
|
|
if bigEndian {
|
|
copy(tmp[8-size:], b)
|
|
v := int64(binary.BigEndian.Uint64(tmp))
|
|
if neg {
|
|
v = -v
|
|
}
|
|
return v, nil
|
|
}
|
|
copy(tmp[:size], b)
|
|
v := int64(binary.LittleEndian.Uint64(tmp))
|
|
if neg {
|
|
v = -v
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
func (s *oracleSession) getNullTermString(maxSize int) (string, error) {
|
|
oldIndex := s.index
|
|
b, err := s.read(maxSize)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if i := bytes.IndexByte(b, 0); i >= 0 {
|
|
s.index = oldIndex + i + 1
|
|
return string(b[:i]), nil
|
|
}
|
|
return string(b), nil
|
|
}
|
|
|
|
func (s *oracleSession) getClr() ([]byte, error) {
|
|
nb, err := s.getByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if nb == 0 || nb == 0xff || nb == 0xfd {
|
|
return nil, nil
|
|
}
|
|
chunkSize := int(nb)
|
|
var out bytes.Buffer
|
|
if chunkSize == 0xfe {
|
|
for {
|
|
if s.useBigClrChunks {
|
|
chunkSize, err = s.getInt(4, true, true)
|
|
} else {
|
|
nb, err = s.getByte()
|
|
chunkSize = int(nb)
|
|
}
|
|
if err != nil || chunkSize == 0 {
|
|
return out.Bytes(), err
|
|
}
|
|
chunk, err := s.getBytes(chunkSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out.Write(chunk)
|
|
}
|
|
}
|
|
chunk, err := s.getBytes(chunkSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out.Write(chunk)
|
|
return out.Bytes(), nil
|
|
}
|
|
|
|
func (s *oracleSession) getDlc() ([]byte, error) {
|
|
length, err := s.getInt(4, true, true)
|
|
if err != nil || length <= 0 {
|
|
return nil, err
|
|
}
|
|
out, err := s.getClr()
|
|
if len(out) > length {
|
|
out = out[:length]
|
|
}
|
|
return out, err
|
|
}
|
|
|
|
func (s *oracleSession) getKeyVal() ([]byte, []byte, int, error) {
|
|
key, err := s.getDlc()
|
|
if err != nil {
|
|
return nil, nil, 0, err
|
|
}
|
|
val, err := s.getDlc()
|
|
if err != nil {
|
|
return nil, nil, 0, err
|
|
}
|
|
num, err := s.getInt(4, true, true)
|
|
return key, val, num, err
|
|
}
|
|
|
|
func toInt64(v interface{}) int64 {
|
|
switch n := v.(type) {
|
|
case int:
|
|
return int64(n)
|
|
case int16:
|
|
return int64(n)
|
|
case int32:
|
|
return int64(n)
|
|
case int64:
|
|
return n
|
|
case uint8:
|
|
return int64(n)
|
|
case uint16:
|
|
return int64(n)
|
|
case uint32:
|
|
return int64(n)
|
|
case uint64:
|
|
return int64(n)
|
|
case uint:
|
|
return int64(n)
|
|
default:
|
|
panic("oracle integer encoder expects an integer")
|
|
}
|
|
}
|
|
|
|
func toUint64(v interface{}) uint64 {
|
|
switch n := v.(type) {
|
|
case int:
|
|
return uint64(n)
|
|
case int16:
|
|
return uint64(n)
|
|
case int32:
|
|
return uint64(n)
|
|
case int64:
|
|
return uint64(n)
|
|
case uint8:
|
|
return uint64(n)
|
|
case uint16:
|
|
return uint64(n)
|
|
case uint32:
|
|
return uint64(n)
|
|
case uint64:
|
|
return n
|
|
case uint:
|
|
return uint64(n)
|
|
default:
|
|
panic("oracle integer encoder expects an integer")
|
|
}
|
|
}
|
|
|
|
func (s *oracleSession) advancedNegotiation() error {
|
|
// 按 go-ora 参考实现,构造 ANO 请求
|
|
// Service 4 (supervisor): version + cid + servArray
|
|
// Service 1 (auth): version + UB2(0xE0E1) + status(0xFCFF)
|
|
// Service 2 (encrypt): version + algorithms([0]=rejected) + UB1(1)
|
|
// Service 3 (data integrity): version + algorithms([0]=rejected)
|
|
|
|
// 构建 ANO body 到临时 buffer 计算精确 length
|
|
var ab oracleSession
|
|
ab.clrChunkSize = s.clrChunkSize
|
|
|
|
// Service 4 (supervisor): cid + service array
|
|
ab.writeANOServiceHeader(4, 3)
|
|
ab.writeANOVersion()
|
|
ab.writeANOBytes([]byte{0, 0, 16, 28, 102, 236, 40, 234})
|
|
ab.writeANOUB2Array([]int{4, 1, 2, 3})
|
|
|
|
// Service 1 (auth): UB2(0xE0E1) + status(0xFCFF)
|
|
ab.writeANOServiceHeader(1, 3)
|
|
ab.writeANOVersion()
|
|
ab.writeANOPacketHeader(2, 3)
|
|
ab.putInt(0xE0E1, 2, true, false)
|
|
ab.writeANOStatus(0xfcff)
|
|
|
|
// Service 2 (encrypt): supported algos + driver
|
|
ab.writeANOServiceHeader(2, 3)
|
|
ab.writeANOVersion()
|
|
ab.writeANOBytes([]byte{0, 1, 8, 10, 6, 2, 15, 16, 17})
|
|
ab.writeANOUB1(1)
|
|
|
|
// Service 3 (data integrity): supported algos
|
|
ab.writeANOServiceHeader(3, 2)
|
|
ab.writeANOVersion()
|
|
ab.writeANOBytes([]byte{0, 1, 3, 4, 5, 6})
|
|
|
|
body := ab.out.Bytes()
|
|
s.reset()
|
|
s.writeANOHeader(13+len(body), 4, 0)
|
|
s.putBytes(body...)
|
|
|
|
if err := s.writeData(); err != nil {
|
|
return err
|
|
}
|
|
header, err := s.readANOHeader()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := 0; i < header.serviceCount; i++ {
|
|
serviceType, subPackets, errCode, err := s.readANOServiceHeader()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if errCode != 0 {
|
|
return fmt.Errorf("oracle advanced negotiation error ora-%d", errCode)
|
|
}
|
|
if err := s.readANOServiceData(serviceType, subPackets); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type oracleANOHeader struct {
|
|
serviceCount int
|
|
}
|
|
|
|
func (s *oracleSession) writeANOHeader(length, serviceCount int, flags uint8) {
|
|
s.putInt(uint64(0xdeadbeef), 4, true, false)
|
|
s.putInt(length, 2, true, false)
|
|
s.putInt(0x0b200200, 4, true, false)
|
|
s.putInt(serviceCount, 2, true, false)
|
|
s.putBytes(flags)
|
|
}
|
|
|
|
func (s *oracleSession) writeANOServiceHeader(serviceType, subPackets int) {
|
|
s.putInt(serviceType, 2, true, false)
|
|
s.putInt(subPackets, 2, true, false)
|
|
s.putInt(0, 4, true, false)
|
|
}
|
|
|
|
func (s *oracleSession) writeANOPacketHeader(length, typ int) {
|
|
s.putInt(length, 2, true, false)
|
|
s.putInt(typ, 2, true, false)
|
|
}
|
|
|
|
func (s *oracleSession) writeANOVersion() {
|
|
s.writeANOPacketHeader(4, 5)
|
|
s.putInt(0x0b200200, 4, true, false)
|
|
}
|
|
|
|
func (s *oracleSession) writeANOStatus(status int) {
|
|
s.writeANOPacketHeader(2, 6)
|
|
s.putInt(status, 2, true, false)
|
|
}
|
|
|
|
func (s *oracleSession) writeANOBytes(b []byte) {
|
|
s.writeANOPacketHeader(len(b), 1)
|
|
s.putBytes(b...)
|
|
}
|
|
|
|
func (s *oracleSession) writeANOUB1(v uint8) {
|
|
s.writeANOPacketHeader(1, 2)
|
|
s.putBytes(v)
|
|
}
|
|
|
|
func (s *oracleSession) writeANOUB2Array(v []int) {
|
|
s.writeANOPacketHeader(10+len(v)*2, 1)
|
|
s.putInt(uint64(0xdeadbeef), 4, true, false)
|
|
s.putInt(3, 2, true, false)
|
|
s.putInt(len(v), 4, true, false)
|
|
for _, n := range v {
|
|
s.putInt(n, 2, true, false)
|
|
}
|
|
}
|
|
|
|
func (s *oracleSession) readANOHeader() (*oracleANOHeader, error) {
|
|
magic, err := s.getInt64(4, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if magic != 0xdeadbeef {
|
|
return nil, errors.New("oracle advanced negotiation header mismatch")
|
|
}
|
|
if _, err = s.getInt(2, false, true); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = s.getInt(4, false, true); err != nil {
|
|
return nil, err
|
|
}
|
|
count, err := s.getInt(2, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = s.getByte(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &oracleANOHeader{serviceCount: count}, nil
|
|
}
|
|
|
|
func (s *oracleSession) readANOServiceHeader() (int, int, int, error) {
|
|
serviceType, err := s.getInt(2, false, true)
|
|
if err != nil {
|
|
return 0, 0, 0, err
|
|
}
|
|
subPackets, err := s.getInt(2, false, true)
|
|
if err != nil {
|
|
return 0, 0, 0, err
|
|
}
|
|
errCode, err := s.getInt(4, false, true)
|
|
return serviceType, subPackets, errCode, err
|
|
}
|
|
|
|
func (s *oracleSession) readANOPacketHeader(expectType int) (int, error) {
|
|
length, err := s.getInt(2, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
typ, err := s.getInt(2, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if typ != expectType {
|
|
return 0, fmt.Errorf("oracle advanced negotiation type mismatch: %d", typ)
|
|
}
|
|
return length, nil
|
|
}
|
|
|
|
func (s *oracleSession) readANOServiceData(serviceType, subPackets int) error {
|
|
switch serviceType {
|
|
case 1:
|
|
if _, err := s.readANOVersion(); err != nil {
|
|
return err
|
|
}
|
|
status, err := s.readANOStatus()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if status == 0xfaff && subPackets > 2 {
|
|
if _, err = s.readANOUB1(); err != nil {
|
|
return err
|
|
}
|
|
name, err := s.readANOString()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if name != "" && name != "TCPS" {
|
|
return fmt.Errorf("unsupported oracle authentication service %s", name)
|
|
}
|
|
} else if status != 0xfbff {
|
|
return errors.New("oracle advanced authentication negotiation failed")
|
|
}
|
|
case 2, 3:
|
|
if _, err := s.readANOVersion(); err != nil {
|
|
return err
|
|
}
|
|
algo, err := s.readANOUB1()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if algo != 0 {
|
|
return fmt.Errorf("unsupported oracle advanced service algorithm %d", algo)
|
|
}
|
|
for i := 2; i < subPackets; i++ {
|
|
if err := s.skipANOPacket(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case 4:
|
|
if _, err := s.readANOVersion(); err != nil {
|
|
return err
|
|
}
|
|
if _, err := s.readANOStatus(); err != nil {
|
|
return err
|
|
}
|
|
_, err := s.readANOBytes()
|
|
return err
|
|
default:
|
|
for i := 0; i < subPackets; i++ {
|
|
if err := s.skipANOPacket(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *oracleSession) readANOVersion() (uint32, error) {
|
|
if _, err := s.readANOPacketHeader(5); err != nil {
|
|
return 0, err
|
|
}
|
|
v, err := s.getInt(4, false, true)
|
|
return uint32(v), err
|
|
}
|
|
|
|
func (s *oracleSession) readANOStatus() (int, error) {
|
|
if _, err := s.readANOPacketHeader(6); err != nil {
|
|
return 0, err
|
|
}
|
|
return s.getInt(2, false, true)
|
|
}
|
|
|
|
func (s *oracleSession) readANOUB1() (uint8, error) {
|
|
if _, err := s.readANOPacketHeader(2); err != nil {
|
|
return 0, err
|
|
}
|
|
return s.getByte()
|
|
}
|
|
|
|
func (s *oracleSession) readANOString() (string, error) {
|
|
length, err := s.readANOPacketHeader(0)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
b, err := s.getBytes(length)
|
|
return string(b), err
|
|
}
|
|
|
|
func (s *oracleSession) readANOBytes() ([]byte, error) {
|
|
length, err := s.readANOPacketHeader(1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.getBytes(length)
|
|
}
|
|
|
|
func (s *oracleSession) skipANOPacket() error {
|
|
length, err := s.getInt(2, false, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = s.getInt(2, false, true); err != nil {
|
|
return err
|
|
}
|
|
if length > 0 {
|
|
_, err = s.getBytes(length)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (s *oracleSession) protocolNegotiation() (*oracleTCPNego, error) {
|
|
s.reset()
|
|
s.putBytes(1, 6, 0)
|
|
s.putBytes([]byte("OracleClientGo\x00")...)
|
|
if err := s.writeData(); err != nil {
|
|
return nil, err
|
|
}
|
|
msg, err := s.getByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if msg != 1 {
|
|
return nil, fmt.Errorf("oracle protocol negotiation expected message 1, got %d", msg)
|
|
}
|
|
proto, err := s.getByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if proto != 4 && proto != 5 && proto != 6 {
|
|
return nil, errors.New("unsupported oracle server protocol version")
|
|
}
|
|
if _, err = s.getByte(); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = s.getNullTermString(50); err != nil {
|
|
return nil, err
|
|
}
|
|
serverCharset, err := s.getInt(2, false, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
serverFlags, err := s.getByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
charsetElem, err := s.getInt(2, false, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if charsetElem > 0 {
|
|
if _, err = s.getBytes(charsetElem * 5); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
len1, err := s.getInt(2, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
numArray, err := s.getBytes(len1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(numArray) < 11 {
|
|
return nil, errors.New("short oracle charset negotiation")
|
|
}
|
|
offset := int(6 + numArray[5] + numArray[6])
|
|
if len(numArray) < offset+5 {
|
|
return nil, errors.New("short oracle ncharset negotiation")
|
|
}
|
|
serverNCharset := int(binary.BigEndian.Uint16(numArray[offset+3 : offset+5]))
|
|
len2, err := s.getByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
compileCaps, err := s.getBytes(int(len2))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
len3, err := s.getByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
runtimeCaps, err := s.getBytes(int(len3))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(compileCaps) < 8 {
|
|
return nil, errors.New("oracle server compile caps too short")
|
|
}
|
|
if len(compileCaps) > 15 && compileCaps[15]&1 != 0 {
|
|
s.hasEOSCapability = true
|
|
}
|
|
if len(compileCaps) > 16 && compileCaps[16]&1 != 0 {
|
|
s.hasFSAPCapability = true
|
|
}
|
|
if len(compileCaps) > 37 && compileCaps[37]&32 != 0 {
|
|
s.useBigClrChunks = true
|
|
s.clrChunkSize = 0x7fff
|
|
}
|
|
return &oracleTCPNego{
|
|
serverCharset: serverCharset,
|
|
serverFlags: serverFlags | 2,
|
|
serverNCharset: serverNCharset,
|
|
serverCompileTimeCaps: compileCaps,
|
|
serverRuntimeCaps: runtimeCaps,
|
|
}, nil
|
|
}
|
|
|
|
func (s *oracleSession) dataTypeNegotiation(nego *oracleTCPNego) error {
|
|
compileCaps := []byte{
|
|
6, 1, 0, 0, 106, 1, 1, 11,
|
|
1, 1, 1, 1, 1, 1, 0, 41,
|
|
144, 3, 7, 3, 0, 1, 0, 235,
|
|
1, 0, 5, 1, 0, 0, 0, 24,
|
|
0, 0, 7, 32, 2, 58, 0, 0,
|
|
5, 0, 0, 0, 8,
|
|
}
|
|
if len(nego.serverCompileTimeCaps) <= 27 || nego.serverCompileTimeCaps[27] == 0 {
|
|
compileCaps[27] = 0
|
|
}
|
|
if len(nego.serverCompileTimeCaps) > 7 && nego.serverCompileTimeCaps[7] < 7 {
|
|
compileCaps[36] = 0
|
|
}
|
|
if len(nego.serverCompileTimeCaps) <= 37 || nego.serverCompileTimeCaps[37]&2 != 2 {
|
|
compileCaps[37] = 0
|
|
compileCaps[1] = 0
|
|
}
|
|
runtimeCaps := []byte{2, 1, 0, 0, 0, 0, 0}
|
|
if len(nego.serverRuntimeCaps) < 2 || nego.serverRuntimeCaps[1]&1 != 1 {
|
|
runtimeCaps[1] = 0
|
|
}
|
|
if len(nego.serverRuntimeCaps) > 6 {
|
|
if nego.serverRuntimeCaps[6]&4 == 4 {
|
|
runtimeCaps[6] |= 4
|
|
}
|
|
if nego.serverRuntimeCaps[6]&2 == 2 {
|
|
runtimeCaps[6] |= 2
|
|
}
|
|
}
|
|
typeReps := oracleTypeReps(nego, compileCaps)
|
|
s.reset()
|
|
s.putBytes(2)
|
|
s.putInt(nego.serverCharset, 2, false, false)
|
|
s.putInt(nego.serverCharset, 2, false, false)
|
|
s.putBytes(nego.serverFlags, uint8(len(compileCaps)))
|
|
s.putBytes(compileCaps...)
|
|
s.putBytes(uint8(len(runtimeCaps)))
|
|
s.putBytes(runtimeCaps...)
|
|
if runtimeCaps[1]&1 == 1 {
|
|
s.putBytes(oracleTZBytes()...)
|
|
if compileCaps[37]&2 == 2 {
|
|
s.putInt(0x20, 4, true, false)
|
|
}
|
|
}
|
|
s.putInt(nego.serverNCharset, 2, false, false)
|
|
if compileCaps[27] == 0 {
|
|
for _, v := range typeReps {
|
|
s.putBytes(uint8(v))
|
|
}
|
|
s.putBytes(0)
|
|
} else {
|
|
for _, v := range typeReps {
|
|
s.putInt(v, 2, true, false)
|
|
}
|
|
s.putBytes(0, 0)
|
|
}
|
|
if err := s.writeData(); err != nil {
|
|
return err
|
|
}
|
|
msg, err := s.getByte()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if msg != 2 {
|
|
return fmt.Errorf("oracle data type negotiation expected message 2, got %d", msg)
|
|
}
|
|
if runtimeCaps[1] == 1 {
|
|
if _, err = s.getBytes(11); err != nil {
|
|
return err
|
|
}
|
|
if compileCaps[37]&2 == 2 {
|
|
if _, err = s.getInt(4, false, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
level := 0
|
|
for {
|
|
var n int
|
|
if compileCaps[27] == 0 {
|
|
n, err = s.getInt(1, false, false)
|
|
} else {
|
|
n, err = s.getInt(2, false, true)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n == 0 && level == 0 {
|
|
break
|
|
}
|
|
if n == 0 && level == 1 {
|
|
level = 0
|
|
continue
|
|
}
|
|
if level == 3 {
|
|
level = 0
|
|
continue
|
|
}
|
|
level++
|
|
}
|
|
if len(compileCaps) > 7 && len(nego.serverCompileTimeCaps) > 7 {
|
|
s.ttcVersion = compileCaps[7]
|
|
if nego.serverCompileTimeCaps[7] < s.ttcVersion {
|
|
s.ttcVersion = nego.serverCompileTimeCaps[7]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func oracleTypeReps(nego *oracleTCPNego, compileCaps []byte) []int16 {
|
|
reps := make([]int16, 0, 96)
|
|
add := func(dty, ndty, rep int16) {
|
|
reps = append(reps, dty, ndty)
|
|
if ndty != 0 {
|
|
reps = append(reps, rep, 0)
|
|
}
|
|
}
|
|
add(1, 1, oracleTypeRepUniversal) // NCHAR
|
|
add(2, 2, oracleTypeRepOracle) // NUMBER
|
|
add(8, 8, oracleTypeRepUniversal) // LONG
|
|
add(12, 12, oracleTypeRepOracle) // DATE
|
|
add(23, 23, oracleTypeRepUniversal) // RAW
|
|
add(24, 24, oracleTypeRepUniversal) // LONG RAW
|
|
add(25, 25, oracleTypeRepUniversal)
|
|
add(26, 26, oracleTypeRepUniversal)
|
|
add(27, 27, oracleTypeRepUniversal)
|
|
add(28, 28, oracleTypeRepUniversal)
|
|
add(29, 29, oracleTypeRepUniversal)
|
|
add(30, 30, oracleTypeRepUniversal)
|
|
add(31, 31, oracleTypeRepUniversal)
|
|
add(32, 32, oracleTypeRepUniversal)
|
|
add(33, 33, oracleTypeRepUniversal)
|
|
add(10, 10, oracleTypeRepUniversal)
|
|
add(11, 11, oracleTypeRepUniversal) // ROWID
|
|
add(3, 2, oracleTypeRepOracle)
|
|
add(4, 2, oracleTypeRepOracle)
|
|
add(5, 1, oracleTypeRepUniversal)
|
|
add(6, 2, oracleTypeRepOracle)
|
|
add(7, 2, oracleTypeRepOracle)
|
|
add(9, 1, oracleTypeRepUniversal) // VARCHAR
|
|
add(15, 1, oracleTypeRepUniversal)
|
|
add(39, 120, oracleTypeRepUniversal)
|
|
add(58, 0, oracleTypeRepNative)
|
|
add(68, 2, oracleTypeRepOracle)
|
|
add(69, 0, oracleTypeRepNative)
|
|
add(70, 0, oracleTypeRepNative)
|
|
add(74, 0, oracleTypeRepNative)
|
|
add(76, 0, oracleTypeRepNative)
|
|
add(91, 2, oracleTypeRepOracle)
|
|
add(94, 1, oracleTypeRepUniversal)
|
|
add(95, 23, oracleTypeRepUniversal)
|
|
add(96, 96, oracleTypeRepUniversal)
|
|
add(97, 96, oracleTypeRepUniversal)
|
|
add(100, 100, oracleTypeRepUniversal)
|
|
add(101, 101, oracleTypeRepUniversal)
|
|
add(102, 102, oracleTypeRepUniversal)
|
|
add(104, 11, oracleTypeRepUniversal)
|
|
add(105, 0, oracleTypeRepNative)
|
|
add(106, 106, oracleTypeRepUniversal)
|
|
add(112, 112, oracleTypeRepUniversal)
|
|
add(113, 113, oracleTypeRepUniversal)
|
|
add(114, 114, oracleTypeRepUniversal)
|
|
add(115, 115, oracleTypeRepUniversal)
|
|
add(116, 102, oracleTypeRepUniversal)
|
|
add(118, 0, oracleTypeRepNative)
|
|
add(119, 119, oracleTypeRepNative) // JSON
|
|
add(127, 127, oracleTypeRepUniversal)
|
|
add(208, 208, oracleTypeRepUniversal)
|
|
_ = nego
|
|
_ = compileCaps
|
|
return reps
|
|
}
|
|
|
|
func oracleTZBytes() []byte {
|
|
_, offset := time.Now().Zone()
|
|
hours := int8(offset / 3600)
|
|
minutes := int8((offset / 60) % 60)
|
|
seconds := int8(offset % 60)
|
|
return []byte{128, 0, 0, 0, uint8(hours + 60), uint8(minutes + 60), uint8(seconds + 60), 128, 0, 0, 0}
|
|
}
|
|
|
|
func (s *oracleSession) authenticate(nego *oracleTCPNego, host string, port int, serviceName, username, password string) error {
|
|
s.reset()
|
|
s.putBytes(3, 0x76, 0, 1)
|
|
s.putUint(len(username), 4, true, true)
|
|
s.putUint(oracleNoNewPass, 4, true, true)
|
|
s.putBytes(1, 1, 5, 1, 1)
|
|
if username != "" {
|
|
s.putString(username)
|
|
}
|
|
s.putKeyValString("AUTH_TERMINAL", "", 0)
|
|
s.putKeyValString("AUTH_PROGRAM_NM", "", 0)
|
|
s.putKeyValString("AUTH_MACHINE", "", 0)
|
|
s.putKeyValString("AUTH_PID", "0", 0)
|
|
s.putKeyValString("AUTH_SID", "", 0)
|
|
if err := s.writeData(); err != nil {
|
|
return err
|
|
}
|
|
auth, err := s.readAuthChallenge(username, password, nego)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return s.writeAuthResponse(auth, nego, host, port, serviceName, username)
|
|
}
|
|
|
|
type oracleAuthObject struct {
|
|
eServerSessKey string
|
|
eClientSessKey string
|
|
ePassword string
|
|
eSpeedyKey string
|
|
serverSessKey []byte
|
|
clientSessKey []byte
|
|
salt string
|
|
pbkdf2ChkSalt string
|
|
pbkdf2VgenCount int
|
|
pbkdf2SderCount int
|
|
verifierType int
|
|
customHash bool
|
|
}
|
|
|
|
func (s *oracleSession) readAuthChallenge(username, password string, nego *oracleTCPNego) (*oracleAuthObject, error) {
|
|
auth := &oracleAuthObject{customHash: len(nego.serverCompileTimeCaps) > 4 && nego.serverCompileTimeCaps[4]&32 != 0}
|
|
for {
|
|
msg, err := s.getByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch msg {
|
|
case 8:
|
|
dictLen, err := s.getInt(4, true, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := 0; i < dictLen; i++ {
|
|
key, val, num, err := s.getKeyVal()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch string(key) {
|
|
case "AUTH_SESSKEY":
|
|
if auth.eServerSessKey == "" {
|
|
auth.eServerSessKey = string(val)
|
|
}
|
|
case "AUTH_VFR_DATA":
|
|
if auth.salt == "" {
|
|
auth.salt = string(val)
|
|
auth.verifierType = num
|
|
}
|
|
case "AUTH_PBKDF2_CSK_SALT":
|
|
auth.pbkdf2ChkSalt = string(val)
|
|
if len(auth.pbkdf2ChkSalt) != 32 {
|
|
return nil, errors.New("oracle authentication protocol internal error")
|
|
}
|
|
case "AUTH_PBKDF2_VGEN_COUNT":
|
|
auth.pbkdf2VgenCount, _ = strconv.Atoi(string(val))
|
|
if auth.pbkdf2VgenCount < 4096 || auth.pbkdf2VgenCount > 100000000 {
|
|
auth.pbkdf2VgenCount = 4096
|
|
}
|
|
case "AUTH_PBKDF2_SDER_COUNT":
|
|
auth.pbkdf2SderCount, _ = strconv.Atoi(string(val))
|
|
if auth.pbkdf2SderCount < 3 || auth.pbkdf2SderCount > 100000000 {
|
|
auth.pbkdf2SderCount = 3
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
err := s.readMsg(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if msg == 4 {
|
|
if s.hasError() {
|
|
return nil, s.oracleError()
|
|
}
|
|
return auth.finish(username, password, nego)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (auth *oracleAuthObject) finish(username, password string, nego *oracleTCPNego) (*oracleAuthObject, error) {
|
|
if len(auth.eServerSessKey) != 64 && len(auth.eServerSessKey) != 96 {
|
|
return nil, errors.New("oracle session key should be either 64 or 96 bytes long")
|
|
}
|
|
var key []byte
|
|
var speedyKey []byte
|
|
padding := false
|
|
var err error
|
|
switch auth.verifierType {
|
|
case 2361:
|
|
key, err = oracleKeyFromUserPass(username, password)
|
|
case 6949:
|
|
if len(nego.serverCompileTimeCaps) > 4 && nego.serverCompileTimeCaps[4]&2 == 0 {
|
|
padding = true
|
|
}
|
|
salt, err := hex.DecodeString(auth.salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
h := sha1.New()
|
|
_, _ = h.Write(append([]byte(password), salt...))
|
|
key = append(h.Sum(nil), 0, 0, 0, 0)
|
|
case 18453:
|
|
salt, err := hex.DecodeString(auth.salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
message := append(salt, []byte("AUTH_PBKDF2_SPEEDY_KEY")...)
|
|
speedyKey = oracleGenerateSpeedyKey(message, []byte(password), auth.pbkdf2VgenCount)
|
|
h := sha512.New()
|
|
_, _ = h.Write(append(speedyKey, salt...))
|
|
key = h.Sum(nil)[:32]
|
|
default:
|
|
return nil, fmt.Errorf("unsupported oracle verifier type %d", auth.verifierType)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
auth.serverSessKey, err = oracleDecryptSessionKey(padding, key, auth.eServerSessKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
auth.clientSessKey = make([]byte, len(auth.serverSessKey))
|
|
for {
|
|
if _, err = rand.Read(auth.clientSessKey); err != nil {
|
|
return nil, err
|
|
}
|
|
if !bytes.Equal(auth.clientSessKey, auth.serverSessKey) {
|
|
break
|
|
}
|
|
}
|
|
auth.eClientSessKey, err = oracleEncryptSessionKey(padding, key, auth.clientSessKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
newKey, err := auth.passwordEncKey(nego)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
auth.ePassword, err = oracleEncryptPassword([]byte(password), newKey, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if auth.verifierType == 18453 {
|
|
auth.eSpeedyKey, err = oracleEncryptPassword(speedyKey, newKey, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return auth, nil
|
|
}
|
|
|
|
func (s *oracleSession) writeAuthResponse(auth *oracleAuthObject, nego *oracleTCPNego, host string, port int, serviceName, username string) error {
|
|
keys := []struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{
|
|
{"AUTH_SESSKEY", auth.eClientSessKey, 1},
|
|
{"AUTH_PASSWORD", auth.ePassword, 0},
|
|
}
|
|
if auth.eSpeedyKey != "" {
|
|
keys = append(keys, struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"AUTH_PBKDF2_SPEEDY_KEY", auth.eSpeedyKey, 0})
|
|
}
|
|
keys = append(keys,
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"AUTH_TERMINAL", "", 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"AUTH_PROGRAM_NM", "", 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"AUTH_MACHINE", "", 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"AUTH_PID", "0", 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"AUTH_SID", "", 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"AUTH_CONNECT_STRING", oracleConnectData(host, port, serviceName), 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"SESSION_CLIENT_CHARSET", strconv.Itoa(nego.serverCharset), 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"SESSION_CLIENT_LIB_TYPE", "0", 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"SESSION_CLIENT_DRIVER_NAME", "fscan", 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"SESSION_CLIENT_VERSION", "2.0.0.0", 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"SESSION_CLIENT_LOBATTR", "1", 0},
|
|
struct {
|
|
key string
|
|
val string
|
|
flag uint8
|
|
}{"AUTH_ALTER_SESSION", oracleAlterSession(), 1},
|
|
)
|
|
s.reset()
|
|
s.putBytes(3, 0x73, 0)
|
|
if username != "" {
|
|
s.putBytes(1)
|
|
s.putInt(len(username), 4, true, true)
|
|
} else {
|
|
s.putBytes(0, 0)
|
|
}
|
|
s.putUint(oracleUserAndPass|oracleNoNewPass, 4, true, true)
|
|
s.putBytes(1)
|
|
s.putUint(len(keys), 4, true, true)
|
|
s.putBytes(1, 1)
|
|
if username != "" {
|
|
s.putString(username)
|
|
}
|
|
for _, kv := range keys {
|
|
if kv.val == "" && (kv.key == "AUTH_SESSKEY" || kv.key == "AUTH_PASSWORD") {
|
|
continue
|
|
}
|
|
s.putKeyValString(kv.key, kv.val, kv.flag)
|
|
}
|
|
if err := s.writeData(); err != nil {
|
|
return err
|
|
}
|
|
for {
|
|
msg, err := s.getByte()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := s.readMsg(msg); err != nil {
|
|
return err
|
|
}
|
|
if msg == 4 || msg == 9 {
|
|
if s.hasError() {
|
|
err := s.oracleError()
|
|
if classifyOracleErrorType(err) == ErrorTypeAuth {
|
|
return fmt.Errorf("%w: %v", errOracleAuthFailed, err)
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func oracleAlterSession() string {
|
|
_, offset := time.Now().Zone()
|
|
hours := int8(offset / 3600)
|
|
minutes := int8((offset / 60) % 60)
|
|
if minutes < 0 {
|
|
minutes = -minutes
|
|
}
|
|
tz := fmt.Sprintf("%+03d:%02d", hours, minutes)
|
|
return fmt.Sprintf("ALTER SESSION SET NLS_LANGUAGE='AMERICAN' NLS_TERRITORY='AMERICA' TIME_ZONE='%s'\x00", tz)
|
|
}
|
|
|
|
func (s *oracleSession) readMsg(msg uint8) error {
|
|
switch msg {
|
|
case 4:
|
|
sum, err := s.readSummary()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.summary = sum
|
|
case 8:
|
|
size, err := s.getInt(2, true, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := 0; i < size; i++ {
|
|
if _, err = s.getInt(4, true, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, err = s.getInt(2, true, true); err != nil {
|
|
return err
|
|
}
|
|
size, err = s.getInt(2, true, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := 0; i < size; i++ {
|
|
if _, _, _, err = s.getKeyVal(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if s.ttcVersion >= 4 {
|
|
qLen, err := s.getInt(4, true, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if qLen > 0 {
|
|
if _, err = s.getBytes(qLen); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
if s.ttcVersion >= 7 {
|
|
length, err := s.getInt(4, true, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := 0; i < length; i++ {
|
|
if _, err = s.getInt(8, true, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
case 9:
|
|
if s.hasEOSCapability {
|
|
if _, err := s.getInt(4, true, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if s.hasFSAPCapability {
|
|
if _, err := s.getInt(2, true, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case 15:
|
|
if _, err := s.getInt(2, true, true); err != nil {
|
|
return err
|
|
}
|
|
length, err := s.getInt(2, true, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err = s.getInt(2, true, true); err != nil {
|
|
return err
|
|
}
|
|
if length > 0 {
|
|
_, err = s.getClr()
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("oracle TTC unexpected message %d", msg)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *oracleSession) readSummary() (*oracleSummary, error) {
|
|
sum := &oracleSummary{}
|
|
var err error
|
|
if s.hasEOSCapability {
|
|
if _, err = s.getInt(4, true, true); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if s.ttcVersion >= 3 && s.hasFSAPCapability {
|
|
if _, err = s.getInt(2, true, true); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if _, err = s.getInt(4, true, true); err != nil {
|
|
return nil, err
|
|
}
|
|
sum.retCode, err = s.getInt(2, true, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fields := []struct {
|
|
size int
|
|
compress bool
|
|
}{
|
|
{2, true}, {2, true}, {2, true}, {2, true},
|
|
}
|
|
for _, f := range fields {
|
|
if _, err = s.getInt(f.size, f.compress, true); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if _, err = s.getByte(); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = s.getByte(); err != nil {
|
|
return nil, err
|
|
}
|
|
if s.ttcVersion >= 4 {
|
|
if _, err = s.getInt(2, true, true); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = s.getInt(2, true, true); err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
if _, err = s.getByte(); err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = s.getByte(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
for _, size := range []int{1, 1, 4, 2, 1, 4, 2, 4, 1, 1, 2, 4} {
|
|
if size == 1 {
|
|
if _, err = s.getByte(); err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
if _, err = s.getInt(size, true, true); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
_, _ = s.getDlc()
|
|
if s.ttcVersion < 7 {
|
|
_, _ = s.getDlc()
|
|
_, _ = s.getDlc()
|
|
_, _ = s.getDlc()
|
|
} else {
|
|
if err = s.skipSummaryBindBlocks(); err != nil {
|
|
return nil, err
|
|
}
|
|
sum.retCode, err = s.getInt(4, true, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err = s.getInt(8, true, true); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if sum.retCode != 0 {
|
|
sum.errorMessage, err = s.getClr()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return sum, nil
|
|
}
|
|
|
|
func (s *oracleSession) skipSummaryBindBlocks() error {
|
|
length, err := s.getInt(2, true, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if length > 0 {
|
|
flag, err := s.getByte()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
chunked := flag == 0xfe
|
|
for i := 0; i < length; i++ {
|
|
if chunked {
|
|
if s.useBigClrChunks {
|
|
if _, err = s.getInt(4, true, true); err != nil {
|
|
return err
|
|
}
|
|
} else if _, err = s.getByte(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, err = s.getInt(2, true, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if chunked {
|
|
if _, err = s.getByte(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
length, err = s.getInt(4, true, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if length > 0 {
|
|
flag, err := s.getByte()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
chunked := flag == 0xfe
|
|
for i := 0; i < length; i++ {
|
|
if chunked {
|
|
if s.useBigClrChunks {
|
|
if _, err = s.getInt(4, true, true); err != nil {
|
|
return err
|
|
}
|
|
} else if _, err = s.getByte(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if _, err = s.getInt(4, true, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if chunked {
|
|
if _, err = s.getByte(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
length, err = s.getInt(2, true, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := 0; i < length; i++ {
|
|
if _, err = s.getByte(); err != nil {
|
|
return err
|
|
}
|
|
if _, err = s.getInt(2, true, true); err != nil {
|
|
return err
|
|
}
|
|
if _, err = s.getClr(); err != nil {
|
|
return err
|
|
}
|
|
if _, err = s.getByte(); err != nil {
|
|
return err
|
|
}
|
|
if _, err = s.getByte(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *oracleSession) hasError() bool {
|
|
return s.summary != nil && s.summary.retCode != 0 && s.summary.retCode != 1403
|
|
}
|
|
|
|
func (s *oracleSession) oracleError() error {
|
|
if s.summary == nil {
|
|
return errors.New("oracle error")
|
|
}
|
|
msg := string(s.summary.errorMessage)
|
|
if msg == "" {
|
|
msg = fmt.Sprintf("ORA-%05d", s.summary.retCode)
|
|
}
|
|
return fmt.Errorf("%s", msg)
|
|
}
|
|
|
|
func oracleRefuseError(raw []byte) error {
|
|
if len(raw) < 12 {
|
|
return errors.New("oracle connection refused")
|
|
}
|
|
dataLen := int(binary.BigEndian.Uint16(raw[10:12]))
|
|
if len(raw) < 12+dataLen {
|
|
return errors.New("oracle connection refused")
|
|
}
|
|
msg := string(raw[12 : 12+dataLen])
|
|
code := oracleExtractCode(msg)
|
|
if code == 0 {
|
|
return fmt.Errorf("oracle connection refused: %s", msg)
|
|
}
|
|
return fmt.Errorf("ORA-%05d: %s", code, msg)
|
|
}
|
|
|
|
func oracleExtractCode(msg string) int {
|
|
upper := strings.ToUpper(msg)
|
|
for _, marker := range []string{"ERR=", "CODE="} {
|
|
idx := strings.Index(upper, marker)
|
|
if idx < 0 {
|
|
continue
|
|
}
|
|
idx += len(marker)
|
|
for idx < len(upper) && (upper[idx] < '0' || upper[idx] > '9') {
|
|
idx++
|
|
}
|
|
start := idx
|
|
for idx < len(upper) && upper[idx] >= '0' && upper[idx] <= '9' {
|
|
idx++
|
|
}
|
|
if start < idx {
|
|
code, _ := strconv.Atoi(upper[start:idx])
|
|
return code
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func oracleGenerateSpeedyKey(buffer, key []byte, turns int) []byte {
|
|
mac := hmac.New(sha512.New, key)
|
|
mac.Write(append(buffer, 0, 0, 0, 1))
|
|
firstHash := mac.Sum(nil)
|
|
tempHash := make([]byte, len(firstHash))
|
|
copy(tempHash, firstHash)
|
|
for i := 2; i <= turns; i++ {
|
|
mac.Reset()
|
|
mac.Write(tempHash)
|
|
tempHash = mac.Sum(nil)
|
|
for j := 0; j < 64; j++ {
|
|
firstHash[j] ^= tempHash[j]
|
|
}
|
|
}
|
|
return firstHash
|
|
}
|
|
|
|
func oracleKeyFromUserPass(username, password string) ([]byte, error) {
|
|
username = strings.ToUpper(username)
|
|
password = strings.ToUpper(password)
|
|
extend := func(s string) []byte {
|
|
out := make([]byte, len(s)*2)
|
|
for i, c := range []byte(s) {
|
|
out[i*2] = 0
|
|
out[i*2+1] = c
|
|
}
|
|
return out
|
|
}
|
|
buf := append(extend(username), extend(password)...)
|
|
if len(buf)%8 != 0 {
|
|
buf = append(buf, make([]byte, 8-len(buf)%8)...)
|
|
}
|
|
desEnc := func(input, key []byte) ([]byte, error) {
|
|
ret := make([]byte, 8)
|
|
enc, err := des.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := 0; i < len(input)/8; i++ {
|
|
for j := 0; j < 8; j++ {
|
|
ret[j] ^= input[i*8+j]
|
|
}
|
|
out := make([]byte, 8)
|
|
enc.Encrypt(out, ret)
|
|
copy(ret, out)
|
|
}
|
|
return ret, nil
|
|
}
|
|
key1, err := desEnc(buf, []byte{1, 35, 69, 103, 137, 171, 205, 239})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key2, err := desEnc(buf, key1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return append(key2, make([]byte, 8)...), nil
|
|
}
|
|
|
|
func oracleDecryptSessionKey(padding bool, encKey []byte, sessionKey string) ([]byte, error) {
|
|
data, err := hex.DecodeString(sessionKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
blk, err := aes.NewCipher(encKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]byte, len(data))
|
|
cipher.NewCBCDecrypter(blk, make([]byte, 16)).CryptBlocks(out, data)
|
|
cut := 0
|
|
if padding && len(out) > 0 {
|
|
n := int(out[len(out)-1])
|
|
if n < blk.BlockSize() && n <= len(out) {
|
|
ok := true
|
|
for i := len(out) - n; i < len(out); i++ {
|
|
if out[i] != byte(n) {
|
|
ok = false
|
|
break
|
|
}
|
|
}
|
|
if ok {
|
|
cut = n
|
|
}
|
|
}
|
|
}
|
|
return out[:len(out)-cut], nil
|
|
}
|
|
|
|
func oracleEncryptSessionKey(padding bool, encKey, sessionKey []byte) (string, error) {
|
|
blk, err := aes.NewCipher(encKey)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
origLen := len(sessionKey)
|
|
sessionKey = oraclePKCS5Padding(sessionKey, blk.BlockSize())
|
|
out := make([]byte, len(sessionKey))
|
|
cipher.NewCBCEncrypter(blk, make([]byte, 16)).CryptBlocks(out, sessionKey)
|
|
if !padding {
|
|
return fmt.Sprintf("%X", out[:origLen]), nil
|
|
}
|
|
return fmt.Sprintf("%X", out), nil
|
|
}
|
|
|
|
func oracleEncryptPassword(password, key []byte, padding bool) (string, error) {
|
|
prefix := make([]byte, 0x10)
|
|
if _, err := rand.Read(prefix); err != nil {
|
|
return "", err
|
|
}
|
|
return oracleEncryptSessionKey(padding, key, append(prefix, password...))
|
|
}
|
|
|
|
func (auth *oracleAuthObject) passwordEncKey(nego *oracleTCPNego) ([]byte, error) {
|
|
hash := md5.New()
|
|
key1 := auth.serverSessKey
|
|
key2 := auth.clientSessKey
|
|
start := 16
|
|
if len(nego.serverCompileTimeCaps) > 4 && nego.serverCompileTimeCaps[4]&32 != 0 {
|
|
var keyBuffer string
|
|
var retLen int
|
|
switch auth.verifierType {
|
|
case 2361:
|
|
keyBuffer = fmt.Sprintf("%X", append(key2[:len(key2)/2], key1[:len(key1)/2]...))
|
|
retLen = 16
|
|
case 6949:
|
|
keyBuffer = fmt.Sprintf("%X", append(key2[:24], key1[:24]...))
|
|
retLen = 24
|
|
case 18453:
|
|
keyBuffer = fmt.Sprintf("%X", append(key2, key1...))
|
|
retLen = 32
|
|
default:
|
|
return nil, errors.New("unsupported oracle verifier type")
|
|
}
|
|
df2key, err := hex.DecodeString(auth.pbkdf2ChkSalt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return oracleGenerateSpeedyKey(df2key, []byte(keyBuffer), auth.pbkdf2SderCount)[:retLen], nil
|
|
}
|
|
switch auth.verifierType {
|
|
case 2361:
|
|
buf := make([]byte, 16)
|
|
for i := 0; i < 16; i++ {
|
|
buf[i] = key1[i+start] ^ key2[i+start]
|
|
}
|
|
_, _ = hash.Write(buf)
|
|
return hash.Sum(nil), nil
|
|
case 6949:
|
|
buf := make([]byte, 24)
|
|
for i := 0; i < 24; i++ {
|
|
buf[i] = key1[i+start] ^ key2[i+start]
|
|
}
|
|
_, _ = hash.Write(buf[:16])
|
|
ret := hash.Sum(nil)
|
|
hash.Reset()
|
|
_, _ = hash.Write(buf[16:])
|
|
ret = append(ret, hash.Sum(nil)...)
|
|
return ret[:24], nil
|
|
default:
|
|
return nil, errors.New("unsupported oracle verifier type")
|
|
}
|
|
}
|
|
|
|
func oraclePKCS5Padding(src []byte, blockSize int) []byte {
|
|
padding := blockSize - len(src)%blockSize
|
|
return append(src, bytes.Repeat([]byte{byte(padding)}, padding)...)
|
|
}
|