Skip to content

Commit b547467

Browse files
committed
copied from PR go-ldap#449
1 parent 25b14db commit b547467

File tree

3 files changed

+244
-2
lines changed

3 files changed

+244
-2
lines changed

v3/go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74
88
github.com/go-asn1-ber/asn1-ber v1.5.5
99
github.com/google/uuid v1.3.1
10-
github.com/stretchr/testify v1.8.0
10+
github.com/jcmturner/gokrb5/v8 v8.4.4
11+
github.com/stretchr/testify v1.8.1
1112
golang.org/x/crypto v0.17.0 // indirect
1213
)

v3/go.sum

+28-1
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,50 @@ github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD
99
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
1010
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
1111
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
12+
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
13+
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
14+
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
15+
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
16+
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
17+
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
18+
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
19+
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
20+
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
21+
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
22+
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
23+
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
24+
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
25+
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
26+
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
27+
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
28+
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
29+
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
30+
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
1231
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1332
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1433
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1534
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
35+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
36+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
1637
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
17-
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
1838
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
39+
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
40+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
1941
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
2042
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
2143
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
44+
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
2245
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
2346
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
2447
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
2548
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
2649
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
50+
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
2751
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
2852
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
2953
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
54+
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
55+
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
3056
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
3157
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
3258
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -57,6 +83,7 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
5783
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
5884
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
5985
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
86+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
6087
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
6188
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
6289
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

v3/gssapi/client.go

