authenticator.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package service
  2. import (
  3. "encoding/base64"
  4. "errors"
  5. "fmt"
  6. goidentity "gopkg.in/jcmturner/goidentity.v1"
  7. "gopkg.in/jcmturner/gokrb5.v2/client"
  8. "gopkg.in/jcmturner/gokrb5.v2/config"
  9. "gopkg.in/jcmturner/gokrb5.v2/credentials"
  10. "gopkg.in/jcmturner/gokrb5.v2/gssapi"
  11. "gopkg.in/jcmturner/gokrb5.v2/keytab"
  12. "strings"
  13. "time"
  14. )
  15. // SPNEGOAuthenticator implements gopkg.in/jcmturner/goidentity.v1.Authenticator interface
  16. type SPNEGOAuthenticator struct {
  17. SPNEGOHeaderValue string
  18. Keytab *keytab.Keytab
  19. ServiceAccount string
  20. ClientAddr string
  21. }
  22. // Authenticate and retrieve a goidentity.Identity. In this case it is a pointer to a credentials.Credentials
  23. func (a SPNEGOAuthenticator) Authenticate() (i goidentity.Identity, ok bool, err error) {
  24. b, err := base64.StdEncoding.DecodeString(a.SPNEGOHeaderValue)
  25. if err != nil {
  26. err = fmt.Errorf("SPNEGO error in base64 decoding negotiation header: %v", err)
  27. return
  28. }
  29. var spnego gssapi.SPNEGO
  30. err = spnego.Unmarshal(b)
  31. if !spnego.Init {
  32. err = fmt.Errorf("SPNEGO negotiation token is not a NegTokenInit: %v", err)
  33. return
  34. }
  35. if !spnego.NegTokenInit.MechTypes[0].Equal(gssapi.MechTypeOIDKRB5) {
  36. err = errors.New("SPNEGO OID of MechToken is not of type KRB5")
  37. return
  38. }
  39. var mt gssapi.MechToken
  40. err = mt.Unmarshal(spnego.NegTokenInit.MechToken)
  41. if err != nil {
  42. err = fmt.Errorf("SPNEGO error unmarshaling MechToken: %v", err)
  43. return
  44. }
  45. if !mt.IsAPReq() {
  46. err = errors.New("MechToken does not contain an AP_REQ - KRB_AP_ERR_MSG_TYPE")
  47. return
  48. }
  49. ok, c, err := ValidateAPREQ(mt.APReq, *a.Keytab, a.ServiceAccount, a.ClientAddr)
  50. if err != nil {
  51. err = fmt.Errorf("SPNEGO validation error: %v", err)
  52. return
  53. }
  54. i = &c
  55. return
  56. }
  57. // Mechanism returns the authentication mechanism.
  58. func (a SPNEGOAuthenticator) Mechanism() string {
  59. return "SPNEGO Kerberos"
  60. }
  61. // KRB5BasicAuthenticator implements gopkg.in/jcmturner/goidentity.v1.Authenticator interface.
  62. // It takes username and password so can be used for basic authentication.
  63. type KRB5BasicAuthenticator struct {
  64. BasicHeaderValue string
  65. realm string
  66. username string
  67. password string
  68. ServiceKeytab *keytab.Keytab
  69. ServiceAccount string
  70. Config *config.Config
  71. SPN string
  72. }
  73. // Authenticate and return the identity. The boolean indicates if the authentication was successful.
  74. func (a KRB5BasicAuthenticator) Authenticate() (i goidentity.Identity, ok bool, err error) {
  75. a.realm, a.username, a.password, err = parseBasicHeaderValue(a.BasicHeaderValue)
  76. if err != nil {
  77. err = fmt.Errorf("could not parse basic authentication header: %v", err)
  78. return
  79. }
  80. cl := client.NewClientWithPassword(a.username, a.realm, a.password)
  81. cl.WithConfig(a.Config)
  82. err = cl.Login()
  83. if err != nil {
  84. // Username and/or password could be wrong
  85. err = fmt.Errorf("Error with user credentials during login: %v", err)
  86. return
  87. }
  88. tkt, _, err := cl.GetServiceTicket(a.SPN)
  89. if err != nil {
  90. err = fmt.Errorf("Could not get service ticket: %v", err)
  91. return
  92. }
  93. err = tkt.DecryptEncPart(*a.ServiceKeytab, a.ServiceAccount)
  94. if err != nil {
  95. err = fmt.Errorf("Could not decrypt service ticket: %v", err)
  96. return
  97. }
  98. cl.Credentials.SetAuthTime(time.Now().UTC())
  99. cl.Credentials.SetAuthenticated(true)
  100. isPAC, pac, err := tkt.GetPACType(*a.ServiceKeytab, a.ServiceAccount)
  101. if isPAC && err != nil {
  102. err = fmt.Errorf("Error processing PAC: %v", err)
  103. return
  104. }
  105. if isPAC {
  106. // There is a valid PAC. Adding attributes to creds
  107. cl.Credentials.SetADCredentials(credentials.ADCredentials{
  108. GroupMembershipSIDs: pac.KerbValidationInfo.GetGroupMembershipSIDs(),
  109. LogOnTime: pac.KerbValidationInfo.LogOnTime.Time(),
  110. LogOffTime: pac.KerbValidationInfo.LogOffTime.Time(),
  111. PasswordLastSet: pac.KerbValidationInfo.PasswordLastSet.Time(),
  112. EffectiveName: pac.KerbValidationInfo.EffectiveName.Value,
  113. FullName: pac.KerbValidationInfo.FullName.Value,
  114. UserID: int(pac.KerbValidationInfo.UserID),
  115. PrimaryGroupID: int(pac.KerbValidationInfo.PrimaryGroupID),
  116. LogonServer: pac.KerbValidationInfo.LogonServer.Value,
  117. LogonDomainName: pac.KerbValidationInfo.LogonDomainName.Value,
  118. LogonDomainID: pac.KerbValidationInfo.LogonDomainID.ToString(),
  119. })
  120. }
  121. ok = true
  122. i = cl.Credentials
  123. return
  124. }
  125. // Mechanism returns the authentication mechanism.
  126. func (a KRB5BasicAuthenticator) Mechanism() string {
  127. return "Kerberos Basic"
  128. }
  129. func parseBasicHeaderValue(s string) (domain, username, password string, err error) {
  130. b, err := base64.StdEncoding.DecodeString(s)
  131. if err != nil {
  132. return
  133. }
  134. v := string(b)
  135. vc := strings.SplitN(v, ":", 2)
  136. password = vc[1]
  137. // Domain and username can be specified in 2 formats:
  138. // <Username> - no domain specified
  139. // <Domain>\<Username>
  140. // <Username>@<Domain>
  141. if strings.Contains(vc[0], `\`) {
  142. u := strings.SplitN(vc[0], `\`, 2)
  143. domain = u[0]
  144. username = u[1]
  145. } else if strings.Contains(vc[0], `@`) {
  146. u := strings.SplitN(vc[0], `@`, 2)
  147. domain = u[1]
  148. username = u[0]
  149. } else {
  150. username = vc[0]
  151. }
  152. return
  153. }