go-hep.org/x/hep@v0.38.1/xrootd/xrdproto/auth/krb5/krb5.go (about) 1 // Copyright ©2018 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package krb5 contains the implementation of krb5 (Kerberos) security provider. 6 package krb5 // import "go-hep.org/x/hep/xrootd/xrdproto/auth/krb5" 7 8 import ( 9 "errors" 10 "fmt" 11 "strings" 12 13 "github.com/jcmturner/gokrb5/v8/client" 14 "github.com/jcmturner/gokrb5/v8/config" 15 "github.com/jcmturner/gokrb5/v8/credentials" 16 "github.com/jcmturner/gokrb5/v8/crypto" 17 "github.com/jcmturner/gokrb5/v8/messages" 18 "github.com/jcmturner/gokrb5/v8/types" 19 "go-hep.org/x/hep/xrootd/xrdproto/auth" 20 ) 21 22 // Default is a Kerberos 5 client configured from cached credentials. 23 // If the credentials could not be correctly configured, Default will be nil. 24 var Default auth.Auther 25 26 func init() { 27 v, err := WithCredCache() 28 if err == nil { 29 Default = v 30 } 31 } 32 33 // Auth implements krb5 (Kerberos) security provider. 34 type Auth struct { 35 client *client.Client 36 } 37 38 // WithPassword creates a new Auth configured from the provided user, realm and password. 39 func WithPassword(user, realm, password string) (*Auth, error) { 40 cfg, err := config.Load(configPath) 41 if err != nil { 42 return nil, fmt.Errorf("auth/krb5: could not load kerberos-5 configuration: %w", err) 43 } 44 45 krb := client.NewWithPassword(user, realm, password, cfg) 46 47 err = krb.Login() 48 if err != nil { 49 return nil, fmt.Errorf("auth/krb5: could not login: %w", err) 50 } 51 52 return &Auth{client: krb}, nil 53 } 54 55 // WithCredCache creates a new Auth configured from cached credentials. 56 func WithCredCache() (*Auth, error) { 57 cfg, err := config.Load(configPath) 58 if err != nil { 59 switch err.(type) { 60 case config.UnsupportedDirective: 61 // ok. just ignore it. 62 default: 63 return nil, fmt.Errorf("auth/krb5: could not load kerberos-5 configuration: %w", err) 64 } 65 } 66 67 cred, err := credentials.LoadCCache(cachePath()) 68 if err != nil { 69 return nil, fmt.Errorf("auth/krb5: could not load kerberos-5 cached credentials: %w", err) 70 } 71 72 krb, err := client.NewFromCCache(cred, cfg) 73 if err != nil { 74 return nil, fmt.Errorf("auth/krb5: could not create kerberos-5 client from cached credentials: %w", err) 75 } 76 77 return &Auth{client: krb}, nil 78 } 79 80 // WithClient creates a new Auth using the provided krb5 client. 81 func WithClient(client *client.Client) *Auth { 82 return &Auth{client: client} 83 84 } 85 86 // Provider implements auth.Auther 87 func (*Auth) Provider() string { 88 return "krb5" 89 } 90 91 // Type indicates that krb5 (Kerberos) authentication protocol is used. 92 var Type = [4]byte{'k', 'r', 'b', '5'} 93 94 // Request implements auth.Auther 95 func (a *Auth) Request(params []string) (*auth.Request, error) { 96 if len(params) == 0 { 97 return nil, errors.New("auth/krb5: want at least 1 parameter, got 0") 98 } 99 serviceName := string(params[0]) 100 if strings.Contains(serviceName, "@") { 101 // Service name from the XRootD server may be in the following format: "xrootd/server.example.com@example.com" 102 // While gokrb5 expects server name in that format: "xrootd/server.example.com". 103 // The "@example.com" part (realm) will be guessed from the instance name "server.example.com". 104 index := strings.Index(serviceName, "@") 105 serviceName = serviceName[:index] 106 } 107 tkt, key, err := a.client.GetServiceTicket(serviceName) 108 if err != nil { 109 return nil, fmt.Errorf("auth/krb5: could not retrieve kerberos service ticket: %w", err) 110 } 111 authenticator, err := types.NewAuthenticator(a.client.Credentials.Realm(), a.client.Credentials.CName()) 112 if err != nil { 113 return nil, fmt.Errorf("auth/krb5: could not create kerberos authenticator: %w", err) 114 } 115 etype, err := crypto.GetEtype(key.KeyType) 116 if err != nil { 117 return nil, fmt.Errorf("auth/krb5: could not retrieve crypto key type: %w", err) 118 } 119 err = authenticator.GenerateSeqNumberAndSubKey(key.KeyType, etype.GetKeyByteSize()) 120 if err != nil { 121 return nil, fmt.Errorf("auth/krb5: could not generate sequence number or sub key: %w", err) 122 } 123 APReq, err := messages.NewAPReq(tkt, key, authenticator) 124 if err != nil { 125 return nil, fmt.Errorf("auth/krb5: could not generate AP request: %w", err) 126 } 127 request, err := APReq.Marshal() 128 if err != nil { 129 return nil, fmt.Errorf("auth/krb5: could not marshal AP request: %w", err) 130 } 131 132 return &auth.Request{Type: Type, Credentials: "krb5\000" + string(request)}, nil 133 } 134 135 var ( 136 _ auth.Auther = (*Auth)(nil) 137 )