Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

krb5.conf parameter expansion #429

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 114 additions & 12 deletions v8/config/krb5conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import (
"io"
"net"
"os"
"os/exec"
"os/user"
"regexp"
"runtime"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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{}
Expand All @@ -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,
Expand Down Expand Up @@ -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":
Expand Down Expand Up @@ -726,3 +731,100 @@ func (c *Config) JSON() (string, error) {
}
return string(b), nil
}

var expandMap map[string]string
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have been trying to avoid global variables. Could this be a local variable to the LibDefaults.parseLines() method function?


func init() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like to avoid using Go's init function as if there are issues with it during runtime it can be confusing to debug as it was not explicitly called by another function. Could we initiate the expandMap within the LibDefaults.parseLines() method?

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) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make this a private function.

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")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you share some details on this file please? I've not been able to understand what this relates to. thanks

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",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These things are unix specific and gokrb5 is intended to be platform independent. I'm not sure I completely understand the purpose of needing things lie exec_prefix in a krb5.conf. I need to understand this more so we can figure out what would be an appropriate platform independent approach.

"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",
}
95 changes: 94 additions & 1 deletion v8/config/krb5conf_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"fmt"
"io/ioutil"
"os"
"testing"
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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
`
)

Expand All @@ -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")
Expand Down Expand Up @@ -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)
Expand All @@ -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}"))
}