credentials.go 11 KB

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