smb: add --smb-kerberos-ccache option to set kerberos ccache per smb backend

This commit is contained in:
Sudipto Baral
2025-07-10 05:17:42 -04:00
committed by GitHub
parent b6767820de
commit 2964b1a169
8 changed files with 264 additions and 39 deletions

View File

@@ -14,65 +14,81 @@ import (
)
var (
kerberosClient *client.Client
kerberosErr error
kerberosOnce sync.Once
kerberosClient sync.Map // map[string]*client.Client
kerberosErr sync.Map // map[string]error
)
// getKerberosClient returns a Kerberos client that can be used to authenticate.
func getKerberosClient() (*client.Client, error) {
if kerberosClient == nil || kerberosErr == nil {
kerberosOnce.Do(func() {
kerberosClient, kerberosErr = createKerberosClient()
})
func resolveCcachePath(ccachePath string) (string, error) {
if ccachePath == "" {
ccachePath = os.Getenv("KRB5CCNAME")
}
return kerberosClient, kerberosErr
}
// createKerberosClient creates a new Kerberos client.
func createKerberosClient() (*client.Client, error) {
cfgPath := os.Getenv("KRB5_CONFIG")
if cfgPath == "" {
cfgPath = "/etc/krb5.conf"
}
cfg, err := config.Load(cfgPath)
if err != nil {
return nil, err
}
// Determine the ccache location from the environment, falling back to the
// default location.
ccachePath := os.Getenv("KRB5CCNAME")
switch {
case strings.Contains(ccachePath, ":"):
parts := strings.SplitN(ccachePath, ":", 2)
switch parts[0] {
prefix, path := parts[0], parts[1]
switch prefix {
case "FILE":
ccachePath = parts[1]
return path, nil
case "DIR":
primary, err := os.ReadFile(filepath.Join(parts[1], "primary"))
primary, err := os.ReadFile(filepath.Join(path, "primary"))
if err != nil {
return nil, err
return "", err
}
ccachePath = filepath.Join(parts[1], strings.TrimSpace(string(primary)))
return filepath.Join(path, strings.TrimSpace(string(primary))), nil
default:
return nil, fmt.Errorf("unsupported KRB5CCNAME: %s", ccachePath)
return "", fmt.Errorf("unsupported KRB5CCNAME: %s", ccachePath)
}
case ccachePath == "":
u, err := user.Current()
if err != nil {
return nil, err
return "", err
}
ccachePath = "/tmp/krb5cc_" + u.Uid
return "/tmp/krb5cc_" + u.Uid, nil
default:
return ccachePath, nil
}
}
ccache, err := credentials.LoadCCache(ccachePath)
func loadKerberosConfig() (*config.Config, error) {
cfgPath := os.Getenv("KRB5_CONFIG")
if cfgPath == "" {
cfgPath = "/etc/krb5.conf"
}
return config.Load(cfgPath)
}
// createKerberosClient creates a new Kerberos client.
func createKerberosClient(ccachePath string) (*client.Client, error) {
ccachePath, err := resolveCcachePath(ccachePath)
if err != nil {
return nil, err
}
return client.NewFromCCache(ccache, cfg)
// check if we already have a client or an error for this ccache path
if errVal, ok := kerberosErr.Load(ccachePath); ok {
return nil, errVal.(error)
}
if clientVal, ok := kerberosClient.Load(ccachePath); ok {
return clientVal.(*client.Client), nil
}
// create a new client if not found in the map
cfg, err := loadKerberosConfig()
if err != nil {
kerberosErr.Store(ccachePath, err)
return nil, err
}
ccache, err := credentials.LoadCCache(ccachePath)
if err != nil {
kerberosErr.Store(ccachePath, err)
return nil, err
}
cl, err := client.NewFromCCache(ccache, cfg)
if err != nil {
kerberosErr.Store(ccachePath, err)
return nil, err
}
kerberosClient.Store(ccachePath, cl)
return cl, nil
}