-
Notifications
You must be signed in to change notification settings - Fork 88
Draft: Add support for EPA #295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "bufio" | ||
| "context" | ||
| "crypto/tls" | ||
| "database/sql" | ||
| "flag" | ||
| "fmt" | ||
| "io" | ||
| "log" | ||
| "os" | ||
| "time" | ||
|
|
||
| // mssqldb "github.com/denisenkom/go-mssqldb" | ||
| // "github.com/denisenkom/go-mssqldb/msdsn" | ||
| "github.com/google/uuid" | ||
| mssqldb "github.com/microsoft/go-mssqldb" | ||
| "github.com/microsoft/go-mssqldb/msdsn" | ||
| _ "github.com/microsoft/go-mssqldb/integratedauth/krb5" | ||
| ) | ||
|
|
||
| func main() { | ||
| var ( | ||
| userid = flag.String("U", "", "login_id") | ||
| password = flag.String("P", "", "password") | ||
| server = flag.String("S", "localhost", "server_name[\\instance_name]") | ||
| port = flag.Uint64("p", 1433, "server port") | ||
| keyLog = flag.String("K", "tlslog.log", "path to sslkeylog file") | ||
| database = flag.String("d", "", "db_name") | ||
| spn = flag.String("spn", "", "SPN") | ||
| auth = flag.String("a", "ntlm", "Authentication method: ntlm or krb5") | ||
| ) | ||
| flag.Parse() | ||
|
|
||
| keyLogFile, err := os.OpenFile(*keyLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) | ||
| if err != nil { | ||
| log.Fatal("failed to open keylog file:", err) | ||
| } | ||
| defer keyLogFile.Close() | ||
paraddise marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| cfg := msdsn.Config{ | ||
| User: *userid, | ||
| Database: *database, | ||
| Host: *server, | ||
| Port: *port, | ||
| Password: *password, | ||
| ChangePassword: "", | ||
| AppName: "go-mssqldb", | ||
| ServerSPN: *spn, | ||
| TLSConfig: &tls.Config{ | ||
| InsecureSkipVerify: true, // adjust for your case | ||
| ServerName: *server, | ||
| KeyLogWriter: keyLogFile, | ||
| DynamicRecordSizingDisabled: true, | ||
| MinVersion: tls.VersionTLS11, | ||
| MaxVersion: tls.VersionTLS12, | ||
| }, | ||
| Encryption: msdsn.EncryptionRequired, | ||
|
|
||
| Parameters: map[string]string{ | ||
| "authenticator": *auth, | ||
| "krb5-credcachefile": "/tmp/krb5cc_719880", | ||
paraddise marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "krb5-configfile": "/etc/krb5.conf", | ||
| }, | ||
| ProtocolParameters: map[string]interface{}{ | ||
|
|
||
| }, | ||
| Protocols: []string{ | ||
| "tcp", | ||
| }, | ||
| Encoding: msdsn.EncodeParameters{ | ||
| Timezone: time.UTC, | ||
| GuidConversion: false, | ||
| }, | ||
| DialTimeout: time.Second * 5, | ||
| ConnTimeout: time.Second * 10, | ||
| KeepAlive: time.Second * 30, | ||
|
|
||
| } | ||
|
|
||
| // if *spn != "" { | ||
| // cfg.Parameters["authenticator"] = "krb5" | ||
| // // cfg.Parameters["krb5-credcachefile"] = "/tmp/krb5cc_719880" | ||
| // } | ||
paraddise marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| activityid, uerr := uuid.NewRandom() | ||
| if uerr == nil { | ||
| cfg.ActivityID = activityid[:] | ||
| } | ||
|
|
||
| workstation, err := os.Hostname() | ||
| if err == nil { | ||
| cfg.Workstation = workstation | ||
| } | ||
|
|
||
| connector := mssqldb.NewConnectorConfig(cfg) | ||
| // dsn := "server=" + *server + ";user id=" + *userid + ";password=" + *password + ";database=" + *database | ||
| // connector,err = mssqldb.NewConnector(dsn) | ||
| // if err != nil { | ||
| // fmt.Println("failed to create connector: ", err.Error()) | ||
| // return | ||
| // } | ||
|
|
||
| _, err = connector.Connect(context.Background()) | ||
| if err != nil { | ||
| fmt.Println("connector.Connect: ", err.Error()) | ||
| return | ||
| } | ||
|
|
||
| db := sql.OpenDB(connector) | ||
| defer db.Close() | ||
|
|
||
| // if err != nil { | ||
| // fmt.Println("Cannot connect: ", err.Error()) | ||
| // return | ||
| // } | ||
paraddise marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| err = db.Ping() | ||
| if err != nil { | ||
| fmt.Println("Cannot connect: ", err.Error()) | ||
| return | ||
| } | ||
| r := bufio.NewReader(os.Stdin) | ||
| for { | ||
| _, err = os.Stdout.Write([]byte("> ")) | ||
| if err != nil { | ||
| fmt.Println(err) | ||
| return | ||
| } | ||
| cmd, err := r.ReadString('\n') | ||
| if err != nil { | ||
| if err == io.EOF { | ||
| fmt.Println() | ||
| return | ||
| } | ||
| fmt.Println(err) | ||
| return | ||
| } | ||
| err = exec(db, cmd) | ||
| if err != nil { | ||
| fmt.Println(err) | ||
| } | ||
| } | ||
| } | ||
| func exec(db *sql.DB, cmd string) error { | ||
| rows, err := db.Query(cmd) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| defer rows.Close() | ||
| cols, err := rows.Columns() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if cols == nil { | ||
| return nil | ||
| } | ||
| vals := make([]interface{}, len(cols)) | ||
| for i := 0; i < len(cols); i++ { | ||
| vals[i] = new(interface{}) | ||
| if i != 0 { | ||
| fmt.Print("\t") | ||
| } | ||
| fmt.Print(cols[i]) | ||
| } | ||
| fmt.Println() | ||
| for rows.Next() { | ||
| err = rows.Scan(vals...) | ||
| if err != nil { | ||
| fmt.Println(err) | ||
| continue | ||
| } | ||
| for i := 0; i < len(vals); i++ { | ||
| if i != 0 { | ||
| fmt.Print("\t") | ||
| } | ||
| printValue(vals[i].(*interface{})) | ||
| } | ||
| fmt.Println() | ||
|
|
||
| } | ||
| if rows.Err() != nil { | ||
| return rows.Err() | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func printValue(pval *interface{}) { | ||
| switch v := (*pval).(type) { | ||
| case nil: | ||
| fmt.Print("NULL") | ||
| case bool: | ||
| if v { | ||
| fmt.Print("1") | ||
| } else { | ||
| fmt.Print("0") | ||
| } | ||
| case []byte: | ||
| fmt.Print(string(v)) | ||
| case time.Time: | ||
| fmt.Print(v.Format("2006-01-02 15:04:05.999")) | ||
| default: | ||
| fmt.Print(v) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package integratedauth | ||
|
|
||
| import ( | ||
| "crypto/md5" | ||
| "encoding/binary" | ||
| ) | ||
|
|
||
paraddise marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| func GenerateCBTFromTLSUnique(tlsUnique []byte) []byte { | ||
| // Initialize the channel binding structure with empty addresses | ||
| // These fields are defined in the RFC but not used for TLS bindings | ||
| initiatorAddress := make([]byte, 8) | ||
| acceptorAddress := make([]byte, 8) | ||
|
|
||
| // Create the application data with the "tls-unique:" prefix | ||
| applicationDataRaw := append([]byte("tls-unique:"), tlsUnique...) | ||
|
|
||
| // Add the length prefix to the application data (little-endian 32-bit integer) | ||
| lenApplicationData := make([]byte, 4) | ||
| binary.LittleEndian.PutUint32(lenApplicationData, uint32(len(applicationDataRaw))) | ||
| applicationData := append(lenApplicationData, applicationDataRaw...) | ||
|
|
||
| // Assemble the complete channel binding structure | ||
| channelBindingStruct := append(append(initiatorAddress, acceptorAddress...), applicationData...) | ||
|
|
||
| // Return the MD5 hash of the structure | ||
| hash := md5.Sum(channelBindingStruct) | ||
| return hash[:] | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -252,6 +252,11 @@ type krbAuth struct { | |||||||||||||||
| krb5Config *krb5Login | ||||||||||||||||
| spnegoClient *spnego.SPNEGO | ||||||||||||||||
| krb5Client *client.Client | ||||||||||||||||
| channelBinding []byte | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| func (k *krbAuth) SetChannelBinding(channelBinding []byte) { | ||||||||||||||||
| k.channelBinding = channelBinding | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
|
||||||||||||||||
| channelBinding []byte | |
| } | |
| func (k *krbAuth) SetChannelBinding(channelBinding []byte) { | |
| k.channelBinding = channelBinding | |
| } | |
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -57,11 +57,20 @@ const _NEGOTIATE_FLAGS = _NEGOTIATE_UNICODE | | |||||||||||
| _NEGOTIATE_ALWAYS_SIGN | | ||||||||||||
| _NEGOTIATE_EXTENDED_SESSIONSECURITY | ||||||||||||
|
|
||||||||||||
| const ( | ||||||||||||
| AV_PAIR_MsvAvChannelBindings = 0x000A | ||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| type Auth struct { | ||||||||||||
| Domain string | ||||||||||||
| UserName string | ||||||||||||
| Password string | ||||||||||||
| Workstation string | ||||||||||||
| ChannelBinding []byte | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| func (auth *Auth) SetChannelBinding(channelBinding []byte) { | ||||||||||||
| auth.ChannelBinding = channelBinding | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // getAuth returns an authentication handle Auth to provide authentication content | ||||||||||||
|
|
@@ -76,6 +85,7 @@ func getAuth(config msdsn.Config) (integratedauth.IntegratedAuthenticator, error | |||||||||||
| UserName: domainUser[1], | ||||||||||||
| Password: config.Password, | ||||||||||||
| Workstation: config.Workstation, | ||||||||||||
| ChannelBinding: []byte{}, | ||||||||||||
| }, nil | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
|
|
@@ -243,7 +253,7 @@ func getNTLMv2AndLMv2ResponsePayloads(userDomain, username, password string, cha | |||||||||||
| return | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| func negotiateExtendedSessionSecurity(flags uint32, message []byte, challenge [8]byte, username, password, userDom string) (lm, nt []byte, err error) { | ||||||||||||
| func negotiateExtendedSessionSecurity(flags uint32, message []byte, challenge [8]byte, username, password, userDom string, channelBinding []byte) (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 | ||||||||||||
|
|
@@ -254,6 +264,18 @@ func negotiateExtendedSessionSecurity(flags uint32, message []byte, challenge [8 | |||||||||||
| return lm, nt, err | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if len(channelBinding) > 0 { | ||||||||||||
| av_pair_cb := make([]byte, 4) | ||||||||||||
| // AvId | ||||||||||||
| // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/83f5e789-660d-4781-8491-5f8c6641f75e | ||||||||||||
|
Comment on lines
+269
to
+270
|
||||||||||||
| // AvId | |
| // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/83f5e789-660d-4781-8491-5f8c6641f75e | |
| // Create the AV_PAIR structure for channel bindings as specified in MS-NLMP. | |
| // Set AvId to MsvAvChannelBindings and AvLen to the length of the channel binding data. | |
| // See: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/83f5e789-660d-4781-8491-5f8c6641f75e |
Uh oh!
There was an error while loading. Please reload this page.