package credentials import ( "bufio" "bytes" "fmt" "io" "os" "os/exec" "os/user" "sync" "testing" "github.com/jcmturner/gokrb5/v8/iana/nametype" "github.com/jcmturner/gokrb5/v8/test" "github.com/jcmturner/gokrb5/v8/test/testdata" "github.com/jcmturner/gokrb5/v8/types" "github.com/stretchr/testify/assert" ) const ( kinitCmd = "kinit" kvnoCmd = "kvno" klistCmd = "klist" spn = "HTTP/host.test.gokrb5" ) type output struct { buf *bytes.Buffer lines []string *sync.Mutex } func newOutput() *output { return &output{ buf: &bytes.Buffer{}, lines: []string{}, Mutex: &sync.Mutex{}, } } func (rw *output) Write(p []byte) (int, error) { rw.Lock() defer rw.Unlock() return rw.buf.Write(p) } func (rw *output) Lines() []string { rw.Lock() defer rw.Unlock() s := bufio.NewScanner(rw.buf) for s.Scan() { rw.lines = append(rw.lines, s.Text()) } return rw.lines } func login() error { file, err := os.Create("/etc/krb5.conf") if err != nil { return fmt.Errorf("cannot open krb5.conf: %v", err) } defer file.Close() fmt.Fprintf(file, testdata.KRB5_CONF) cmd := exec.Command(kinitCmd, "testuser1@TEST.GOKRB5") stdinR, stdinW := io.Pipe() stderrR, stderrW := io.Pipe() cmd.Stdin = stdinR cmd.Stderr = stderrW err = cmd.Start() if err != nil { return fmt.Errorf("could not start %s command: %v", kinitCmd, err) } go func() { io.WriteString(stdinW, "passwordvalue") stdinW.Close() }() errBuf := new(bytes.Buffer) go func() { io.Copy(errBuf, stderrR) stderrR.Close() }() err = cmd.Wait() if err != nil { return fmt.Errorf("%s did not run successfully: %v stderr: %s", kinitCmd, err, string(errBuf.Bytes())) } return nil } func getServiceTkt() error { cmd := exec.Command(kvnoCmd, spn) err := cmd.Start() if err != nil { return fmt.Errorf("could not start %s command: %v", kvnoCmd, err) } err = cmd.Wait() if err != nil { return fmt.Errorf("%s did not run successfully: %v", kvnoCmd, err) } return nil } func klist() ([]string, error) { cmd := exec.Command(klistCmd, "-Aef") stdout := newOutput() cmd.Stdout = stdout err := cmd.Start() if err != nil { return []string{}, fmt.Errorf("could not start %s command: %v", klistCmd, err) } err = cmd.Wait() if err != nil { return []string{}, fmt.Errorf("%s did not run successfully: %v", klistCmd, err) } return stdout.Lines(), nil } func loadCCache() (*CCache, error) { usr, _ := user.Current() cpath := "/tmp/krb5cc_" + usr.Uid return LoadCCache(cpath) } func TestLoadCCache(t *testing.T) { test.Privileged(t) err := login() if err != nil { t.Fatalf("error logging in with kinit: %v", err) } c, err := loadCCache() if err != nil { t.Errorf("error loading CCache: %v", err) } pn := c.GetClientPrincipalName() assert.Equal(t, "testuser1", pn.PrincipalNameString(), "principal not as expected") assert.Equal(t, "TEST.GOKRB5", c.GetClientRealm(), "realm not as expected") } func TestCCacheEntries(t *testing.T) { test.Privileged(t) err := login() if err != nil { t.Fatalf("error logging in with kinit: %v", err) } err = getServiceTkt() if err != nil { t.Fatalf("error getting service ticket: %v", err) } clist, _ := klist() t.Log("OS Creds Cache contents:") for _, l := range clist { t.Log(l) } c, err := loadCCache() if err != nil { t.Errorf("error loading CCache: %v", err) } creds := c.GetEntries() var found bool n := types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, spn) for _, cred := range creds { if cred.Server.PrincipalName.Equal(n) { found = true break } } if !found { t.Errorf("Entry for %s not found in CCache", spn) } }