forked from masterzen/winrm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
kerberos.go
128 lines (112 loc) · 3.33 KB
/
kerberos.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package winrm
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"github.com/masterzen/winrm/soap"
"github.com/jcmturner/gokrb5/v8/client"
"github.com/jcmturner/gokrb5/v8/config"
"github.com/jcmturner/gokrb5/v8/credentials"
"github.com/jcmturner/gokrb5/v8/spnego"
)
// Settings holds all the information necessary to configure the provider
type Settings struct {
WinRMUsername string
WinRMPassword string
WinRMHost string
WinRMPort int
WinRMProto string
WinRMInsecure bool
KrbRealm string
KrbConfig string
KrbSpn string
KrbCCache string
WinRMUseNTLM bool
WinRMPassCredentials bool
}
type ClientKerberos struct {
clientRequest
Username string
Password string
Realm string
Hostname string
Port int
Proto string
SPN string
KrbConf string
KrbCCache string
}
func NewClientKerberos(settings *Settings) *ClientKerberos {
return &ClientKerberos{
Username: settings.WinRMUsername,
Password: settings.WinRMPassword,
Realm: settings.KrbRealm,
Hostname: settings.WinRMHost,
Port: settings.WinRMPort,
Proto: settings.WinRMProto,
KrbConf: settings.KrbConfig,
KrbCCache: settings.KrbCCache,
SPN: settings.KrbSpn,
}
}
func (c *ClientKerberos) Transport(endpoint *Endpoint) error {
return c.clientRequest.Transport(endpoint)
}
func (c *ClientKerberos) Post(clt *Client, request *soap.SoapMessage) (string, error) {
cfg, err := config.Load(c.KrbConf)
if err != nil {
return "", err
}
// setup the kerberos client
var kerberosClient *client.Client
if len(c.KrbCCache) > 0 {
b, err := ioutil.ReadFile(c.KrbCCache)
if err != nil {
return "", fmt.Errorf("unable to read ccache file %s: %w", c.KrbCCache, err)
}
cc := new(credentials.CCache)
err = cc.Unmarshal(b)
if err != nil {
return "", fmt.Errorf("unable to parse ccache file %s: %w", c.KrbCCache, err)
}
kerberosClient, err = client.NewFromCCache(cc, cfg, client.DisablePAFXFAST(true))
if err != nil {
return "", fmt.Errorf("unable to create kerberos client from ccache: %w", err)
}
} else {
kerberosClient = client.NewWithPassword(c.Username, c.Realm, c.Password, cfg,
client.DisablePAFXFAST(true), client.AssumePreAuthentication(true))
}
//create an http request
winrmURL := fmt.Sprintf("%s://%s:%d/wsman", c.Proto, c.Hostname, c.Port)
//nolint:noctx
winRMRequest, _ := http.NewRequest("POST", winrmURL, strings.NewReader(request.String()))
winRMRequest.Header.Add("Content-Type", "application/soap+xml;charset=UTF-8")
err = spnego.SetSPNEGOHeader(kerberosClient, winRMRequest, c.SPN)
if err != nil {
return "", fmt.Errorf("unable to set SPNego Header: %w", err)
}
httpClient := &http.Client{Transport: c.transport}
resp, err := httpClient.Do(winRMRequest)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
var bodyMsg string
respBody, err := io.ReadAll(resp.Body)
if err != nil {
bodyMsg = fmt.Sprintf("Error retrieving the response's body: %s", err)
} else {
bodyMsg = fmt.Sprintf("Response body:\n%s", string(respBody))
}
return "", fmt.Errorf("request returned: %d - %s. %s", resp.StatusCode, resp.Status, bodyMsg)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), err
}