forked from jcmturner/gokrb5
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ASExchange.go
163 lines (157 loc) · 5.9 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package client
import (
"gopkg.in/jcmturner/gokrb5.v4/crypto"
"gopkg.in/jcmturner/gokrb5.v4/crypto/etype"
"gopkg.in/jcmturner/gokrb5.v4/iana/errorcode"
"gopkg.in/jcmturner/gokrb5.v4/iana/keyusage"
"gopkg.in/jcmturner/gokrb5.v4/iana/patype"
"gopkg.in/jcmturner/gokrb5.v4/krberror"
"gopkg.in/jcmturner/gokrb5.v4/messages"
"gopkg.in/jcmturner/gokrb5.v4/types"
)
// 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")
}
var et etype.EType
if krberr.ErrorCode == 0 {
// This is not in response to an error from the KDC. It is preemptive
et, err = crypto.GetEtype(ASReq.ReqBody.EType[0]) // Take the first as preference
if err != nil {
return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
}
} else {
// Get the etype to use from the PA data in the KRBError e-data
et, err = preAuthEType(krberr)
if err != nil {
return krberror.Errorf(err, krberror.EncryptingError, "error getting etype for pre-auth encryption")
}
}
key, err := cl.Key(et, 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
}
func preAuthEType(krberr messages.KRBError) (etype etype.EType, err error) {
//The preferred ordering of the "hint" pre-authentication data that
//affect client key selection is: ETYPE-INFO2, followed by ETYPE-INFO,
//followed by PW-SALT.
//A KDC SHOULD NOT send PA-PW-SALT when issuing a KRB-ERROR message
//that requests additional pre-authentication. Implementation note:
//Some KDC implementations issue an erroneous PA-PW-SALT when issuing a
//KRB-ERROR message that requests additional pre-authentication.
//Therefore, clients SHOULD ignore a PA-PW-SALT accompanying a
//KRB-ERROR message that requests additional pre-authentication.
var etypeID int32
var pas types.PADataSequence
e := pas.Unmarshal(krberr.EData)
if e != nil {
err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling KRBError data")
return
}
for _, pa := range pas {
switch pa.PADataType {
case patype.PA_ETYPE_INFO2:
info, e := pa.GetETypeInfo2()
if e != nil {
err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling ETYPE-INFO2 data")
return
}
etypeID = info[0].EType
break
case patype.PA_ETYPE_INFO:
info, e := pa.GetETypeInfo()
if e != nil {
err = krberror.Errorf(e, krberror.EncodingError, "error unmashalling ETYPE-INFO data")
return
}
etypeID = info[0].EType
}
}
etype, e = crypto.GetEtype(etypeID)
if e != nil {
err = krberror.Errorf(e, krberror.EncryptingError, "Error creating etype")
return
}
return etype, nil
}