From dcfd29419e98e880854ed7fda49436a54b1ae362 Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Tue, 12 Jan 2021 22:44:28 +0000 Subject: [PATCH] Remove NTLM support Our NTLM support has been known to be broken in various situations for a while, specifically on Windows. The core team is unable to troubleshoot these problems, and nobody has stepped up to maintain the NTLM support. In addition, NTLM uses cryptography and security techniques that are known to be insecure, such as the algorithms DES, MD4, and MD5, as well as simple, unsalted hashes of passwords. Since we now support Kerberos, most users should be able to replace their use of NTLM with Kerberos instead. Users have reported this working on Windows and it is known to work well on at least Debian as well. Drop support for NTLM and remove it from the codebase. --- creds/access.go | 1 - lfsapi/auth.go | 13 +-- lfsapi/auth_test.go | 29 ----- lfsapi/endpoint_finder_test.go | 8 +- lfsapi/kerberos.go | 30 +---- lfsapi/lfsapi.go | 5 - lfsapi/ntlm.go | 154 -------------------------- lfsapi/ntlm_auth_nix.go | 73 ------------ lfsapi/ntlm_auth_test_nix.go | 36 ------ lfsapi/ntlm_auth_windows.go | 47 -------- lfsapi/ntlm_test.go | 196 --------------------------------- t/cmd/lfstest-gitserver.go | 60 +--------- t/t-batch-transfer.sh | 22 ---- tq/manifest_test.go | 12 -- tq/transfer_queue.go | 5 - 15 files changed, 14 insertions(+), 677 deletions(-) delete mode 100644 lfsapi/ntlm.go delete mode 100644 lfsapi/ntlm_auth_nix.go delete mode 100644 lfsapi/ntlm_auth_test_nix.go delete mode 100644 lfsapi/ntlm_auth_windows.go delete mode 100644 lfsapi/ntlm_test.go diff --git a/creds/access.go b/creds/access.go index 23190d10..66c18fa9 100644 --- a/creds/access.go +++ b/creds/access.go @@ -7,7 +7,6 @@ const ( BasicAccess AccessMode = "basic" PrivateAccess AccessMode = "private" NegotiateAccess AccessMode = "negotiate" - NTLMAccess AccessMode = "ntlm" EmptyAccess AccessMode = "" ) diff --git a/lfsapi/auth.go b/lfsapi/auth.go index 3fbc8cfb..9efe9c24 100644 --- a/lfsapi/auth.go +++ b/lfsapi/auth.go @@ -20,7 +20,7 @@ var ( // DoWithAuth sends an HTTP request to get an HTTP response. It attempts to add // authentication from netrc or git's credential helpers if necessary, -// supporting basic and ntlm authentication. +// supporting basic authentication. func (c *Client) DoWithAuth(remote string, access creds.Access, req *http.Request) (*http.Response, error) { res, err := c.doWithAuth(remote, access, req, nil) @@ -87,9 +87,7 @@ func (c *Client) doWithAuth(remote string, access creds.Access, req *http.Reques } func (c *Client) doWithCreds(req *http.Request, credWrapper creds.CredentialHelperWrapper, access creds.Access, via []*http.Request) (*http.Response, error) { - if access.Mode() == creds.NTLMAccess { - return c.doWithNTLM(req, credWrapper) - } else if access.Mode() == creds.NegotiateAccess { + if access.Mode() == creds.NegotiateAccess { return c.doWithNegotiate(req, credWrapper) } @@ -142,7 +140,7 @@ func (c *Client) getCreds(remote string, access creds.Access, req *http.Request) operation := getReqOperation(req) apiEndpoint := ef.Endpoint(operation, remote) - if access.Mode() != creds.NTLMAccess && access.Mode() != creds.NegotiateAccess { + if access.Mode() != creds.NegotiateAccess { if requestHasAuth(req) || access.Mode() == creds.NoneAccess { return creds.CredentialHelperWrapper{CredentialHelper: creds.NullCreds, Input: nil, Url: nil, Creds: nil}, nil } @@ -165,7 +163,7 @@ func (c *Client) getCreds(remote string, access creds.Access, req *http.Request) return credWrapper, err } - // NTLM and Negotiate only + // Negotiate only credsURL, err := url.Parse(apiEndpoint.Url) if err != nil { @@ -292,7 +290,6 @@ func setRequestAuthFromURL(req *http.Request, u *url.URL) bool { } func setRequestAuth(req *http.Request, user, pass string) { - // better not be NTLM! if len(user) == 0 && len(pass) == 0 { return } @@ -323,7 +320,7 @@ func getAuthAccess(res *http.Response) creds.AccessMode { } switch creds.AccessMode(pieces[0]) { - case creds.NegotiateAccess, creds.NTLMAccess: + case creds.NegotiateAccess: return creds.AccessMode(pieces[0]) } } diff --git a/lfsapi/auth_test.go b/lfsapi/auth_test.go index 55ec4b29..2e72a3e0 100644 --- a/lfsapi/auth_test.go +++ b/lfsapi/auth_test.go @@ -28,10 +28,6 @@ func TestAuthenticateHeaderAccess(t *testing.T) { "basic 123": creds.BasicAccess, "basic": creds.BasicAccess, "unknown": creds.BasicAccess, - "NTLM": creds.NTLMAccess, - "ntlm": creds.NTLMAccess, - "NTLM 1 2 3": creds.NTLMAccess, - "ntlm 1 2 3": creds.NTLMAccess, "NEGOTIATE": creds.NegotiateAccess, "negotiate": creds.NegotiateAccess, "NEGOTIATE 1 2 3": creds.NegotiateAccess, @@ -442,26 +438,6 @@ func TestGetCreds(t *testing.T) { }, }, }, - "ntlm": getCredsTest{ - Remote: "origin", - Method: "GET", - Href: "https://git-server.com/repo/lfs/locks", - Endpoint: "https://git-server.com/repo/lfs", - Config: map[string]string{ - "lfs.url": "https://git-server.com/repo/lfs", - "lfs.https://git-server.com/repo/lfs.access": "ntlm", - }, - Expected: getCredsExpected{ - Access: creds.NTLMAccess, - CredsURL: "https://git-server.com/repo/lfs", - Creds: map[string]string{ - "protocol": "https", - "host": "git-server.com", - "username": "git-server.com", - "password": "monkey", - }, - }, - }, "custom auth": getCredsTest{ Remote: "origin", Method: "GET", @@ -663,11 +639,6 @@ func TestGetCreds(t *testing.T) { assert.Equal(t, test.Expected.Authorization, req.Header.Get("Authorization"), "authorization") if test.Expected.Creds != nil { - if desc == "ntlm" { - // For NTLM we initially try with no provided credentials to test SSPI and then prompt. We want to test both sets. - assert.Nil(t, credWrapper.Creds, "creds") - credWrapper.FillCreds() - } assert.EqualValues(t, test.Expected.Creds, credWrapper.Creds) } else { assert.Nil(t, credWrapper.Creds, "creds") diff --git a/lfsapi/endpoint_finder_test.go b/lfsapi/endpoint_finder_test.go index 91934ac2..ce6476d5 100644 --- a/lfsapi/endpoint_finder_test.go +++ b/lfsapi/endpoint_finder_test.go @@ -476,10 +476,10 @@ func TestSetAccess(t *testing.T) { assert.Equal(t, creds.NoneAccess, access.Mode()) assert.Equal(t, url, access.URL()) - finder.SetAccess(access.Upgrade(creds.NTLMAccess)) + finder.SetAccess(access.Upgrade(creds.NegotiateAccess)) newAccess := finder.AccessFor(url) - assert.Equal(t, creds.NTLMAccess, newAccess.Mode()) + assert.Equal(t, creds.NegotiateAccess, newAccess.Mode()) assert.Equal(t, url, newAccess.URL()) } @@ -493,10 +493,10 @@ func TestChangeAccess(t *testing.T) { assert.Equal(t, creds.BasicAccess, access.Mode()) assert.Equal(t, url, access.URL()) - finder.SetAccess(access.Upgrade(creds.NTLMAccess)) + finder.SetAccess(access.Upgrade(creds.NegotiateAccess)) newAccess := finder.AccessFor(url) - assert.Equal(t, creds.NTLMAccess, newAccess.Mode()) + assert.Equal(t, creds.NegotiateAccess, newAccess.Mode()) assert.Equal(t, url, newAccess.URL()) } diff --git a/lfsapi/kerberos.go b/lfsapi/kerberos.go index 47abe254..e9862e47 100644 --- a/lfsapi/kerberos.go +++ b/lfsapi/kerberos.go @@ -4,36 +4,12 @@ import ( "net/http" "github.com/git-lfs/git-lfs/creds" - "github.com/git-lfs/git-lfs/errors" - "github.com/rubyist/tracerx" ) func (c *Client) doWithNegotiate(req *http.Request, credWrapper creds.CredentialHelperWrapper) (*http.Response, error) { // There are two possibilities here if we're using Negotiate // authentication. One is that we're using Kerberos, which we try - // first. The other is that we're using NTLM, which we try second with - // single sign-on credentials. Finally, if that also fails, we fall - // back to prompting for credentials with NTLM and trying that. - res, err := c.doWithAccess(req, "", nil, creds.NegotiateAccess) - if err == nil || errors.IsAuthError(err) { - if res.StatusCode != 401 { - return res, nil - } - } - - // If we received an error, fall back to NTLM. That will be the case if - // we don't have any cached credentials, which on Unix will look like a - // failed attempt to read a file in /tmp, not a standard auth error. - - tracerx.Printf("attempting NTLM as fallback") - res, err = c.doWithAccess(req, "", nil, creds.NTLMAccess) - if err != nil && !errors.IsAuthError(err) { - return res, err - } - - if res.StatusCode != 401 { - return res, nil - } - - return c.ntlmReAuth(req, credWrapper, true) + // first. The other is that we're using NTLM, which we no longer + // support. Fail in that case. + return c.doWithAccess(req, "", nil, creds.NegotiateAccess) } diff --git a/lfsapi/lfsapi.go b/lfsapi/lfsapi.go index 85dd3afc..a3e66b21 100644 --- a/lfsapi/lfsapi.go +++ b/lfsapi/lfsapi.go @@ -2,21 +2,16 @@ package lfsapi import ( "fmt" - "sync" "github.com/git-lfs/git-lfs/creds" "github.com/git-lfs/git-lfs/errors" "github.com/git-lfs/git-lfs/lfshttp" - "github.com/git-lfs/go-ntlm/ntlm" ) type Client struct { Endpoints EndpointFinder Credentials creds.CredentialHelper - ntlmSessions map[string]ntlm.ClientSession - ntlmMu sync.Mutex - credContext *creds.CredentialHelperContext client *lfshttp.Client diff --git a/lfsapi/ntlm.go b/lfsapi/ntlm.go deleted file mode 100644 index a78461cd..00000000 --- a/lfsapi/ntlm.go +++ /dev/null @@ -1,154 +0,0 @@ -package lfsapi - -import ( - "encoding/base64" - "fmt" - "io" - "io/ioutil" - "net/http" - "strings" - - "github.com/git-lfs/git-lfs/creds" - "github.com/git-lfs/git-lfs/errors" -) - -type ntmlCredentials struct { - domain string - username string - password string -} - -func (c *Client) doWithNTLM(req *http.Request, credWrapper creds.CredentialHelperWrapper) (*http.Response, error) { - res, err := c.do(req, "", nil) - if err != nil && !errors.IsAuthError(err) { - return res, err - } - - if res.StatusCode != 401 { - return res, nil - } - - return c.ntlmReAuth(req, credWrapper, true) -} - -// If the status is 401 then we need to re-authenticate -func (c *Client) ntlmReAuth(req *http.Request, credWrapper creds.CredentialHelperWrapper, retry bool) (*http.Response, error) { - // Try SSPI first. - if c.ntlmSupportsSSPI() == true { - res, err := c.ntlmAuthenticateRequest(req, nil) - if err != nil && !errors.IsAuthError(err) { - return res, err - } - - // If SSPI succeeded, then we can move on. - if res.StatusCode < 300 && res.StatusCode > 199 { - return res, nil - } - } - - // If SSPI failed, then we need to try the normal. - credWrapper.FillCreds() - ntmlCreds, err := ntlmGetCredentials(credWrapper.Creds) - if err != nil { - return nil, err - } - - res, err := c.ntlmAuthenticateRequest(req, ntmlCreds) - if err != nil && !errors.IsAuthError(err) { - return res, err - } - - switch res.StatusCode { - case 401: - credWrapper.CredentialHelper.Reject(credWrapper.Creds) - if retry { - return c.ntlmReAuth(req, credWrapper, false) - } - case 403: - credWrapper.CredentialHelper.Reject(credWrapper.Creds) - default: - if res.StatusCode < 300 && res.StatusCode > 199 { - credWrapper.CredentialHelper.Approve(credWrapper.Creds) - } - } - - return res, nil -} - -func (c *Client) ntlmSendType1Message(req *http.Request, message []byte) (*http.Response, []byte, error) { - res, err := c.ntlmSendMessage(req, message) - if err != nil && !errors.IsAuthError(err) { - return res, nil, err - } - - io.Copy(ioutil.Discard, res.Body) - res.Body.Close() - - by, err := parseChallengeResponse(res) - return res, by, err -} - -func (c *Client) ntlmSendType3Message(req *http.Request, authenticate []byte) (*http.Response, error) { - return c.ntlmSendMessage(req, authenticate) -} - -func (c *Client) ntlmSendMessage(req *http.Request, message []byte) (*http.Response, error) { - body, err := rewoundRequestBody(req) - if err != nil { - return nil, err - } - req.Body = body - - msg := base64.StdEncoding.EncodeToString(message) - req.Header.Set("Authorization", "NTLM "+msg) - return c.do(req, "", nil) -} - -func parseChallengeResponse(res *http.Response) ([]byte, error) { - header := res.Header.Get("Www-Authenticate") - if len(header) < 6 { - return nil, fmt.Errorf("invalid NTLM challenge response: %q", header) - } - - //parse out the "NTLM " at the beginning of the response - challenge := header[5:] - val, err := base64.StdEncoding.DecodeString(challenge) - - if err != nil { - return nil, err - } - return []byte(val), nil -} - -func rewoundRequestBody(req *http.Request) (io.ReadCloser, error) { - if req.Body == nil { - return nil, nil - } - - body, ok := req.Body.(ReadSeekCloser) - if !ok { - return nil, fmt.Errorf("Request body must implement io.ReadCloser and io.Seeker. Got: %T", body) - } - - _, err := body.Seek(0, io.SeekStart) - return body, err -} - -func ntlmGetCredentials(creds creds.Creds) (*ntmlCredentials, error) { - username := creds["username"] - password := creds["password"] - - if username == "" && password == "" { - return nil, nil - } - - splits := strings.Split(username, "\\") - if len(splits) != 2 { - return nil, fmt.Errorf("Your user name must be of the form DOMAIN\\user. It is currently '%s'", username) - } - - domain := strings.ToUpper(splits[0]) - username = splits[1] - - return &ntmlCredentials{domain: domain, username: username, password: password}, nil -} diff --git a/lfsapi/ntlm_auth_nix.go b/lfsapi/ntlm_auth_nix.go deleted file mode 100644 index 8c21e404..00000000 --- a/lfsapi/ntlm_auth_nix.go +++ /dev/null @@ -1,73 +0,0 @@ -// +build !windows - -package lfsapi - -import ( - "encoding/base64" - "fmt" - "net/http" - - "github.com/git-lfs/go-ntlm/ntlm" -) - -func (c *Client) ntlmSupportsSSPI() bool { - return false -} - -func (c *Client) ntlmAuthenticateRequest(req *http.Request, creds *ntmlCredentials) (*http.Response, error) { - negotiate, err := base64.StdEncoding.DecodeString(ntlmNegotiateMessage) - if err != nil { - return nil, err - } - - chRes, challengeMsg, err := c.ntlmSendType1Message(req, negotiate) - if err != nil { - return chRes, err - } - - challenge, err := ntlm.ParseChallengeMessage(challengeMsg) - if err != nil { - return nil, err - } - - session, err := c.ntlmClientSession(creds) - if err != nil { - return nil, err - } - - session.ProcessChallengeMessage(challenge) - authenticate, err := session.GenerateAuthenticateMessage() - if err != nil { - return nil, err - } - - return c.ntlmSendType3Message(req, authenticate.Bytes()) -} - -func (c *Client) ntlmClientSession(creds *ntmlCredentials) (ntlm.ClientSession, error) { - c.ntlmMu.Lock() - defer c.ntlmMu.Unlock() - - if creds == nil { - return nil, fmt.Errorf("Your user name must be of the form DOMAIN\\user. Single-sign-on is not supported.") - } - - if c.ntlmSessions == nil { - c.ntlmSessions = make(map[string]ntlm.ClientSession) - } - - if ses, ok := c.ntlmSessions[creds.domain]; ok { - return ses, nil - } - - session, err := ntlm.CreateClientSession(ntlm.Version2, ntlm.ConnectionOrientedMode) - if err != nil { - return nil, err - } - - session.SetUserInfo(creds.username, creds.password, creds.domain) - c.ntlmSessions[creds.domain] = session - return session, nil -} - -const ntlmNegotiateMessage = "TlRMTVNTUAABAAAAB7IIogwADAAzAAAACwALACgAAAAKAAAoAAAAD1dJTExISS1NQUlOTk9SVEhBTUVSSUNB" diff --git a/lfsapi/ntlm_auth_test_nix.go b/lfsapi/ntlm_auth_test_nix.go deleted file mode 100644 index b3980a8b..00000000 --- a/lfsapi/ntlm_auth_test_nix.go +++ /dev/null @@ -1,36 +0,0 @@ -// +build !windows - -package lfsapi - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestNtlmClientSession(t *testing.T) { - cli, err := NewClient(nil) - require.Nil(t, err) - - creds := &ntmlCredentials{domain: "MOOSEDOMAIN", username: "canadian", password: "MooseAntlersYeah"} - session1, err := cli.ntlmClientSession(creds) - assert.Nil(t, err) - assert.NotNil(t, session1) - - // The second call should ignore creds and give the session we just created. - badCreds := &ntmlCredentials{domain: "MOOSEDOMAIN", username: "badusername", password: "MooseAntlersYeah"} - session2, err := cli.ntlmClientSession(badCreds) - assert.Nil(t, err) - assert.NotNil(t, session2) - assert.EqualValues(t, session1, session2) -} - -func TestNtlmClientSessionBadCreds(t *testing.T) { - cli, err := NewClient(nil) - require.Nil(t, err) - - // Single-Sign-On is not supported on *nix - _, err = cli.ntlmClientSession(nil) - assert.NotNil(t, err) -} diff --git a/lfsapi/ntlm_auth_windows.go b/lfsapi/ntlm_auth_windows.go deleted file mode 100644 index d339c2d8..00000000 --- a/lfsapi/ntlm_auth_windows.go +++ /dev/null @@ -1,47 +0,0 @@ -// +build windows - -package lfsapi - -import ( - "net/http" - - "github.com/alexbrainman/sspi" - "github.com/alexbrainman/sspi/ntlm" -) - -func (c *Client) ntlmSupportsSSPI() bool { - return true -} - -func (c *Client) ntlmAuthenticateRequest(req *http.Request, creds *ntmlCredentials) (*http.Response, error) { - var sspiCreds *sspi.Credentials - var err error - if creds == nil { - sspiCreds, err = ntlm.AcquireCurrentUserCredentials() - } else { - sspiCreds, err = ntlm.AcquireUserCredentials(creds.domain, creds.username, creds.password) - } - - if err != nil { - return nil, err - } - defer sspiCreds.Release() - - secctx, negotiate, err := ntlm.NewClientContext(sspiCreds) - if err != nil { - return nil, err - } - defer secctx.Release() - - chRes, challengeMsg, err := c.ntlmSendType1Message(req, negotiate) - if err != nil { - return chRes, err - } - - authenticateMsg, err := secctx.Update(challengeMsg) - if err != nil { - return nil, err - } - - return c.ntlmSendType3Message(req, authenticateMsg) -} diff --git a/lfsapi/ntlm_test.go b/lfsapi/ntlm_test.go deleted file mode 100644 index 3db2eec5..00000000 --- a/lfsapi/ntlm_test.go +++ /dev/null @@ -1,196 +0,0 @@ -package lfsapi - -import ( - "encoding/base64" - "io/ioutil" - "net/http" - "net/http/httptest" - "net/url" - "runtime" - "strings" - "sync/atomic" - "testing" - - "github.com/git-lfs/git-lfs/creds" - "github.com/git-lfs/git-lfs/lfshttp" - "github.com/git-lfs/go-ntlm/ntlm" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestNtlmAuth(t *testing.T) { - session, err := ntlm.CreateServerSession(ntlm.Version2, ntlm.ConnectionOrientedMode) - require.Nil(t, err) - session.SetUserInfo("ntlmuser", "ntlmpass", "NTLMDOMAIN") - - var called uint32 - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - reqIndex := atomic.LoadUint32(&called) - atomic.AddUint32(&called, 1) - - if called == 4 && runtime.GOOS != "windows" { - // We don't want to hit the second class 1 path if we are on *nix. - atomic.AddUint32(&called, 1) - } - - authHeader := req.Header.Get("Authorization") - t.Logf("REQUEST %d: %s %s", reqIndex, req.Method, req.URL) - t.Logf("AUTH: %q", authHeader) - - // assert full body is sent each time - by, err := ioutil.ReadAll(req.Body) - req.Body.Close() - if assert.Nil(t, err) { - assert.Equal(t, "ntlm", string(by)) - } - - switch called { - case 1: - w.Header().Set("Www-Authenticate", "ntlm") - w.WriteHeader(401) - case 2, 4: - assert.True(t, strings.HasPrefix(req.Header.Get("Authorization"), "NTLM ")) - neg := authHeader[5:] // strip "ntlm " prefix - _, err := base64.StdEncoding.DecodeString(neg) - if !assert.Nil(t, err) { - t.Logf("neg base64 error: %+v", err) - w.WriteHeader(500) - return - } - - // ntlm implementation can't currently parse the negotiate message - ch, err := session.GenerateChallengeMessage() - if !assert.Nil(t, err) { - t.Logf("challenge gen error: %+v", err) - w.WriteHeader(500) - return - } - chMsg := base64.StdEncoding.EncodeToString(ch.Bytes()) - w.Header().Set("Www-Authenticate", "ntlm "+chMsg) - w.WriteHeader(401) - default: // should be an auth msg - authHeader := req.Header.Get("Authorization") - assert.True(t, strings.HasPrefix(strings.ToUpper(authHeader), "NTLM ")) - auth := authHeader[5:] // strip "ntlm " prefix - val, err := base64.StdEncoding.DecodeString(auth) - if !assert.Nil(t, err) { - t.Logf("auth base64 error: %+v", err) - w.WriteHeader(500) - return - } - - authMsg, err := ntlm.ParseAuthenticateMessage(val, 2) - if !assert.Nil(t, err) { - t.Logf("auth parse error: %+v", err) - w.WriteHeader(500) - return - } - - if called == 3 && runtime.GOOS == "windows" { - // This is the SSPI call that should return unauth so that standard NTLM can run. - w.WriteHeader(401) - return - } - - err = session.ProcessAuthenticateMessage(authMsg) - if !assert.Nil(t, err) { - t.Logf("auth process error: %+v", err) - w.WriteHeader(500) - return - } - w.WriteHeader(200) - - } - })) - defer srv.Close() - - req, err := http.NewRequest("POST", srv.URL+"/ntlm", NewByteBody([]byte("ntlm"))) - require.Nil(t, err) - - credHelper := newMockCredentialHelper() - cli, err := NewClient(lfshttp.NewContext(nil, nil, map[string]string{ - "lfs.url": srv.URL + "/ntlm", - "lfs." + srv.URL + "/ntlm.access": "ntlm", - })) - cli.Credentials = credHelper - require.Nil(t, err) - - // ntlm support pulls domain and login info from git credentials - srvURL, err := url.Parse(srv.URL) - require.Nil(t, err) - cred := creds.Creds{ - "protocol": srvURL.Scheme, - "host": srvURL.Host, - "username": "ntlmdomain\\ntlmuser", - "password": "ntlmpass", - } - credHelper.Approve(cred) - - res, err := cli.DoWithAuth("remote", cli.Endpoints.AccessFor(srv.URL+"/ntlm"), req) - require.Nil(t, err) - assert.Equal(t, 200, res.StatusCode) - assert.True(t, credHelper.IsApproved(cred)) -} - -func TestNtlmGetCredentials(t *testing.T) { - cred := creds.Creds{"username": "MOOSEDOMAIN\\canadian", "password": "MooseAntlersYeah"} - ntmlCreds, err := ntlmGetCredentials(cred) - assert.Nil(t, err) - assert.NotNil(t, ntmlCreds) - assert.Equal(t, "MOOSEDOMAIN", ntmlCreds.domain) - assert.Equal(t, "canadian", ntmlCreds.username) - assert.Equal(t, "MooseAntlersYeah", ntmlCreds.password) - - cred = creds.Creds{"username": "", "password": ""} - ntmlCreds, err = ntlmGetCredentials(cred) - assert.Nil(t, err) - assert.Nil(t, ntmlCreds) -} - -func TestNtlmGetCredentialsBadCreds(t *testing.T) { - cred := creds.Creds{"username": "badusername", "password": "MooseAntlersYeah"} - _, err := ntlmGetCredentials(cred) - assert.NotNil(t, err) -} - -func TestNtlmHeaderParseValid(t *testing.T) { - res := http.Response{} - res.Header = make(map[string][]string) - res.Header.Add("Www-Authenticate", "NTLM "+base64.StdEncoding.EncodeToString([]byte("I am a moose"))) - bytes, err := parseChallengeResponse(&res) - assert.Nil(t, err) - assert.False(t, strings.HasPrefix(string(bytes), "NTLM")) -} - -func TestNtlmHeaderParseInvalidLength(t *testing.T) { - res := http.Response{} - res.Header = make(map[string][]string) - res.Header.Add("Www-Authenticate", "NTL") - ret, err := parseChallengeResponse(&res) - assert.NotNil(t, err) - assert.Nil(t, ret) -} - -func TestNtlmHeaderParseInvalid(t *testing.T) { - res := http.Response{} - res.Header = make(map[string][]string) - res.Header.Add("Www-Authenticate", base64.StdEncoding.EncodeToString([]byte("NTLM I am a moose"))) - ret, err := parseChallengeResponse(&res) - assert.NotNil(t, err) - assert.Nil(t, ret) -} - -func assertRequestsEqual(t *testing.T, req1 *http.Request, req2 *http.Request, req1Body []byte) { - assert.Equal(t, req1.Method, req2.Method) - - for k, v := range req1.Header { - assert.Equal(t, v, req2.Header[k]) - } - - if req1.Body == nil { - assert.Nil(t, req2.Body) - } else { - bytes2, _ := ioutil.ReadAll(req2.Body) - assert.Equal(t, req1Body, bytes2) - } -} diff --git a/t/cmd/lfstest-gitserver.go b/t/cmd/lfstest-gitserver.go index 24a4a91a..b50a694e 100644 --- a/t/cmd/lfstest-gitserver.go +++ b/t/cmd/lfstest-gitserver.go @@ -34,8 +34,6 @@ import ( "strings" "sync" "time" - - "github.com/git-lfs/go-ntlm/ntlm" ) var ( @@ -88,13 +86,6 @@ func main() { } serverClientCert.StartTLS() - ntlmSession, err := ntlm.CreateServerSession(ntlm.Version2, ntlm.ConnectionOrientedMode) - if err != nil { - fmt.Println("Error creating ntlm session:", err) - os.Exit(1) - } - ntlmSession.SetUserInfo("ntlmuser", "ntlmpass", "NTLMDOMAIN") - stopch := make(chan bool) mux.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) { @@ -120,7 +111,7 @@ func main() { } if strings.Contains(r.URL.Path, "/info/lfs") { - if !skipIfBadAuth(w, r, id, ntlmSession) { + if !skipIfBadAuth(w, r, id) { lfsHandler(w, r, id) } @@ -1571,12 +1562,8 @@ func skipIfNoCookie(w http.ResponseWriter, r *http.Request, id string) bool { return true } -func skipIfBadAuth(w http.ResponseWriter, r *http.Request, id string, ntlmSession ntlm.ServerSession) bool { +func skipIfBadAuth(w http.ResponseWriter, r *http.Request, id string) bool { auth := r.Header.Get("Authorization") - if strings.Contains(r.URL.Path, "ntlm") { - return false - } - if auth == "" { w.WriteHeader(401) return true @@ -1608,49 +1595,6 @@ func skipIfBadAuth(w http.ResponseWriter, r *http.Request, id string, ntlmSessio return true } -func handleNTLM(w http.ResponseWriter, r *http.Request, authHeader string, session ntlm.ServerSession) { - if strings.HasPrefix(strings.ToUpper(authHeader), "BASIC ") { - authHeader = "" - } - - switch authHeader { - case "": - w.Header().Set("Www-Authenticate", "ntlm") - w.WriteHeader(401) - - // ntlmNegotiateMessage from httputil pkg - case "NTLM TlRMTVNTUAABAAAAB7IIogwADAAzAAAACwALACgAAAAKAAAoAAAAD1dJTExISS1NQUlOTk9SVEhBTUVSSUNB": - ch, err := session.GenerateChallengeMessage() - if err != nil { - writeLFSError(w, 500, err.Error()) - return - } - - chMsg := base64.StdEncoding.EncodeToString(ch.Bytes()) - w.Header().Set("Www-Authenticate", "ntlm "+chMsg) - w.WriteHeader(401) - - default: - if !strings.HasPrefix(strings.ToUpper(authHeader), "NTLM ") { - writeLFSError(w, 500, "bad authorization header: "+authHeader) - return - } - - auth := authHeader[5:] // strip "ntlm " prefix - val, err := base64.StdEncoding.DecodeString(auth) - if err != nil { - writeLFSError(w, 500, "base64 decode error: "+err.Error()) - return - } - - _, err = ntlm.ParseAuthenticateMessage(val, 2) - if err != nil { - writeLFSError(w, 500, "auth parse error: "+err.Error()) - return - } - } -} - func init() { oidHandlers = make(map[string]string) for _, content := range contentHandlers { diff --git a/t/t-batch-transfer.sh b/t/t-batch-transfer.sh index a3bd64a8..44a159a9 100755 --- a/t/t-batch-transfer.sh +++ b/t/t-batch-transfer.sh @@ -123,25 +123,3 @@ begin_test "batch transfers with ssh endpoint" git push origin main 2>&1 ) end_test - -begin_test "batch transfers with ntlm server" -( - set -e - - reponame="ntlmtest" - setup_remote_repo "$reponame" - - printf "ntlmdomain\\\ntlmuser:ntlmpass" > "$CREDSDIR/127.0.0.1--$reponame" - - clone_repo "$reponame" "$reponame" - - contents="test" - oid="$(calc_oid "$contents")" - git lfs track "*.dat" - printf "%s" "$contents" > test.dat - git add .gitattributes test.dat - git commit -m "initial commit" - - GIT_CURL_VERBOSE=1 git push origin main 2>&1 -) -end_test diff --git a/tq/manifest_test.go b/tq/manifest_test.go index 680feb29..7d1db006 100644 --- a/tq/manifest_test.go +++ b/tq/manifest_test.go @@ -19,18 +19,6 @@ func TestManifestIsConfigurable(t *testing.T) { assert.Equal(t, 3, m.MaxRetries()) } -func TestManifestChecksNTLM(t *testing.T) { - cli, err := lfsapi.NewClient(lfshttp.NewContext(nil, nil, map[string]string{ - "lfs.url": "http://foo", - "lfs.http://foo.access": "ntlm", - "lfs.concurrenttransfers": "3", - })) - require.Nil(t, err) - - m := NewManifest(nil, cli, "", "") - assert.Equal(t, 8, m.MaxRetries()) -} - func TestManifestClampsValidValues(t *testing.T) { cli, err := lfsapi.NewClient(lfshttp.NewContext(nil, nil, map[string]string{ "lfs.transfer.maxretries": "-1", diff --git a/tq/transfer_queue.go b/tq/transfer_queue.go index 512f2591..323a92ca 100644 --- a/tq/transfer_queue.go +++ b/tq/transfer_queue.go @@ -7,7 +7,6 @@ import ( "sync" "time" - "github.com/git-lfs/git-lfs/creds" "github.com/git-lfs/git-lfs/errors" "github.com/git-lfs/git-lfs/git" "github.com/git-lfs/git-lfs/lfshttp" @@ -906,10 +905,6 @@ func (q *TransferQueue) ensureAdapterBegun(e lfshttp.Endpoint) error { func (q *TransferQueue) toAdapterCfg(e lfshttp.Endpoint) AdapterConfig { apiClient := q.manifest.APIClient() concurrency := q.manifest.ConcurrentTransfers() - access := apiClient.Endpoints.AccessFor(e.Url) - if access.Mode() == creds.NTLMAccess { - concurrency = 1 - } return &adapterConfig{ concurrentTransfers: concurrency,