##// END OF EJS Templates
hgweb: use sslutil.wrapserversocket()...
Gregory Szorc -
r29555:121d1181 default
parent child Browse files
Show More
@@ -1,326 +1,334
1 # hgweb/server.py - The standalone hg web server.
1 # hgweb/server.py - The standalone hg web server.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import BaseHTTPServer
11 import BaseHTTPServer
12 import errno
12 import errno
13 import os
13 import os
14 import socket
14 import socket
15 import sys
15 import sys
16 import traceback
16 import traceback
17
17
18 from ..i18n import _
18 from ..i18n import _
19
19
20 from .. import (
20 from .. import (
21 error,
21 error,
22 util,
22 util,
23 )
23 )
24
24
25 socketserver = util.socketserver
25 socketserver = util.socketserver
26 urlerr = util.urlerr
26 urlerr = util.urlerr
27 urlreq = util.urlreq
27 urlreq = util.urlreq
28
28
29 from . import (
29 from . import (
30 common,
30 common,
31 )
31 )
32
32
33 def _splitURI(uri):
33 def _splitURI(uri):
34 """Return path and query that has been split from uri
34 """Return path and query that has been split from uri
35
35
36 Just like CGI environment, the path is unquoted, the query is
36 Just like CGI environment, the path is unquoted, the query is
37 not.
37 not.
38 """
38 """
39 if '?' in uri:
39 if '?' in uri:
40 path, query = uri.split('?', 1)
40 path, query = uri.split('?', 1)
41 else:
41 else:
42 path, query = uri, ''
42 path, query = uri, ''
43 return urlreq.unquote(path), query
43 return urlreq.unquote(path), query
44
44
45 class _error_logger(object):
45 class _error_logger(object):
46 def __init__(self, handler):
46 def __init__(self, handler):
47 self.handler = handler
47 self.handler = handler
48 def flush(self):
48 def flush(self):
49 pass
49 pass
50 def write(self, str):
50 def write(self, str):
51 self.writelines(str.split('\n'))
51 self.writelines(str.split('\n'))
52 def writelines(self, seq):
52 def writelines(self, seq):
53 for msg in seq:
53 for msg in seq:
54 self.handler.log_error("HG error: %s", msg)
54 self.handler.log_error("HG error: %s", msg)
55
55
56 class _httprequesthandler(BaseHTTPServer.BaseHTTPRequestHandler):
56 class _httprequesthandler(BaseHTTPServer.BaseHTTPRequestHandler):
57
57
58 url_scheme = 'http'
58 url_scheme = 'http'
59
59
60 @staticmethod
60 @staticmethod
61 def preparehttpserver(httpserver, ui):
61 def preparehttpserver(httpserver, ui):
62 """Prepare .socket of new HTTPServer instance"""
62 """Prepare .socket of new HTTPServer instance"""
63 pass
63 pass
64
64
65 def __init__(self, *args, **kargs):
65 def __init__(self, *args, **kargs):
66 self.protocol_version = 'HTTP/1.1'
66 self.protocol_version = 'HTTP/1.1'
67 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
67 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
68
68
69 def _log_any(self, fp, format, *args):
69 def _log_any(self, fp, format, *args):
70 fp.write("%s - - [%s] %s\n" % (self.client_address[0],
70 fp.write("%s - - [%s] %s\n" % (self.client_address[0],
71 self.log_date_time_string(),
71 self.log_date_time_string(),
72 format % args))
72 format % args))
73 fp.flush()
73 fp.flush()
74
74
75 def log_error(self, format, *args):
75 def log_error(self, format, *args):
76 self._log_any(self.server.errorlog, format, *args)
76 self._log_any(self.server.errorlog, format, *args)
77
77
78 def log_message(self, format, *args):
78 def log_message(self, format, *args):
79 self._log_any(self.server.accesslog, format, *args)
79 self._log_any(self.server.accesslog, format, *args)
80
80
81 def log_request(self, code='-', size='-'):
81 def log_request(self, code='-', size='-'):
82 xheaders = []
82 xheaders = []
83 if util.safehasattr(self, 'headers'):
83 if util.safehasattr(self, 'headers'):
84 xheaders = [h for h in self.headers.items()
84 xheaders = [h for h in self.headers.items()
85 if h[0].startswith('x-')]
85 if h[0].startswith('x-')]
86 self.log_message('"%s" %s %s%s',
86 self.log_message('"%s" %s %s%s',
87 self.requestline, str(code), str(size),
87 self.requestline, str(code), str(size),
88 ''.join([' %s:%s' % h for h in sorted(xheaders)]))
88 ''.join([' %s:%s' % h for h in sorted(xheaders)]))
89
89
90 def do_write(self):
90 def do_write(self):
91 try:
91 try:
92 self.do_hgweb()
92 self.do_hgweb()
93 except socket.error as inst:
93 except socket.error as inst:
94 if inst[0] != errno.EPIPE:
94 if inst[0] != errno.EPIPE:
95 raise
95 raise
96
96
97 def do_POST(self):
97 def do_POST(self):
98 try:
98 try:
99 self.do_write()
99 self.do_write()
100 except Exception:
100 except Exception:
101 self._start_response("500 Internal Server Error", [])
101 self._start_response("500 Internal Server Error", [])
102 self._write("Internal Server Error")
102 self._write("Internal Server Error")
103 self._done()
103 self._done()
104 tb = "".join(traceback.format_exception(*sys.exc_info()))
104 tb = "".join(traceback.format_exception(*sys.exc_info()))
105 self.log_error("Exception happened during processing "
105 self.log_error("Exception happened during processing "
106 "request '%s':\n%s", self.path, tb)
106 "request '%s':\n%s", self.path, tb)
107
107
108 def do_GET(self):
108 def do_GET(self):
109 self.do_POST()
109 self.do_POST()
110
110
111 def do_hgweb(self):
111 def do_hgweb(self):
112 path, query = _splitURI(self.path)
112 path, query = _splitURI(self.path)
113
113
114 env = {}
114 env = {}
115 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
115 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
116 env['REQUEST_METHOD'] = self.command
116 env['REQUEST_METHOD'] = self.command
117 env['SERVER_NAME'] = self.server.server_name
117 env['SERVER_NAME'] = self.server.server_name
118 env['SERVER_PORT'] = str(self.server.server_port)
118 env['SERVER_PORT'] = str(self.server.server_port)
119 env['REQUEST_URI'] = self.path
119 env['REQUEST_URI'] = self.path
120 env['SCRIPT_NAME'] = self.server.prefix
120 env['SCRIPT_NAME'] = self.server.prefix
121 env['PATH_INFO'] = path[len(self.server.prefix):]
121 env['PATH_INFO'] = path[len(self.server.prefix):]
122 env['REMOTE_HOST'] = self.client_address[0]
122 env['REMOTE_HOST'] = self.client_address[0]
123 env['REMOTE_ADDR'] = self.client_address[0]
123 env['REMOTE_ADDR'] = self.client_address[0]
124 if query:
124 if query:
125 env['QUERY_STRING'] = query
125 env['QUERY_STRING'] = query
126
126
127 if self.headers.typeheader is None:
127 if self.headers.typeheader is None:
128 env['CONTENT_TYPE'] = self.headers.type
128 env['CONTENT_TYPE'] = self.headers.type
129 else:
129 else:
130 env['CONTENT_TYPE'] = self.headers.typeheader
130 env['CONTENT_TYPE'] = self.headers.typeheader
131 length = self.headers.getheader('content-length')
131 length = self.headers.getheader('content-length')
132 if length:
132 if length:
133 env['CONTENT_LENGTH'] = length
133 env['CONTENT_LENGTH'] = length
134 for header in [h for h in self.headers.keys()
134 for header in [h for h in self.headers.keys()
135 if h not in ('content-type', 'content-length')]:
135 if h not in ('content-type', 'content-length')]:
136 hkey = 'HTTP_' + header.replace('-', '_').upper()
136 hkey = 'HTTP_' + header.replace('-', '_').upper()
137 hval = self.headers.getheader(header)
137 hval = self.headers.getheader(header)
138 hval = hval.replace('\n', '').strip()
138 hval = hval.replace('\n', '').strip()
139 if hval:
139 if hval:
140 env[hkey] = hval
140 env[hkey] = hval
141 env['SERVER_PROTOCOL'] = self.request_version
141 env['SERVER_PROTOCOL'] = self.request_version
142 env['wsgi.version'] = (1, 0)
142 env['wsgi.version'] = (1, 0)
143 env['wsgi.url_scheme'] = self.url_scheme
143 env['wsgi.url_scheme'] = self.url_scheme
144 if env.get('HTTP_EXPECT', '').lower() == '100-continue':
144 if env.get('HTTP_EXPECT', '').lower() == '100-continue':
145 self.rfile = common.continuereader(self.rfile, self.wfile.write)
145 self.rfile = common.continuereader(self.rfile, self.wfile.write)
146
146
147 env['wsgi.input'] = self.rfile
147 env['wsgi.input'] = self.rfile
148 env['wsgi.errors'] = _error_logger(self)
148 env['wsgi.errors'] = _error_logger(self)
149 env['wsgi.multithread'] = isinstance(self.server,
149 env['wsgi.multithread'] = isinstance(self.server,
150 socketserver.ThreadingMixIn)
150 socketserver.ThreadingMixIn)
151 env['wsgi.multiprocess'] = isinstance(self.server,
151 env['wsgi.multiprocess'] = isinstance(self.server,
152 socketserver.ForkingMixIn)
152 socketserver.ForkingMixIn)
153 env['wsgi.run_once'] = 0
153 env['wsgi.run_once'] = 0
154
154
155 self.saved_status = None
155 self.saved_status = None
156 self.saved_headers = []
156 self.saved_headers = []
157 self.sent_headers = False
157 self.sent_headers = False
158 self.length = None
158 self.length = None
159 self._chunked = None
159 self._chunked = None
160 for chunk in self.server.application(env, self._start_response):
160 for chunk in self.server.application(env, self._start_response):
161 self._write(chunk)
161 self._write(chunk)
162 if not self.sent_headers:
162 if not self.sent_headers:
163 self.send_headers()
163 self.send_headers()
164 self._done()
164 self._done()
165
165
166 def send_headers(self):
166 def send_headers(self):
167 if not self.saved_status:
167 if not self.saved_status:
168 raise AssertionError("Sending headers before "
168 raise AssertionError("Sending headers before "
169 "start_response() called")
169 "start_response() called")
170 saved_status = self.saved_status.split(None, 1)
170 saved_status = self.saved_status.split(None, 1)
171 saved_status[0] = int(saved_status[0])
171 saved_status[0] = int(saved_status[0])
172 self.send_response(*saved_status)
172 self.send_response(*saved_status)
173 self.length = None
173 self.length = None
174 self._chunked = False
174 self._chunked = False
175 for h in self.saved_headers:
175 for h in self.saved_headers:
176 self.send_header(*h)
176 self.send_header(*h)
177 if h[0].lower() == 'content-length':
177 if h[0].lower() == 'content-length':
178 self.length = int(h[1])
178 self.length = int(h[1])
179 if (self.length is None and
179 if (self.length is None and
180 saved_status[0] != common.HTTP_NOT_MODIFIED):
180 saved_status[0] != common.HTTP_NOT_MODIFIED):
181 self._chunked = (not self.close_connection and
181 self._chunked = (not self.close_connection and
182 self.request_version == "HTTP/1.1")
182 self.request_version == "HTTP/1.1")
183 if self._chunked:
183 if self._chunked:
184 self.send_header('Transfer-Encoding', 'chunked')
184 self.send_header('Transfer-Encoding', 'chunked')
185 else:
185 else:
186 self.send_header('Connection', 'close')
186 self.send_header('Connection', 'close')
187 self.end_headers()
187 self.end_headers()
188 self.sent_headers = True
188 self.sent_headers = True
189
189
190 def _start_response(self, http_status, headers, exc_info=None):
190 def _start_response(self, http_status, headers, exc_info=None):
191 code, msg = http_status.split(None, 1)
191 code, msg = http_status.split(None, 1)
192 code = int(code)
192 code = int(code)
193 self.saved_status = http_status
193 self.saved_status = http_status
194 bad_headers = ('connection', 'transfer-encoding')
194 bad_headers = ('connection', 'transfer-encoding')
195 self.saved_headers = [h for h in headers
195 self.saved_headers = [h for h in headers
196 if h[0].lower() not in bad_headers]
196 if h[0].lower() not in bad_headers]
197 return self._write
197 return self._write
198
198
199 def _write(self, data):
199 def _write(self, data):
200 if not self.saved_status:
200 if not self.saved_status:
201 raise AssertionError("data written before start_response() called")
201 raise AssertionError("data written before start_response() called")
202 elif not self.sent_headers:
202 elif not self.sent_headers:
203 self.send_headers()
203 self.send_headers()
204 if self.length is not None:
204 if self.length is not None:
205 if len(data) > self.length:
205 if len(data) > self.length:
206 raise AssertionError("Content-length header sent, but more "
206 raise AssertionError("Content-length header sent, but more "
207 "bytes than specified are being written.")
207 "bytes than specified are being written.")
208 self.length = self.length - len(data)
208 self.length = self.length - len(data)
209 elif self._chunked and data:
209 elif self._chunked and data:
210 data = '%x\r\n%s\r\n' % (len(data), data)
210 data = '%x\r\n%s\r\n' % (len(data), data)
211 self.wfile.write(data)
211 self.wfile.write(data)
212 self.wfile.flush()
212 self.wfile.flush()
213
213
214 def _done(self):
214 def _done(self):
215 if self._chunked:
215 if self._chunked:
216 self.wfile.write('0\r\n\r\n')
216 self.wfile.write('0\r\n\r\n')
217 self.wfile.flush()
217 self.wfile.flush()
218
218
219 class _httprequesthandlerssl(_httprequesthandler):
219 class _httprequesthandlerssl(_httprequesthandler):
220 """HTTPS handler based on Python's ssl module"""
220 """HTTPS handler based on Python's ssl module"""
221
221
222 url_scheme = 'https'
222 url_scheme = 'https'
223
223
224 @staticmethod
224 @staticmethod
225 def preparehttpserver(httpserver, ui):
225 def preparehttpserver(httpserver, ui):
226 try:
226 try:
227 import ssl
227 from .. import sslutil
228 ssl.wrap_socket
228 sslutil.modernssl
229 except ImportError:
229 except ImportError:
230 raise error.Abort(_("SSL support is unavailable"))
230 raise error.Abort(_("SSL support is unavailable"))
231
231
232 certfile = ui.config('web', 'certificate')
232 certfile = ui.config('web', 'certificate')
233 httpserver.socket = ssl.wrap_socket(
233
234 httpserver.socket, server_side=True,
234 # These config options are currently only meant for testing. Use
235 certfile=certfile, ssl_version=ssl.PROTOCOL_TLSv1)
235 # at your own risk.
236 cafile = ui.config('devel', 'servercafile')
237 reqcert = ui.configbool('devel', 'serverrequirecert')
238
239 httpserver.socket = sslutil.wrapserversocket(httpserver.socket,
240 ui,
241 certfile=certfile,
242 cafile=cafile,
243 requireclientcert=reqcert)
236
244
237 def setup(self):
245 def setup(self):
238 self.connection = self.request
246 self.connection = self.request
239 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
247 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
240 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
248 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
241
249
242 try:
250 try:
243 import threading
251 import threading
244 threading.activeCount() # silence pyflakes and bypass demandimport
252 threading.activeCount() # silence pyflakes and bypass demandimport
245 _mixin = socketserver.ThreadingMixIn
253 _mixin = socketserver.ThreadingMixIn
246 except ImportError:
254 except ImportError:
247 if util.safehasattr(os, "fork"):
255 if util.safehasattr(os, "fork"):
248 _mixin = socketserver.ForkingMixIn
256 _mixin = socketserver.ForkingMixIn
249 else:
257 else:
250 class _mixin(object):
258 class _mixin(object):
251 pass
259 pass
252
260
253 def openlog(opt, default):
261 def openlog(opt, default):
254 if opt and opt != '-':
262 if opt and opt != '-':
255 return open(opt, 'a')
263 return open(opt, 'a')
256 return default
264 return default
257
265
258 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
266 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
259
267
260 # SO_REUSEADDR has broken semantics on windows
268 # SO_REUSEADDR has broken semantics on windows
261 if os.name == 'nt':
269 if os.name == 'nt':
262 allow_reuse_address = 0
270 allow_reuse_address = 0
263
271
264 def __init__(self, ui, app, addr, handler, **kwargs):
272 def __init__(self, ui, app, addr, handler, **kwargs):
265 BaseHTTPServer.HTTPServer.__init__(self, addr, handler, **kwargs)
273 BaseHTTPServer.HTTPServer.__init__(self, addr, handler, **kwargs)
266 self.daemon_threads = True
274 self.daemon_threads = True
267 self.application = app
275 self.application = app
268
276
269 handler.preparehttpserver(self, ui)
277 handler.preparehttpserver(self, ui)
270
278
271 prefix = ui.config('web', 'prefix', '')
279 prefix = ui.config('web', 'prefix', '')
272 if prefix:
280 if prefix:
273 prefix = '/' + prefix.strip('/')
281 prefix = '/' + prefix.strip('/')
274 self.prefix = prefix
282 self.prefix = prefix
275
283
276 alog = openlog(ui.config('web', 'accesslog', '-'), sys.stdout)
284 alog = openlog(ui.config('web', 'accesslog', '-'), sys.stdout)
277 elog = openlog(ui.config('web', 'errorlog', '-'), sys.stderr)
285 elog = openlog(ui.config('web', 'errorlog', '-'), sys.stderr)
278 self.accesslog = alog
286 self.accesslog = alog
279 self.errorlog = elog
287 self.errorlog = elog
280
288
281 self.addr, self.port = self.socket.getsockname()[0:2]
289 self.addr, self.port = self.socket.getsockname()[0:2]
282 self.fqaddr = socket.getfqdn(addr[0])
290 self.fqaddr = socket.getfqdn(addr[0])
283
291
284 class IPv6HTTPServer(MercurialHTTPServer):
292 class IPv6HTTPServer(MercurialHTTPServer):
285 address_family = getattr(socket, 'AF_INET6', None)
293 address_family = getattr(socket, 'AF_INET6', None)
286 def __init__(self, *args, **kwargs):
294 def __init__(self, *args, **kwargs):
287 if self.address_family is None:
295 if self.address_family is None:
288 raise error.RepoError(_('IPv6 is not available on this system'))
296 raise error.RepoError(_('IPv6 is not available on this system'))
289 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
297 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
290
298
291 def create_server(ui, app):
299 def create_server(ui, app):
292
300
293 if ui.config('web', 'certificate'):
301 if ui.config('web', 'certificate'):
294 handler = _httprequesthandlerssl
302 handler = _httprequesthandlerssl
295 else:
303 else:
296 handler = _httprequesthandler
304 handler = _httprequesthandler
297
305
298 if ui.configbool('web', 'ipv6'):
306 if ui.configbool('web', 'ipv6'):
299 cls = IPv6HTTPServer
307 cls = IPv6HTTPServer
300 else:
308 else:
301 cls = MercurialHTTPServer
309 cls = MercurialHTTPServer
302
310
303 # ugly hack due to python issue5853 (for threaded use)
311 # ugly hack due to python issue5853 (for threaded use)
304 try:
312 try:
305 import mimetypes
313 import mimetypes
306 mimetypes.init()
314 mimetypes.init()
307 except UnicodeDecodeError:
315 except UnicodeDecodeError:
308 # Python 2.x's mimetypes module attempts to decode strings
316 # Python 2.x's mimetypes module attempts to decode strings
309 # from Windows' ANSI APIs as ascii (fail), then re-encode them
317 # from Windows' ANSI APIs as ascii (fail), then re-encode them
310 # as ascii (clown fail), because the default Python Unicode
318 # as ascii (clown fail), because the default Python Unicode
311 # codec is hardcoded as ascii.
319 # codec is hardcoded as ascii.
312
320
313 sys.argv # unwrap demand-loader so that reload() works
321 sys.argv # unwrap demand-loader so that reload() works
314 reload(sys) # resurrect sys.setdefaultencoding()
322 reload(sys) # resurrect sys.setdefaultencoding()
315 oldenc = sys.getdefaultencoding()
323 oldenc = sys.getdefaultencoding()
316 sys.setdefaultencoding("latin1") # or any full 8-bit encoding
324 sys.setdefaultencoding("latin1") # or any full 8-bit encoding
317 mimetypes.init()
325 mimetypes.init()
318 sys.setdefaultencoding(oldenc)
326 sys.setdefaultencoding(oldenc)
319
327
320 address = ui.config('web', 'address', '')
328 address = ui.config('web', 'address', '')
321 port = util.getport(ui.config('web', 'port', 8000))
329 port = util.getport(ui.config('web', 'port', 8000))
322 try:
330 try:
323 return cls(ui, app, (address, port), handler)
331 return cls(ui, app, (address, port), handler)
324 except socket.error as inst:
332 except socket.error as inst:
325 raise error.Abort(_("cannot start server at '%s:%d': %s")
333 raise error.Abort(_("cannot start server at '%s:%d': %s")
326 % (address, port, inst.args[1]))
334 % (address, port, inst.args[1]))
@@ -1,451 +1,435
1 #require serve ssl
1 #require serve ssl
2
2
3 Proper https client requires the built-in ssl from Python 2.6.
3 Proper https client requires the built-in ssl from Python 2.6.
4
4
5 Make server certificates:
5 Make server certificates:
6
6
7 $ CERTSDIR="$TESTDIR/sslcerts"
7 $ CERTSDIR="$TESTDIR/sslcerts"
8 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem
8 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub.pem" >> server.pem
9 $ PRIV=`pwd`/server.pem
9 $ PRIV=`pwd`/server.pem
10 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-not-yet.pem" > server-not-yet.pem
10 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-not-yet.pem" > server-not-yet.pem
11 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-expired.pem" > server-expired.pem
11 $ cat "$CERTSDIR/priv.pem" "$CERTSDIR/pub-expired.pem" > server-expired.pem
12
12
13 $ hg init test
13 $ hg init test
14 $ cd test
14 $ cd test
15 $ echo foo>foo
15 $ echo foo>foo
16 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
16 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
17 $ echo foo>foo.d/foo
17 $ echo foo>foo.d/foo
18 $ echo bar>foo.d/bAr.hg.d/BaR
18 $ echo bar>foo.d/bAr.hg.d/BaR
19 $ echo bar>foo.d/baR.d.hg/bAR
19 $ echo bar>foo.d/baR.d.hg/bAR
20 $ hg commit -A -m 1
20 $ hg commit -A -m 1
21 adding foo
21 adding foo
22 adding foo.d/bAr.hg.d/BaR
22 adding foo.d/bAr.hg.d/BaR
23 adding foo.d/baR.d.hg/bAR
23 adding foo.d/baR.d.hg/bAR
24 adding foo.d/foo
24 adding foo.d/foo
25 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
25 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
26 $ cat ../hg0.pid >> $DAEMON_PIDS
26 $ cat ../hg0.pid >> $DAEMON_PIDS
27
27
28 cacert not found
28 cacert not found
29
29
30 $ hg in --config web.cacerts=no-such.pem https://localhost:$HGPORT/
30 $ hg in --config web.cacerts=no-such.pem https://localhost:$HGPORT/
31 abort: could not find web.cacerts: no-such.pem
31 abort: could not find web.cacerts: no-such.pem
32 [255]
32 [255]
33
33
34 Test server address cannot be reused
34 Test server address cannot be reused
35
35
36 #if windows
36 #if windows
37 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
37 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
38 abort: cannot start server at ':$HGPORT':
38 abort: cannot start server at ':$HGPORT':
39 [255]
39 [255]
40 #else
40 #else
41 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
41 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
42 abort: cannot start server at ':$HGPORT': Address already in use
42 abort: cannot start server at ':$HGPORT': Address already in use
43 [255]
43 [255]
44 #endif
44 #endif
45 $ cd ..
45 $ cd ..
46
46
47 Our test cert is not signed by a trusted CA. It should fail to verify if
47 Our test cert is not signed by a trusted CA. It should fail to verify if
48 we are able to load CA certs.
48 we are able to load CA certs.
49
49
50 #if sslcontext defaultcacerts no-defaultcacertsloaded
50 #if sslcontext defaultcacerts no-defaultcacertsloaded
51 $ hg clone https://localhost:$HGPORT/ copy-pull
51 $ hg clone https://localhost:$HGPORT/ copy-pull
52 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
52 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
53 abort: error: *certificate verify failed* (glob)
53 abort: error: *certificate verify failed* (glob)
54 [255]
54 [255]
55 #endif
55 #endif
56
56
57 #if no-sslcontext defaultcacerts
57 #if no-sslcontext defaultcacerts
58 $ hg clone https://localhost:$HGPORT/ copy-pull
58 $ hg clone https://localhost:$HGPORT/ copy-pull
59 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
59 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
60 abort: error: *certificate verify failed* (glob)
60 abort: error: *certificate verify failed* (glob)
61 [255]
61 [255]
62 #endif
62 #endif
63
63
64 #if no-sslcontext windows
64 #if no-sslcontext windows
65 $ hg clone https://localhost:$HGPORT/ copy-pull
65 $ hg clone https://localhost:$HGPORT/ copy-pull
66 (unable to load Windows CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
66 (unable to load Windows CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
67 abort: error: *certificate verify failed* (glob)
67 abort: error: *certificate verify failed* (glob)
68 [255]
68 [255]
69 #endif
69 #endif
70
70
71 #if no-sslcontext osx
71 #if no-sslcontext osx
72 $ hg clone https://localhost:$HGPORT/ copy-pull
72 $ hg clone https://localhost:$HGPORT/ copy-pull
73 (unable to load CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
73 (unable to load CA certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message)
74 abort: localhost certificate error: no certificate received
74 abort: localhost certificate error: no certificate received
75 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
75 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
76 [255]
76 [255]
77 #endif
77 #endif
78
78
79 #if defaultcacertsloaded
79 #if defaultcacertsloaded
80 $ hg clone https://localhost:$HGPORT/ copy-pull
80 $ hg clone https://localhost:$HGPORT/ copy-pull
81 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
81 (using CA certificates from *; if you see this message, your Mercurial install is not properly configured; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
82 abort: error: *certificate verify failed* (glob)
82 abort: error: *certificate verify failed* (glob)
83 [255]
83 [255]
84 #endif
84 #endif
85
85
86 #if no-defaultcacerts
86 #if no-defaultcacerts
87 $ hg clone https://localhost:$HGPORT/ copy-pull
87 $ hg clone https://localhost:$HGPORT/ copy-pull
88 (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
88 (unable to load * certificates; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this message) (glob) (?)
89 abort: localhost certificate error: no certificate received
89 abort: localhost certificate error: no certificate received
90 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
90 (set hostsecurity.localhost:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely)
91 [255]
91 [255]
92 #endif
92 #endif
93
93
94 Specifying a per-host certificate file that doesn't exist will abort
94 Specifying a per-host certificate file that doesn't exist will abort
95
95
96 $ hg --config hostsecurity.localhost:verifycertsfile=/does/not/exist clone https://localhost:$HGPORT/
96 $ hg --config hostsecurity.localhost:verifycertsfile=/does/not/exist clone https://localhost:$HGPORT/
97 abort: path specified by hostsecurity.localhost:verifycertsfile does not exist: /does/not/exist
97 abort: path specified by hostsecurity.localhost:verifycertsfile does not exist: /does/not/exist
98 [255]
98 [255]
99
99
100 A malformed per-host certificate file will raise an error
100 A malformed per-host certificate file will raise an error
101
101
102 $ echo baddata > badca.pem
102 $ echo baddata > badca.pem
103 #if sslcontext
103 #if sslcontext
104 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
104 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
105 abort: error loading CA file badca.pem: * (glob)
105 abort: error loading CA file badca.pem: * (glob)
106 (file is empty or malformed?)
106 (file is empty or malformed?)
107 [255]
107 [255]
108 #else
108 #else
109 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
109 $ hg --config hostsecurity.localhost:verifycertsfile=badca.pem clone https://localhost:$HGPORT/
110 abort: error: * (glob)
110 abort: error: * (glob)
111 [255]
111 [255]
112 #endif
112 #endif
113
113
114 A per-host certificate mismatching the server will fail verification
114 A per-host certificate mismatching the server will fail verification
115
115
116 (modern ssl is able to discern whether the loaded cert is a CA cert)
116 (modern ssl is able to discern whether the loaded cert is a CA cert)
117 #if sslcontext
117 #if sslcontext
118 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
118 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
119 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
119 (an attempt was made to load CA certificates but none were loaded; see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error)
120 abort: error: *certificate verify failed* (glob)
120 abort: error: *certificate verify failed* (glob)
121 [255]
121 [255]
122 #else
122 #else
123 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
123 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/client-cert.pem" clone https://localhost:$HGPORT/
124 abort: error: *certificate verify failed* (glob)
124 abort: error: *certificate verify failed* (glob)
125 [255]
125 [255]
126 #endif
126 #endif
127
127
128 A per-host certificate matching the server's cert will be accepted
128 A per-host certificate matching the server's cert will be accepted
129
129
130 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" clone -U https://localhost:$HGPORT/ perhostgood1
130 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" clone -U https://localhost:$HGPORT/ perhostgood1
131 requesting all changes
131 requesting all changes
132 adding changesets
132 adding changesets
133 adding manifests
133 adding manifests
134 adding file changes
134 adding file changes
135 added 1 changesets with 4 changes to 4 files
135 added 1 changesets with 4 changes to 4 files
136
136
137 A per-host certificate with multiple certs and one matching will be accepted
137 A per-host certificate with multiple certs and one matching will be accepted
138
138
139 $ cat "$CERTSDIR/client-cert.pem" "$CERTSDIR/pub.pem" > perhost.pem
139 $ cat "$CERTSDIR/client-cert.pem" "$CERTSDIR/pub.pem" > perhost.pem
140 $ hg --config hostsecurity.localhost:verifycertsfile=perhost.pem clone -U https://localhost:$HGPORT/ perhostgood2
140 $ hg --config hostsecurity.localhost:verifycertsfile=perhost.pem clone -U https://localhost:$HGPORT/ perhostgood2
141 requesting all changes
141 requesting all changes
142 adding changesets
142 adding changesets
143 adding manifests
143 adding manifests
144 adding file changes
144 adding file changes
145 added 1 changesets with 4 changes to 4 files
145 added 1 changesets with 4 changes to 4 files
146
146
147 Defining both per-host certificate and a fingerprint will print a warning
147 Defining both per-host certificate and a fingerprint will print a warning
148
148
149 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 clone -U https://localhost:$HGPORT/ caandfingerwarning
149 $ hg --config hostsecurity.localhost:verifycertsfile="$CERTSDIR/pub.pem" --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03 clone -U https://localhost:$HGPORT/ caandfingerwarning
150 (hostsecurity.localhost:verifycertsfile ignored when host fingerprints defined; using host fingerprints for verification)
150 (hostsecurity.localhost:verifycertsfile ignored when host fingerprints defined; using host fingerprints for verification)
151 requesting all changes
151 requesting all changes
152 adding changesets
152 adding changesets
153 adding manifests
153 adding manifests
154 adding file changes
154 adding file changes
155 added 1 changesets with 4 changes to 4 files
155 added 1 changesets with 4 changes to 4 files
156
156
157 $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
157 $ DISABLECACERTS="--config devel.disableloaddefaultcerts=true"
158
158
159 Inability to verify peer certificate will result in abort
159 Inability to verify peer certificate will result in abort
160
160
161 $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLECACERTS
161 $ hg clone https://localhost:$HGPORT/ copy-pull $DISABLECACERTS
162 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
162 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
163 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
163 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
164 [255]
164 [255]
165
165
166 $ hg clone --insecure https://localhost:$HGPORT/ copy-pull
166 $ hg clone --insecure https://localhost:$HGPORT/ copy-pull
167 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
167 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
168 requesting all changes
168 requesting all changes
169 adding changesets
169 adding changesets
170 adding manifests
170 adding manifests
171 adding file changes
171 adding file changes
172 added 1 changesets with 4 changes to 4 files
172 added 1 changesets with 4 changes to 4 files
173 updating to branch default
173 updating to branch default
174 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
175 $ hg verify -R copy-pull
175 $ hg verify -R copy-pull
176 checking changesets
176 checking changesets
177 checking manifests
177 checking manifests
178 crosschecking files in changesets and manifests
178 crosschecking files in changesets and manifests
179 checking files
179 checking files
180 4 files, 1 changesets, 4 total revisions
180 4 files, 1 changesets, 4 total revisions
181 $ cd test
181 $ cd test
182 $ echo bar > bar
182 $ echo bar > bar
183 $ hg commit -A -d '1 0' -m 2
183 $ hg commit -A -d '1 0' -m 2
184 adding bar
184 adding bar
185 $ cd ..
185 $ cd ..
186
186
187 pull without cacert
187 pull without cacert
188
188
189 $ cd copy-pull
189 $ cd copy-pull
190 $ echo '[hooks]' >> .hg/hgrc
190 $ echo '[hooks]' >> .hg/hgrc
191 $ echo "changegroup = printenv.py changegroup" >> .hg/hgrc
191 $ echo "changegroup = printenv.py changegroup" >> .hg/hgrc
192 $ hg pull $DISABLECACERTS
192 $ hg pull $DISABLECACERTS
193 pulling from https://localhost:$HGPORT/
193 pulling from https://localhost:$HGPORT/
194 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
194 abort: unable to verify security of localhost (no loaded CA certificates); refusing to connect
195 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
195 (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server)
196 [255]
196 [255]
197
197
198 $ hg pull --insecure
198 $ hg pull --insecure
199 pulling from https://localhost:$HGPORT/
199 pulling from https://localhost:$HGPORT/
200 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
200 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
201 searching for changes
201 searching for changes
202 adding changesets
202 adding changesets
203 adding manifests
203 adding manifests
204 adding file changes
204 adding file changes
205 added 1 changesets with 1 changes to 1 files
205 added 1 changesets with 1 changes to 1 files
206 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=https://localhost:$HGPORT/ (glob)
206 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=https://localhost:$HGPORT/ (glob)
207 (run 'hg update' to get a working copy)
207 (run 'hg update' to get a working copy)
208 $ cd ..
208 $ cd ..
209
209
210 cacert configured in local repo
210 cacert configured in local repo
211
211
212 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
212 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
213 $ echo "[web]" >> copy-pull/.hg/hgrc
213 $ echo "[web]" >> copy-pull/.hg/hgrc
214 $ echo "cacerts=$CERTSDIR/pub.pem" >> copy-pull/.hg/hgrc
214 $ echo "cacerts=$CERTSDIR/pub.pem" >> copy-pull/.hg/hgrc
215 $ hg -R copy-pull pull --traceback
215 $ hg -R copy-pull pull --traceback
216 pulling from https://localhost:$HGPORT/
216 pulling from https://localhost:$HGPORT/
217 searching for changes
217 searching for changes
218 no changes found
218 no changes found
219 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
219 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
220
220
221 cacert configured globally, also testing expansion of environment
221 cacert configured globally, also testing expansion of environment
222 variables in the filename
222 variables in the filename
223
223
224 $ echo "[web]" >> $HGRCPATH
224 $ echo "[web]" >> $HGRCPATH
225 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
225 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
226 $ P="$CERTSDIR" hg -R copy-pull pull
226 $ P="$CERTSDIR" hg -R copy-pull pull
227 pulling from https://localhost:$HGPORT/
227 pulling from https://localhost:$HGPORT/
228 searching for changes
228 searching for changes
229 no changes found
229 no changes found
230 $ P="$CERTSDIR" hg -R copy-pull pull --insecure
230 $ P="$CERTSDIR" hg -R copy-pull pull --insecure
231 pulling from https://localhost:$HGPORT/
231 pulling from https://localhost:$HGPORT/
232 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
232 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
233 searching for changes
233 searching for changes
234 no changes found
234 no changes found
235
235
236 empty cacert file
236 empty cacert file
237
237
238 $ touch emptycafile
238 $ touch emptycafile
239
239
240 #if sslcontext
240 #if sslcontext
241 $ hg --config web.cacerts=emptycafile -R copy-pull pull
241 $ hg --config web.cacerts=emptycafile -R copy-pull pull
242 pulling from https://localhost:$HGPORT/
242 pulling from https://localhost:$HGPORT/
243 abort: error loading CA file emptycafile: * (glob)
243 abort: error loading CA file emptycafile: * (glob)
244 (file is empty or malformed?)
244 (file is empty or malformed?)
245 [255]
245 [255]
246 #else
246 #else
247 $ hg --config web.cacerts=emptycafile -R copy-pull pull
247 $ hg --config web.cacerts=emptycafile -R copy-pull pull
248 pulling from https://localhost:$HGPORT/
248 pulling from https://localhost:$HGPORT/
249 abort: error: * (glob)
249 abort: error: * (glob)
250 [255]
250 [255]
251 #endif
251 #endif
252
252
253 cacert mismatch
253 cacert mismatch
254
254
255 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
255 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
256 > https://127.0.0.1:$HGPORT/
256 > https://127.0.0.1:$HGPORT/
257 pulling from https://127.0.0.1:$HGPORT/ (glob)
257 pulling from https://127.0.0.1:$HGPORT/ (glob)
258 abort: 127.0.0.1 certificate error: certificate is for localhost (glob)
258 abort: 127.0.0.1 certificate error: certificate is for localhost (glob)
259 (set hostsecurity.127.0.0.1:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely) (glob)
259 (set hostsecurity.127.0.0.1:certfingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e config setting or use --insecure to connect insecurely) (glob)
260 [255]
260 [255]
261 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
261 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub.pem" \
262 > https://127.0.0.1:$HGPORT/ --insecure
262 > https://127.0.0.1:$HGPORT/ --insecure
263 pulling from https://127.0.0.1:$HGPORT/ (glob)
263 pulling from https://127.0.0.1:$HGPORT/ (glob)
264 warning: connection security to 127.0.0.1 is disabled per current settings; communication is susceptible to eavesdropping and tampering (glob)
264 warning: connection security to 127.0.0.1 is disabled per current settings; communication is susceptible to eavesdropping and tampering (glob)
265 searching for changes
265 searching for changes
266 no changes found
266 no changes found
267 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem"
267 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem"
268 pulling from https://localhost:$HGPORT/
268 pulling from https://localhost:$HGPORT/
269 abort: error: *certificate verify failed* (glob)
269 abort: error: *certificate verify failed* (glob)
270 [255]
270 [255]
271 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem" \
271 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-other.pem" \
272 > --insecure
272 > --insecure
273 pulling from https://localhost:$HGPORT/
273 pulling from https://localhost:$HGPORT/
274 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
274 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
275 searching for changes
275 searching for changes
276 no changes found
276 no changes found
277
277
278 Test server cert which isn't valid yet
278 Test server cert which isn't valid yet
279
279
280 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
280 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
281 $ cat hg1.pid >> $DAEMON_PIDS
281 $ cat hg1.pid >> $DAEMON_PIDS
282 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-not-yet.pem" \
282 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-not-yet.pem" \
283 > https://localhost:$HGPORT1/
283 > https://localhost:$HGPORT1/
284 pulling from https://localhost:$HGPORT1/
284 pulling from https://localhost:$HGPORT1/
285 abort: error: *certificate verify failed* (glob)
285 abort: error: *certificate verify failed* (glob)
286 [255]
286 [255]
287
287
288 Test server cert which no longer is valid
288 Test server cert which no longer is valid
289
289
290 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
290 $ hg serve -R test -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
291 $ cat hg2.pid >> $DAEMON_PIDS
291 $ cat hg2.pid >> $DAEMON_PIDS
292 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-expired.pem" \
292 $ hg -R copy-pull pull --config web.cacerts="$CERTSDIR/pub-expired.pem" \
293 > https://localhost:$HGPORT2/
293 > https://localhost:$HGPORT2/
294 pulling from https://localhost:$HGPORT2/
294 pulling from https://localhost:$HGPORT2/
295 abort: error: *certificate verify failed* (glob)
295 abort: error: *certificate verify failed* (glob)
296 [255]
296 [255]
297
297
298 Fingerprints
298 Fingerprints
299
299
300 - works without cacerts (hostkeyfingerprints)
300 - works without cacerts (hostkeyfingerprints)
301 $ hg -R copy-pull id https://localhost:$HGPORT/ --insecure --config hostfingerprints.localhost=ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
301 $ hg -R copy-pull id https://localhost:$HGPORT/ --insecure --config hostfingerprints.localhost=ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
302 5fed3813f7f5
302 5fed3813f7f5
303
303
304 - works without cacerts (hostsecurity)
304 - works without cacerts (hostsecurity)
305 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
305 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
306 5fed3813f7f5
306 5fed3813f7f5
307
307
308 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e
308 $ hg -R copy-pull id https://localhost:$HGPORT/ --config hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e
309 5fed3813f7f5
309 5fed3813f7f5
310
310
311 - multiple fingerprints specified and first matches
311 - multiple fingerprints specified and first matches
312 $ hg --config 'hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
312 $ hg --config 'hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
313 5fed3813f7f5
313 5fed3813f7f5
314
314
315 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
315 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03, sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
316 5fed3813f7f5
316 5fed3813f7f5
317
317
318 - multiple fingerprints specified and last matches
318 - multiple fingerprints specified and last matches
319 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/ --insecure
319 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/ --insecure
320 5fed3813f7f5
320 5fed3813f7f5
321
321
322 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/
322 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03' -R copy-pull id https://localhost:$HGPORT/
323 5fed3813f7f5
323 5fed3813f7f5
324
324
325 - multiple fingerprints specified and none match
325 - multiple fingerprints specified and none match
326
326
327 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
327 $ hg --config 'hostfingerprints.localhost=deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/ --insecure
328 abort: certificate for localhost has unexpected fingerprint ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
328 abort: certificate for localhost has unexpected fingerprint ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
329 (check hostfingerprint configuration)
329 (check hostfingerprint configuration)
330 [255]
330 [255]
331
331
332 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
332 $ hg --config 'hostsecurity.localhost:fingerprints=sha1:deadbeefdeadbeefdeadbeefdeadbeefdeadbeef, sha1:aeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' -R copy-pull id https://localhost:$HGPORT/
333 abort: certificate for localhost has unexpected fingerprint sha1:ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
333 abort: certificate for localhost has unexpected fingerprint sha1:ec:d8:7c:d6:b3:86:d0:4f:c1:b8:b4:1c:9d:8f:5e:16:8e:ef:1c:03
334 (check hostsecurity configuration)
334 (check hostsecurity configuration)
335 [255]
335 [255]
336
336
337 - fails when cert doesn't match hostname (port is ignored)
337 - fails when cert doesn't match hostname (port is ignored)
338 $ hg -R copy-pull id https://localhost:$HGPORT1/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
338 $ hg -R copy-pull id https://localhost:$HGPORT1/ --config hostfingerprints.localhost=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
339 abort: certificate for localhost has unexpected fingerprint f4:2f:5a:0c:3e:52:5b:db:e7:24:a8:32:1d:18:97:6d:69:b5:87:84
339 abort: certificate for localhost has unexpected fingerprint f4:2f:5a:0c:3e:52:5b:db:e7:24:a8:32:1d:18:97:6d:69:b5:87:84
340 (check hostfingerprint configuration)
340 (check hostfingerprint configuration)
341 [255]
341 [255]
342
342
343
343
344 - ignores that certificate doesn't match hostname
344 - ignores that certificate doesn't match hostname
345 $ hg -R copy-pull id https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
345 $ hg -R copy-pull id https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
346 5fed3813f7f5
346 5fed3813f7f5
347
347
348 HGPORT1 is reused below for tinyproxy tests. Kill that server.
348 HGPORT1 is reused below for tinyproxy tests. Kill that server.
349 $ killdaemons.py hg1.pid
349 $ killdaemons.py hg1.pid
350
350
351 Prepare for connecting through proxy
351 Prepare for connecting through proxy
352
352
353 $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 &
353 $ tinyproxy.py $HGPORT1 localhost >proxy.log </dev/null 2>&1 &
354 $ while [ ! -f proxy.pid ]; do sleep 0; done
354 $ while [ ! -f proxy.pid ]; do sleep 0; done
355 $ cat proxy.pid >> $DAEMON_PIDS
355 $ cat proxy.pid >> $DAEMON_PIDS
356
356
357 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
357 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
358 $ echo "always=True" >> copy-pull/.hg/hgrc
358 $ echo "always=True" >> copy-pull/.hg/hgrc
359 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
359 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
360 $ echo "localhost =" >> copy-pull/.hg/hgrc
360 $ echo "localhost =" >> copy-pull/.hg/hgrc
361
361
362 Test unvalidated https through proxy
362 Test unvalidated https through proxy
363
363
364 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback
364 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback
365 pulling from https://localhost:$HGPORT/
365 pulling from https://localhost:$HGPORT/
366 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
366 warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
367 searching for changes
367 searching for changes
368 no changes found
368 no changes found
369
369
370 Test https with cacert and fingerprint through proxy
370 Test https with cacert and fingerprint through proxy
371
371
372 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
372 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
373 > --config web.cacerts="$CERTSDIR/pub.pem"
373 > --config web.cacerts="$CERTSDIR/pub.pem"
374 pulling from https://localhost:$HGPORT/
374 pulling from https://localhost:$HGPORT/
375 searching for changes
375 searching for changes
376 no changes found
376 no changes found
377 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
377 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://127.0.0.1:$HGPORT/ --config hostfingerprints.127.0.0.1=ecd87cd6b386d04fc1b8b41c9d8f5e168eef1c03
378 pulling from https://127.0.0.1:$HGPORT/ (glob)
378 pulling from https://127.0.0.1:$HGPORT/ (glob)
379 searching for changes
379 searching for changes
380 no changes found
380 no changes found
381
381
382 Test https with cert problems through proxy
382 Test https with cert problems through proxy
383
383
384 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
384 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
385 > --config web.cacerts="$CERTSDIR/pub-other.pem"
385 > --config web.cacerts="$CERTSDIR/pub-other.pem"
386 pulling from https://localhost:$HGPORT/
386 pulling from https://localhost:$HGPORT/
387 abort: error: *certificate verify failed* (glob)
387 abort: error: *certificate verify failed* (glob)
388 [255]
388 [255]
389 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
389 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull \
390 > --config web.cacerts="$CERTSDIR/pub-expired.pem" https://localhost:$HGPORT2/
390 > --config web.cacerts="$CERTSDIR/pub-expired.pem" https://localhost:$HGPORT2/
391 pulling from https://localhost:$HGPORT2/
391 pulling from https://localhost:$HGPORT2/
392 abort: error: *certificate verify failed* (glob)
392 abort: error: *certificate verify failed* (glob)
393 [255]
393 [255]
394
394
395
395
396 $ killdaemons.py hg0.pid
396 $ killdaemons.py hg0.pid
397
397
398 #if sslcontext
398 #if sslcontext
399
399
400 Start patched hgweb that requires client certificates:
400 Start hgweb that requires client certificates:
401
401
402 $ cat << EOT > reqclientcert.py
403 > import ssl
404 > from mercurial.hgweb import server
405 > class _httprequesthandlersslclientcert(server._httprequesthandlerssl):
406 > @staticmethod
407 > def preparehttpserver(httpserver, ui):
408 > certfile = ui.config('web', 'certificate')
409 > sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
410 > sslcontext.verify_mode = ssl.CERT_REQUIRED
411 > sslcontext.load_cert_chain(certfile)
412 > # verify clients by server certificate
413 > sslcontext.load_verify_locations(certfile)
414 > httpserver.socket = sslcontext.wrap_socket(httpserver.socket,
415 > server_side=True)
416 > server._httprequesthandlerssl = _httprequesthandlersslclientcert
417 > EOT
418 $ cd test
402 $ cd test
419 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
403 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV \
420 > --config extensions.reqclientcert=../reqclientcert.py
404 > --config devel.servercafile=$PRIV --config devel.serverrequirecert=true
421 $ cat ../hg0.pid >> $DAEMON_PIDS
405 $ cat ../hg0.pid >> $DAEMON_PIDS
422 $ cd ..
406 $ cd ..
423
407
424 without client certificate:
408 without client certificate:
425
409
426 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
410 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/
427 abort: error: *handshake failure* (glob)
411 abort: error: *handshake failure* (glob)
428 [255]
412 [255]
429
413
430 with client certificate:
414 with client certificate:
431
415
432 $ cat << EOT >> $HGRCPATH
416 $ cat << EOT >> $HGRCPATH
433 > [auth]
417 > [auth]
434 > l.prefix = localhost
418 > l.prefix = localhost
435 > l.cert = $CERTSDIR/client-cert.pem
419 > l.cert = $CERTSDIR/client-cert.pem
436 > l.key = $CERTSDIR/client-key.pem
420 > l.key = $CERTSDIR/client-key.pem
437 > EOT
421 > EOT
438
422
439 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
423 $ P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
440 > --config auth.l.key="$CERTSDIR/client-key-decrypted.pem"
424 > --config auth.l.key="$CERTSDIR/client-key-decrypted.pem"
441 5fed3813f7f5
425 5fed3813f7f5
442
426
443 $ printf '1234\n' | env P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
427 $ printf '1234\n' | env P="$CERTSDIR" hg id https://localhost:$HGPORT/ \
444 > --config ui.interactive=True --config ui.nontty=True
428 > --config ui.interactive=True --config ui.nontty=True
445 passphrase for */client-key.pem: 5fed3813f7f5 (glob)
429 passphrase for */client-key.pem: 5fed3813f7f5 (glob)
446
430
447 $ env P="$CERTSDIR" hg id https://localhost:$HGPORT/
431 $ env P="$CERTSDIR" hg id https://localhost:$HGPORT/
448 abort: error: * (glob)
432 abort: error: * (glob)
449 [255]
433 [255]
450
434
451 #endif
435 #endif
General Comments 0
You need to be logged in to leave comments. Login now