266 lines
6.8 KiB
Go
266 lines
6.8 KiB
Go
package mssql
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
var (
|
|
secur32_dll = syscall.NewLazyDLL("secur32.dll")
|
|
initSecurityInterface = secur32_dll.NewProc("InitSecurityInterfaceW")
|
|
sec_fn *SecurityFunctionTable
|
|
)
|
|
|
|
func init() {
|
|
ptr, _, _ := initSecurityInterface.Call()
|
|
sec_fn = (*SecurityFunctionTable)(unsafe.Pointer(ptr))
|
|
}
|
|
|
|
const (
|
|
SEC_E_OK = 0
|
|
SECPKG_CRED_OUTBOUND = 2
|
|
SEC_WINNT_AUTH_IDENTITY_UNICODE = 2
|
|
ISC_REQ_DELEGATE = 0x00000001
|
|
ISC_REQ_REPLAY_DETECT = 0x00000004
|
|
ISC_REQ_SEQUENCE_DETECT = 0x00000008
|
|
ISC_REQ_CONFIDENTIALITY = 0x00000010
|
|
ISC_REQ_CONNECTION = 0x00000800
|
|
SECURITY_NETWORK_DREP = 0
|
|
SEC_I_CONTINUE_NEEDED = 0x00090312
|
|
SEC_I_COMPLETE_NEEDED = 0x00090313
|
|
SEC_I_COMPLETE_AND_CONTINUE = 0x00090314
|
|
SECBUFFER_VERSION = 0
|
|
SECBUFFER_TOKEN = 2
|
|
NTLMBUF_LEN = 12000
|
|
)
|
|
|
|
const ISC_REQ = ISC_REQ_CONFIDENTIALITY |
|
|
ISC_REQ_REPLAY_DETECT |
|
|
ISC_REQ_SEQUENCE_DETECT |
|
|
ISC_REQ_CONNECTION |
|
|
ISC_REQ_DELEGATE
|
|
|
|
type SecurityFunctionTable struct {
|
|
dwVersion uint32
|
|
EnumerateSecurityPackages uintptr
|
|
QueryCredentialsAttributes uintptr
|
|
AcquireCredentialsHandle uintptr
|
|
FreeCredentialsHandle uintptr
|
|
Reserved2 uintptr
|
|
InitializeSecurityContext uintptr
|
|
AcceptSecurityContext uintptr
|
|
CompleteAuthToken uintptr
|
|
DeleteSecurityContext uintptr
|
|
ApplyControlToken uintptr
|
|
QueryContextAttributes uintptr
|
|
ImpersonateSecurityContext uintptr
|
|
RevertSecurityContext uintptr
|
|
MakeSignature uintptr
|
|
VerifySignature uintptr
|
|
FreeContextBuffer uintptr
|
|
QuerySecurityPackageInfo uintptr
|
|
Reserved3 uintptr
|
|
Reserved4 uintptr
|
|
Reserved5 uintptr
|
|
Reserved6 uintptr
|
|
Reserved7 uintptr
|
|
Reserved8 uintptr
|
|
QuerySecurityContextToken uintptr
|
|
EncryptMessage uintptr
|
|
DecryptMessage uintptr
|
|
}
|
|
|
|
type SEC_WINNT_AUTH_IDENTITY struct {
|
|
User *uint16
|
|
UserLength uint32
|
|
Domain *uint16
|
|
DomainLength uint32
|
|
Password *uint16
|
|
PasswordLength uint32
|
|
Flags uint32
|
|
}
|
|
|
|
type TimeStamp struct {
|
|
LowPart uint32
|
|
HighPart int32
|
|
}
|
|
|
|
type SecHandle struct {
|
|
dwLower uintptr
|
|
dwUpper uintptr
|
|
}
|
|
|
|
type SecBuffer struct {
|
|
cbBuffer uint32
|
|
BufferType uint32
|
|
pvBuffer *byte
|
|
}
|
|
|
|
type SecBufferDesc struct {
|
|
ulVersion uint32
|
|
cBuffers uint32
|
|
pBuffers *SecBuffer
|
|
}
|
|
|
|
type SSPIAuth struct {
|
|
Domain string
|
|
UserName string
|
|
Password string
|
|
Service string
|
|
cred SecHandle
|
|
ctxt SecHandle
|
|
}
|
|
|
|
func getAuth(user, password, service, workstation string) (auth, bool) {
|
|
if user == "" {
|
|
return &SSPIAuth{Service: service}, true
|
|
}
|
|
if !strings.ContainsRune(user, '\\') {
|
|
return nil, false
|
|
}
|
|
domain_user := strings.SplitN(user, "\\", 2)
|
|
return &SSPIAuth{
|
|
Domain: domain_user[0],
|
|
UserName: domain_user[1],
|
|
Password: password,
|
|
Service: service,
|
|
}, true
|
|
}
|
|
|
|
func (auth *SSPIAuth) InitialBytes() ([]byte, error) {
|
|
var identity *SEC_WINNT_AUTH_IDENTITY
|
|
if auth.UserName != "" {
|
|
identity = &SEC_WINNT_AUTH_IDENTITY{
|
|
Flags: SEC_WINNT_AUTH_IDENTITY_UNICODE,
|
|
Password: syscall.StringToUTF16Ptr(auth.Password),
|
|
PasswordLength: uint32(len(auth.Password)),
|
|
Domain: syscall.StringToUTF16Ptr(auth.Domain),
|
|
DomainLength: uint32(len(auth.Domain)),
|
|
User: syscall.StringToUTF16Ptr(auth.UserName),
|
|
UserLength: uint32(len(auth.UserName)),
|
|
}
|
|
}
|
|
var ts TimeStamp
|
|
sec_ok, _, _ := syscall.Syscall9(sec_fn.AcquireCredentialsHandle,
|
|
9,
|
|
0,
|
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Negotiate"))),
|
|
SECPKG_CRED_OUTBOUND,
|
|
0,
|
|
uintptr(unsafe.Pointer(identity)),
|
|
0,
|
|
0,
|
|
uintptr(unsafe.Pointer(&auth.cred)),
|
|
uintptr(unsafe.Pointer(&ts)))
|
|
if sec_ok != SEC_E_OK {
|
|
return nil, fmt.Errorf("AcquireCredentialsHandle failed %x", sec_ok)
|
|
}
|
|
|
|
var buf SecBuffer
|
|
var desc SecBufferDesc
|
|
desc.ulVersion = SECBUFFER_VERSION
|
|
desc.cBuffers = 1
|
|
desc.pBuffers = &buf
|
|
|
|
outbuf := make([]byte, NTLMBUF_LEN)
|
|
buf.cbBuffer = NTLMBUF_LEN
|
|
buf.BufferType = SECBUFFER_TOKEN
|
|
buf.pvBuffer = &outbuf[0]
|
|
|
|
var attrs uint32
|
|
sec_ok, _, _ = syscall.Syscall12(sec_fn.InitializeSecurityContext,
|
|
12,
|
|
uintptr(unsafe.Pointer(&auth.cred)),
|
|
0,
|
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(auth.Service))),
|
|
ISC_REQ,
|
|
0,
|
|
SECURITY_NETWORK_DREP,
|
|
0,
|
|
0,
|
|
uintptr(unsafe.Pointer(&auth.ctxt)),
|
|
uintptr(unsafe.Pointer(&desc)),
|
|
uintptr(unsafe.Pointer(&attrs)),
|
|
uintptr(unsafe.Pointer(&ts)))
|
|
if sec_ok == SEC_I_COMPLETE_AND_CONTINUE ||
|
|
sec_ok == SEC_I_COMPLETE_NEEDED {
|
|
syscall.Syscall6(sec_fn.CompleteAuthToken,
|
|
2,
|
|
uintptr(unsafe.Pointer(&auth.ctxt)),
|
|
uintptr(unsafe.Pointer(&desc)),
|
|
0, 0, 0, 0)
|
|
} else if sec_ok != SEC_E_OK &&
|
|
sec_ok != SEC_I_CONTINUE_NEEDED {
|
|
syscall.Syscall6(sec_fn.FreeCredentialsHandle,
|
|
1,
|
|
uintptr(unsafe.Pointer(&auth.cred)),
|
|
0, 0, 0, 0, 0)
|
|
return nil, fmt.Errorf("InitialBytes InitializeSecurityContext failed %x", sec_ok)
|
|
}
|
|
return outbuf[:buf.cbBuffer], nil
|
|
}
|
|
|
|
func (auth *SSPIAuth) NextBytes(bytes []byte) ([]byte, error) {
|
|
var in_buf, out_buf SecBuffer
|
|
var in_desc, out_desc SecBufferDesc
|
|
|
|
in_desc.ulVersion = SECBUFFER_VERSION
|
|
in_desc.cBuffers = 1
|
|
in_desc.pBuffers = &in_buf
|
|
|
|
out_desc.ulVersion = SECBUFFER_VERSION
|
|
out_desc.cBuffers = 1
|
|
out_desc.pBuffers = &out_buf
|
|
|
|
in_buf.BufferType = SECBUFFER_TOKEN
|
|
in_buf.pvBuffer = &bytes[0]
|
|
in_buf.cbBuffer = uint32(len(bytes))
|
|
|
|
outbuf := make([]byte, NTLMBUF_LEN)
|
|
out_buf.BufferType = SECBUFFER_TOKEN
|
|
out_buf.pvBuffer = &outbuf[0]
|
|
out_buf.cbBuffer = NTLMBUF_LEN
|
|
|
|
var attrs uint32
|
|
var ts TimeStamp
|
|
sec_ok, _, _ := syscall.Syscall12(sec_fn.InitializeSecurityContext,
|
|
12,
|
|
uintptr(unsafe.Pointer(&auth.cred)),
|
|
uintptr(unsafe.Pointer(&auth.ctxt)),
|
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(auth.Service))),
|
|
ISC_REQ,
|
|
0,
|
|
SECURITY_NETWORK_DREP,
|
|
uintptr(unsafe.Pointer(&in_desc)),
|
|
0,
|
|
uintptr(unsafe.Pointer(&auth.ctxt)),
|
|
uintptr(unsafe.Pointer(&out_desc)),
|
|
uintptr(unsafe.Pointer(&attrs)),
|
|
uintptr(unsafe.Pointer(&ts)))
|
|
if sec_ok == SEC_I_COMPLETE_AND_CONTINUE ||
|
|
sec_ok == SEC_I_COMPLETE_NEEDED {
|
|
syscall.Syscall6(sec_fn.CompleteAuthToken,
|
|
2,
|
|
uintptr(unsafe.Pointer(&auth.ctxt)),
|
|
uintptr(unsafe.Pointer(&out_desc)),
|
|
0, 0, 0, 0)
|
|
} else if sec_ok != SEC_E_OK &&
|
|
sec_ok != SEC_I_CONTINUE_NEEDED {
|
|
return nil, fmt.Errorf("NextBytes InitializeSecurityContext failed %x", sec_ok)
|
|
}
|
|
|
|
return outbuf[:out_buf.cbBuffer], nil
|
|
}
|
|
|
|
func (auth *SSPIAuth) Free() {
|
|
syscall.Syscall6(sec_fn.DeleteSecurityContext,
|
|
1,
|
|
uintptr(unsafe.Pointer(&auth.ctxt)),
|
|
0, 0, 0, 0, 0)
|
|
syscall.Syscall6(sec_fn.FreeCredentialsHandle,
|
|
1,
|
|
uintptr(unsafe.Pointer(&auth.cred)),
|
|
0, 0, 0, 0, 0)
|
|
}
|