APExchange.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package service
  2. import (
  3. "fmt"
  4. "gopkg.in/jcmturner/gokrb5.v2/credentials"
  5. "gopkg.in/jcmturner/gokrb5.v2/crypto"
  6. "gopkg.in/jcmturner/gokrb5.v2/iana/errorcode"
  7. "gopkg.in/jcmturner/gokrb5.v2/iana/flags"
  8. "gopkg.in/jcmturner/gokrb5.v2/iana/keyusage"
  9. "gopkg.in/jcmturner/gokrb5.v2/keytab"
  10. "gopkg.in/jcmturner/gokrb5.v2/krberror"
  11. "gopkg.in/jcmturner/gokrb5.v2/messages"
  12. "gopkg.in/jcmturner/gokrb5.v2/types"
  13. "time"
  14. )
  15. // ValidateAPREQ validates an AP_REQ sent to the service. Returns a boolean for if the AP_REQ is valid and the client's principal name and realm.
  16. func ValidateAPREQ(APReq messages.APReq, kt keytab.Keytab, sa string, cAddr string) (bool, credentials.Credentials, error) {
  17. var creds credentials.Credentials
  18. err := APReq.Ticket.DecryptEncPart(kt, sa)
  19. if err != nil {
  20. return false, creds, krberror.Errorf(err, krberror.DecryptingError, "Error decrypting encpart of service ticket provided")
  21. }
  22. ab, err := crypto.DecryptEncPart(APReq.Authenticator, APReq.Ticket.DecryptedEncPart.Key, keyusage.AP_REQ_AUTHENTICATOR)
  23. if err != nil {
  24. return false, creds, krberror.Errorf(err, krberror.DecryptingError, "Error decrypting authenticator")
  25. }
  26. var a types.Authenticator
  27. err = a.Unmarshal(ab)
  28. if err != nil {
  29. return false, creds, krberror.Errorf(err, krberror.EncodingError, "Error unmarshaling authenticator")
  30. }
  31. // Check CName in Authenticator is the same as that in the ticket
  32. if !a.CName.Equal(APReq.Ticket.DecryptedEncPart.CName) {
  33. err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADMATCH, "CName in Authenticator does not match that in service ticket")
  34. return false, creds, err
  35. }
  36. if len(APReq.Ticket.DecryptedEncPart.CAddr) > 0 {
  37. //The addresses in the ticket (if any) are then
  38. //searched for an address matching the operating-system reported
  39. //address of the client. If no match is found or the server insists on
  40. //ticket addresses but none are present in the ticket, the
  41. //KRB_AP_ERR_BADADDR error is returned.
  42. h, err := types.GetHostAddress(cAddr)
  43. if err != nil {
  44. err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, err.Error())
  45. return false, creds, err
  46. }
  47. if !types.HostAddressesContains(APReq.Ticket.DecryptedEncPart.CAddr, h) {
  48. err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_BADADDR, "Client address not within the list contained in the service ticket")
  49. return false, creds, err
  50. }
  51. }
  52. // Check the clock skew between the client and the service server
  53. ct := a.CTime.Add(time.Duration(a.Cusec) * time.Microsecond)
  54. t := time.Now().UTC()
  55. // Hardcode 5 min max skew. May want to make this configurable
  56. d := time.Duration(5) * time.Minute
  57. if t.Sub(ct) > d || ct.Sub(t) > d {
  58. err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_SKEW, fmt.Sprintf("Clock skew with client too large. Greater than %v seconds", d))
  59. return false, creds, err
  60. }
  61. // Check for replay
  62. rc := GetReplayCache(d)
  63. if rc.IsReplay(APReq.Ticket.SName, a) {
  64. err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_REPEAT, "Replay detected")
  65. return false, creds, err
  66. }
  67. // Check for future tickets or invalid tickets
  68. if APReq.Ticket.DecryptedEncPart.StartTime.Sub(t) > d || types.IsFlagSet(&APReq.Ticket.DecryptedEncPart.Flags, flags.Invalid) {
  69. err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_TKT_NYV, "Service ticket provided is not yet valid")
  70. return false, creds, err
  71. }
  72. // Check for expired ticket
  73. if t.Sub(APReq.Ticket.DecryptedEncPart.EndTime) > d {
  74. err := messages.NewKRBError(APReq.Ticket.SName, APReq.Ticket.Realm, errorcode.KRB_AP_ERR_TKT_EXPIRED, "Service ticket provided has expired")
  75. return false, creds, err
  76. }
  77. creds = credentials.NewCredentialsFromPrincipal(a.CName, a.CRealm)
  78. creds.SetAuthTime(t)
  79. creds.SetAuthenticated(true)
  80. isPAC, pac, err := APReq.Ticket.GetPACType(kt, sa)
  81. if isPAC && err != nil {
  82. return false, creds, err
  83. }
  84. if isPAC {
  85. // There is a valid PAC. Adding attributes to creds
  86. creds.SetADCredentials(credentials.ADCredentials{
  87. GroupMembershipSIDs: pac.KerbValidationInfo.GetGroupMembershipSIDs(),
  88. LogOnTime: pac.KerbValidationInfo.LogOnTime.Time(),
  89. LogOffTime: pac.KerbValidationInfo.LogOffTime.Time(),
  90. PasswordLastSet: pac.KerbValidationInfo.PasswordLastSet.Time(),
  91. EffectiveName: pac.KerbValidationInfo.EffectiveName.Value,
  92. FullName: pac.KerbValidationInfo.FullName.Value,
  93. UserID: int(pac.KerbValidationInfo.UserID),
  94. PrimaryGroupID: int(pac.KerbValidationInfo.PrimaryGroupID),
  95. LogonServer: pac.KerbValidationInfo.LogonServer.Value,
  96. LogonDomainName: pac.KerbValidationInfo.LogonDomainName.Value,
  97. LogonDomainID: pac.KerbValidationInfo.LogonDomainID.ToString(),
  98. })
  99. }
  100. return true, creds, nil
  101. }