Repository: ansible/ansible

Issue:
Wrong parsing of proxy response lead to SSL handshake failure
##### Issue Type:

Bug report. Really annoying and hard to spot.
##### Ansible Version:

```
ansible 1.9.0.1
  configured module search path = None
```
##### Environment:

Master: openSuse 13.2 with a running tinyproxy 1.8.3
Host: SLES11 SP3 with environment variable https_proxy="http://master"
##### Summary:

In urls.py the method http_request of the SSLValidationHandler class validates the proxy response by itself. However, if the proxy is slow to send its headers, the first header can be read, but not the remaining. When the socket is wrapped, the SSL module tries to read the SSL protocol, and fails, because it contains unexpected HTTP headers.

The validation should wait for an line containing only '\r\n', as requested by HTTP.
##### Steps To Reproduce:

Try something like:

```
- name: Download openssl 1.0.2a
  get_url:
      url: https://www.openssl.org/source/openssl-1.0.2a.tar.gz
      dest: ~{{ install_user }}/openssl-1.0.2a.tar.gz
      sha256sum: 15b6393c20030aab02c8e2fe0243cb1d1d18062f6c095d67bca91871dc7f324a
```

with a proxy, which answer very slowly after the first header.
##### Expected Results:

The task should be ok.
##### Actual Results:

```
msg: Failed to validate the SSL certificate for www.openssl.org:443. Use validate_certs=no or make sure your managed systems have a valid CA certificate installed. Paths checked for this platform: /etc/ssl/certs, /etc/pki/ca-trust/extracted/pem, /etc/pki/tls/certs, /usr/share/ca-certificates/cacert.org, /etc/ansible
```

In fact, the real SSL issue is:

```
[Errno 1] _ssl.c:491: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol
```

Wrong parsing of proxy response lead to SSL handshake failure
##### Issue Type:

Bug report. Really annoying and hard to spot.
##### Ansible Version:

```
ansible 1.9.0.1
  configured module search path = None
```
##### Environment:

Master: openSuse 13.2 with a running tinyproxy 1.8.3
Host: SLES11 SP3 with environment variable https_proxy="http://master"
##### Summary:

In urls.py the method http_request of the SSLValidationHandler class validates the proxy response by itself. However, if the proxy is slow to send its headers, the first header can be read, but not the remaining. When the socket is wrapped, the SSL module tries to read the SSL protocol, and fails, because it contains unexpected HTTP headers.

The validation should wait for an line containing only '\r\n', as requested by HTTP.
##### Steps To Reproduce:

Try something like:

```
- name: Download openssl 1.0.2a
  get_url:
      url: https://www.openssl.org/source/openssl-1.0.2a.tar.gz
      dest: ~{{ install_user }}/openssl-1.0.2a.tar.gz
      sha256sum: 15b6393c20030aab02c8e2fe0243cb1d1d18062f6c095d67bca91871dc7f324a
```

with a proxy, which answer very slowly after the first header.
##### Expected Results:

The task should be ok.
##### Actual Results:

```
msg: Failed to validate the SSL certificate for www.openssl.org:443. Use validate_certs=no or make sure your managed systems have a valid CA certificate installed. Paths checked for this platform: /etc/ssl/certs, /etc/pki/ca-trust/extracted/pem, /etc/pki/tls/certs, /usr/share/ca-certificates/cacert.org, /etc/ansible
```

In fact, the real SSL issue is:

```
[Errno 1] _ssl.c:491: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol
```



Discussion:
I can reproduce this one on latest ansible `ansible 2.1.1.0`

I can reproduce this one on latest ansible `ansible 2.1.1.0`


Resolving patch:
diff --git a/lib/ansible/module_utils/urls.py b/lib/ansible/module_utils/urls.py
--- a/lib/ansible/module_utils/urls.py
+++ b/lib/ansible/module_utils/urls.py
@@ -674,7 +674,12 @@ def http_request(self, req):
                         credentials = "%s:%s" % (proxy_parts.get('username',''), proxy_parts.get('password',''))
                         s.sendall('Proxy-Authorization: Basic %s\r\n' % credentials.encode('base64').strip())
                     s.sendall('\r\n')
-                    connect_result = s.recv(4096)
+                    connect_result = ""
+                    while connect_result.find("\r\n\r\n") <= 0:
+                        connect_result += s.recv(4096)
+                        # 128 kilobytes of headers should be enough for everyone.
+                        if len(connect_result) > 131072:
+                            raise ProxyError('Proxy sent too verbose headers. Only 128KiB allowed.')
                     self.validate_proxy_response(connect_result)
                     if context:
                         ssl_s = context.wrap_socket(s, server_hostname=self.hostname)


Test patch: