Monday, July 25, 2016

Cannot Connect to TFS/Team Services Git from Linux via NTLM?

Team Foundation Server (TFS) and Visual Studio Team Services support multiple methods of authentication from Linux:
  • SSH
  • Kerberos
  • Basic authentication using username/password
  • Basic authentication using a personal access token
  • NTLM
When authenticating with NTLM, or in other words, with a user's Windows Active Directory domain credentials (MYDOMAIN\MyUsername), Git prompts for username and password.  

When Active Directory is configured with the LAN Manager authentication level set to "Send NTLMv2 response only. Refuse LM & NTLM", the Git operation may fail with a vague HTTP 401 reponse:
$ git pull
Username for 'http://tfs.mydomain.com:8080': MyDomain\MyUsername
Password for 'http://MyDomain\MyUsername@tfs.mydomain.com:8080':
fatal: Authentication failed for 'http://tfs.mydomain.com:8080/tfs/project1/_git/repo1/'
This often occurs when the domain enforces NTLMv2 and the version of Git uses a version of libcurl that does not support NTLMv2.  This can be detected with the following commands.  Replace the argument in the second command with the highlighted output of the first.
$ find / -name git-http-fetch -print0 2> /dev/null | xargs --null ldd | grep libcurl
        libcurl.so.4 => /usr/lib64/libcurl.so.4 (0x000000347b800000)
        libcurl.so.4 => /usr/lib64/
libcurl.so.4 (0x000000347b800000)
        libcurl.so.4 => /usr/lib64/
libcurl.so.4 (0x000000347b800000)
        libcurl.so.4 => /usr/lib64/
libcurl.so.4 (0x000000347b800000)
        libcurl.so.4 => /usr/lib64/
libcurl.so.4 (0x000000347b800000)
$ strings /usr/lib64/libcurl.so.4 | grep -i 'CLIENT libcurl'
        CLIENT libcurl 7.19.7
        CLIENT libcurl 7.19.7
        CLIENT libcurl 7.19.7
This identifies the versions of libcurl in use by the available versions of Git.  In this case, libcurl version 7.19.7.  Versions 7.36.0 and earlier do not support NTLMv2.

To solve this, you need to use a version of Git that was compiled with a version of cURL that supports NTLMv2.  If you are working on RHEL, the version of Git that ships with the OS, or was installed by the package manager, was likely compiled with a version of cURL that does not support NTLMv2.  You will need to compile cURL and Git from source to be able to authenticate with NTLMv2.  Below are the commands to do this that work on both RHEL5 and RHEL6.

Install autoconf, automake, and libtool

Download curl-7.50.0.tar.gz from https://curl.haxx.se/download.html

Download git-2.9.2.tar.gz from https://www.kernel.org/pub/software/scm/git/

Unzip the tar.gz file and extract the contents of the tar file that contains the source for cURL 7.50.0.
$ pwd
/home/
myusername/opt/curl
$ gunzip curl-7.50.0.tar.gz
$ tar xvf curl-7.50.0.tar
$ pwd

/home/
myusername/opt/curl
$ cd ..
$ pwd

/home/
myusername/opt

Create directories where you will install cURL 7.50.0 and Git 2.9.2
$ mkdir curl_rhel5_7_50_0
$ mkdir git_rhel5_2_9_2

Go to the directory that contains the source for cURL 7.50.0.
$ cd curl
$ cd curl-7.50.0

Compile cURL 7.50.0
$ ./configure prefix=/home/myusername/opt/curl_rhel5_7_50_0
$ make && make install

Test cURL 7.50.0
$ /home/myusername/opt/curl_rhel5_7_50_0/bin/curl --version
curl 7.50.0 (x86_64-pc-linux-gnu) libcurl/7.50.0 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5

Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: IDN IPv6 Largefile NTLM NTLM_WB SSL libz UnixSockets

Unzip the tar.gz file and extract the contents of the tar file that contains the source for Git 2.9.2.
$ cd /home/myusername/opt/git
$ gunzip git-2.9.2.tar.gz
$ tar xvf git-2.9.2.tar

Go to the directory that contains the source for Git 2.9.2
$ cd git-2.9.2

Compile Git 2.9.2
$ make configure
$ ./configure --prefix=/home/
myusername/opt/git_rhel5_2_9_2 --with-curl=/home/myusername/opt/curl_rhel5_7_50_0
$ make all && make install

Test Git 2.9.2
$ /home/myusername/opt/git_rhel5_2_9_2/bin/git --version
git version 2.9.2