github.com/jcmturner/gokrb5/v8@v8.4.4/service/authenticator.go (about)

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