-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathkrb5.go
121 lines (96 loc) · 3.14 KB
/
krb5.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
package smb2
import (
"encoding/asn1"
"errors"
"fmt"
"time"
"github.com/jcmturner/gokrb5/v8/client"
"github.com/jcmturner/gokrb5/v8/crypto"
"github.com/jcmturner/gokrb5/v8/gssapi"
"github.com/jcmturner/gokrb5/v8/iana/flags"
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
"github.com/jcmturner/gokrb5/v8/messages"
"github.com/jcmturner/gokrb5/v8/spnego"
"github.com/jcmturner/gokrb5/v8/types"
)
// Krb5Initiator is a GSSAPI initiator for Kerberos 5.
// It implements the Initiator interface.
type Krb5Initiator struct {
Client *client.Client
TargetSPN string
sessKey types.EncryptionKey
sessSubkey types.EncryptionKey
}
// OID returns the Kerberos 5 OID.
func (ki *Krb5Initiator) OID() asn1.ObjectIdentifier {
return asn1.ObjectIdentifier(gssapi.OIDKRB5.OID())
}
// InitSecContext initiates the security context.
func (ki *Krb5Initiator) InitSecContext() ([]byte, error) {
if ki.Client == nil {
return nil, errors.New("Kerberos client is not set")
}
if ki.TargetSPN == "" {
return nil, errors.New("Kerberos target SPN is not set")
}
tkt, key, err := ki.Client.GetServiceTicket(ki.TargetSPN)
if err != nil {
return nil, fmt.Errorf("failed to get Kerberos service ticket: %w", err)
}
req, err := spnego.NewKRB5TokenAPREQ(ki.Client, tkt, key, []int{gssapi.ContextFlagMutual, gssapi.ContextFlagConf}, []int{flags.APOptionMutualRequired})
if err != nil {
return nil, fmt.Errorf("failed to create Kerberos AP-REQ: %w", err)
}
reqBytes, err := req.Marshal()
if err != nil {
return nil, fmt.Errorf("failed to marshal Kerberos AP-REQ: %w", err)
}
ki.sessKey = key
return reqBytes, nil
}
// AcceptSecContext accepts the security context.
func (ki *Krb5Initiator) AcceptSecContext(sc []byte) ([]byte, error) {
var token spnego.KRB5Token
err := token.Unmarshal(sc)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal Kerberos token: %w", err)
}
if !token.IsAPRep() {
return nil, fmt.Errorf("expected Kerberos AP-REP, got: %#v", token)
}
data, err := crypto.DecryptEncPart(token.APRep.EncPart, ki.sessKey, keyusage.AP_REP_ENCPART)
if err != nil {
return nil, fmt.Errorf("failed to decrypt Kerberos AP-REP EncPart: %w", err)
}
var payload messages.EncAPRepPart
if err := payload.Unmarshal(data); err != nil {
return nil, fmt.Errorf("failed to unmarshal Kerberos AP-REP EncPart: %w", err)
}
if time.Since(payload.CTime).Abs() > ki.Client.Config.LibDefaults.Clockskew {
return nil, fmt.Errorf("AP_REP time out of range (now: %v, AP_REP time: %v)", time.Now().UTC().Truncate(time.Second), payload.CTime)
}
ki.sessSubkey = payload.Subkey
return []byte{}, nil
}
// Sum creates a checksum with the session subkey.
func (ki *Krb5Initiator) Sum(bs []byte) []byte {
token, err := gssapi.NewInitiatorMICToken(bs, ki.sessSubkey)
if err != nil {
return nil
}
b, err := token.Marshal()
if err != nil {
return nil
}
return b
}
// SessionKey returns the session key.
func (ki *Krb5Initiator) SessionKey() []byte {
// Only the first 16 bytes are used, if less than that are available
// zero padding is added.
sliced := ki.sessSubkey.KeyValue[:16]
for len(sliced) < 16 {
sliced = append(sliced, 0x00)
}
return sliced
}