From 3a5ccd198a558017ddec0e1610ce9b75956d4bc6 Mon Sep 17 00:00:00 2001 From: Jake Scott Date: Sun, 21 Feb 2021 12:01:43 -0500 Subject: [PATCH] krb5.conf parameter expansion Fixes #428 Support parameter expansion for default_ccache_name, default_client_keytab_name and default_keytab_name. Additionally, parse krb5-config if it exists, for defaults that aren't set in krb5.conf. This should support MIT sites that use a self-maintained version or distros that change MIT defaults. --- v8/config/krb5conf.go | 126 +++++++++++++++++++++++++++++++++---- v8/config/krb5conf_test.go | 95 +++++++++++++++++++++++++++- 2 files changed, 208 insertions(+), 13 deletions(-) diff --git a/v8/config/krb5conf.go b/v8/config/krb5conf.go index a7638433..dd25e76e 100644 --- a/v8/config/krb5conf.go +++ b/v8/config/krb5conf.go @@ -10,8 +10,10 @@ import ( "io" "net" "os" + "os/exec" "os/user" "regexp" + "runtime" "strconv" "strings" "time" @@ -46,12 +48,12 @@ func New() *Config { type LibDefaults struct { AllowWeakCrypto bool //default false // ap_req_checksum_type int //unlikely to support this - Canonicalize bool //default false - CCacheType int //default is 4. unlikely to implement older - Clockskew time.Duration //max allowed skew in seconds, default 300 - //Default_ccache_name string // default /tmp/krb5cc_%{uid} //Not implementing as will hold in memory - DefaultClientKeytabName string //default /usr/local/var/krb5/user/%{euid}/client.keytab - DefaultKeytabName string //default /etc/krb5.keytab + Canonicalize bool //default false + CCacheType int //default is 4. unlikely to implement older + Clockskew time.Duration //max allowed skew in seconds, default 300 + DefaultCcacheName string // default /tmp/krb5cc_%{uid} //Not implementing as will hold in memory + DefaultClientKeytabName string //default /usr/local/var/krb5/user/%{euid}/client.keytab + DefaultKeytabName string //default /etc/krb5.keytab DefaultRealm string DefaultTGSEnctypes []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4 DefaultTktEnctypes []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4 @@ -85,11 +87,11 @@ type LibDefaults struct { // Create a new LibDefaults struct. func newLibDefaults() LibDefaults { - uid := "0" + vars := PkgConfigVars(nil) + var hdir string usr, _ := user.Current() if usr != nil { - uid = usr.Uid hdir = usr.HomeDir } opts := asn1.BitString{} @@ -98,8 +100,9 @@ func newLibDefaults() LibDefaults { return LibDefaults{ CCacheType: 4, Clockskew: time.Duration(300) * time.Second, - DefaultClientKeytabName: fmt.Sprintf("/usr/local/var/krb5/user/%s/client.keytab", uid), - DefaultKeytabName: "/etc/krb5.keytab", + DefaultCcacheName: ExpandParams(vars["DEFCCNAME"]), + DefaultClientKeytabName: ExpandParams(vars["DEFCKTNAME"]), + DefaultKeytabName: ExpandParams(vars["DEFKTNAME"]), DefaultTGSEnctypes: []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96", "des3-cbc-sha1", "arcfour-hmac-md5", "camellia256-cts-cmac", "camellia128-cts-cmac", "des-cbc-crc", "des-cbc-md5", "des-cbc-md4"}, DefaultTktEnctypes: []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96", "des3-cbc-sha1", "arcfour-hmac-md5", "camellia256-cts-cmac", "camellia128-cts-cmac", "des-cbc-crc", "des-cbc-md5", "des-cbc-md4"}, DNSCanonicalizeHostname: true, @@ -160,10 +163,12 @@ func (l *LibDefaults) parseLines(lines []string) error { return InvalidErrorf("libdefaults section line (%s): %v", line, err) } l.Clockskew = d + case "default_ccache_name": + l.DefaultCcacheName = ExpandParams(strings.TrimSpace(p[1])) case "default_client_keytab_name": - l.DefaultClientKeytabName = strings.TrimSpace(p[1]) + l.DefaultClientKeytabName = ExpandParams(strings.TrimSpace(p[1])) case "default_keytab_name": - l.DefaultKeytabName = strings.TrimSpace(p[1]) + l.DefaultKeytabName = ExpandParams(strings.TrimSpace(p[1])) case "default_realm": l.DefaultRealm = strings.TrimSpace(p[1]) case "default_tgs_enctypes": @@ -726,3 +731,100 @@ func (c *Config) JSON() (string, error) { } return string(b), nil } + +var expandMap map[string]string + +func init() { + expandMap = makeExpandMap() +} + +func makeExpandMap() (out map[string]string) { + out = make(map[string]string) + vars := PkgConfigVars(nil) + + out["TEMP"] = os.TempDir() + + user, err := user.Current() + if err == nil { + out["uid"] = user.Uid + out["USERID"] = user.Uid + out["username"] = user.Username + } + + if runtime.GOOS == "windows" { + out["euid"] = out["uid"] + } else { + out["euid"] = fmt.Sprintf("%d", os.Geteuid()) + } + + out["null"] = "" + out["LIBDIR"] = vars["libdir"] + out["BINDIR"] = vars["exec_prefix"] + "/bin" + out["SBINDIR"] = vars["exec_prefix"] + "/sbin" + + return +} + +func ExpandParams(in string) (out string) { + out = in + + for k, v := range expandMap { + repl := fmt.Sprintf("%%{%s}", k) + out = strings.ReplaceAll(out, repl, v) + } + return +} + +// PkgConfigVars returns useful variables from the krb5 pkg-config file, if it is found +// otherwise returns a set of defaults +func PkgConfigVars(cfg *string) (out map[string]string) { + out = make(map[string]string) + for k, v := range defaultPkgConfigVars { + out[k] = v + } + + if cfg == nil { + // find krb5-config + file, err := exec.LookPath("krb5-config") + if err == nil { + cfg = &file + } + } + + if cfg == nil { + return + } + + fh, err := os.Open(*cfg) + if err != nil { + return + } + defer fh.Close() + + var lineRegex = regexp.MustCompile(`^(\w+)='?([^']+)'?$`) + + scanner := bufio.NewScanner(fh) + for scanner.Scan() { + line := scanner.Text() + kv := lineRegex.FindStringSubmatch(line) + if len(kv) != 3 { + continue + } + + if _, ok := defaultPkgConfigVars[kv[1]]; ok { + out[kv[1]] = kv[2] + } + } + + return +} + +var defaultPkgConfigVars = map[string]string{ + "prefix": "/usr", + "exec_prefix": "/usr", + "includedir": "/usr/include", + "libdir": "/usr/lib", + "DEFCCNAME": "FILE:/tmp/krb5cc_%{uid}", + "DEFKTNAME": "FILE:/etc/krb5.keytab", + "DEFCKTNAME": "FILE:/var/kerberos/krb5/user/%{euid}/client.keytab", +} diff --git a/v8/config/krb5conf_test.go b/v8/config/krb5conf_test.go index 55621af6..d0ea0354 100644 --- a/v8/config/krb5conf_test.go +++ b/v8/config/krb5conf_test.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "io/ioutil" "os" "testing" @@ -31,7 +32,7 @@ const ( default_client_keytab_name = FILE:/home/gokrb5/client.keytab default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 # comment to be ignored - + default_ccache_name = FILE:%{TEMP}/krb5cc.%{uid} [realms] TEST.GOKRB5 = { @@ -85,6 +86,7 @@ const ( "Canonicalize": false, "CCacheType": 4, "Clockskew": 300000000000, + "DefaultCcacheName": "/tmp/testcc", "DefaultClientKeytabName": "FILE:/home/gokrb5/client.keytab", "DefaultKeytabName": "FILE:/etc/krb5.keytab", "DefaultRealm": "TEST.GOKRB5", @@ -440,6 +442,53 @@ const ( forwardable = true krb4_convert = false } +` + + pkg_config = ` +#!/usr/bin/sh + +# Copyright 2001, 2002, 2003 by the Massachusetts Institute of Technology. +# All Rights Reserved. +# + +# Configurable parameters set by autoconf +version_string="Kerberos 5 release 1.18.2" + +prefix=/test/usr +exec_prefix=/test/usr +includedir=/usr/include +libdir=/test/usr/lib +CC_LINK='$(CC) $(PROG_LIBPATH) $(PROG_RPATH_FLAGS) $(CFLAGS) -pie -Wl,-z,relro -Wl,-z,now $(LDFLAGS)' +KDB5_DB_LIB= +LDFLAGS='-Wl,-z,relro -Wl,--as-needed -Wl,-z,now ' +RPATH_FLAG='' +PROG_RPATH_FLAGS='' +PTHREAD_CFLAGS='-pthread' +DL_LIB='-ldl' +DEFCCNAME='FILE:/test/tmp/krb5cc_%{uid}' +DEFKTNAME='FILE:/test/etc/krb5.keytab' +DEFCKTNAME='FILE:/test/var/kerberos/krb5/user/%{euid}/client.keytab' +SELINUX_LIBS='-lselinux ' + +LIBS='-lkeyutils -lcrypto -lresolv' +GEN_LIB= + +# Defaults for program +library=krb5 + +# Some constants +vendor_string="Massachusetts Institute of Technology" + +# Process arguments +# Yes, we are sloppy, library specifications can come before options +while test $# != 0; do + case $1 in + --all) + do_all=1 + ;; + +#### truncated for test +exit 0 ` ) @@ -462,6 +511,7 @@ func TestLoad(t *testing.T) { assert.Equal(t, "FILE:/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected") assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected") assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected") + assert.Equal(t, fmt.Sprintf("FILE:%s/krb5cc.%d", os.TempDir(), os.Getuid()), c.LibDefaults.DefaultCcacheName, "[libdefaults] default_ccache_name not as expected") assert.Equal(t, 3, len(c.Realms), "Number of realms not as expected") assert.Equal(t, "TEST.GOKRB5", c.Realms[0].Realm, "[realm] realm name not as expectd") @@ -671,6 +721,7 @@ func TestJSON(t *testing.T) { t.Fatalf("Error loading config: %v", err) } c.LibDefaults.K5LoginDirectory = "/home/test" + c.LibDefaults.DefaultCcacheName = "/tmp/testcc" j, err := c.JSON() if err != nil { t.Errorf("error marshaling krb config to JSON: %v", err) @@ -679,3 +730,45 @@ func TestJSON(t *testing.T) { t.Log(j) } + +func TestPkgConfigVars(t *testing.T) { + t.Parallel() + + fh, err := ioutil.TempFile("", "test") + if err != nil { + t.Fatalf("creating temp file: %v", err) + } + + defer os.Remove(fh.Name()) + + if _, err := fh.Write([]byte(pkg_config)); err != nil { + t.Fatalf("writing temp file: %v", err) + } + + fh.Sync() + + fname := fh.Name() + vars := PkgConfigVars(&fname) + assert.Equal(t, "/test/usr", vars["prefix"]) + assert.Equal(t, "/test/usr", vars["exec_prefix"]) + assert.Equal(t, "/usr/include", vars["includedir"]) + assert.Equal(t, "/test/usr/lib", vars["libdir"]) + assert.Equal(t, "FILE:/test/tmp/krb5cc_%{uid}", vars["DEFCCNAME"]) + assert.Equal(t, "FILE:/test/etc/krb5.keytab", vars["DEFKTNAME"]) + assert.Equal(t, "FILE:/test/var/kerberos/krb5/user/%{euid}/client.keytab", vars["DEFCKTNAME"]) +} + +func TestExpandParams(t *testing.T) { + t.Parallel() + + vars := PkgConfigVars(nil) + + want := fmt.Sprintf("FILE:/tmp/krb5cc_%d", os.Getuid()) + assert.Equal(t, want, ExpandParams("FILE:/tmp/krb5cc_%{uid}")) + + want = fmt.Sprintf("test %s", vars["libdir"]) + assert.Equal(t, want, ExpandParams("test %{LIBDIR}")) + + want = fmt.Sprintf("%s/user-%d", os.TempDir(), os.Getuid()) + assert.Equal(t, want, ExpandParams("%{TEMP}/user-%{uid}")) +}