github.com/jcmturner/gokrb5/v8@v8.4.4/messages/KDCReq.go (about) 1 package messages 2 3 // Reference: https://www.ietf.org/rfc/rfc4120.txt 4 // Section: 5.4.1 5 6 import ( 7 "crypto/rand" 8 "fmt" 9 "math" 10 "math/big" 11 "time" 12 13 "github.com/jcmturner/gofork/encoding/asn1" 14 "github.com/jcmturner/gokrb5/v8/asn1tools" 15 "github.com/jcmturner/gokrb5/v8/config" 16 "github.com/jcmturner/gokrb5/v8/crypto" 17 "github.com/jcmturner/gokrb5/v8/iana" 18 "github.com/jcmturner/gokrb5/v8/iana/asnAppTag" 19 "github.com/jcmturner/gokrb5/v8/iana/flags" 20 "github.com/jcmturner/gokrb5/v8/iana/keyusage" 21 "github.com/jcmturner/gokrb5/v8/iana/msgtype" 22 "github.com/jcmturner/gokrb5/v8/iana/nametype" 23 "github.com/jcmturner/gokrb5/v8/iana/patype" 24 "github.com/jcmturner/gokrb5/v8/krberror" 25 "github.com/jcmturner/gokrb5/v8/types" 26 ) 27 28 type marshalKDCReq struct { 29 PVNO int `asn1:"explicit,tag:1"` 30 MsgType int `asn1:"explicit,tag:2"` 31 PAData types.PADataSequence `asn1:"explicit,optional,tag:3"` 32 ReqBody asn1.RawValue `asn1:"explicit,tag:4"` 33 } 34 35 // KDCReqFields represents the KRB_KDC_REQ fields. 36 type KDCReqFields struct { 37 PVNO int 38 MsgType int 39 PAData types.PADataSequence 40 ReqBody KDCReqBody 41 Renewal bool 42 } 43 44 // ASReq implements RFC 4120 KRB_AS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1. 45 type ASReq struct { 46 KDCReqFields 47 } 48 49 // TGSReq implements RFC 4120 KRB_TGS_REQ: https://tools.ietf.org/html/rfc4120#section-5.4.1. 50 type TGSReq struct { 51 KDCReqFields 52 } 53 54 type marshalKDCReqBody struct { 55 KDCOptions asn1.BitString `asn1:"explicit,tag:0"` 56 CName types.PrincipalName `asn1:"explicit,optional,tag:1"` 57 Realm string `asn1:"generalstring,explicit,tag:2"` 58 SName types.PrincipalName `asn1:"explicit,optional,tag:3"` 59 From time.Time `asn1:"generalized,explicit,optional,tag:4"` 60 Till time.Time `asn1:"generalized,explicit,tag:5"` 61 RTime time.Time `asn1:"generalized,explicit,optional,tag:6"` 62 Nonce int `asn1:"explicit,tag:7"` 63 EType []int32 `asn1:"explicit,tag:8"` 64 Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"` 65 EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"` 66 // Ticket needs to be a raw value as it is wrapped in an APPLICATION tag 67 AdditionalTickets asn1.RawValue `asn1:"explicit,optional,tag:11"` 68 } 69 70 // KDCReqBody implements the KRB_KDC_REQ request body. 71 type KDCReqBody struct { 72 KDCOptions asn1.BitString `asn1:"explicit,tag:0"` 73 CName types.PrincipalName `asn1:"explicit,optional,tag:1"` 74 Realm string `asn1:"generalstring,explicit,tag:2"` 75 SName types.PrincipalName `asn1:"explicit,optional,tag:3"` 76 From time.Time `asn1:"generalized,explicit,optional,tag:4"` 77 Till time.Time `asn1:"generalized,explicit,tag:5"` 78 RTime time.Time `asn1:"generalized,explicit,optional,tag:6"` 79 Nonce int `asn1:"explicit,tag:7"` 80 EType []int32 `asn1:"explicit,tag:8"` 81 Addresses []types.HostAddress `asn1:"explicit,optional,tag:9"` 82 EncAuthData types.EncryptedData `asn1:"explicit,optional,tag:10"` 83 AdditionalTickets []Ticket `asn1:"explicit,optional,tag:11"` 84 } 85 86 // NewASReqForTGT generates a new KRB_AS_REQ struct for a TGT request. 87 func NewASReqForTGT(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) { 88 sname := types.PrincipalName{ 89 NameType: nametype.KRB_NT_SRV_INST, 90 NameString: []string{"krbtgt", realm}, 91 } 92 return NewASReq(realm, c, cname, sname) 93 } 94 95 // NewASReqForChgPasswd generates a new KRB_AS_REQ struct for a change password request. 96 func NewASReqForChgPasswd(realm string, c *config.Config, cname types.PrincipalName) (ASReq, error) { 97 sname := types.PrincipalName{ 98 NameType: nametype.KRB_NT_PRINCIPAL, 99 NameString: []string{"kadmin", "changepw"}, 100 } 101 return NewASReq(realm, c, cname, sname) 102 } 103 104 // NewASReq generates a new KRB_AS_REQ struct for a given SNAME. 105 func NewASReq(realm string, c *config.Config, cname, sname types.PrincipalName) (ASReq, error) { 106 nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32)) 107 if err != nil { 108 return ASReq{}, err 109 } 110 t := time.Now().UTC() 111 // Copy the default options to make this thread safe 112 kopts := types.NewKrbFlags() 113 copy(kopts.Bytes, c.LibDefaults.KDCDefaultOptions.Bytes) 114 kopts.BitLength = c.LibDefaults.KDCDefaultOptions.BitLength 115 a := ASReq{ 116 KDCReqFields{ 117 PVNO: iana.PVNO, 118 MsgType: msgtype.KRB_AS_REQ, 119 PAData: types.PADataSequence{}, 120 ReqBody: KDCReqBody{ 121 KDCOptions: kopts, 122 Realm: realm, 123 CName: cname, 124 SName: sname, 125 Till: t.Add(c.LibDefaults.TicketLifetime), 126 Nonce: int(nonce.Int64()), 127 EType: c.LibDefaults.DefaultTktEnctypeIDs, 128 }, 129 }, 130 } 131 if c.LibDefaults.Forwardable { 132 types.SetFlag(&a.ReqBody.KDCOptions, flags.Forwardable) 133 } 134 if c.LibDefaults.Canonicalize { 135 types.SetFlag(&a.ReqBody.KDCOptions, flags.Canonicalize) 136 } 137 if c.LibDefaults.Proxiable { 138 types.SetFlag(&a.ReqBody.KDCOptions, flags.Proxiable) 139 } 140 if c.LibDefaults.RenewLifetime != 0 { 141 types.SetFlag(&a.ReqBody.KDCOptions, flags.Renewable) 142 a.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime) 143 a.ReqBody.RTime = t.Add(time.Duration(48) * time.Hour) 144 } 145 if !c.LibDefaults.NoAddresses { 146 ha, err := types.LocalHostAddresses() 147 if err != nil { 148 return a, fmt.Errorf("could not get local addresses: %v", err) 149 } 150 ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...) 151 a.ReqBody.Addresses = ha 152 } 153 return a, nil 154 } 155 156 // NewTGSReq generates a new KRB_TGS_REQ struct. 157 func NewTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, tgt Ticket, sessionKey types.EncryptionKey, sname types.PrincipalName, renewal bool) (TGSReq, error) { 158 a, err := tgsReq(cname, sname, kdcRealm, renewal, c) 159 if err != nil { 160 return a, err 161 } 162 err = a.setPAData(tgt, sessionKey) 163 return a, err 164 } 165 166 // NewUser2UserTGSReq returns a TGS-REQ suitable for user-to-user authentication (https://tools.ietf.org/html/rfc4120#section-3.7) 167 func NewUser2UserTGSReq(cname types.PrincipalName, kdcRealm string, c *config.Config, clientTGT Ticket, sessionKey types.EncryptionKey, sname types.PrincipalName, renewal bool, verifyingTGT Ticket) (TGSReq, error) { 168 a, err := tgsReq(cname, sname, kdcRealm, renewal, c) 169 if err != nil { 170 return a, err 171 } 172 a.ReqBody.AdditionalTickets = []Ticket{verifyingTGT} 173 types.SetFlag(&a.ReqBody.KDCOptions, flags.EncTktInSkey) 174 err = a.setPAData(clientTGT, sessionKey) 175 return a, err 176 } 177 178 // tgsReq populates the fields for a TGS_REQ 179 func tgsReq(cname, sname types.PrincipalName, kdcRealm string, renewal bool, c *config.Config) (TGSReq, error) { 180 nonce, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32)) 181 if err != nil { 182 return TGSReq{}, err 183 } 184 t := time.Now().UTC() 185 k := KDCReqFields{ 186 PVNO: iana.PVNO, 187 MsgType: msgtype.KRB_TGS_REQ, 188 ReqBody: KDCReqBody{ 189 KDCOptions: types.NewKrbFlags(), 190 Realm: kdcRealm, 191 CName: cname, // Add the CName to make validation of the reply easier 192 SName: sname, 193 Till: t.Add(c.LibDefaults.TicketLifetime), 194 Nonce: int(nonce.Int64()), 195 EType: c.LibDefaults.DefaultTGSEnctypeIDs, 196 }, 197 Renewal: renewal, 198 } 199 if c.LibDefaults.Forwardable { 200 types.SetFlag(&k.ReqBody.KDCOptions, flags.Forwardable) 201 } 202 if c.LibDefaults.Canonicalize { 203 types.SetFlag(&k.ReqBody.KDCOptions, flags.Canonicalize) 204 } 205 if c.LibDefaults.Proxiable { 206 types.SetFlag(&k.ReqBody.KDCOptions, flags.Proxiable) 207 } 208 if c.LibDefaults.RenewLifetime > time.Duration(0) { 209 types.SetFlag(&k.ReqBody.KDCOptions, flags.Renewable) 210 k.ReqBody.RTime = t.Add(c.LibDefaults.RenewLifetime) 211 } 212 if !c.LibDefaults.NoAddresses { 213 ha, err := types.LocalHostAddresses() 214 if err != nil { 215 return TGSReq{}, fmt.Errorf("could not get local addresses: %v", err) 216 } 217 ha = append(ha, types.HostAddressesFromNetIPs(c.LibDefaults.ExtraAddresses)...) 218 k.ReqBody.Addresses = ha 219 } 220 if renewal { 221 types.SetFlag(&k.ReqBody.KDCOptions, flags.Renew) 222 types.SetFlag(&k.ReqBody.KDCOptions, flags.Renewable) 223 } 224 return TGSReq{ 225 k, 226 }, nil 227 } 228 229 func (k *TGSReq) setPAData(tgt Ticket, sessionKey types.EncryptionKey) error { 230 // Marshal the request and calculate checksum 231 b, err := k.ReqBody.Marshal() 232 if err != nil { 233 return krberror.Errorf(err, krberror.EncodingError, "error marshaling TGS_REQ body") 234 } 235 etype, err := crypto.GetEtype(sessionKey.KeyType) 236 if err != nil { 237 return krberror.Errorf(err, krberror.EncryptingError, "error getting etype to encrypt authenticator") 238 } 239 cb, err := etype.GetChecksumHash(sessionKey.KeyValue, b, keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM) 240 if err != nil { 241 return krberror.Errorf(err, krberror.ChksumError, "error getting etype checksum hash") 242 } 243 244 // Form PAData for TGS_REQ 245 // Create authenticator 246 auth, err := types.NewAuthenticator(tgt.Realm, k.ReqBody.CName) 247 if err != nil { 248 return krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator") 249 } 250 auth.Cksum = types.Checksum{ 251 CksumType: etype.GetHashID(), 252 Checksum: cb, 253 } 254 // Create AP_REQ 255 apReq, err := NewAPReq(tgt, sessionKey, auth) 256 if err != nil { 257 return krberror.Errorf(err, krberror.KRBMsgError, "error generating new AP_REQ") 258 } 259 apb, err := apReq.Marshal() 260 if err != nil { 261 return krberror.Errorf(err, krberror.EncodingError, "error marshaling AP_REQ for pre-authentication data") 262 } 263 k.PAData = types.PADataSequence{ 264 types.PAData{ 265 PADataType: patype.PA_TGS_REQ, 266 PADataValue: apb, 267 }, 268 } 269 return nil 270 } 271 272 // Unmarshal bytes b into the ASReq struct. 273 func (k *ASReq) Unmarshal(b []byte) error { 274 var m marshalKDCReq 275 _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREQ)) 276 if err != nil { 277 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling AS_REQ") 278 } 279 expectedMsgType := msgtype.KRB_AS_REQ 280 if m.MsgType != expectedMsgType { 281 return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a AS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType) 282 } 283 var reqb KDCReqBody 284 err = reqb.Unmarshal(m.ReqBody.Bytes) 285 if err != nil { 286 return krberror.Errorf(err, krberror.EncodingError, "error processing AS_REQ body") 287 } 288 k.MsgType = m.MsgType 289 k.PAData = m.PAData 290 k.PVNO = m.PVNO 291 k.ReqBody = reqb 292 return nil 293 } 294 295 // Unmarshal bytes b into the TGSReq struct. 296 func (k *TGSReq) Unmarshal(b []byte) error { 297 var m marshalKDCReq 298 _, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.TGSREQ)) 299 if err != nil { 300 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling TGS_REQ") 301 } 302 expectedMsgType := msgtype.KRB_TGS_REQ 303 if m.MsgType != expectedMsgType { 304 return krberror.NewErrorf(krberror.KRBMsgError, "message ID does not indicate a TGS_REQ. Expected: %v; Actual: %v", expectedMsgType, m.MsgType) 305 } 306 var reqb KDCReqBody 307 err = reqb.Unmarshal(m.ReqBody.Bytes) 308 if err != nil { 309 return krberror.Errorf(err, krberror.EncodingError, "error processing TGS_REQ body") 310 } 311 k.MsgType = m.MsgType 312 k.PAData = m.PAData 313 k.PVNO = m.PVNO 314 k.ReqBody = reqb 315 return nil 316 } 317 318 // Unmarshal bytes b into the KRB_KDC_REQ body struct. 319 func (k *KDCReqBody) Unmarshal(b []byte) error { 320 var m marshalKDCReqBody 321 _, err := asn1.Unmarshal(b, &m) 322 if err != nil { 323 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling KDC_REQ body") 324 } 325 k.KDCOptions = m.KDCOptions 326 if len(k.KDCOptions.Bytes) < 4 { 327 tb := make([]byte, 4-len(k.KDCOptions.Bytes)) 328 k.KDCOptions.Bytes = append(tb, k.KDCOptions.Bytes...) 329 k.KDCOptions.BitLength = len(k.KDCOptions.Bytes) * 8 330 } 331 k.CName = m.CName 332 k.Realm = m.Realm 333 k.SName = m.SName 334 k.From = m.From 335 k.Till = m.Till 336 k.RTime = m.RTime 337 k.Nonce = m.Nonce 338 k.EType = m.EType 339 k.Addresses = m.Addresses 340 k.EncAuthData = m.EncAuthData 341 if len(m.AdditionalTickets.Bytes) > 0 { 342 k.AdditionalTickets, err = unmarshalTicketsSequence(m.AdditionalTickets) 343 if err != nil { 344 return krberror.Errorf(err, krberror.EncodingError, "error unmarshaling additional tickets") 345 } 346 } 347 return nil 348 } 349 350 // Marshal ASReq struct. 351 func (k *ASReq) Marshal() ([]byte, error) { 352 m := marshalKDCReq{ 353 PVNO: k.PVNO, 354 MsgType: k.MsgType, 355 PAData: k.PAData, 356 } 357 b, err := k.ReqBody.Marshal() 358 if err != nil { 359 var mk []byte 360 return mk, err 361 } 362 m.ReqBody = asn1.RawValue{ 363 Class: asn1.ClassContextSpecific, 364 IsCompound: true, 365 Tag: 4, 366 Bytes: b, 367 } 368 mk, err := asn1.Marshal(m) 369 if err != nil { 370 return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ") 371 } 372 mk = asn1tools.AddASNAppTag(mk, asnAppTag.ASREQ) 373 return mk, nil 374 } 375 376 // Marshal TGSReq struct. 377 func (k *TGSReq) Marshal() ([]byte, error) { 378 m := marshalKDCReq{ 379 PVNO: k.PVNO, 380 MsgType: k.MsgType, 381 PAData: k.PAData, 382 } 383 b, err := k.ReqBody.Marshal() 384 if err != nil { 385 var mk []byte 386 return mk, err 387 } 388 m.ReqBody = asn1.RawValue{ 389 Class: asn1.ClassContextSpecific, 390 IsCompound: true, 391 Tag: 4, 392 Bytes: b, 393 } 394 mk, err := asn1.Marshal(m) 395 if err != nil { 396 return mk, krberror.Errorf(err, krberror.EncodingError, "error marshaling AS_REQ") 397 } 398 mk = asn1tools.AddASNAppTag(mk, asnAppTag.TGSREQ) 399 return mk, nil 400 } 401 402 // Marshal KRB_KDC_REQ body struct. 403 func (k *KDCReqBody) Marshal() ([]byte, error) { 404 var b []byte 405 m := marshalKDCReqBody{ 406 KDCOptions: k.KDCOptions, 407 CName: k.CName, 408 Realm: k.Realm, 409 SName: k.SName, 410 From: k.From, 411 Till: k.Till, 412 RTime: k.RTime, 413 Nonce: k.Nonce, 414 EType: k.EType, 415 Addresses: k.Addresses, 416 EncAuthData: k.EncAuthData, 417 } 418 rawtkts, err := MarshalTicketSequence(k.AdditionalTickets) 419 if err != nil { 420 return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body additional tickets") 421 } 422 //The asn1.rawValue needs the tag setting on it for where it is in the KDCReqBody 423 rawtkts.Tag = 11 424 if len(rawtkts.Bytes) > 0 { 425 m.AdditionalTickets = rawtkts 426 } 427 b, err = asn1.Marshal(m) 428 if err != nil { 429 return b, krberror.Errorf(err, krberror.EncodingError, "error in marshaling KDC request body") 430 } 431 return b, nil 432 }