blob: bddbc7e3e3ab520989684bdbc9f91c0b7f361332 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// Package credentials provides credentials management for Kerberos 5 authentication.
2package credentials
3
4import (
5 "bytes"
6 "encoding/gob"
7 "encoding/json"
8 "time"
9
10 "github.com/hashicorp/go-uuid"
11 "github.com/jcmturner/gokrb5/v8/iana/nametype"
12 "github.com/jcmturner/gokrb5/v8/keytab"
13 "github.com/jcmturner/gokrb5/v8/types"
14)
15
16const (
17 // AttributeKeyADCredentials assigned number for AD credentials.
18 AttributeKeyADCredentials = "gokrb5AttributeKeyADCredentials"
19)
20
21// Credentials struct for a user.
22// Contains either a keytab, password or both.
23// Keytabs are used over passwords if both are defined.
24type Credentials struct {
25 username string
26 displayName string
27 realm string
28 cname types.PrincipalName
29 keytab *keytab.Keytab
30 password string
31 attributes map[string]interface{}
32 validUntil time.Time
33 authenticated bool
34 human bool
35 authTime time.Time
36 groupMembership map[string]bool
37 sessionID string
38}
39
40// marshalCredentials is used to enable marshaling and unmarshaling of credentials
41// without having exported fields on the Credentials struct
42type marshalCredentials struct {
43 Username string
44 DisplayName string
45 Realm string
46 CName types.PrincipalName `json:"-"`
47 Keytab bool
48 Password bool
49 Attributes map[string]interface{} `json:"-"`
50 ValidUntil time.Time
51 Authenticated bool
52 Human bool
53 AuthTime time.Time
54 GroupMembership map[string]bool `json:"-"`
55 SessionID string
56}
57
58// ADCredentials contains information obtained from the PAC.
59type ADCredentials struct {
60 EffectiveName string
61 FullName string
62 UserID int
63 PrimaryGroupID int
64 LogOnTime time.Time
65 LogOffTime time.Time
66 PasswordLastSet time.Time
67 GroupMembershipSIDs []string
68 LogonDomainName string
69 LogonDomainID string
70 LogonServer string
71}
72
73// New creates a new Credentials instance.
74func New(username string, realm string) *Credentials {
75 uid, err := uuid.GenerateUUID()
76 if err != nil {
77 uid = "00unique-sess-ions-uuid-unavailable0"
78 }
79 return &Credentials{
80 username: username,
81 displayName: username,
82 realm: realm,
83 cname: types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, username),
84 keytab: keytab.New(),
85 attributes: make(map[string]interface{}),
86 groupMembership: make(map[string]bool),
87 sessionID: uid,
88 human: true,
89 }
90}
91
92// NewFromPrincipalName creates a new Credentials instance with the user details provides as a PrincipalName type.
93func NewFromPrincipalName(cname types.PrincipalName, realm string) *Credentials {
94 c := New(cname.PrincipalNameString(), realm)
95 c.cname = cname
96 return c
97}
98
99// WithKeytab sets the Keytab in the Credentials struct.
100func (c *Credentials) WithKeytab(kt *keytab.Keytab) *Credentials {
101 c.keytab = kt
102 c.password = ""
103 return c
104}
105
106// Keytab returns the credential's Keytab.
107func (c *Credentials) Keytab() *keytab.Keytab {
108 return c.keytab
109}
110
111// HasKeytab queries if the Credentials has a keytab defined.
112func (c *Credentials) HasKeytab() bool {
113 if c.keytab != nil && len(c.keytab.Entries) > 0 {
114 return true
115 }
116 return false
117}
118
119// WithPassword sets the password in the Credentials struct.
120func (c *Credentials) WithPassword(password string) *Credentials {
121 c.password = password
122 c.keytab = keytab.New() // clear any keytab
123 return c
124}
125
126// Password returns the credential's password.
127func (c *Credentials) Password() string {
128 return c.password
129}
130
131// HasPassword queries if the Credentials has a password defined.
132func (c *Credentials) HasPassword() bool {
133 if c.password != "" {
134 return true
135 }
136 return false
137}
138
139// SetValidUntil sets the expiry time of the credentials
140func (c *Credentials) SetValidUntil(t time.Time) {
141 c.validUntil = t
142}
143
144// SetADCredentials adds ADCredentials attributes to the credentials
145func (c *Credentials) SetADCredentials(a ADCredentials) {
146 c.SetAttribute(AttributeKeyADCredentials, a)
147 if a.FullName != "" {
148 c.SetDisplayName(a.FullName)
149 }
150 if a.EffectiveName != "" {
151 c.SetUserName(a.EffectiveName)
152 }
153 for i := range a.GroupMembershipSIDs {
154 c.AddAuthzAttribute(a.GroupMembershipSIDs[i])
155 }
156}
157
158// GetADCredentials returns ADCredentials attributes sorted in the credential
159func (c *Credentials) GetADCredentials() ADCredentials {
160 if a, ok := c.attributes[AttributeKeyADCredentials].(ADCredentials); ok {
161 return a
162 }
163 return ADCredentials{}
164}
165
166// Methods to implement goidentity.Identity interface
167
168// UserName returns the credential's username.
169func (c *Credentials) UserName() string {
170 return c.username
171}
172
173// SetUserName sets the username value on the credential.
174func (c *Credentials) SetUserName(s string) {
175 c.username = s
176}
177
178// CName returns the credential's client principal name.
179func (c *Credentials) CName() types.PrincipalName {
180 return c.cname
181}
182
183// SetCName sets the client principal name on the credential.
184func (c *Credentials) SetCName(pn types.PrincipalName) {
185 c.cname = pn
186}
187
188// Domain returns the credential's domain.
189func (c *Credentials) Domain() string {
190 return c.realm
191}
192
193// SetDomain sets the domain value on the credential.
194func (c *Credentials) SetDomain(s string) {
195 c.realm = s
196}
197
198// Realm returns the credential's realm. Same as the domain.
199func (c *Credentials) Realm() string {
200 return c.Domain()
201}
202
203// SetRealm sets the realm value on the credential. Same as the domain
204func (c *Credentials) SetRealm(s string) {
205 c.SetDomain(s)
206}
207
208// DisplayName returns the credential's display name.
209func (c *Credentials) DisplayName() string {
210 return c.displayName
211}
212
213// SetDisplayName sets the display name value on the credential.
214func (c *Credentials) SetDisplayName(s string) {
215 c.displayName = s
216}
217
218// Human returns if the credential represents a human or not.
219func (c *Credentials) Human() bool {
220 return c.human
221}
222
223// SetHuman sets the credential as human.
224func (c *Credentials) SetHuman(b bool) {
225 c.human = b
226}
227
228// AuthTime returns the time the credential was authenticated.
229func (c *Credentials) AuthTime() time.Time {
230 return c.authTime
231}
232
233// SetAuthTime sets the time the credential was authenticated.
234func (c *Credentials) SetAuthTime(t time.Time) {
235 c.authTime = t
236}
237
238// AuthzAttributes returns the credentials authorizing attributes.
239func (c *Credentials) AuthzAttributes() []string {
240 s := make([]string, len(c.groupMembership))
241 i := 0
242 for a := range c.groupMembership {
243 s[i] = a
244 i++
245 }
246 return s
247}
248
249// Authenticated indicates if the credential has been successfully authenticated or not.
250func (c *Credentials) Authenticated() bool {
251 return c.authenticated
252}
253
254// SetAuthenticated sets the credential as having been successfully authenticated.
255func (c *Credentials) SetAuthenticated(b bool) {
256 c.authenticated = b
257}
258
259// AddAuthzAttribute adds an authorization attribute to the credential.
260func (c *Credentials) AddAuthzAttribute(a string) {
261 c.groupMembership[a] = true
262}
263
264// RemoveAuthzAttribute removes an authorization attribute from the credential.
265func (c *Credentials) RemoveAuthzAttribute(a string) {
266 if _, ok := c.groupMembership[a]; !ok {
267 return
268 }
269 delete(c.groupMembership, a)
270}
271
272// EnableAuthzAttribute toggles an authorization attribute to an enabled state on the credential.
273func (c *Credentials) EnableAuthzAttribute(a string) {
274 if enabled, ok := c.groupMembership[a]; ok && !enabled {
275 c.groupMembership[a] = true
276 }
277}
278
279// DisableAuthzAttribute toggles an authorization attribute to a disabled state on the credential.
280func (c *Credentials) DisableAuthzAttribute(a string) {
281 if enabled, ok := c.groupMembership[a]; ok && enabled {
282 c.groupMembership[a] = false
283 }
284}
285
286// Authorized indicates if the credential has the specified authorizing attribute.
287func (c *Credentials) Authorized(a string) bool {
288 if enabled, ok := c.groupMembership[a]; ok && enabled {
289 return true
290 }
291 return false
292}
293
294// SessionID returns the credential's session ID.
295func (c *Credentials) SessionID() string {
296 return c.sessionID
297}
298
299// Expired indicates if the credential has expired.
300func (c *Credentials) Expired() bool {
301 if !c.validUntil.IsZero() && time.Now().UTC().After(c.validUntil) {
302 return true
303 }
304 return false
305}
306
307// ValidUntil returns the credential's valid until date
308func (c *Credentials) ValidUntil() time.Time {
309 return c.validUntil
310}
311
312// Attributes returns the Credentials' attributes map.
313func (c *Credentials) Attributes() map[string]interface{} {
314 return c.attributes
315}
316
317// SetAttribute sets the value of an attribute.
318func (c *Credentials) SetAttribute(k string, v interface{}) {
319 c.attributes[k] = v
320}
321
322// SetAttributes replaces the attributes map with the one provided.
323func (c *Credentials) SetAttributes(a map[string]interface{}) {
324 c.attributes = a
325}
326
327// RemoveAttribute deletes an attribute from the attribute map that has the key provided.
328func (c *Credentials) RemoveAttribute(k string) {
329 delete(c.attributes, k)
330}
331
332// Marshal the Credentials into a byte slice
333func (c *Credentials) Marshal() ([]byte, error) {
334 gob.Register(map[string]interface{}{})
335 gob.Register(ADCredentials{})
336 buf := new(bytes.Buffer)
337 enc := gob.NewEncoder(buf)
338 mc := marshalCredentials{
339 Username: c.username,
340 DisplayName: c.displayName,
341 Realm: c.realm,
342 CName: c.cname,
343 Keytab: c.HasKeytab(),
344 Password: c.HasPassword(),
345 Attributes: c.attributes,
346 ValidUntil: c.validUntil,
347 Authenticated: c.authenticated,
348 Human: c.human,
349 AuthTime: c.authTime,
350 GroupMembership: c.groupMembership,
351 SessionID: c.sessionID,
352 }
353 err := enc.Encode(&mc)
354 if err != nil {
355 return []byte{}, err
356 }
357 return buf.Bytes(), nil
358}
359
360// Unmarshal a byte slice into Credentials
361func (c *Credentials) Unmarshal(b []byte) error {
362 gob.Register(map[string]interface{}{})
363 gob.Register(ADCredentials{})
364 mc := new(marshalCredentials)
365 buf := bytes.NewBuffer(b)
366 dec := gob.NewDecoder(buf)
367 err := dec.Decode(mc)
368 if err != nil {
369 return err
370 }
371 c.username = mc.Username
372 c.displayName = mc.DisplayName
373 c.realm = mc.Realm
374 c.cname = mc.CName
375 c.attributes = mc.Attributes
376 c.validUntil = mc.ValidUntil
377 c.authenticated = mc.Authenticated
378 c.human = mc.Human
379 c.authTime = mc.AuthTime
380 c.groupMembership = mc.GroupMembership
381 c.sessionID = mc.SessionID
382 return nil
383}
384
385// JSON return details of the Credentials in a JSON format.
386func (c *Credentials) JSON() (string, error) {
387 mc := marshalCredentials{
388 Username: c.username,
389 DisplayName: c.displayName,
390 Realm: c.realm,
391 CName: c.cname,
392 Keytab: c.HasKeytab(),
393 Password: c.HasPassword(),
394 ValidUntil: c.validUntil,
395 Authenticated: c.authenticated,
396 Human: c.human,
397 AuthTime: c.authTime,
398 SessionID: c.sessionID,
399 }
400 b, err := json.MarshalIndent(mc, "", " ")
401 if err != nil {
402 return "", err
403 }
404 return string(b), nil
405}