authenticator.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package service
  2. import (
  3. "encoding/base64"
  4. "fmt"
  5. "strings"
  6. "time"
  7. goidentity "gopkg.in/jcmturner/goidentity.v3"
  8. "gopkg.in/jcmturner/gokrb5.v7/client"
  9. "gopkg.in/jcmturner/gokrb5.v7/config"
  10. "gopkg.in/jcmturner/gokrb5.v7/credentials"
  11. )
  12. // NewKRB5BasicAuthenticator creates a new NewKRB5BasicAuthenticator
  13. func NewKRB5BasicAuthenticator(headerVal string, krb5conf *config.Config, serviceSettings *Settings, clientSettings *client.Settings) KRB5BasicAuthenticator {
  14. return KRB5BasicAuthenticator{
  15. BasicHeaderValue: headerVal,
  16. clientConfig: krb5conf,
  17. serviceSettings: serviceSettings,
  18. clientSettings: clientSettings,
  19. }
  20. }
  21. // KRB5BasicAuthenticator implements gopkg.in/jcmturner/goidentity.v3.Authenticator interface.
  22. // It takes username and password so can be used for basic authentication.
  23. type KRB5BasicAuthenticator struct {
  24. BasicHeaderValue string
  25. serviceSettings *Settings
  26. clientSettings *client.Settings
  27. clientConfig *config.Config
  28. realm string
  29. username string
  30. password string
  31. }
  32. // Authenticate and return the identity. The boolean indicates if the authentication was successful.
  33. func (a KRB5BasicAuthenticator) Authenticate() (i goidentity.Identity, ok bool, err error) {
  34. a.realm, a.username, a.password, err = parseBasicHeaderValue(a.BasicHeaderValue)
  35. if err != nil {
  36. err = fmt.Errorf("could not parse basic authentication header: %v", err)
  37. return
  38. }
  39. cl := client.NewClientWithPassword(a.username, a.realm, a.password, a.clientConfig)
  40. err = cl.Login()
  41. if err != nil {
  42. // Username and/or password could be wrong
  43. err = fmt.Errorf("error with user credentials during login: %v", err)
  44. return
  45. }
  46. tkt, _, err := cl.GetServiceTicket(a.serviceSettings.SName())
  47. if err != nil {
  48. err = fmt.Errorf("could not get service ticket: %v", err)
  49. return
  50. }
  51. err = tkt.DecryptEncPart(a.serviceSettings.Keytab, a.serviceSettings.KeytabPrincipal())
  52. if err != nil {
  53. err = fmt.Errorf("could not decrypt service ticket: %v", err)
  54. return
  55. }
  56. cl.Credentials.SetAuthTime(time.Now().UTC())
  57. cl.Credentials.SetAuthenticated(true)
  58. isPAC, pac, err := tkt.GetPACType(a.serviceSettings.Keytab, a.serviceSettings.KeytabPrincipal(), a.serviceSettings.Logger())
  59. if isPAC && err != nil {
  60. err = fmt.Errorf("error processing PAC: %v", err)
  61. return
  62. }
  63. if isPAC {
  64. // There is a valid PAC. Adding attributes to creds
  65. cl.Credentials.SetADCredentials(credentials.ADCredentials{
  66. GroupMembershipSIDs: pac.KerbValidationInfo.GetGroupMembershipSIDs(),
  67. LogOnTime: pac.KerbValidationInfo.LogOnTime.Time(),
  68. LogOffTime: pac.KerbValidationInfo.LogOffTime.Time(),
  69. PasswordLastSet: pac.KerbValidationInfo.PasswordLastSet.Time(),
  70. EffectiveName: pac.KerbValidationInfo.EffectiveName.Value,
  71. FullName: pac.KerbValidationInfo.FullName.Value,
  72. UserID: int(pac.KerbValidationInfo.UserID),
  73. PrimaryGroupID: int(pac.KerbValidationInfo.PrimaryGroupID),
  74. LogonServer: pac.KerbValidationInfo.LogonServer.Value,
  75. LogonDomainName: pac.KerbValidationInfo.LogonDomainName.Value,
  76. LogonDomainID: pac.KerbValidationInfo.LogonDomainID.String(),
  77. })
  78. }
  79. ok = true
  80. i = cl.Credentials
  81. return
  82. }
  83. // Mechanism returns the authentication mechanism.
  84. func (a KRB5BasicAuthenticator) Mechanism() string {
  85. return "Kerberos Basic"
  86. }
  87. func parseBasicHeaderValue(s string) (domain, username, password string, err error) {
  88. b, err := base64.StdEncoding.DecodeString(s)
  89. if err != nil {
  90. return
  91. }
  92. v := string(b)
  93. vc := strings.SplitN(v, ":", 2)
  94. password = vc[1]
  95. // Domain and username can be specified in 2 formats:
  96. // <Username> - no domain specified
  97. // <Domain>\<Username>
  98. // <Username>@<Domain>
  99. if strings.Contains(vc[0], `\`) {
  100. u := strings.SplitN(vc[0], `\`, 2)
  101. domain = u[0]
  102. username = u[1]
  103. } else if strings.Contains(vc[0], `@`) {
  104. u := strings.SplitN(vc[0], `@`, 2)
  105. domain = u[1]
  106. username = u[0]
  107. } else {
  108. username = vc[0]
  109. }
  110. return
  111. }