| ... | ... |
@@ -11,6 +11,9 @@ import ( |
| 11 | 11 |
|
| 12 | 12 |
// Negotiater defines the minimal interface needed to interact with GSSAPI to perform a negotiate challenge/response |
| 13 | 13 |
type Negotiater interface {
|
| 14 |
+ // Load gives the negotiator a chance to load any resources needed to handle a challenge/response sequence. |
|
| 15 |
+ // It may be invoked multiple times. If an error is returned, InitSecContext and IsComplete are not called, but Release() is. |
|
| 16 |
+ Load() error |
|
| 14 | 17 |
// InitSecContext returns the response token for a Negotiate challenge token from a given URL, |
| 15 | 18 |
// or an error if no response token could be obtained or the incoming token is invalid. |
| 16 | 19 |
InitSecContext(requestURL string, challengeToken []byte) (tokenToSend []byte, err error) |
| ... | ... |
@@ -34,8 +37,14 @@ func NewNegotiateChallengeHandler(negotiater Negotiater) ChallengeHandler {
|
| 34 | 34 |
|
| 35 | 35 |
func (c *NegotiateChallengeHandler) CanHandle(headers http.Header) bool {
|
| 36 | 36 |
// Make sure this is a negotiate request |
| 37 |
- isNegotiate, _, err := getNegotiateToken(headers) |
|
| 38 |
- return err == nil && isNegotiate |
|
| 37 |
+ if isNegotiate, _, err := getNegotiateToken(headers); err != nil || !isNegotiate {
|
|
| 38 |
+ return false |
|
| 39 |
+ } |
|
| 40 |
+ // Make sure our negotiator can initialize |
|
| 41 |
+ if err := c.negotiater.Load(); err != nil {
|
|
| 42 |
+ return false |
|
| 43 |
+ } |
|
| 44 |
+ return true |
|
| 39 | 45 |
} |
| 40 | 46 |
|
| 41 | 47 |
func (c *NegotiateChallengeHandler) HandleChallenge(requestURL string, headers http.Header) (http.Header, bool, error) {
|
| ... | ... |
@@ -49,6 +49,11 @@ func NewGSSAPINegotiator(principalName string) Negotiater {
|
| 49 | 49 |
return &gssapiNegotiator{principalName: principalName}
|
| 50 | 50 |
} |
| 51 | 51 |
|
| 52 |
+func (g *gssapiNegotiator) Load() error {
|
|
| 53 |
+ _, err := g.loadLib() |
|
| 54 |
+ return err |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 52 | 57 |
func (g *gssapiNegotiator) InitSecContext(requestURL string, challengeToken []byte) (tokenToSend []byte, err error) {
|
| 53 | 58 |
lib, err := g.loadLib() |
| 54 | 59 |
if err != nil {
|
| ... | ... |
@@ -14,6 +14,9 @@ func NewGSSAPINegotiator(principalName string) Negotiater {
|
| 14 | 14 |
return &gssapiUnsupported{}
|
| 15 | 15 |
} |
| 16 | 16 |
|
| 17 |
+func (g *gssapiUnsupported) Load() error {
|
|
| 18 |
+ return errors.New("GSSAPI support is not enabled")
|
|
| 19 |
+} |
|
| 17 | 20 |
func (g *gssapiUnsupported) InitSecContext(requestURL string, challengeToken []byte) (tokenToSend []byte, err error) {
|
| 18 | 21 |
return nil, errors.New("GSSAPI support is not enabled")
|
| 19 | 22 |
} |
| ... | ... |
@@ -11,10 +11,31 @@ import ( |
| 11 | 11 |
"k8s.io/kubernetes/pkg/client/restclient" |
| 12 | 12 |
) |
| 13 | 13 |
|
| 14 |
+type unloadableNegotiator struct {
|
|
| 15 |
+ releaseCalls int |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func (n *unloadableNegotiator) Load() error {
|
|
| 19 |
+ return errors.New("Load failed")
|
|
| 20 |
+} |
|
| 21 |
+func (n *unloadableNegotiator) InitSecContext(requestURL string, challengeToken []byte) (tokenToSend []byte, err error) {
|
|
| 22 |
+ return nil, errors.New("InitSecContext failed")
|
|
| 23 |
+} |
|
| 24 |
+func (n *unloadableNegotiator) IsComplete() bool {
|
|
| 25 |
+ return false |
|
| 26 |
+} |
|
| 27 |
+func (n *unloadableNegotiator) Release() error {
|
|
| 28 |
+ n.releaseCalls++ |
|
| 29 |
+ return errors.New("Release failed")
|
|
| 30 |
+} |
|
| 31 |
+ |
|
| 14 | 32 |
type failingNegotiator struct {
|
| 15 | 33 |
releaseCalls int |
| 16 | 34 |
} |
| 17 | 35 |
|
| 36 |
+func (n *failingNegotiator) Load() error {
|
|
| 37 |
+ return nil |
|
| 38 |
+} |
|
| 18 | 39 |
func (n *failingNegotiator) InitSecContext(requestURL string, challengeToken []byte) (tokenToSend []byte, err error) {
|
| 19 | 40 |
return nil, errors.New("InitSecContext failed")
|
| 20 | 41 |
} |
| ... | ... |
@@ -29,9 +50,14 @@ func (n *failingNegotiator) Release() error {
|
| 29 | 29 |
type successfulNegotiator struct {
|
| 30 | 30 |
rounds int |
| 31 | 31 |
initSecContextCalls int |
| 32 |
+ loadCalls int |
|
| 32 | 33 |
releaseCalls int |
| 33 | 34 |
} |
| 34 | 35 |
|
| 36 |
+func (n *successfulNegotiator) Load() error {
|
|
| 37 |
+ n.loadCalls++ |
|
| 38 |
+ return nil |
|
| 39 |
+} |
|
| 35 | 40 |
func (n *successfulNegotiator) InitSecContext(requestURL string, challengeToken []byte) (tokenToSend []byte, err error) {
|
| 36 | 41 |
n.initSecContextCalls++ |
| 37 | 42 |
|
| ... | ... |
@@ -94,6 +120,10 @@ func TestRequestToken(t *testing.T) {
|
| 94 | 94 |
if negotiator.releaseCalls != 1 {
|
| 95 | 95 |
t.Errorf("%s: expected one call to Release(), saw %d", test, negotiator.releaseCalls)
|
| 96 | 96 |
} |
| 97 |
+ case *unloadableNegotiator: |
|
| 98 |
+ if negotiator.releaseCalls != 1 {
|
|
| 99 |
+ t.Errorf("%s: expected one call to Release(), saw %d", test, negotiator.releaseCalls)
|
|
| 100 |
+ } |
|
| 97 | 101 |
default: |
| 98 | 102 |
t.Errorf("%s: unrecognized negotiator: %#v", test, handler)
|
| 99 | 103 |
} |
| ... | ... |
@@ -255,6 +285,29 @@ func TestRequestToken(t *testing.T) {
|
| 255 | 255 |
ExpectedError: "client requires final negotiate token, none provided", |
| 256 | 256 |
}, |
| 257 | 257 |
|
| 258 |
+ // Unloadable negotiate handler |
|
| 259 |
+ "unloadable negotiate handler, no challenge, success": {
|
|
| 260 |
+ Handler: &NegotiateChallengeHandler{negotiater: &unloadableNegotiator{}},
|
|
| 261 |
+ Requests: []requestResponse{
|
|
| 262 |
+ {initialRequest, success},
|
|
| 263 |
+ }, |
|
| 264 |
+ ExpectedToken: successfulToken, |
|
| 265 |
+ }, |
|
| 266 |
+ "unloadable negotiate handler, negotiate challenge, failure": {
|
|
| 267 |
+ Handler: &NegotiateChallengeHandler{negotiater: &unloadableNegotiator{}},
|
|
| 268 |
+ Requests: []requestResponse{
|
|
| 269 |
+ {initialRequest, negotiateChallenge1},
|
|
| 270 |
+ }, |
|
| 271 |
+ ExpectedError: "unhandled challenge", |
|
| 272 |
+ }, |
|
| 273 |
+ "unloadable negotiate handler, basic challenge, failure": {
|
|
| 274 |
+ Handler: &NegotiateChallengeHandler{negotiater: &unloadableNegotiator{}},
|
|
| 275 |
+ Requests: []requestResponse{
|
|
| 276 |
+ {initialRequest, basicChallenge1},
|
|
| 277 |
+ }, |
|
| 278 |
+ ExpectedError: "unhandled challenge", |
|
| 279 |
+ }, |
|
| 280 |
+ |
|
| 258 | 281 |
// Failing negotiate handler |
| 259 | 282 |
"failing negotiate handler, no challenge, success": {
|
| 260 | 283 |
Handler: &NegotiateChallengeHandler{negotiater: &failingNegotiator{}},
|