negotiationToken.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. package spnego
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "github.com/jcmturner/gofork/encoding/asn1"
  7. "gopkg.in/jcmturner/gokrb5.v7/client"
  8. "gopkg.in/jcmturner/gokrb5.v7/gssapi"
  9. "gopkg.in/jcmturner/gokrb5.v7/messages"
  10. "gopkg.in/jcmturner/gokrb5.v7/service"
  11. "gopkg.in/jcmturner/gokrb5.v7/types"
  12. )
  13. // https://msdn.microsoft.com/en-us/library/ms995330.aspx
  14. // Negotiation state values.
  15. const (
  16. NegStateAcceptCompleted NegState = 0
  17. NegStateAcceptIncomplete NegState = 1
  18. NegStateReject NegState = 2
  19. NegStateRequestMIC NegState = 3
  20. )
  21. // NegState is a type to indicate the SPNEGO negotiation state.
  22. type NegState int
  23. // NegTokenInit implements Negotiation Token of type Init.
  24. type NegTokenInit struct {
  25. MechTypes []asn1.ObjectIdentifier
  26. ReqFlags gssapi.ContextFlags
  27. MechTokenBytes []byte
  28. MechListMIC []byte
  29. mechToken gssapi.ContextToken
  30. settings *service.Settings
  31. }
  32. type marshalNegTokenInit struct {
  33. MechTypes []asn1.ObjectIdentifier `asn1:"explicit,tag:0"`
  34. ReqFlags gssapi.ContextFlags `asn1:"explicit,optional,tag:1"`
  35. MechTokenBytes []byte `asn1:"explicit,optional,omitempty,tag:2"`
  36. MechListMIC []byte `asn1:"explicit,optional,omitempty,tag:3"` // This field is not used when negotiating Kerberos tokens
  37. }
  38. // NegTokenResp implements Negotiation Token of type Resp/Targ
  39. type NegTokenResp struct {
  40. NegState asn1.Enumerated
  41. SupportedMech asn1.ObjectIdentifier
  42. ResponseToken []byte
  43. MechListMIC []byte
  44. mechToken gssapi.ContextToken
  45. settings *service.Settings
  46. }
  47. type marshalNegTokenResp struct {
  48. NegState asn1.Enumerated `asn1:"explicit,tag:0"`
  49. SupportedMech asn1.ObjectIdentifier `asn1:"explicit,optional,tag:1"`
  50. ResponseToken []byte `asn1:"explicit,optional,omitempty,tag:2"`
  51. MechListMIC []byte `asn1:"explicit,optional,omitempty,tag:3"` // This field is not used when negotiating Kerberos tokens
  52. }
  53. // NegTokenTarg implements Negotiation Token of type Resp/Targ
  54. type NegTokenTarg NegTokenResp
  55. // Marshal an Init negotiation token
  56. func (n *NegTokenInit) Marshal() ([]byte, error) {
  57. m := marshalNegTokenInit{
  58. MechTypes: n.MechTypes,
  59. ReqFlags: n.ReqFlags,
  60. MechTokenBytes: n.MechTokenBytes,
  61. MechListMIC: n.MechListMIC,
  62. }
  63. b, err := asn1.Marshal(m)
  64. if err != nil {
  65. return nil, err
  66. }
  67. nt := asn1.RawValue{
  68. Tag: 0,
  69. Class: 2,
  70. IsCompound: true,
  71. Bytes: b,
  72. }
  73. nb, err := asn1.Marshal(nt)
  74. if err != nil {
  75. return nil, err
  76. }
  77. return nb, nil
  78. }
  79. // Unmarshal an Init negotiation token
  80. func (n *NegTokenInit) Unmarshal(b []byte) error {
  81. init, nt, err := UnmarshalNegToken(b)
  82. if err != nil {
  83. return err
  84. }
  85. if !init {
  86. return errors.New("bytes were not that of a NegTokenInit")
  87. }
  88. nInit := nt.(NegTokenInit)
  89. n.MechTokenBytes = nInit.MechTokenBytes
  90. n.MechListMIC = nInit.MechListMIC
  91. n.MechTypes = nInit.MechTypes
  92. n.ReqFlags = nInit.ReqFlags
  93. return nil
  94. }
  95. // Verify an Init negotiation token
  96. func (n *NegTokenInit) Verify() (bool, gssapi.Status) {
  97. // Check if supported mechanisms are in the MechTypeList
  98. var mtSupported bool
  99. for _, m := range n.MechTypes {
  100. if m.Equal(gssapi.OID(gssapi.OIDKRB5)) || m.Equal(gssapi.OID(gssapi.OIDMSLegacyKRB5)) {
  101. if n.mechToken == nil && n.MechTokenBytes == nil {
  102. return false, gssapi.Status{Code: gssapi.StatusContinueNeeded}
  103. }
  104. mtSupported = true
  105. break
  106. }
  107. }
  108. if !mtSupported {
  109. return false, gssapi.Status{Code: gssapi.StatusBadMech, Message: "no supported mechanism specified in negotiation"}
  110. }
  111. // There should be some mechtoken bytes for a KRB5Token (other mech types are not supported)
  112. mt := new(KRB5Token)
  113. mt.settings = n.settings
  114. if n.mechToken == nil {
  115. err := mt.Unmarshal(n.MechTokenBytes)
  116. if err != nil {
  117. return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: err.Error()}
  118. }
  119. n.mechToken = mt
  120. } else {
  121. var ok bool
  122. mt, ok = n.mechToken.(*KRB5Token)
  123. if !ok {
  124. return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "MechToken is not a KRB5 token as expected"}
  125. }
  126. }
  127. // Verify the mechtoken
  128. return n.mechToken.Verify()
  129. }
  130. // Context returns the SPNEGO context which will contain any verify user identity information.
  131. func (n *NegTokenInit) Context() context.Context {
  132. if n.mechToken != nil {
  133. mt, ok := n.mechToken.(*KRB5Token)
  134. if !ok {
  135. return nil
  136. }
  137. return mt.Context()
  138. }
  139. return nil
  140. }
  141. // Marshal a Resp/Targ negotiation token
  142. func (n *NegTokenResp) Marshal() ([]byte, error) {
  143. m := marshalNegTokenResp{
  144. NegState: n.NegState,
  145. SupportedMech: n.SupportedMech,
  146. ResponseToken: n.ResponseToken,
  147. MechListMIC: n.MechListMIC,
  148. }
  149. b, err := asn1.Marshal(m)
  150. if err != nil {
  151. return nil, err
  152. }
  153. nt := asn1.RawValue{
  154. Tag: 1,
  155. Class: 2,
  156. IsCompound: true,
  157. Bytes: b,
  158. }
  159. nb, err := asn1.Marshal(nt)
  160. if err != nil {
  161. return nil, err
  162. }
  163. return nb, nil
  164. }
  165. // Unmarshal a Resp/Targ negotiation token
  166. func (n *NegTokenResp) Unmarshal(b []byte) error {
  167. init, nt, err := UnmarshalNegToken(b)
  168. if err != nil {
  169. return err
  170. }
  171. if init {
  172. return errors.New("bytes were not that of a NegTokenResp")
  173. }
  174. nResp := nt.(NegTokenResp)
  175. n.MechListMIC = nResp.MechListMIC
  176. n.NegState = nResp.NegState
  177. n.ResponseToken = nResp.ResponseToken
  178. n.SupportedMech = nResp.SupportedMech
  179. return nil
  180. }
  181. // Verify a Resp/Targ negotiation token
  182. func (n *NegTokenResp) Verify() (bool, gssapi.Status) {
  183. if n.SupportedMech.Equal(gssapi.OID(gssapi.OIDKRB5)) || n.SupportedMech.Equal(gssapi.OID(gssapi.OIDMSLegacyKRB5)) {
  184. if n.mechToken == nil && n.ResponseToken == nil {
  185. return false, gssapi.Status{Code: gssapi.StatusContinueNeeded}
  186. }
  187. mt := new(KRB5Token)
  188. mt.settings = n.settings
  189. if n.mechToken == nil {
  190. err := mt.Unmarshal(n.ResponseToken)
  191. if err != nil {
  192. return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: err.Error()}
  193. }
  194. n.mechToken = mt
  195. } else {
  196. var ok bool
  197. mt, ok = n.mechToken.(*KRB5Token)
  198. if !ok {
  199. return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "MechToken is not a KRB5 token as expected"}
  200. }
  201. }
  202. if mt == nil {
  203. return false, gssapi.Status{Code: gssapi.StatusContinueNeeded}
  204. }
  205. // Verify the mechtoken
  206. return mt.Verify()
  207. }
  208. return false, gssapi.Status{Code: gssapi.StatusBadMech, Message: "no supported mechanism specified in negotiation"}
  209. }
  210. // State returns the negotiation state of the negotiation response.
  211. func (n *NegTokenResp) State() NegState {
  212. return NegState(n.NegState)
  213. }
  214. // Context returns the SPNEGO context which will contain any verify user identity information.
  215. func (n *NegTokenResp) Context() context.Context {
  216. if n.mechToken != nil {
  217. mt, ok := n.mechToken.(*KRB5Token)
  218. if !ok {
  219. return nil
  220. }
  221. return mt.Context()
  222. }
  223. return nil
  224. }
  225. // UnmarshalNegToken umarshals and returns either a NegTokenInit or a NegTokenResp.
  226. //
  227. // The boolean indicates if the response is a NegTokenInit.
  228. // If error is nil and the boolean is false the response is a NegTokenResp.
  229. func UnmarshalNegToken(b []byte) (bool, interface{}, error) {
  230. var a asn1.RawValue
  231. _, err := asn1.Unmarshal(b, &a)
  232. if err != nil {
  233. return false, nil, fmt.Errorf("error unmarshalling NegotiationToken: %v", err)
  234. }
  235. switch a.Tag {
  236. case 0:
  237. var n marshalNegTokenInit
  238. _, err = asn1.Unmarshal(a.Bytes, &n)
  239. if err != nil {
  240. return false, nil, fmt.Errorf("error unmarshalling NegotiationToken type %d (Init): %v", a.Tag, err)
  241. }
  242. nt := NegTokenInit{
  243. MechTypes: n.MechTypes,
  244. ReqFlags: n.ReqFlags,
  245. MechTokenBytes: n.MechTokenBytes,
  246. MechListMIC: n.MechListMIC,
  247. }
  248. return true, nt, nil
  249. case 1:
  250. var n marshalNegTokenResp
  251. _, err = asn1.Unmarshal(a.Bytes, &n)
  252. if err != nil {
  253. return false, nil, fmt.Errorf("error unmarshalling NegotiationToken type %d (Resp/Targ): %v", a.Tag, err)
  254. }
  255. nt := NegTokenResp{
  256. NegState: n.NegState,
  257. SupportedMech: n.SupportedMech,
  258. ResponseToken: n.ResponseToken,
  259. MechListMIC: n.MechListMIC,
  260. }
  261. return false, nt, nil
  262. default:
  263. return false, nil, errors.New("unknown choice type for NegotiationToken")
  264. }
  265. }
  266. // NewNegTokenInitKRB5 creates new Init negotiation token for Kerberos 5
  267. func NewNegTokenInitKRB5(cl *client.Client, tkt messages.Ticket, sessionKey types.EncryptionKey) (NegTokenInit, error) {
  268. mt, err := NewKRB5TokenAPREQ(cl, tkt, sessionKey, []int{gssapi.ContextFlagInteg, gssapi.ContextFlagConf}, []int{})
  269. if err != nil {
  270. return NegTokenInit{}, fmt.Errorf("error getting KRB5 token; %v", err)
  271. }
  272. mtb, err := mt.Marshal()
  273. if err != nil {
  274. return NegTokenInit{}, fmt.Errorf("error marshalling KRB5 token; %v", err)
  275. }
  276. return NegTokenInit{
  277. MechTypes: []asn1.ObjectIdentifier{gssapi.OID(gssapi.OIDKRB5)},
  278. MechTokenBytes: mtb,
  279. }, nil
  280. }