-
Notifications
You must be signed in to change notification settings - Fork 242
/
ASExchange.go
110 lines (105 loc) · 4.09 KB
/
ASExchange.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
package client
import (
"gopkg.in/jcmturner/gokrb5.v2/crypto"
"gopkg.in/jcmturner/gokrb5.v2/iana/errorcode"
"gopkg.in/jcmturner/gokrb5.v2/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v2/iana/patype"
"gopkg.in/jcmturner/gokrb5.v2/krberror"
"gopkg.in/jcmturner/gokrb5.v2/messages"
"gopkg.in/jcmturner/gokrb5.v2/types"
"sort"
)
// ASExchange performs an AS exchange for the client to retrieve a TGT.
func (cl *Client) ASExchange(realm string, referral int) error {
if ok, err := cl.IsConfigured(); !ok {
return krberror.Errorf(err, krberror.ConfigError, "AS Exchange cannot be preformed")
}
ASReq, err := messages.NewASReq(realm, cl.Config, cl.Credentials.CName)
if err != nil {
return krberror.Errorf(err, krberror.KRBMsgError, "Error generating new AS_REQ")
}
err = setPAData(cl, messages.KRBError{}, &ASReq)
if err != nil {
return krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: failed setting AS_REQ PAData")
}
b, err := ASReq.Marshal()
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ")
}
var ASRep messages.ASRep
rb, err := cl.SendToKDC(b, realm)
if err != nil {
if e, ok := err.(messages.KRBError); ok {
switch e.ErrorCode {
case errorcode.KDC_ERR_PREAUTH_REQUIRED:
// From now on assume this client will need to do this pre-auth and set the PAData
cl.GoKrb5Conf.AssumePAEncTimestampRequired = true
err = setPAData(cl, e, &ASReq)
if err != nil {
return krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: failed setting AS_REQ PAData for pre-authentication required")
}
b, err := ASReq.Marshal()
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed marshaling AS_REQ with PAData")
}
rb, err = cl.SendToKDC(b, realm)
if err != nil {
return krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
}
case errorcode.KDC_ERR_WRONG_REALM:
// Client referral https://tools.ietf.org/html/rfc6806.html#section-7
if referral > 5 {
return krberror.Errorf(err, krberror.KRBMsgError, "maximum number of client referrals exceeded")
}
referral += 1
return cl.ASExchange(e.CRealm, referral)
}
} else {
return krberror.Errorf(err, krberror.NetworkingError, "AS Exchange Error: failed sending AS_REQ to KDC")
}
}
err = ASRep.Unmarshal(rb)
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "AS Exchange Error: failed to process the AS_REP")
}
if ok, err := ASRep.IsValid(cl.Config, cl.Credentials, ASReq); !ok {
return krberror.Errorf(err, krberror.KRBMsgError, "AS Exchange Error: AS_REP is not valid")
}
cl.AddSession(ASRep.Ticket, ASRep.DecryptedEncPart)
return nil
}
func setPAData(cl *Client, krberr messages.KRBError, ASReq *messages.ASReq) error {
if !cl.GoKrb5Conf.DisablePAFXFast {
pa := types.PAData{PADataType: patype.PA_REQ_ENC_PA_REP}
ASReq.PAData = append(ASReq.PAData, pa)
}
if cl.GoKrb5Conf.AssumePAEncTimestampRequired {
paTSb, err := types.GetPAEncTSEncAsnMarshalled()
if err != nil {
return krberror.Errorf(err, krberror.KRBMsgError, "Error creating PAEncTSEnc for Pre-Authentication")
}
sort.Sort(sort.Reverse(sort.IntSlice(cl.Config.LibDefaults.DefaultTktEnctypeIDs)))
etype, err := crypto.GetEtype(cl.Config.LibDefaults.DefaultTktEnctypeIDs[0])
if err != nil {
return krberror.Errorf(err, krberror.EncryptingError, "Error creating etype")
}
key, err := cl.Key(etype, krberr)
if err != nil {
return krberror.Errorf(err, krberror.EncryptingError, "Error getting key from credentials")
}
paEncTS, err := crypto.GetEncryptedData(paTSb, key, keyusage.AS_REQ_PA_ENC_TIMESTAMP, 1)
if err != nil {
return krberror.Errorf(err, krberror.EncryptingError, "Error encrypting pre-authentication timestamp")
}
pb, err := paEncTS.Marshal()
if err != nil {
return krberror.Errorf(err, krberror.EncodingError, "Error marshaling the PAEncTSEnc encrypted data")
}
pa := types.PAData{
PADataType: patype.PA_ENC_TIMESTAMP,
PADataValue: pb,
}
ASReq.PAData = append(ASReq.PAData, pa)
}
return nil
}