authenticator.go 5.1 KB

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