forked from forgejo/forgejo
[Vendor] mssqldb: 2019-11-28 -> 2020-04-28 (#11364)
update go-mssqldb 2019-11-28 (1d7a30a10f73) -> 2020-04-28 (06a60b6afbbc)
This commit is contained in:
parent
da5e3fa299
commit
6e23a1b843
27 changed files with 987 additions and 1378 deletions
22
vendor/github.com/denisenkom/go-mssqldb/README.md
generated
vendored
22
vendor/github.com/denisenkom/go-mssqldb/README.md
generated
vendored
|
@ -18,7 +18,7 @@ Other supported formats are listed below.
|
|||
|
||||
### Common parameters:
|
||||
|
||||
* `user id` - enter the SQL Server Authentication user id or the Windows Authentication user id in the DOMAIN\User format. On Windows, if user id is empty or missing Single-Sign-On is used.
|
||||
* `user id` - enter the SQL Server Authentication user id or the Windows Authentication user id in the DOMAIN\User format. On Windows, if user id is empty or missing Single-Sign-On is used. The user domain sensitive to the case which is defined in the connection string.
|
||||
* `password`
|
||||
* `database`
|
||||
* `connection timeout` - in seconds (default is 0 for no timeout), set to 0 for no timeout. Recommended to set to 0 and use context to manage query and connection timeouts.
|
||||
|
@ -106,6 +106,26 @@ Other supported formats are listed below.
|
|||
* `odbc:server=localhost;user id=sa;password={foo{bar}` // Literal `{`, password is "foo{bar"
|
||||
* `odbc:server=localhost;user id=sa;password={foo}}bar}` // Escaped `} with `}}`, password is "foo}bar"
|
||||
|
||||
### Azure Active Directory authentication - preview
|
||||
|
||||
The configuration of functionality might change in the future.
|
||||
|
||||
Azure Active Directory (AAD) access tokens are relatively short lived and need to be
|
||||
valid when a new connection is made. Authentication is supported using a callback func that
|
||||
provides a fresh and valid token using a connector:
|
||||
``` golang
|
||||
conn, err := mssql.NewAccessTokenConnector(
|
||||
"Server=test.database.windows.net;Database=testdb",
|
||||
tokenProvider)
|
||||
if err != nil {
|
||||
// handle errors in DSN
|
||||
}
|
||||
db := sql.OpenDB(conn)
|
||||
```
|
||||
Where `tokenProvider` is a function that returns a fresh access token or an error. None of these statements
|
||||
actually trigger the retrieval of a token, this happens when the first statment is issued and a connection
|
||||
is created.
|
||||
|
||||
## Executing Stored Procedures
|
||||
|
||||
To run a stored procedure, set the query text to the procedure name:
|
||||
|
|
51
vendor/github.com/denisenkom/go-mssqldb/accesstokenconnector.go
generated
vendored
Normal file
51
vendor/github.com/denisenkom/go-mssqldb/accesstokenconnector.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// +build go1.10
|
||||
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var _ driver.Connector = &accessTokenConnector{}
|
||||
|
||||
// accessTokenConnector wraps Connector and injects a
|
||||
// fresh access token when connecting to the database
|
||||
type accessTokenConnector struct {
|
||||
Connector
|
||||
|
||||
accessTokenProvider func() (string, error)
|
||||
}
|
||||
|
||||
// NewAccessTokenConnector creates a new connector from a DSN and a token provider.
|
||||
// The token provider func will be called when a new connection is requested and should return a valid access token.
|
||||
// The returned connector may be used with sql.OpenDB.
|
||||
func NewAccessTokenConnector(dsn string, tokenProvider func() (string, error)) (driver.Connector, error) {
|
||||
if tokenProvider == nil {
|
||||
return nil, errors.New("mssql: tokenProvider cannot be nil")
|
||||
}
|
||||
|
||||
conn, err := NewConnector(dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &accessTokenConnector{
|
||||
Connector: *conn,
|
||||
accessTokenProvider: tokenProvider,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Connect returns a new database connection
|
||||
func (c *accessTokenConnector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
var err error
|
||||
c.Connector.params.fedAuthAccessToken, err = c.accessTokenProvider()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mssql: error retrieving access token: %+v", err)
|
||||
}
|
||||
|
||||
return c.Connector.Connect(ctx)
|
||||
}
|
1
vendor/github.com/denisenkom/go-mssqldb/conn_str.go
generated
vendored
1
vendor/github.com/denisenkom/go-mssqldb/conn_str.go
generated
vendored
|
@ -37,6 +37,7 @@ type connectParams struct {
|
|||
failOverPartner string
|
||||
failOverPort uint64
|
||||
packetSize uint16
|
||||
fedAuthAccessToken string
|
||||
}
|
||||
|
||||
func parseConnectParams(dsn string) (connectParams, error) {
|
||||
|
|
5
vendor/github.com/denisenkom/go-mssqldb/mssql.go
generated
vendored
5
vendor/github.com/denisenkom/go-mssqldb/mssql.go
generated
vendored
|
@ -397,7 +397,10 @@ func (s *Stmt) Close() error {
|
|||
}
|
||||
|
||||
func (s *Stmt) SetQueryNotification(id, options string, timeout time.Duration) {
|
||||
to := uint32(timeout / time.Second)
|
||||
// 2.2.5.3.1 Query Notifications Header
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/e168d373-a7b7-41aa-b6ca-25985466a7e0
|
||||
// Timeout in milliseconds in TDS protocol.
|
||||
to := uint32(timeout / time.Millisecond)
|
||||
if to < 1 {
|
||||
to = 1
|
||||
}
|
||||
|
|
177
vendor/github.com/denisenkom/go-mssqldb/ntlm.go
generated
vendored
177
vendor/github.com/denisenkom/go-mssqldb/ntlm.go
generated
vendored
|
@ -4,11 +4,14 @@ package mssql
|
|||
|
||||
import (
|
||||
"crypto/des"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf16"
|
||||
|
||||
"golang.org/x/crypto/md4"
|
||||
|
@ -198,86 +201,204 @@ func ntlmSessionResponse(clientNonce [8]byte, serverChallenge [8]byte, password
|
|||
return response(hash, passwordHash)
|
||||
}
|
||||
|
||||
func (auth *ntlmAuth) NextBytes(bytes []byte) ([]byte, error) {
|
||||
if string(bytes[0:8]) != "NTLMSSP\x00" {
|
||||
return nil, errorNTLM
|
||||
}
|
||||
if binary.LittleEndian.Uint32(bytes[8:12]) != _CHALLENGE_MESSAGE {
|
||||
return nil, errorNTLM
|
||||
}
|
||||
flags := binary.LittleEndian.Uint32(bytes[20:24])
|
||||
var challenge [8]byte
|
||||
copy(challenge[:], bytes[24:32])
|
||||
func ntlmHashNoPadding(val string) []byte {
|
||||
hash := make([]byte, 16)
|
||||
h := md4.New()
|
||||
h.Write(utf16le(val))
|
||||
h.Sum(hash[:0])
|
||||
|
||||
var lm, nt []byte
|
||||
if (flags & _NEGOTIATE_EXTENDED_SESSIONSECURITY) != 0 {
|
||||
nonce := clientChallenge()
|
||||
var lm_bytes [24]byte
|
||||
copy(lm_bytes[:8], nonce[:])
|
||||
lm = lm_bytes[:]
|
||||
nt_bytes := ntlmSessionResponse(nonce, challenge, auth.Password)
|
||||
nt = nt_bytes[:]
|
||||
} else {
|
||||
lm_bytes := lmResponse(challenge, auth.Password)
|
||||
lm = lm_bytes[:]
|
||||
nt_bytes := ntResponse(challenge, auth.Password)
|
||||
nt = nt_bytes[:]
|
||||
return hash
|
||||
}
|
||||
|
||||
func hmacMD5(passwordHash, data []byte) []byte {
|
||||
hmacEntity := hmac.New(md5.New, passwordHash)
|
||||
hmacEntity.Write(data)
|
||||
|
||||
return hmacEntity.Sum(nil)
|
||||
}
|
||||
|
||||
func getNTLMv2AndLMv2ResponsePayloads(userDomain, username, password string, challenge, nonce [8]byte, targetInfoFields []byte, timestamp time.Time) (ntlmV2Payload, lmV2Payload []byte) {
|
||||
// NTLMv2 response payload: http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response
|
||||
|
||||
ntlmHash := ntlmHashNoPadding(password)
|
||||
usernameAndTargetBytes := utf16le(strings.ToUpper(username) + userDomain)
|
||||
ntlmV2Hash := hmacMD5(ntlmHash, usernameAndTargetBytes)
|
||||
targetInfoLength := len(targetInfoFields)
|
||||
blob := make([]byte, 32+targetInfoLength)
|
||||
binary.BigEndian.PutUint32(blob[:4], 0x01010000)
|
||||
binary.BigEndian.PutUint32(blob[4:8], 0x00000000)
|
||||
binary.BigEndian.PutUint64(blob[8:16], uint64(timestamp.UnixNano()))
|
||||
copy(blob[16:24], nonce[:])
|
||||
binary.BigEndian.PutUint32(blob[24:28], 0x00000000)
|
||||
copy(blob[28:], targetInfoFields)
|
||||
binary.BigEndian.PutUint32(blob[28+targetInfoLength:], 0x00000000)
|
||||
challengeLength := len(challenge)
|
||||
blobLength := len(blob)
|
||||
challengeAndBlob := make([]byte, challengeLength+blobLength)
|
||||
copy(challengeAndBlob[:challengeLength], challenge[:])
|
||||
copy(challengeAndBlob[challengeLength:], blob)
|
||||
hashedChallenge := hmacMD5(ntlmV2Hash, challengeAndBlob)
|
||||
ntlmV2Payload = append(hashedChallenge, blob...)
|
||||
|
||||
// LMv2 response payload: http://davenport.sourceforge.net/ntlm.html#theLmv2Response
|
||||
ntlmV2hash := hmacMD5(ntlmHash, usernameAndTargetBytes)
|
||||
challengeAndNonce := make([]byte, 16)
|
||||
copy(challengeAndNonce[:8], challenge[:])
|
||||
copy(challengeAndNonce[8:], nonce[:])
|
||||
hashedChallenge = hmacMD5(ntlmV2hash, challengeAndNonce)
|
||||
lmV2Payload = append(hashedChallenge, nonce[:]...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func negotiateExtendedSessionSecurity(flags uint32, message []byte, challenge [8]byte, username, password, userDom string) (lm, nt []byte, err error) {
|
||||
nonce := clientChallenge()
|
||||
|
||||
// Official specification: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b38c36ed-2804-4868-a9ff-8dd3182128e4
|
||||
// Unofficial walk through referenced by https://www.freetds.org/userguide/domains.htm: http://davenport.sourceforge.net/ntlm.html
|
||||
if (flags & _NEGOTIATE_TARGET_INFO) != 0 {
|
||||
targetInfoFields, err := getNTLMv2TargetInfoFields(message)
|
||||
if err != nil {
|
||||
return lm, nt, err
|
||||
}
|
||||
|
||||
nt, lm = getNTLMv2AndLMv2ResponsePayloads(userDom, username, password, challenge, nonce, targetInfoFields, time.Now())
|
||||
|
||||
return lm, nt, nil
|
||||
}
|
||||
|
||||
var lm_bytes [24]byte
|
||||
copy(lm_bytes[:8], nonce[:])
|
||||
lm = lm_bytes[:]
|
||||
nt_bytes := ntlmSessionResponse(nonce, challenge, password)
|
||||
nt = nt_bytes[:]
|
||||
|
||||
return lm, nt, nil
|
||||
}
|
||||
|
||||
func getNTLMv2TargetInfoFields(type2Message []byte) (info []byte, err error) {
|
||||
type2MessageError := "mssql: while parsing NTLMv2 type 2 message, length %d too small for offset %d"
|
||||
type2MessageLength := len(type2Message)
|
||||
if type2MessageLength < 20 {
|
||||
return nil, fmt.Errorf(type2MessageError, type2MessageLength, 20)
|
||||
}
|
||||
|
||||
targetNameAllocated := binary.LittleEndian.Uint16(type2Message[14:16])
|
||||
targetNameOffset := binary.LittleEndian.Uint32(type2Message[16:20])
|
||||
endOfOffset := int(targetNameOffset + uint32(targetNameAllocated))
|
||||
if type2MessageLength < endOfOffset {
|
||||
return nil, fmt.Errorf(type2MessageError, type2MessageLength, endOfOffset)
|
||||
}
|
||||
|
||||
targetInformationAllocated := binary.LittleEndian.Uint16(type2Message[42:44])
|
||||
targetInformationDataOffset := binary.LittleEndian.Uint32(type2Message[44:48])
|
||||
endOfOffset = int(targetInformationDataOffset + uint32(targetInformationAllocated))
|
||||
if type2MessageLength < endOfOffset {
|
||||
return nil, fmt.Errorf(type2MessageError, type2MessageLength, endOfOffset)
|
||||
}
|
||||
|
||||
targetInformationBytes := make([]byte, targetInformationAllocated)
|
||||
copy(targetInformationBytes, type2Message[targetInformationDataOffset:targetInformationDataOffset+uint32(targetInformationAllocated)])
|
||||
|
||||
return targetInformationBytes, nil
|
||||
}
|
||||
|
||||
func buildNTLMResponsePayload(lm, nt []byte, flags uint32, domain, workstation, username string) ([]byte, error) {
|
||||
lm_len := len(lm)
|
||||
nt_len := len(nt)
|
||||
|
||||
domain16 := utf16le(auth.Domain)
|
||||
domain16 := utf16le(domain)
|
||||
domain_len := len(domain16)
|
||||
user16 := utf16le(auth.UserName)
|
||||
user16 := utf16le(username)
|
||||
user_len := len(user16)
|
||||
workstation16 := utf16le(auth.Workstation)
|
||||
workstation16 := utf16le(workstation)
|
||||
workstation_len := len(workstation16)
|
||||
|
||||
msg := make([]byte, 88+lm_len+nt_len+domain_len+user_len+workstation_len)
|
||||
copy(msg, []byte("NTLMSSP\x00"))
|
||||
binary.LittleEndian.PutUint32(msg[8:], _AUTHENTICATE_MESSAGE)
|
||||
|
||||
// Lm Challenge Response Fields
|
||||
binary.LittleEndian.PutUint16(msg[12:], uint16(lm_len))
|
||||
binary.LittleEndian.PutUint16(msg[14:], uint16(lm_len))
|
||||
binary.LittleEndian.PutUint32(msg[16:], 88)
|
||||
|
||||
// Nt Challenge Response Fields
|
||||
binary.LittleEndian.PutUint16(msg[20:], uint16(nt_len))
|
||||
binary.LittleEndian.PutUint16(msg[22:], uint16(nt_len))
|
||||
binary.LittleEndian.PutUint32(msg[24:], uint32(88+lm_len))
|
||||
|
||||
// Domain Name Fields
|
||||
binary.LittleEndian.PutUint16(msg[28:], uint16(domain_len))
|
||||
binary.LittleEndian.PutUint16(msg[30:], uint16(domain_len))
|
||||
binary.LittleEndian.PutUint32(msg[32:], uint32(88+lm_len+nt_len))
|
||||
|
||||
// User Name Fields
|
||||
binary.LittleEndian.PutUint16(msg[36:], uint16(user_len))
|
||||
binary.LittleEndian.PutUint16(msg[38:], uint16(user_len))
|
||||
binary.LittleEndian.PutUint32(msg[40:], uint32(88+lm_len+nt_len+domain_len))
|
||||
|
||||
// Workstation Fields
|
||||
binary.LittleEndian.PutUint16(msg[44:], uint16(workstation_len))
|
||||
binary.LittleEndian.PutUint16(msg[46:], uint16(workstation_len))
|
||||
binary.LittleEndian.PutUint32(msg[48:], uint32(88+lm_len+nt_len+domain_len+user_len))
|
||||
|
||||
// Encrypted Random Session Key Fields
|
||||
binary.LittleEndian.PutUint16(msg[52:], 0)
|
||||
binary.LittleEndian.PutUint16(msg[54:], 0)
|
||||
binary.LittleEndian.PutUint32(msg[56:], uint32(88+lm_len+nt_len+domain_len+user_len+workstation_len))
|
||||
|
||||
// Negotiate Flags
|
||||
binary.LittleEndian.PutUint32(msg[60:], flags)
|
||||
|
||||
// Version
|
||||
binary.LittleEndian.PutUint32(msg[64:], 0)
|
||||
binary.LittleEndian.PutUint32(msg[68:], 0)
|
||||
|
||||
// MIC
|
||||
binary.LittleEndian.PutUint32(msg[72:], 0)
|
||||
binary.LittleEndian.PutUint32(msg[76:], 0)
|
||||
binary.LittleEndian.PutUint32(msg[88:], 0)
|
||||
binary.LittleEndian.PutUint32(msg[84:], 0)
|
||||
|
||||
// Payload
|
||||
copy(msg[88:], lm)
|
||||
copy(msg[88+lm_len:], nt)
|
||||
copy(msg[88+lm_len+nt_len:], domain16)
|
||||
copy(msg[88+lm_len+nt_len+domain_len:], user16)
|
||||
copy(msg[88+lm_len+nt_len+domain_len+user_len:], workstation16)
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func (auth *ntlmAuth) NextBytes(bytes []byte) ([]byte, error) {
|
||||
signature := string(bytes[0:8])
|
||||
if signature != "NTLMSSP\x00" {
|
||||
return nil, errorNTLM
|
||||
}
|
||||
|
||||
messageTypeIndicator := binary.LittleEndian.Uint32(bytes[8:12])
|
||||
if messageTypeIndicator != _CHALLENGE_MESSAGE {
|
||||
return nil, errorNTLM
|
||||
}
|
||||
|
||||
var challenge [8]byte
|
||||
copy(challenge[:], bytes[24:32])
|
||||
flags := binary.LittleEndian.Uint32(bytes[20:24])
|
||||
if (flags & _NEGOTIATE_EXTENDED_SESSIONSECURITY) != 0 {
|
||||
lm, nt, err := negotiateExtendedSessionSecurity(flags, bytes, challenge, auth.UserName, auth.Password, auth.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buildNTLMResponsePayload(lm, nt, flags, auth.Domain, auth.Workstation, auth.UserName)
|
||||
}
|
||||
|
||||
lm_bytes := lmResponse(challenge, auth.Password)
|
||||
lm := lm_bytes[:]
|
||||
nt_bytes := ntResponse(challenge, auth.Password)
|
||||
nt := nt_bytes[:]
|
||||
|
||||
return buildNTLMResponsePayload(lm, nt, flags, auth.Domain, auth.Workstation, auth.UserName)
|
||||
}
|
||||
|
||||
func (auth *ntlmAuth) Free() {
|
||||
}
|
||||
|
|
146
vendor/github.com/denisenkom/go-mssqldb/tds.go
generated
vendored
146
vendor/github.com/denisenkom/go-mssqldb/tds.go
generated
vendored
|
@ -100,13 +100,15 @@ const (
|
|||
// prelogin fields
|
||||
// http://msdn.microsoft.com/en-us/library/dd357559.aspx
|
||||
const (
|
||||
preloginVERSION = 0
|
||||
preloginENCRYPTION = 1
|
||||
preloginINSTOPT = 2
|
||||
preloginTHREADID = 3
|
||||
preloginMARS = 4
|
||||
preloginTRACEID = 5
|
||||
preloginTERMINATOR = 0xff
|
||||
preloginVERSION = 0
|
||||
preloginENCRYPTION = 1
|
||||
preloginINSTOPT = 2
|
||||
preloginTHREADID = 3
|
||||
preloginMARS = 4
|
||||
preloginTRACEID = 5
|
||||
preloginFEDAUTHREQUIRED = 6
|
||||
preloginNONCEOPT = 7
|
||||
preloginTERMINATOR = 0xff
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -245,6 +247,12 @@ const (
|
|||
fReadOnlyIntent = 32
|
||||
)
|
||||
|
||||
// OptionFlags3
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/773a62b6-ee89-4c02-9e5e-344882630aac
|
||||
const (
|
||||
fExtension = 0x10
|
||||
)
|
||||
|
||||
type login struct {
|
||||
TDSVersion uint32
|
||||
PacketSize uint32
|
||||
|
@ -269,6 +277,89 @@ type login struct {
|
|||
SSPI []byte
|
||||
AtchDBFile string
|
||||
ChangePassword string
|
||||
FeatureExt featureExts
|
||||
}
|
||||
|
||||
type featureExts struct {
|
||||
features map[byte]featureExt
|
||||
}
|
||||
|
||||
type featureExt interface {
|
||||
featureID() byte
|
||||
toBytes() []byte
|
||||
}
|
||||
|
||||
func (e *featureExts) Add(f featureExt) error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
id := f.featureID()
|
||||
if _, exists := e.features[id]; exists {
|
||||
f := "Login error: Feature with ID '%v' is already present in FeatureExt block."
|
||||
return fmt.Errorf(f, id)
|
||||
}
|
||||
if e.features == nil {
|
||||
e.features = make(map[byte]featureExt)
|
||||
}
|
||||
e.features[id] = f
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e featureExts) toBytes() []byte {
|
||||
if len(e.features) == 0 {
|
||||
return nil
|
||||
}
|
||||
var d []byte
|
||||
for featureID, f := range e.features {
|
||||
featureData := f.toBytes()
|
||||
|
||||
hdr := make([]byte, 5)
|
||||
hdr[0] = featureID // FedAuth feature extension BYTE
|
||||
binary.LittleEndian.PutUint32(hdr[1:], uint32(len(featureData))) // FeatureDataLen DWORD
|
||||
d = append(d, hdr...)
|
||||
|
||||
d = append(d, featureData...) // FeatureData *BYTE
|
||||
}
|
||||
if d != nil {
|
||||
d = append(d, 0xff) // Terminator
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
type featureExtFedAuthSTS struct {
|
||||
FedAuthEcho bool
|
||||
FedAuthToken string
|
||||
Nonce []byte
|
||||
}
|
||||
|
||||
func (e *featureExtFedAuthSTS) featureID() byte {
|
||||
return 0x02
|
||||
}
|
||||
|
||||
func (e *featureExtFedAuthSTS) toBytes() []byte {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
options := byte(0x01) << 1 // 0x01 => STS bFedAuthLibrary 7BIT
|
||||
if e.FedAuthEcho {
|
||||
options |= 1 // fFedAuthEcho
|
||||
}
|
||||
|
||||
d := make([]byte, 5)
|
||||
d[0] = options
|
||||
|
||||
// looks like string in
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/f88b63bb-b479-49e1-a87b-deda521da508
|
||||
tokenBytes := str2ucs2(e.FedAuthToken)
|
||||
binary.LittleEndian.PutUint32(d[1:], uint32(len(tokenBytes))) // Should be a signed int32, but since the length is relatively small, this should work
|
||||
d = append(d, tokenBytes...)
|
||||
|
||||
if len(e.Nonce) == 32 {
|
||||
d = append(d, e.Nonce...)
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
type loginHeader struct {
|
||||
|
@ -295,7 +386,7 @@ type loginHeader struct {
|
|||
ServerNameOffset uint16
|
||||
ServerNameLength uint16
|
||||
ExtensionOffset uint16
|
||||
ExtensionLenght uint16
|
||||
ExtensionLength uint16
|
||||
CtlIntNameOffset uint16
|
||||
CtlIntNameLength uint16
|
||||
LanguageOffset uint16
|
||||
|
@ -357,6 +448,8 @@ func sendLogin(w *tdsBuffer, login login) error {
|
|||
database := str2ucs2(login.Database)
|
||||
atchdbfile := str2ucs2(login.AtchDBFile)
|
||||
changepassword := str2ucs2(login.ChangePassword)
|
||||
featureExt := login.FeatureExt.toBytes()
|
||||
|
||||
hdr := loginHeader{
|
||||
TDSVersion: login.TDSVersion,
|
||||
PacketSize: login.PacketSize,
|
||||
|
@ -405,7 +498,18 @@ func sendLogin(w *tdsBuffer, login login) error {
|
|||
offset += uint16(len(atchdbfile))
|
||||
hdr.ChangePasswordOffset = offset
|
||||
offset += uint16(len(changepassword))
|
||||
hdr.Length = uint32(offset)
|
||||
|
||||
featureExtOffset := uint32(0)
|
||||
featureExtLen := len(featureExt)
|
||||
if featureExtLen > 0 {
|
||||
hdr.OptionFlags3 |= fExtension
|
||||
hdr.ExtensionOffset = offset
|
||||
hdr.ExtensionLength = 4
|
||||
offset += hdr.ExtensionLength // DWORD
|
||||
featureExtOffset = uint32(offset)
|
||||
}
|
||||
hdr.Length = uint32(offset) + uint32(featureExtLen)
|
||||
|
||||
var err error
|
||||
err = binary.Write(w, binary.LittleEndian, &hdr)
|
||||
if err != nil {
|
||||
|
@ -455,6 +559,16 @@ func sendLogin(w *tdsBuffer, login login) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if featureExtOffset > 0 {
|
||||
err = binary.Write(w, binary.LittleEndian, featureExtOffset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(featureExt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return w.FinishPacket()
|
||||
}
|
||||
|
||||
|
@ -844,15 +958,23 @@ initiate_connection:
|
|||
AppName: p.appname,
|
||||
TypeFlags: p.typeFlags,
|
||||
}
|
||||
auth, auth_ok := getAuth(p.user, p.password, p.serverSPN, p.workstation)
|
||||
if auth_ok {
|
||||
auth, authOk := getAuth(p.user, p.password, p.serverSPN, p.workstation)
|
||||
switch {
|
||||
case p.fedAuthAccessToken != "": // accesstoken ignores user/password
|
||||
featurext := &featureExtFedAuthSTS{
|
||||
FedAuthEcho: len(fields[preloginFEDAUTHREQUIRED]) > 0 && fields[preloginFEDAUTHREQUIRED][0] == 1,
|
||||
FedAuthToken: p.fedAuthAccessToken,
|
||||
Nonce: fields[preloginNONCEOPT],
|
||||
}
|
||||
login.FeatureExt.Add(featurext)
|
||||
case authOk:
|
||||
login.SSPI, err = auth.InitialBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
login.OptionFlags2 |= fIntSecurity
|
||||
defer auth.Free()
|
||||
} else {
|
||||
default:
|
||||
login.UserName = p.user
|
||||
login.Password = p.password
|
||||
}
|
||||
|
|
47
vendor/github.com/denisenkom/go-mssqldb/token.go
generated
vendored
47
vendor/github.com/denisenkom/go-mssqldb/token.go
generated
vendored
|
@ -17,20 +17,21 @@ type token byte
|
|||
|
||||
// token ids
|
||||
const (
|
||||
tokenReturnStatus token = 121 // 0x79
|
||||
tokenColMetadata token = 129 // 0x81
|
||||
tokenOrder token = 169 // 0xA9
|
||||
tokenError token = 170 // 0xAA
|
||||
tokenInfo token = 171 // 0xAB
|
||||
tokenReturnValue token = 0xAC
|
||||
tokenLoginAck token = 173 // 0xad
|
||||
tokenRow token = 209 // 0xd1
|
||||
tokenNbcRow token = 210 // 0xd2
|
||||
tokenEnvChange token = 227 // 0xE3
|
||||
tokenSSPI token = 237 // 0xED
|
||||
tokenDone token = 253 // 0xFD
|
||||
tokenDoneProc token = 254
|
||||
tokenDoneInProc token = 255
|
||||
tokenReturnStatus token = 121 // 0x79
|
||||
tokenColMetadata token = 129 // 0x81
|
||||
tokenOrder token = 169 // 0xA9
|
||||
tokenError token = 170 // 0xAA
|
||||
tokenInfo token = 171 // 0xAB
|
||||
tokenReturnValue token = 0xAC
|
||||
tokenLoginAck token = 173 // 0xad
|
||||
tokenFeatureExtAck token = 174 // 0xae
|
||||
tokenRow token = 209 // 0xd1
|
||||
tokenNbcRow token = 210 // 0xd2
|
||||
tokenEnvChange token = 227 // 0xE3
|
||||
tokenSSPI token = 237 // 0xED
|
||||
tokenDone token = 253 // 0xFD
|
||||
tokenDoneProc token = 254
|
||||
tokenDoneInProc token = 255
|
||||
)
|
||||
|
||||
// done flags
|
||||
|
@ -447,6 +448,22 @@ func parseLoginAck(r *tdsBuffer) loginAckStruct {
|
|||
return res
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/2eb82f8e-11f0-46dc-b42d-27302fa4701a
|
||||
func parseFeatureExtAck(r *tdsBuffer) {
|
||||
// at most 1 featureAck per feature in featureExt
|
||||
// go-mssqldb will add at most 1 feature, the spec defines 7 different features
|
||||
for i := 0; i < 8; i++ {
|
||||
featureID := r.byte() // FeatureID
|
||||
if featureID == 0xff {
|
||||
return
|
||||
}
|
||||
size := r.uint32() // FeatureAckDataLen
|
||||
d := make([]byte, size)
|
||||
r.ReadFull(d)
|
||||
}
|
||||
panic("parsed more than 7 featureAck's, protocol implementation error?")
|
||||
}
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/dd357363.aspx
|
||||
func parseColMetadata72(r *tdsBuffer) (columns []columnStruct) {
|
||||
count := r.uint16()
|
||||
|
@ -577,6 +594,8 @@ func processSingleResponse(sess *tdsSession, ch chan tokenStruct, outs map[strin
|
|||
case tokenLoginAck:
|
||||
loginAck := parseLoginAck(sess.buf)
|
||||
ch <- loginAck
|
||||
case tokenFeatureExtAck:
|
||||
parseFeatureExtAck(sess.buf)
|
||||
case tokenOrder:
|
||||
order := parseOrder(sess.buf)
|
||||
ch <- order
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue