krb5Token.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package spnego
  2. import (
  3. "context"
  4. "encoding/binary"
  5. "encoding/hex"
  6. "errors"
  7. "fmt"
  8. "github.com/jcmturner/gofork/encoding/asn1"
  9. "gopkg.in/jcmturner/gokrb5.v7/asn1tools"
  10. "gopkg.in/jcmturner/gokrb5.v7/client"
  11. "gopkg.in/jcmturner/gokrb5.v7/credentials"
  12. "gopkg.in/jcmturner/gokrb5.v7/gssapi"
  13. "gopkg.in/jcmturner/gokrb5.v7/iana/chksumtype"
  14. "gopkg.in/jcmturner/gokrb5.v7/iana/msgtype"
  15. "gopkg.in/jcmturner/gokrb5.v7/krberror"
  16. "gopkg.in/jcmturner/gokrb5.v7/messages"
  17. "gopkg.in/jcmturner/gokrb5.v7/service"
  18. "gopkg.in/jcmturner/gokrb5.v7/types"
  19. )
  20. // GSSAPI KRB5 MechToken IDs.
  21. const (
  22. TOK_ID_KRB_AP_REQ = "0100"
  23. TOK_ID_KRB_AP_REP = "0200"
  24. TOK_ID_KRB_ERROR = "0300"
  25. )
  26. // KRB5Token context token implementation for GSSAPI.
  27. type KRB5Token struct {
  28. OID asn1.ObjectIdentifier
  29. tokID []byte
  30. APReq messages.APReq
  31. APRep messages.APRep
  32. KRBError messages.KRBError
  33. settings *service.Settings
  34. context context.Context
  35. }
  36. // Marshal a KRB5Token into a slice of bytes.
  37. func (m *KRB5Token) Marshal() ([]byte, error) {
  38. // Create the header
  39. b, _ := asn1.Marshal(m.OID)
  40. b = append(b, m.tokID...)
  41. var tb []byte
  42. var err error
  43. switch hex.EncodeToString(m.tokID) {
  44. case TOK_ID_KRB_AP_REQ:
  45. tb, err = m.APReq.Marshal()
  46. if err != nil {
  47. return []byte{}, fmt.Errorf("error marshalling AP_REQ for MechToken: %v", err)
  48. }
  49. case TOK_ID_KRB_AP_REP:
  50. return []byte{}, errors.New("marshal of AP_REP GSSAPI MechToken not supported by gokrb5")
  51. case TOK_ID_KRB_ERROR:
  52. return []byte{}, errors.New("marshal of KRB_ERROR GSSAPI MechToken not supported by gokrb5")
  53. }
  54. if err != nil {
  55. return []byte{}, fmt.Errorf("error mashalling kerberos message within mech token: %v", err)
  56. }
  57. b = append(b, tb...)
  58. return asn1tools.AddASNAppTag(b, 0), nil
  59. }
  60. // Unmarshal a KRB5Token.
  61. func (m *KRB5Token) Unmarshal(b []byte) error {
  62. var oid asn1.ObjectIdentifier
  63. r, err := asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0))
  64. if err != nil {
  65. return fmt.Errorf("error unmarshalling KRB5Token OID: %v", err)
  66. }
  67. if !oid.Equal(gssapi.OID(gssapi.OIDKRB5)) {
  68. return fmt.Errorf("error unmarshalling KRB5Token, OID is %s not %s", oid.String(), gssapi.OID(gssapi.OIDKRB5).String())
  69. }
  70. m.OID = oid
  71. if len(r) < 2 {
  72. return fmt.Errorf("krb5token too short")
  73. }
  74. m.tokID = r[0:2]
  75. switch hex.EncodeToString(m.tokID) {
  76. case TOK_ID_KRB_AP_REQ:
  77. var a messages.APReq
  78. err = a.Unmarshal(r[2:])
  79. if err != nil {
  80. return fmt.Errorf("error unmarshalling KRB5Token AP_REQ: %v", err)
  81. }
  82. m.APReq = a
  83. case TOK_ID_KRB_AP_REP:
  84. var a messages.APRep
  85. err = a.Unmarshal(r[2:])
  86. if err != nil {
  87. return fmt.Errorf("error unmarshalling KRB5Token AP_REP: %v", err)
  88. }
  89. m.APRep = a
  90. case TOK_ID_KRB_ERROR:
  91. var a messages.KRBError
  92. err = a.Unmarshal(r[2:])
  93. if err != nil {
  94. return fmt.Errorf("error unmarshalling KRB5Token KRBError: %v", err)
  95. }
  96. m.KRBError = a
  97. }
  98. return nil
  99. }
  100. // Verify a KRB5Token.
  101. func (m *KRB5Token) Verify() (bool, gssapi.Status) {
  102. switch hex.EncodeToString(m.tokID) {
  103. case TOK_ID_KRB_AP_REQ:
  104. ok, creds, err := service.VerifyAPREQ(m.APReq, m.settings)
  105. if err != nil {
  106. return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: err.Error()}
  107. }
  108. if !ok {
  109. return false, gssapi.Status{Code: gssapi.StatusDefectiveCredential, Message: "KRB5_AP_REQ token not valid"}
  110. }
  111. m.context = context.Background()
  112. m.context = context.WithValue(m.context, CTXKeyCredentials, creds)
  113. m.context = context.WithValue(m.context, CTXKeyAuthenticated, ok)
  114. return true, gssapi.Status{Code: gssapi.StatusComplete}
  115. case TOK_ID_KRB_AP_REP:
  116. // Client side
  117. // TODO how to verify the AP_REP - not yet implemented
  118. return false, gssapi.Status{Code: gssapi.StatusFailure, Message: "verifying an AP_REP is not currently supported by gokrb5"}
  119. case TOK_ID_KRB_ERROR:
  120. if m.KRBError.MsgType != msgtype.KRB_ERROR {
  121. return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "KRB5_Error token not valid"}
  122. }
  123. return true, gssapi.Status{Code: gssapi.StatusUnavailable}
  124. }
  125. return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "unknown TOK_ID in KRB5 token"}
  126. }
  127. // IsAPReq tests if the MechToken contains an AP_REQ.
  128. func (m *KRB5Token) IsAPReq() bool {
  129. if hex.EncodeToString(m.tokID) == TOK_ID_KRB_AP_REQ {
  130. return true
  131. }
  132. return false
  133. }
  134. // IsAPRep tests if the MechToken contains an AP_REP.
  135. func (m *KRB5Token) IsAPRep() bool {
  136. if hex.EncodeToString(m.tokID) == TOK_ID_KRB_AP_REP {
  137. return true
  138. }
  139. return false
  140. }
  141. // IsKRBError tests if the MechToken contains an KRB_ERROR.
  142. func (m *KRB5Token) IsKRBError() bool {
  143. if hex.EncodeToString(m.tokID) == TOK_ID_KRB_ERROR {
  144. return true
  145. }
  146. return false
  147. }
  148. // Context returns the KRB5 token's context which will contain any verify user identity information.
  149. func (m *KRB5Token) Context() context.Context {
  150. return m.context
  151. }
  152. // NewKRB5TokenAPREQ creates a new KRB5 token with AP_REQ
  153. func NewKRB5TokenAPREQ(cl *client.Client, tkt messages.Ticket, sessionKey types.EncryptionKey, GSSAPIFlags []int, APOptions []int) (KRB5Token, error) {
  154. // TODO consider providing the SPN rather than the specific tkt and key and get these from the krb client.
  155. var m KRB5Token
  156. m.OID = gssapi.OID(gssapi.OIDKRB5)
  157. tb, _ := hex.DecodeString(TOK_ID_KRB_AP_REQ)
  158. m.tokID = tb
  159. auth, err := krb5TokenAuthenticator(cl.Credentials, GSSAPIFlags)
  160. if err != nil {
  161. return m, err
  162. }
  163. APReq, err := messages.NewAPReq(
  164. tkt,
  165. sessionKey,
  166. auth,
  167. )
  168. if err != nil {
  169. return m, err
  170. }
  171. for _, o := range APOptions {
  172. types.SetFlag(&APReq.APOptions, o)
  173. }
  174. m.APReq = APReq
  175. return m, nil
  176. }
  177. // krb5TokenAuthenticator creates a new kerberos authenticator for kerberos MechToken
  178. func krb5TokenAuthenticator(creds *credentials.Credentials, flags []int) (types.Authenticator, error) {
  179. //RFC 4121 Section 4.1.1
  180. auth, err := types.NewAuthenticator(creds.Domain(), creds.CName())
  181. if err != nil {
  182. return auth, krberror.Errorf(err, krberror.KRBMsgError, "error generating new authenticator")
  183. }
  184. auth.Cksum = types.Checksum{
  185. CksumType: chksumtype.GSSAPI,
  186. Checksum: newAuthenticatorChksum(flags),
  187. }
  188. return auth, nil
  189. }
  190. // Create new authenticator checksum for kerberos MechToken
  191. func newAuthenticatorChksum(flags []int) []byte {
  192. a := make([]byte, 24)
  193. binary.LittleEndian.PutUint32(a[:4], 16)
  194. for _, i := range flags {
  195. if i == gssapi.ContextFlagDeleg {
  196. x := make([]byte, 28-len(a))
  197. a = append(a, x...)
  198. }
  199. f := binary.LittleEndian.Uint32(a[20:24])
  200. f |= uint32(i)
  201. binary.LittleEndian.PutUint32(a[20:24], f)
  202. }
  203. return a
  204. }