diff --git a/spnego/http.go b/spnego/http.go index 0cb28449..c174e73c 100644 --- a/spnego/http.go +++ b/spnego/http.go @@ -157,9 +157,10 @@ func respUnauthorizedNegotiate(resp *http.Response) bool { return false } -// SetSPNEGOHeader gets the service ticket and sets it as the SPNEGO authorization header on HTTP request object. -// To auto generate the SPN from the request object pass a null string "". -func SetSPNEGOHeader(cl *client.Client, r *http.Request, spn string) error { +func setSPNEGOHeaderCommon(cl *client.Client, r *http.Request, spn string, headerName string) error { + // Common SPNEGO logic... + // Generate the SPNEGO token, etc. + if spn == "" { h := strings.TrimSuffix(strings.SplitN(r.URL.Host, ":", 2)[0], ".") name, err := net.LookupCNAME(h) @@ -185,10 +186,21 @@ func SetSPNEGOHeader(cl *client.Client, r *http.Request, spn string) error { return krberror.Errorf(err, krberror.EncodingError, "could not marshal SPNEGO") } hs := "Negotiate " + base64.StdEncoding.EncodeToString(nb) - r.Header.Set(HTTPHeaderAuthRequest, hs) + r.Header.Set(headerName, hs) + return nil } +// SetSPNEGOHeader gets the service ticket and sets it as the SPNEGO authorization header on HTTP request object. +// To auto generate the SPN from the request object pass a null string "". +func SetSPNEGOHeader(cl *client.Client, r *http.Request, spn string) error { + return setSPNEGOHeaderCommon(cl, r, spn, HTTPHeaderAuthRequest) +} + +func SetSPNEGOProxyAuthorizationHeader(cl *client.Client, r *http.Request, spn string) error { + return setSPNEGOHeaderCommon(cl, r, spn, HTTPHeaderProxyAuthRequest) +} + // Service side functionality // type ctxKey string @@ -206,6 +218,8 @@ const ( CTXKeyCredentials ctxKey = "github.com/jcmturner/gokrb5/CTXKeyCredentials" // HTTPHeaderAuthRequest is the header that will hold authn/z information. HTTPHeaderAuthRequest = "Authorization" + // HTTPHeaderProxyAuthRequest is the header that will hold proxy authn/z information. + HTTPHeaderProxyAuthRequest = "Proxy-Authorization" // HTTPHeaderAuthResponse is the header that will hold SPNEGO data from the server. HTTPHeaderAuthResponse = "WWW-Authenticate" // HTTPHeaderAuthResponseValueKey is the key in the auth header for SPNEGO. @@ -290,4 +304,4 @@ func spnegoResponseReject(s *SPNEGO, w http.ResponseWriter, format string, v ... func spnegoResponseAcceptCompleted(s *SPNEGO, w http.ResponseWriter, format string, v ...interface{}) { s.Log(format, v...) w.Header().Set(HTTPHeaderAuthResponse, spnegoNegTokenRespKRBAcceptCompleted) -} +} \ No newline at end of file diff --git a/v8/spnego/http.go b/v8/spnego/http.go index 6bc2c0ed..527c7b57 100644 --- a/v8/spnego/http.go +++ b/v8/spnego/http.go @@ -191,10 +191,40 @@ func setRequestSPN(r *http.Request) (types.PrincipalName, error) { r.Host = h return types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, "HTTP/"+h), nil } +func GetSPNEGOHeader(cl *client.Client, r *http.Request, spn string) (string, error) { + // Common SPNEGO logic... + // Generate the SPNEGO token, etc. + + if spn == "" { + pn, err := setRequestSPN(r) + if err != nil { + return "", err + } + spn = pn.PrincipalNameString() + } + cl.Log("using SPN %s", spn) + s := SPNEGOClient(cl, spn) + err := s.AcquireCred() + if err != nil { + return "", fmt.Errorf("could not acquire client credential: %v", err) + } + st, err := s.InitSecContext() + if err != nil { + return "", fmt.Errorf("could not initialize context: %v", err) + } + nb, err := st.Marshal() + if err != nil { + return "", krberror.Errorf(err, krberror.EncodingError, "could not marshal SPNEGO") + } + hs := base64.StdEncoding.EncodeToString(nb) + + return hs, nil +} + +func setSPNEGOHeaderCommon(cl *client.Client, r *http.Request, spn string, headerName string) error { + // Common SPNEGO logic... + // Generate the SPNEGO token, etc. -// SetSPNEGOHeader gets the service ticket and sets it as the SPNEGO authorization header on HTTP request object. -// To auto generate the SPN from the request object pass a null string "". -func SetSPNEGOHeader(cl *client.Client, r *http.Request, spn string) error { if spn == "" { pn, err := setRequestSPN(r) if err != nil { @@ -217,10 +247,21 @@ func SetSPNEGOHeader(cl *client.Client, r *http.Request, spn string) error { return krberror.Errorf(err, krberror.EncodingError, "could not marshal SPNEGO") } hs := "Negotiate " + base64.StdEncoding.EncodeToString(nb) - r.Header.Set(HTTPHeaderAuthRequest, hs) + r.Header.Set(headerName, hs) + return nil } +// SetSPNEGOHeader gets the service ticket and sets it as the SPNEGO authorization header on HTTP request object. +// To auto generate the SPN from the request object pass a null string "". +func SetSPNEGOHeader(cl *client.Client, r *http.Request, spn string) error { + return setSPNEGOHeaderCommon(cl, r, spn, HTTPHeaderAuthRequest) +} + +func SetSPNEGOProxyAuthorizationHeader(cl *client.Client, r *http.Request, spn string) error { + return setSPNEGOHeaderCommon(cl, r, spn, HTTPHeaderProxyAuthRequest) +} + // Service side functionality // const ( @@ -236,6 +277,8 @@ const ( ctxCredentials = "github.com/jcmturner/gokrb5/v8/ctxCredentials" // HTTPHeaderAuthRequest is the header that will hold authn/z information. HTTPHeaderAuthRequest = "Authorization" + // HTTPHeaderProxyAuthRequest is the header that will hold proxy authn/z information. + HTTPHeaderProxyAuthRequest = "Proxy-Authorization" // HTTPHeaderAuthResponse is the header that will hold SPNEGO data from the server. HTTPHeaderAuthResponse = "WWW-Authenticate" // HTTPHeaderAuthResponseValueKey is the key in the auth header for SPNEGO.