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