+214
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package gssapi
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/jcmturner/gokrb5/v8/client"
7+
"github.com/jcmturner/gokrb5/v8/config"
8+
"github.com/jcmturner/gokrb5/v8/keytab"
9+
"github.com/jcmturner/gokrb5/v8/types"
10+
11+
"github.com/jcmturner/gokrb5/v8/gssapi"
12+
"github.com/jcmturner/gokrb5/v8/spnego"
13+
14+
"github.com/jcmturner/gokrb5/v8/crypto"
15+
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
16+
"github.com/jcmturner/gokrb5/v8/messages"
17+
18+
"github.com/jcmturner/gokrb5/v8/credentials"
19+
)
20+
21+
// Client implements ldap.GSSAPIClient interface.
22+
type Client struct {
23+
*client.Client
24+
25+
ekey types.EncryptionKey
26+
Subkey types.EncryptionKey
27+
}
28+
29+
// NewClientWithKeytab creates a new client from a keytab credential.
30+
// Set the realm to empty string to use the default realm from config.
31+
func NewClientWithKeytab(username, realm, keytabPath, krb5confPath string, settings ...func(*client.Settings)) (*Client, error) {
32+
krb5conf, err := config.Load(krb5confPath)
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
keytab, err := keytab.Load(keytabPath)
38+
if err != nil {
39+
return nil, err
40+
}
41+
42+
client := client.NewWithKeytab(username, realm, keytab, krb5conf, settings...)
43+
44+
return &Client{
45+
Client: client,
46+
}, nil
47+
}
48+
49+
// NewClientWithPassword creates a new client from a password credential.
50+
// Set the realm to empty string to use the default realm from config.
51+
func NewClientWithPassword(username, realm, password string, krb5confPath string, settings ...func(*client.Settings)) (*Client, error) {
52+
krb5conf, err := config.Load(krb5confPath)
53+
if err != nil {
54+
return nil, err
55+
}
56+
57+
client := client.NewWithPassword(username, realm, password, krb5conf, settings...)
58+
59+
return &Client{
60+
Client: client,
61+
}, nil
62+
}
63+
64+
// NewClientFromCCache creates a new client from a populated client cache.
65+
func NewClientFromCCache(ccachePath, krb5confPath string, settings ...func(*client.Settings)) (*Client, error) {
66+
krb5conf, err := config.Load(krb5confPath)
67+
if err != nil {
68+
return nil, err
69+
}
70+
71+
ccache, err := credentials.LoadCCache(ccachePath)
72+
if err != nil {
73+
return nil, err
74+
}
75+
76+
client, err := client.NewFromCCache(ccache, krb5conf, settings...)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
return &Client{
82+
Client: client,
83+
}, nil
84+
}
85+
86+
// Close deletes any established secure context and closes the client.
87+
func (client *Client) Close() error {
88+
client.Client.Destroy()
89+
return nil
90+
}
91+
92+
// DeleteSecContext destroys any established secure context.
93+
func (client *Client) DeleteSecContext() error {
94+
client.ekey = types.EncryptionKey{}
95+
client.Subkey = types.EncryptionKey{}
96+
return nil
97+
}
98+
99+
// InitSecContext initiates the establishment of a security context for
100+
// GSS-API between the client and server.
101+
// See RFC 4752 section 3.1.
102+
func (client *Client) InitSecContext(target string, input []byte) ([]byte, bool, error) {
103+
gssapiFlags := []int{gssapi.ContextFlagInteg, gssapi.ContextFlagConf, gssapi.ContextFlagMutual}
104+
105+
switch input {
106+
case nil:
107+
tkt, ekey, err := client.Client.GetServiceTicket(target)
108+
if err != nil {
109+
return nil, false, err
110+
}
111+
client.ekey = ekey
112+
113+
token, err := spnego.NewKRB5TokenAPREQ(client.Client, tkt, ekey, gssapiFlags, []int{})
114+
if err != nil {
115+
return nil, false, err
116+
}
117+
118+
output, err := token.Marshal()
119+
if err != nil {
120+
return nil, false, err
121+
}
122+
123+
return output, true, nil
124+
125+
default:
126+
var token spnego.KRB5Token
127+
128+
err := token.Unmarshal(input)
129+
if err != nil {
130+
return nil, false, err
131+
}
132+
133+
var completed bool
134+
135+
if token.IsAPRep() {
136+
completed = true
137+
138+
encpart, err := crypto.DecryptEncPart(token.APRep.EncPart, client.ekey, keyusage.AP_REP_ENCPART)
139+
if err != nil {
140+
return nil, false, err
141+
}
142+
143+
part := &messages.EncAPRepPart{}
144+
145+
if err = part.Unmarshal(encpart); err != nil {
146+
return nil, false, err
147+
}
148+
client.Subkey = part.Subkey
149+
}
150+
151+
if token.IsKRBError() {
152+
return nil, !false, token.KRBError
153+
}
154+
155+
return make([]byte, 0), !completed, nil
156+
}
157+
}
158+
159+
// NegotiateSaslAuth performs the last step of the SASL handshake.
160+
// See RFC 4752 section 3.1.
161+
func (client *Client) NegotiateSaslAuth(input []byte, authzid string) ([]byte, error) {
162+
token := &gssapi.WrapToken{}
163+
err := token.Unmarshal(input, true)
164+
if err != nil {
165+
return nil, err
166+
}
167+
168+
if (token.Flags & 0b1) == 0 {
169+
return nil, fmt.Errorf("got a Wrapped token that's not from the server")
170+
}
171+
172+
key := client.ekey
173+
if (token.Flags & 0b100) != 0 {
174+
key = client.Subkey
175+
}
176+
177+
_, err = token.Verify(key, keyusage.GSSAPI_ACCEPTOR_SEAL)
178+
if err != nil {
179+
return nil, err
180+
}
181+
182+
pl := token.Payload
183+
if len(pl) != 4 {
184+
return nil, fmt.Errorf("server send bad final token for SASL GSSAPI Handshake")
185+
}
186+
187+
// We never want a security layer
188+
b := [4]byte{0, 0, 0, 0}
189+
payload := append(b[:], []byte(authzid)...)
190+
191+
encType, err := crypto.GetEtype(key.KeyType)
192+
if err != nil {
193+
return nil, err
194+
}
195+
196+
token = &gssapi.WrapToken{
197+
Flags: 0b100,
198+
EC: uint16(encType.GetHMACBitLength() / 8),
199+
RRC: 0,
200+
SndSeqNum: 1,
201+
Payload: payload,
202+
}
203+
204+
if err := token.SetCheckSum(key, keyusage.GSSAPI_INITIATOR_SEAL); err != nil {
205+
return nil, err
206+
}
207+
208+
output, err := token.Marshal()
209+
if err != nil {
210+
return nil, err
211+
}
212+
213+
return output, nil
214+
}

0 commit comments

Comments
 (0)