-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathkerberos_native_linux.go
141 lines (118 loc) · 3.31 KB
/
kerberos_native_linux.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//go:build linux
package kpx
import (
"encoding/base64"
"github.com/jcmturner/gokrb5/v8/client"
"github.com/jcmturner/gokrb5/v8/config"
"github.com/jcmturner/gokrb5/v8/credentials"
"github.com/jcmturner/gokrb5/v8/spnego"
"github.com/palantir/stacktrace"
"net"
"os"
"os/user"
"strings"
"sync"
)
var NativeKerberos = &LinuxKerberos{}
type LinuxKerberos struct {
mutex sync.Mutex
cfg *config.Config
}
func (k *LinuxKerberos) SafeTryLogin() error {
if k.cfg != nil {
return nil
}
logInfo("[-] Authenticating user with Linux native kerberos")
var err error
err = k.makeCfg()
if err != nil {
return stacktrace.Propagate(err, "unable to acquire kerberos config from Linux")
}
_, err = k.makeClient()
if err != nil {
return stacktrace.Propagate(err, "unable to acquire kerberos ccache from Linux")
}
return nil
}
func (k *LinuxKerberos) SafeGetToken(protocol string, host string) (*string, error) {
err := k.makeCfg()
if err != nil {
return nil, stacktrace.Propagate(err, "unable to acquire kerberos config from Linux")
}
kcl, err := k.makeClient()
if err != nil {
return nil, stacktrace.Propagate(err, "unable to acquire kerberos ccache from Linux")
}
cname, err := k.getCanonicalHostname(host)
if err != nil {
return nil, stacktrace.Propagate(err, "unable to resolve host from hostname: %s", host)
}
spn := protocol + "/" + cname
nego := spnego.SPNEGOClient(kcl, spn)
err = nego.AcquireCred()
if err != nil {
return nil, stacktrace.Propagate(err, "unable to acquire client credential from Linux for spn: %s", spn)
}
sec, err := nego.InitSecContext()
if err != nil {
return nil, stacktrace.Propagate(err, "unable to initialize security context from Linux for spn: %s", spn)
}
nb, err := sec.Marshal()
if err != nil {
return nil, stacktrace.Propagate(err, "unable to marshal security token from Linux for spn: %s", spn)
}
hs := base64.StdEncoding.EncodeToString(nb)
return &hs, nil
}
func (k *LinuxKerberos) getCanonicalHostname(hostname string) (string, error) {
cname, err := net.LookupCNAME(hostname)
if err != nil {
return "", err
}
return strings.TrimRight(cname, "."), nil
}
func (k *LinuxKerberos) makeCfg() error {
// mutex is used to have a singleton config
k.mutex.Lock()
defer k.mutex.Unlock()
if k.cfg != nil {
return nil
}
// Macs and Windows have different path, also some Unix may have /etc/krb5/krb5.conf
cfgPath := os.Getenv("KRB5_CONFIG")
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
cfgPath = "/etc/krb5.conf"
}
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
cfgPath = "/etc/krb5/krb5.conf"
}
// Load config
cfg, err := config.Load(cfgPath)
if err != nil {
return err
}
k.cfg = cfg
return nil
}
func (k *LinuxKerberos) makeClient() (*client.Client, error) {
// no mutex here because k.cfg is already set and is never changed
u, err := user.Current()
if err != nil {
return nil, err
}
ccPath := "/tmp/krb5cc_" + u.Uid
// Only support KRB5CCNAME as FILE: path to use another path than default /tmp
ccName := os.Getenv("KRB5CCNAME")
if strings.HasPrefix(ccName, "FILE:") {
ccPath = strings.SplitN(ccName, ":", 2)[1]
}
cCache, err := credentials.LoadCCache(ccPath)
if err != nil {
return nil, err
}
kcl, err := client.NewFromCCache(cCache, k.cfg, client.DisablePAFXFAST(true))
if err != nil {
return nil, err
}
return kcl, nil
}