From 779cb0331be779e19d721b37f4518bd36c82f63a Mon Sep 17 00:00:00 2001 From: Honza Bambas Date: Fri, 5 Sep 2008 09:35:58 -0400 Subject: [PATCH] bug 428009 - hook up ssltunnel to mochitest. r=waldo,kaie --- build/Makefile.in | 4 + build/pgo/Makefile.in | 18 +- build/pgo/automation.py.in | 126 ++++- build/pgo/certs/Makefile.in | 65 +++ build/pgo/certs/pgoca.ca | 15 + build/pgo/certs/pgoca.p12 | Bin 0 -> 1758 bytes build/pgo/genpgocert.py.in | 214 ++++++++ build/pgo/server-locations.txt | 38 +- security/manager/Makefile.in | 10 +- testing/mochitest/Makefile.in | 6 + testing/mochitest/server.js | 2 +- testing/mochitest/ssltunnel/ssltunnel.cpp | 565 ++++++++++++++++++---- toolkit/mozapps/installer/packager.mk | 2 + 13 files changed, 949 insertions(+), 116 deletions(-) create mode 100644 build/pgo/certs/Makefile.in create mode 100644 build/pgo/certs/pgoca.ca create mode 100644 build/pgo/certs/pgoca.p12 create mode 100644 build/pgo/genpgocert.py.in diff --git a/build/Makefile.in b/build/Makefile.in index afd27a45db75..8d161337e008 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -79,9 +79,13 @@ browser_path = \"$(DIST)/bin/$(PROGRAM)\" endif endif +_CERTS_DIR = _profile/pgo/certs + AUTOMATION_PPARGS = \ -DBROWSER_PATH=$(browser_path) \ -DXPC_BIN_PATH=\"$(DIST)/bin\" \ + -DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \ + -DCERTS_DIR=\"../$(_CERTS_DIR)\" \ $(NULL) ifeq ($(OS_ARCH),Darwin) diff --git a/build/pgo/Makefile.in b/build/pgo/Makefile.in index c6deb501c53e..36c147818ecf 100644 --- a/build/pgo/Makefile.in +++ b/build/pgo/Makefile.in @@ -43,20 +43,27 @@ VPATH = @srcdir@ relativesrcdir = build/pgo include $(DEPTH)/config/autoconf.mk + +DIRS = \ + certs \ + $(NULL) + include $(topsrcdir)/config/rules.mk # Stuff to make a build with a profile _PROFILE_DIR = $(DEPTH)/_profile/pgo +_CERTS_DIR = $(_PROFILE_DIR)/certs +_CERTS_SRC_DIR = $(srcdir)/certs _PGO_FILES = \ automation.py \ profileserver.py \ + genpgocert.py \ index.html \ quit.js \ server-locations.txt \ $(NULL) - ifeq ($(USE_SHORT_LIBNAME), 1) PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX) else @@ -80,6 +87,9 @@ endif AUTOMATION_PPARGS = \ -DBROWSER_PATH=$(browser_path) \ -DXPC_BIN_PATH=\"$(DIST)/bin\" \ + -DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \ + -DCERTS_DIR=\"$(_CERTS_DIR)\" \ + -DCERTS_SRC_DIR=\"$(_CERTS_SRC_DIR)\" \ $(NULL) ifeq ($(OS_ARCH),Darwin) @@ -102,11 +112,15 @@ automation.py: automation.py.in $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@ +genpgocert.py: genpgocert.py.in + $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ + $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@ + profileserver.py: profileserver.py.in $(PYTHON) $(topsrcdir)/config/Preprocessor.py $^ > $@ chmod +x $@ -GARBAGE += automation.py profileserver.py +GARBAGE += automation.py profileserver.py genpgocert.py libs:: $(_PGO_FILES) $(INSTALL) $^ $(_PROFILE_DIR) diff --git a/build/pgo/automation.py.in b/build/pgo/automation.py.in index c49901869ca2..645c299bd53d 100644 --- a/build/pgo/automation.py.in +++ b/build/pgo/automation.py.in @@ -53,6 +53,8 @@ Runs the browser from a script, and provides useful utilities for setting up the browser environment. """ +SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) + __all__ = [ "UNIXISH", "IS_WIN32", @@ -62,6 +64,7 @@ __all__ = [ "initializeProfile", "DIST_BIN", "DEFAULT_APP", + "environment", ] # These are generated in mozilla/build/Makefile.in @@ -74,10 +77,12 @@ __all__ = [ IS_CYGWIN = False #endif #expand IS_CAMINO = __IS_CAMINO__ != 0 +#expand BIN_SUFFIX = __BIN_SUFFIX__ UNIXISH = not IS_WIN32 and not IS_MAC #expand DEFAULT_APP = "./" + __BROWSER_PATH__ +#expand CERTS_DIR = __CERTS_DIR__ ########### # LOGGING # @@ -103,7 +108,7 @@ class Process: non-Windows platforms. :-( """ - def __init__(self, command, args, env): + def __init__(self, command, args, env, inputdata = None): """ Creates a process representing the execution of the given command, which must be an absolute path, with the given arguments in the given environment. @@ -111,24 +116,40 @@ class Process: """ command = os.path.abspath(command) if IS_WIN32: + import tempfile import subprocess + + if inputdata: + inputfile = tempfile.TemporaryFile() + inputfile.write(inputdata) + inputfile.seek(0) + else: + inputfile = None + cmd = [command] cmd.extend(args) p = subprocess.Popen(cmd, env = env, stdout = subprocess.PIPE, - stderr = subprocess.STDOUT) + stderr = subprocess.STDOUT, + stdin = inputfile) self._out = p.stdout else: import popen2 cmd = [] - for (k, v) in env.iteritems(): - cmd.append(k + "='" + v + "' ") + if env: + for (k, v) in env.iteritems(): + cmd.append(k + "='" + v + "' ") + cmd.append("'" + command + "'") cmd.extend(map(lambda x: "'" + x + "'", args)) cmd = " ".join(cmd) p = popen2.Popen4(cmd) self._out = p.fromchild + if inputdata: + p.tochild.write(inputdata) + p.tochild.close() + self._process = p self.pid = p.pid @@ -165,8 +186,13 @@ class Process: def kill(self): "Kills this process." try: - if not IS_WIN32: # XXX + if not IS_WIN32: os.kill(self._process.pid, signal.SIGKILL) + else: + import subprocess + pid = "%i" % self.pid + process = subprocess.Popen(["taskkill", "/F", "/PID", pid]) + process.wait() except: pass @@ -201,13 +227,13 @@ class Location: self.options = options -def readLocations(): +def readLocations(locationsPath = "server-locations.txt"): """ Reads the locations at which the Mochitest HTTP server is available from server-locations.txt. """ - locationFile = codecs.open("server-locations.txt", "r", "UTF-8") + locationFile = codecs.open(locationsPath, "r", "UTF-8") # Perhaps more detail than necessary, but it's the easiest way to make sure # we get exactly the format we want. See server-locations.txt for the exact @@ -224,7 +250,7 @@ def readLocations(): r"(?P\d+)" r"(?:" r"\s+" - r"(?P\w+(?:,\w+)*)" + r"(?P\S+(?:,\S+)*)" r")?$") locations = [] lineno = 0 @@ -320,13 +346,20 @@ function FindProxyForURL(url, host) if (!matches) return 'DIRECT'; var isHttp = matches[1] == 'http'; + var isHttps = matches[1] == 'https'; if (!matches[3]) - matches[3] = isHttp ? '80' : '443'; + { + if (isHttp) matches[3] = '80'; + if (isHttps) matches[3] = '443'; + } + var origin = matches[1] + '://' + matches[2] + ':' + matches[3]; if (origins.indexOf(origin) < 0) return 'DIRECT'; if (isHttp) - return 'PROXY localhost:8888'; + return 'PROXY 127.0.0.1:8888'; + if (isHttps) + return 'PROXY 127.0.0.1:4443'; return 'DIRECT'; }""" % { "origins": origins } pacURL = "".join(pacURL.splitlines()) @@ -344,12 +377,81 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t prefsFile.write("".join(prefs)) prefsFile.close() +def fillCertificateDB(profileDir): + + pwfilePath = os.path.join(profileDir, ".crtdbpw") + + pwfile = open(pwfilePath, "w") + pwfile.write("\n") + pwfile.close() + + # Create head of the ssltunnel configuration file + sslTunnelConfigPath = os.path.join(CERTS_DIR, "ssltunnel.cfg") + sslTunnelConfig = open(sslTunnelConfigPath, "w") + + sslTunnelConfig.write("httpproxy:1\n") + sslTunnelConfig.write("certdbdir:%s\n" % CERTS_DIR) + sslTunnelConfig.write("forward:127.0.0.1:8888\n") + sslTunnelConfig.write("listen:*:4443:pgo server certificate\n") + + # Generate automatic certificate and bond custom certificates + locations = readLocations() + locations.pop(0) + for loc in locations: + if loc.scheme == "https" and "nocert" not in loc.options: + customCertRE = re.compile("^cert=(?P[0-9a-zA-Z_ ]+)") + for option in loc.options: + match = customCertRE.match(option) + if match: + customcert = match.group("nickname"); + sslTunnelConfig.write("listen:%s:%s:4443:%s\n" % (loc.host, loc.port, customcert)) + break + + sslTunnelConfig.close() + + # Pre-create the certification database for the profile + certutil = DIST_BIN + "/certutil" + BIN_SUFFIX + status = Process(certutil, ["-N", "-d", profileDir, "-f", pwfilePath], environment()).wait() + if status != 0: + return status + + # Walk the cert directory and add custom CAs as trusted + files = os.listdir(CERTS_DIR) + for item in files: + root, ext = os.path.splitext(item) + if ext == ".ca": + Process(certutil, ["-A", "-i", os.path.join(CERTS_DIR, item), "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", "CT,,"], environment()) + + os.unlink(pwfilePath) + return 0 + +def environment(env = None): + if env == None: + env = dict(os.environ) + + if UNIXISH: + ldLibraryPath = os.path.join(SCRIPT_DIR, DIST_BIN) + if "LD_LIBRARY_PATH" in env: + ldLibraryPath = ldLibraryPath + ":" + env["LD_LIBRARY_PATH"] + env["LD_LIBRARY_PATH"] = ldLibraryPath + + return env ############### # RUN THE APP # ############### def runApp(testURL, env, app, profileDir, extraArgs): + # create certificate database for the profile + certificateStatus = fillCertificateDB(profileDir) + if certificateStatus != 0: + log.info("ERROR FAIL Certificate integration") + return certificateStatus + + ssltunnel = DIST_BIN + "/ssltunnel" + BIN_SUFFIX + ssltunnelProcess = Process(ssltunnel, [os.path.join(CERTS_DIR, "ssltunnel.cfg")], environment()) + log.info("SSL tunnel pid: %d", ssltunnelProcess.pid) + "Run the app, returning the time at which it was started." # mark the start start = datetime.now() @@ -375,10 +477,12 @@ def runApp(testURL, env, app, profileDir, extraArgs): else: args.append((testURL)) args.extend(extraArgs) - proc = Process(cmd, args, env = env) + proc = Process(cmd, args, env = environment(env)) log.info("Application pid: %d", proc.pid) status = proc.wait() if status != 0: log.info("ERROR FAIL Exited with code %d during test run", status) + ssltunnelProcess.kill() + return start diff --git a/build/pgo/certs/Makefile.in b/build/pgo/certs/Makefile.in new file mode 100644 index 000000000000..bb61f748e18c --- /dev/null +++ b/build/pgo/certs/Makefile.in @@ -0,0 +1,65 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla test code +# +# The Initial Developer of the Original Code is +# Mozilla Foundation +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Honza Bambas +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +_PROFILE_DIR = $(DEPTH)/_profile/pgo +_CERTS_DIR = $(_PROFILE_DIR)/certs + +# Extension of files must be '.server' +_SERVER_CERTS = \ + $(NULL) + +# Extension of files must be '.ca' +_CERT_AUTHORITIES = \ + pgoca.ca \ + $(NULL) + +_SERV_FILES = \ + pgoca.p12 \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + +libs:: $(_SERV_FILES) $(_SERVER_CERTS) $(_CERT_AUTHORITIES) + $(INSTALL) $^ $(_CERTS_DIR) diff --git a/build/pgo/certs/pgoca.ca b/build/pgo/certs/pgoca.ca new file mode 100644 index 000000000000..65fb8a589f1a --- /dev/null +++ b/build/pgo/certs/pgoca.ca @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICXTCCAcagAwIBAgIBATANBgkqhkiG9w0BAQUFADBqMSQwIgYDVQQLExtQcm9m +aWxlIEd1aWRlZCBPcHRpbWl6YXRpb24xGDAWBgNVBAoTD01vemlsbGEgVGVzdGlu +ZzEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0w +ODA1MjIwMDM4MDVaFw0xODA1MjIwMDM4MDVaMGoxJDAiBgNVBAsTG1Byb2ZpbGUg +R3VpZGVkIE9wdGltaXphdGlvbjEYMBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSgw +JgYDVQQDEx9UZW1wb3JhcnkgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQDg6iipAXGZYmgTcHfx8M2hcLqmqDalcj7sZ1A7 +a3LiCBb+1uHKKy9hUxRUe61aJF4NgMAF5oc+HpXN0hpvkiNHxqqD7R6hrkP3gAJ3 +eczEFKsFUI6AqaCL0+xpyhaaZmmarcHxU+PL2h5zq6VssxfBAsO0DkzWzk6E8vM+ +jrku7QIDAQABoxMwETAPBgNVHRMECDAGAQH/AgEAMA0GCSqGSIb3DQEBBQUAA4GB +ALPbn3Ztg0m8qDt8Vkf5You6HEqIxZe+ffDTrfq/L7ofHk/OXEpL7OWKRHU33pNG +QS8khBG+sO461C51s6u9giW+eq2PaQv2HGASBpDbvPqc/Hf+zupZsdsXzHv6rt0V +lu5B6nOpMse1nhA494i1ARSuBNzLv5mas38YWG8Rr6jR +-----END CERTIFICATE----- diff --git a/build/pgo/certs/pgoca.p12 b/build/pgo/certs/pgoca.p12 new file mode 100644 index 0000000000000000000000000000000000000000..4867c286bbf6950a51078caedff875a945b2d93c GIT binary patch literal 1758 zcmZvbdpOgJAIHC&S!~SxlFLM{Aya0Qm3y+eB$qjIiwUiUgygb~M6{C_nad^)HYd46 zoXssFDj^jPKPfTlsD~56{EqYc{rWxqp6B;@p7-Z@KCkEbeE#~pP!upo1Vw?0AINaZ zSeFFt0kJ9C6bKD^4Mq7sSOkfpiKy&G5eom$h6D@*X4Am;P&6ke6RNZOPJ_ElpTr)UP8iqU3B@9u?V>R`c zY>h3Wy<~3D^;HLQ*UvL@PE@8%#!2gDZIF)B9+(NA7rXNuMb!Gk^&3m}$I^jw| zP4h2(i@Kt3*L7J%W3vQ%xcp@4*)z*6_vL!6kqjaeibzh3bkSMH zHeZjs=rrXc8IRcR-Qo2=%}mx`Ec2Bx!8O!H{E@rV?k+MhC2-gX#WHwcEvvkWtQr`uH{SdBNK{2?tP}DLDeq1_B#b$6{R3G ztK1Woo6AZNi%8eKB{S3)C!>u$FRa*)%x&JcQpz?`kRtU1I^0L{jcT;Zr`Mjy@<#|< zILpc)<9xzEI29lCK3#kVlbRRM@T%o&w`kOtkPsV{xr4sh0wQg2vOJA$S=DFVT=Ayo z&|zkau}CY~DF(4@=U_44Q+Gr07jmO8@=L;0I!LU_?G*R)uk<_Gw$t1pLU$X0bLd_Z zV|8>WU!|&>-yFW1^1s z%@r7SaC4qWn62Gx4%mwu3)MNk+(5EcC|JJcS2U)P(zR0R-cJbu>k&6 zKZ*lo?|K9V0Y5U(|9ArZ+mmooPn{3!ij?|fYjk^N6m!=T_O6CtLBG=<`hIhTv$-zL z3wp#?n6}w84bcVsjN8PPv*mPzv|4tP>57zKLx3utO177XbTlvEyqe^>u)uSsXbHgD z)7_^2oQ|wt|K32w4KwskC|SAc+e3TH;ofR5eTEkgC0}XeDQB;GG$zX+8IY^d@A@o( zjrjKHaYIjQjjYtpc-$YeB%9+3@!Z}6%6tabH0hvnoJCWt>Ues~$EZu4i){ZT{&v{IyF@S`tU zz<3AO(z&L5bZoUX^K57jcq)Eg^o2q|^5bS-WVgupv@-8CQ%*dl^0>(mI~ZBpp|h5^ zRDxMWZfR*VHTOM0WDe4dM_CrQR7Ib{+XGmZf*^~~R~Rh*JG~Dc2L@@95_iI=Zswfn z+IRAZfNxTEJ=`~KeC)HRZ0ah@N7uuO5 zlM8=vV>9XJ-!A8uDyM(Lg3b$BxXbD7kq!gf8W?_553*D^5e^oDQREc&&}f#vadTgrgYy)WK`t2VCaCopWyLNQwu-Q zEWm5`7%PKT*yccQVc-&s1%NKm@phdZ(ub4Ur>yC3DT^*)1w-2KJZ)7%ZKNbo`=Ocd z*h}O{5WzHT&#eVLi2yez!%G8)R1EN@>?+W+>A5$W_N><4wboWP($ zAw=xh;`7c!7c4OUcId~BLZdWM;!sgTga}wg6oANI8uiwS%^X~pSYlc?y^avT +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +import automation +import os +import re +import sys + +#expand DIST_BIN = "./" + __XPC_BIN_PATH__ +#expand BIN_SUFFIX = __BIN_SUFFIX__ +#expand CERTS_DIR = __CERTS_DIR__ +#expand CERTS_SRC_DIR = __CERTS_SRC_DIR__ + +dbFiles = [ + re.compile("^cert[0-9]+\.db$"), + re.compile("^key[0-9]+\.db$"), + re.compile("^secmod\.db$") +] + +def unlinkDbFiles(path): + for root, dirs, files in os.walk(path): + for name in files: + for dbFile in dbFiles: + if dbFile.match(name) and os.path.exists(os.path.join(root, name)): + os.unlink(os.path.join(root, name)) + + +def runUtil(util, args, inputdata = None): + proc = automation.Process(util, args, automation.environment(), inputdata) + return proc.wait() + + +def createRandomFile(randomFile): + import random + file = open(randomFile, "wb"); + for count in xrange(0, 2048): + file.write(chr(random.randint(0, 255))) + file.close() + + +def createCertificateAuthority(dbDir, srcDir): + certutil = DIST_BIN + "/certutil" + BIN_SUFFIX + pk12util = DIST_BIN + "/pk12util" + BIN_SUFFIX + + tempDbDir = os.path.join(dbDir, ".temp") + if not os.path.exists(tempDbDir): + os.mkdir(tempDbDir) + + pwfilePath = os.path.join(tempDbDir, ".crtdbpw") + rndfilePath = os.path.join(tempDbDir, ".rndfile") + pgoCAModulePathSrc = os.path.join(srcDir, "pgoca.p12") + pgoCAPathSrc = os.path.join(srcDir, "pgoca.ca") + pgoCAModulePath = os.path.join(srcDir, "pgoca.p12") + pgoCAPath = os.path.join(srcDir, "pgoca.ca") + + pwfile = open(pwfilePath, "w") + pwfile.write("\n") + pwfile.close() + + unlinkDbFiles(tempDbDir) + + # Create temporary certification database for CA generation + status = runUtil(certutil, ["-N", "-d", tempDbDir, "-f", pwfilePath]) + if status != 0: + return status + + createRandomFile(rndfilePath); + status = runUtil(certutil, ["-S", "-d", tempDbDir, "-s", "CN=Temporary Certificate Authority, O=Mozilla Testing, OU=Profile Guided Optimization", "-t", "C,,", "-x", "-m", "1", "-v", "120", "-n", "pgo temporary ca", "-2", "-f", pwfilePath, "-z", rndfilePath], "Y\n0\nN\n") + if status != 0: + return status + + status = runUtil(certutil, ["-L", "-d", tempDbDir, "-n", "pgo temporary ca", "-a", "-o", pgoCAPathSrc, "-f", pwfilePath]) + if status != 0: + return status + + status = runUtil(pk12util, ["-o", pgoCAModulePathSrc, "-n", "pgo temporary ca", "-d", tempDbDir, "-w", pwfilePath, "-k", pwfilePath]) + if status != 0: + return status + + unlinkDbFiles(tempDbDir) + os.unlink(pwfilePath) + os.unlink(rndfilePath) + os.rmdir(tempDbDir) + return 0 + + +def createSSLServerCertificate(dbDir): + certutil = DIST_BIN + "/certutil" + BIN_SUFFIX + pk12util = DIST_BIN + "/pk12util" + BIN_SUFFIX + + pwfilePath = os.path.join(dbDir, ".crtdbpw") + rndfilePath = os.path.join(dbDir, ".rndfile") + pgoCAPath = os.path.join(dbDir, "pgoca.p12") + + pwfile = open(pwfilePath, "w") + pwfile.write("\n") + pwfile.close() + + unlinkDbFiles(dbDir) + + # Create certification database for ssltunnel + status = runUtil(certutil, ["-N", "-d", dbDir, "-f", pwfilePath]) + if status != 0: + return status + + status = runUtil(pk12util, ["-i", pgoCAPath, "-w", pwfilePath, "-d", dbDir, "-k", pwfilePath]) + if status != 0: + return status + + # Generate automatic certificate + locations = automation.readLocations(os.path.join(dbDir, "../server-locations.txt")) + locations.pop(0) + locationsParam = "" + firstLocation = "" + for loc in locations: + if loc.scheme == "https" and "nocert" not in loc.options: + customCertOption = False + customCertRE = re.compile("^cert=(?:\w+)") + for option in loc.options: + match = customCertRE.match(option) + if match: + customCertOption = True + break + + if not customCertOption: + if len(locationsParam) > 0: + locationsParam += "," + locationsParam += loc.host + + if firstLocation == "": + firstLocation = loc.host + + if firstLocation == "": + print "Nothing to generate, no automatic secure hosts specified" + else: + createRandomFile(rndfilePath); + status = runUtil(certutil, ["-S", "-s", "CN=%s" % firstLocation, "-t", "Pu,,", "-c", "pgo temporary ca", "-m", "2", "-8", locationsParam, "-v", "12", "-n", "pgo server certificate", "-d", dbDir, "-z", rndfilePath, "-f", pwfilePath]) + if status != 0: + return status + + # Walk the cert directory and add what necessary + files = os.listdir(CERTS_DIR) + for item in files: + root, ext = os.path.splitext(item) + if ext == ".server": + runUtil(pk12util, ["-i", os.path.join(CERTS_DIR, item), "-d", dbDir, "-k", pwfilePath, "-w", pwfilePath]) + + os.unlink(pwfilePath) + os.unlink(rndfilePath) + return 0 + + +if len(sys.argv) == 1: + print "Specify --gen-server or --gen-ca" + sys.exit(1) + +if sys.argv[1] == "--gen-server": + certificateStatus = createSSLServerCertificate(CERTS_DIR) + if certificateStatus != 0: + print "ERROR FAIL: SSL Server Certificate generation" + + sys.exit(certificateStatus) + +if sys.argv[1] == "--gen-ca": + certificateStatus = createCertificateAuthority(CERTS_DIR, CERTS_SRC_DIR) + if certificateStatus != 0: + print "ERROR FAIL: Certificate Authority generation" + else: + print "\n\n" + print "===================================================" + print " IMPORTANT:" + print " To use this new certificate authority in tests" + print " run 'make' at testing/mochitest" + print "===================================================" + + sys.exit(certificateStatus) + +print "Invalid option specified" +sys.exit(1) diff --git a/build/pgo/server-locations.txt b/build/pgo/server-locations.txt index befdb7f0551b..e63b016d0a6a 100644 --- a/build/pgo/server-locations.txt +++ b/build/pgo/server-locations.txt @@ -52,11 +52,24 @@ # number is the default for the protocol. # # Unrecognized options are ignored. Recognized options are "primary" and -# "privileged". "primary" denotes a location which is the canonical location of +# "privileged", "nocert", "cert=some_cert_nickname". +# +# "primary" denotes a location which is the canonical location of # the server; this location is the one assumed for requests which don't -# otherwise identify a particular origin (e.g. HTTP/1.0 requests). "privileged" -# denotes a location which should have the ability to request elevated -# privileges; the default is no privileges. +# otherwise identify a particular origin (e.g. HTTP/1.0 requests). +# +# "privileged" denotes a location which should have the ability to request +# elevated privileges; the default is no privileges. +# +# "nocert" makes sense only for https:// hosts and means there is not +# any certificate automatically generated for this host. +# +# "cert=nickname" tells the pgo server to use a particular certificate +# for this host. The certificate is referenced by its nickname that must +# not contain any spaces. The certificate key files (PKCS12 modules) +# for custom certification are loaded from build/pgo/ssltunnel/certs +# directory. When new certificate is added to this dir pgo/ssltunnel +# must be builded then. # # @@ -90,6 +103,15 @@ http://sub1.test2.example.com:80 privileged http://sub2.test1.example.com:80 privileged http://sub2.test2.example.com:80 privileged +https://example.com:443 privileged +https://test1.example.com:443 privileged +https://test2.example.com:443 privileged +https://sub1.test1.example.com:443 privileged +https://sub1.test2.example.com:443 privileged +https://sub2.test1.example.com:443 privileged +https://sub2.test2.example.com:443 privileged +https://nocert.example.com:443 privileged,nocert + # # These are subdomains of <ält.example.org>. # @@ -98,6 +120,9 @@ http://sub2.xn--lt-uia.example.org:80 privileged http://xn--exmple-cua.test:80 privileged http://sub1.xn--exmple-cua.test:80 privileged +https://xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged +https://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged + # # These are subdomains of <παράδειγμα.δοκιμή>, the Greek IDN for example.test. # @@ -114,3 +139,8 @@ http://sectest1.example.org:80 privileged http://sub.sectest2.example.org:80 privileged http://sectest2.example.org:80 http://sub.sectest1.example.org:80 + +https://sectest1.example.org:443 privileged +https://sub.sectest2.example.org:443 privileged +https://sectest2.example.org:443 +https://sub.sectest1.example.org:443 diff --git a/security/manager/Makefile.in b/security/manager/Makefile.in index 86119f712729..6b29e609f1a5 100644 --- a/security/manager/Makefile.in +++ b/security/manager/Makefile.in @@ -281,8 +281,16 @@ ifndef MOZ_NATIVE_NSS $(MAKE) -C $(topsrcdir)/security/coreconf $(DEFAULT_GMAKE_FLAGS) $(MAKE) -C $(topsrcdir)/security/dbm $(DEFAULT_GMAKE_FLAGS) $(MAKE) -C $(topsrcdir)/security/nss/lib $(DEFAULT_GMAKE_FLAGS) -ifndef SKIP_CHK +ifdef ENABLE_TESTS + # Need certutil binary for mochitest certificates generation $(MAKE) -C $(topsrcdir)/security/nss/cmd/lib $(DEFAULT_GMAKE_FLAGS) + $(MAKE) -C $(topsrcdir)/security/nss/cmd/certutil $(DEFAULT_GMAKE_FLAGS) + $(MAKE) -C $(topsrcdir)/security/nss/cmd/pk12util $(DEFAULT_GMAKE_FLAGS) +endif +ifndef SKIP_CHK +ifndef ENABLE_TESTS # Just avoid secondary compile + $(MAKE) -C $(topsrcdir)/security/nss/cmd/lib $(DEFAULT_GMAKE_FLAGS) +endif $(MAKE) -C $(topsrcdir)/security/nss/cmd/shlibsign $(DEFAULT_GMAKE_FLAGS) endif $(INSTALL) -m 755 $(DIST)/lib/$(LOADABLE_ROOT_MODULE) $(DIST)/bin diff --git a/testing/mochitest/Makefile.in b/testing/mochitest/Makefile.in index 8ef7c909593b..47946da67b79 100644 --- a/testing/mochitest/Makefile.in +++ b/testing/mochitest/Makefile.in @@ -74,6 +74,7 @@ _SERV_FILES = \ _DEST_DIR = $(DEPTH)/_tests/$(relativesrcdir) +_CERTS_DIR = $(DEPTH)/_profile/pgo/certs ifeq ($(USE_SHORT_LIBNAME), 1) PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX) @@ -99,6 +100,8 @@ endif TEST_DRIVER_PPARGS = \ -DBROWSER_PATH=$(browser_path) \ -DXPC_BIN_PATH=\"../$(DIST)/bin\" \ + -DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \ + -DCERTS_DIR=\"../$(_CERTS_DIR)\" \ $(NULL) ifeq ($(OS_ARCH),Darwin) @@ -129,3 +132,6 @@ GARBAGE += runtests.py automation.py libs:: $(_SERV_FILES) $(INSTALL) $^ $(_DEST_DIR) + +libs:: + $(PYTHON) $(DEPTH)/_profile/pgo/genpgocert.py --gen-server diff --git a/testing/mochitest/server.js b/testing/mochitest/server.js index 9972128e5243..47e4537025a9 100644 --- a/testing/mochitest/server.js +++ b/testing/mochitest/server.js @@ -222,7 +222,7 @@ function processLocations(server) "(\\d+)" + "(?:" + "\\s+" + - "(\\w+(?:,\\w+)*)" + + "(\\S+(?:,\\S+)*)" + ")?$"); var line = {}; diff --git a/testing/mochitest/ssltunnel/ssltunnel.cpp b/testing/mochitest/ssltunnel/ssltunnel.cpp index 4d3627e084ca..af663cdde39e 100644 --- a/testing/mochitest/ssltunnel/ssltunnel.cpp +++ b/testing/mochitest/ssltunnel/ssltunnel.cpp @@ -50,6 +50,7 @@ #include "key.h" #include "keyt.h" #include "ssl.h" +#include "plhash.h" using std::string; using std::vector; @@ -57,8 +58,8 @@ using std::vector; // Structs for passing data into jobs on the thread pool typedef struct { PRInt32 listen_port; - PRNetAddr remote_addr; string cert_nickname; + PLHashTable* host_cert_table; } server_info_t; typedef struct { @@ -113,11 +114,16 @@ const PRInt32 DEFAULT_STACKSIZE = (512 * 1024); const PRInt32 BUF_SIZE = 4096; // global data +string nssconfigdir; +vector servers; +PRNetAddr remote_addr; PRThreadPool* threads = NULL; PRLock* shutdown_lock = NULL; PRCondVar* shutdown_condvar = NULL; // Not really used, unless something fails to start bool shutdown_server = false; +bool do_http_proxy = false; +bool any_host_cert_mapping = false; /* * Signal the main thread that the application should shut down. @@ -129,6 +135,89 @@ void SignalShutdown() PR_Unlock(shutdown_lock); } +bool ReadConnectRequest(server_info_t* server_info, + char* bufferhead, char* buffertail, PRInt32* result, string* certificate) +{ + if (buffertail - bufferhead < 4) + return false; + if (strncmp(buffertail-4, "\r\n\r\n", 4)) + return false; + + *result = 400; + + char* token; + token = strtok(bufferhead, " "); + if (!token) + return true; + if (strcmp(token, "CONNECT")) + return true; + + token = strtok(NULL, " "); + void* c = PL_HashTableLookup(server_info->host_cert_table, token); + if (c) + *certificate = (char*)c; + + token = strtok(NULL, "/"); + if (strcmp(token, "HTTP")) + return true; + + *result = 200; + return true; +} + +bool ConfigureSSLServerSocket(PRFileDesc* socket, server_info_t* si, string &certificate) +{ + const char* certnick = certificate.empty() ? + si->cert_nickname.c_str() : certificate.c_str(); + + AutoCert cert(PK11_FindCertFromNickname( + certnick, NULL)); + if (!cert) { + fprintf(stderr, "Failed to find cert %s\n", si->cert_nickname.c_str()); + return false; + } + + AutoKey privKey(PK11_FindKeyByAnyCert(cert, NULL)); + if (!privKey) { + fprintf(stderr, "Failed to find private key\n"); + return false; + } + + PRFileDesc* ssl_socket = SSL_ImportFD(NULL, socket); + if (!ssl_socket) { + fprintf(stderr, "Error importing SSL socket\n"); + return false; + } + + SSLKEAType certKEA = NSS_FindCertKEAType(cert); + if (SSL_ConfigSecureServer(ssl_socket, cert, privKey, certKEA) + != SECSuccess) { + fprintf(stderr, "Error configuring SSL server socket\n"); + return false; + } + + SSL_OptionSet(ssl_socket, SSL_SECURITY, PR_TRUE); + SSL_OptionSet(ssl_socket, SSL_HANDSHAKE_AS_CLIENT, PR_FALSE); + SSL_OptionSet(ssl_socket, SSL_HANDSHAKE_AS_SERVER, PR_TRUE); + SSL_ResetHandshake(ssl_socket, PR_TRUE); + + return true; +} + +bool ConnectSocket(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRStatus stat = PR_Connect(fd, addr, timeout); + if (stat != PR_SUCCESS) + return false; + + PRSocketOptionData option; + option.option = PR_SockOpt_Nonblocking; + option.value.non_blocking = PR_TRUE; + PR_SetSocketOption(fd, &option); + + return true; +} + /* * Handle an incoming client connection. The server thread has already * accepted the connection, so we just need to connect to the remote @@ -145,43 +234,194 @@ void PR_CALLBACK HandleConnection(void* data) AutoFD other_sock(PR_NewTCPSocket()); bool client_done = false; bool client_error = false; - PRUint8 buf[BUF_SIZE]; + bool connect_accepted = !do_http_proxy; + bool ssl_updated = !do_http_proxy; + string certificateToUse; - if (other_sock && - PR_Connect(other_sock, &ci->server_info->remote_addr, connect_timeout) - == PR_SUCCESS) { - PRInt32 bytes = PR_Recv(ci->client_sock, buf, BUF_SIZE, 0, short_timeout); - if (bytes > 0 && - PR_Send(other_sock, buf, bytes, 0, short_timeout) > 0) { - bytes = PR_Recv(other_sock, buf, BUF_SIZE, 0, short_timeout); - while (bytes > 0) { - if (PR_Send(ci->client_sock, buf, bytes, 0, short_timeout) == -1) { - client_error = true; - break; - } - if (!client_done) { - bytes = PR_Recv(ci->client_sock, buf, BUF_SIZE, 0, short_timeout); - if (bytes > 0) { - if (PR_Send(other_sock, buf, bytes, 0, short_timeout) == -1) - break; - } - else if (bytes == 0) { - client_done = true; - } - else {// error - client_error = true; - break; - } - } - bytes = PR_Recv(other_sock, buf, BUF_SIZE, 0, short_timeout); + if (other_sock) + { + PRInt32 numberOfSockets = 1; + + struct relayBuffer + { + char *buffer, *bufferhead, *buffertail, *bufferend; + relayBuffer() + { + bufferhead = buffertail = buffer = new char[BUF_SIZE]; + bufferend = buffer + BUF_SIZE; } + ~relayBuffer() + { + delete [] buffer; + } + + bool empty() + { + return bufferhead == buffertail; + } + PRInt32 free() + { + return bufferend - buffertail; + } + PRInt32 present() + { + return buffertail - bufferhead; + } + void compact() + { + if (buffertail == bufferhead) + buffertail = bufferhead = buffer; + } + } buffers[2]; + + if (!do_http_proxy) + { + if (!ConfigureSSLServerSocket(ci->client_sock, ci->server_info, certificateToUse)) + client_error = true; + else if (!ConnectSocket(other_sock, &remote_addr, connect_timeout)) + client_error = true; + else + numberOfSockets = 2; } - else if (bytes == -1) { - client_error = true; - } + + PRPollDesc sockets[2] = + { + {ci->client_sock, PR_POLL_READ, 0}, + {other_sock, PR_POLL_READ, 0} + }; + + while (!((client_error||client_done) && buffers[0].empty() && buffers[1].empty())) + { + sockets[0].in_flags |= PR_POLL_EXCEPT; + sockets[1].in_flags |= PR_POLL_EXCEPT; + PRInt32 pollStatus = PR_Poll(sockets, numberOfSockets, PR_MillisecondsToInterval(1000)); + if (pollStatus < 0) + { + client_error = true; + break; + } + + if (pollStatus == 0) + // timeout + continue; + + for (PRInt32 s = 0; s < numberOfSockets; ++s) + { + PRInt32 s2 = s == 1 ? 0 : 1; + PRInt16 out_flags = sockets[s].out_flags; + PRInt16 &in_flags = sockets[s].in_flags; + PRInt16 &in_flags2 = sockets[s2].in_flags; + sockets[s].out_flags = 0; + + if (out_flags & PR_POLL_EXCEPT) + { + client_error = true; + continue; + } // PR_POLL_EXCEPT handling + + if (out_flags & PR_POLL_READ && buffers[s].free()) + { + PRInt32 bytesRead = PR_Recv(sockets[s].fd, buffers[s].buffertail, + buffers[s].free(), 0, PR_INTERVAL_NO_TIMEOUT); + + if (bytesRead == 0) + { + client_done = true; + in_flags &= ~PR_POLL_READ; + } + else if (bytesRead < 0) + { + if (PR_GetError() != PR_WOULD_BLOCK_ERROR) + client_error = true; + } + else + { + buffers[s].buffertail += bytesRead; + + // We have to accept and handle the initial CONNECT request here + PRInt32 response; + if (!connect_accepted && ReadConnectRequest(ci->server_info, buffers[s].bufferhead, buffers[s].buffertail, + &response, &certificateToUse)) + { + // Clean the request as it would be read + buffers[s].bufferhead = buffers[s].buffertail = buffers[s].buffer; + + // Store response to the oposite buffer + if (response != 200) + { + client_done = true; + sprintf(buffers[s2].buffer, "HTTP/1.1 %d ERROR\r\nConnection: close\r\n\r\n", response); + buffers[s2].buffertail = buffers[s2].buffer + strlen(buffers[s2].buffer); + break; + } + + strcpy(buffers[s2].buffer, "HTTP/1.1 200 Connected\r\nConnection: keep-alive\r\n\r\n"); + buffers[s2].buffertail = buffers[s2].buffer + strlen(buffers[s2].buffer); + + if (!ConnectSocket(other_sock, &remote_addr, connect_timeout)) + { + client_error = true; + break; + } + + // Send the response to the client socket + in_flags |= PR_POLL_WRITE; + connect_accepted = true; + break; + } // end of CONNECT handling + + if (!buffers[s].free()) // Do not poll for read when the buffer is full + in_flags &= ~PR_POLL_READ; + + if (ssl_updated) + in_flags2 |= PR_POLL_WRITE; + } + } // PR_POLL_READ handling + + if (out_flags & PR_POLL_WRITE) + { + PRInt32 bytesWrite = PR_Send(sockets[s].fd, buffers[s2].bufferhead, + buffers[s2].present(), 0, PR_INTERVAL_NO_TIMEOUT); + + if (bytesWrite < 0) + { + if (PR_GetError() != PR_WOULD_BLOCK_ERROR) + client_error = true; + } + else + { + buffers[s2].bufferhead += bytesWrite; + if (buffers[s2].present()) + in_flags |= PR_POLL_WRITE; + else + { + if (!ssl_updated) + { + // Proxy response has just been writen, update to ssl + ssl_updated = true; + if (!ConfigureSSLServerSocket(ci->client_sock, ci->server_info, certificateToUse)) + { + client_error = true; + break; + } + + numberOfSockets = 2; + } // sslUpdate + + in_flags &= ~PR_POLL_WRITE; + in_flags2 |= PR_POLL_READ; + buffers[s2].compact(); + } + } + } // PR_POLL_WRITE handling + } // for... + } // while, poll } + else + client_error = true; + if (!client_error) - PR_Shutdown(ci->client_sock, PR_SHUTDOWN_BOTH); + PR_Shutdown(ci->client_sock, PR_SHUTDOWN_SEND); PR_Close(ci->client_sock); delete ci; @@ -198,21 +438,6 @@ void PR_CALLBACK StartServer(void* data) server_info_t* si = static_cast(data); //TODO: select ciphers? - AutoCert cert(PK11_FindCertFromNickname(si->cert_nickname.c_str(), - NULL)); - if (!cert) { - fprintf(stderr, "Failed to find cert %s\n", si->cert_nickname.c_str()); - SignalShutdown(); - return; - } - - AutoKey privKey(PK11_FindKeyByAnyCert(cert, NULL)); - if (!privKey) { - fprintf(stderr, "Failed to find private key\n"); - SignalShutdown(); - return; - } - AutoFD listen_socket(PR_NewTCPSocket()); if (!listen_socket) { fprintf(stderr, "failed to create socket\n"); @@ -234,21 +459,6 @@ void PR_CALLBACK StartServer(void* data) return; } - PRFileDesc* ssl_socket = SSL_ImportFD(NULL, listen_socket); - if (!ssl_socket) { - fprintf(stderr, "Error importing SSL socket\n"); - SignalShutdown(); - return; - } - listen_socket.reset(ssl_socket); - - if (SSL_ConfigSecureServer(listen_socket, cert, privKey, kt_rsa) - != SECSuccess) { - fprintf(stderr, "Error configuring SSL listen socket\n"); - SignalShutdown(); - return; - } - printf("Server listening on port %d with cert %s\n", si->listen_port, si->cert_nickname.c_str()); @@ -258,6 +468,12 @@ void PR_CALLBACK StartServer(void* data) // block waiting for connections ci->client_sock = PR_Accept(listen_socket, &ci->client_addr, PR_INTERVAL_NO_TIMEOUT); + + PRSocketOptionData option; + option.option = PR_SockOpt_Nonblocking; + option.value.non_blocking = PR_TRUE; + PR_SetSocketOption(ci->client_sock, &option); + if (ci->client_sock) // Not actually using this PRJob*... //PRJob* job = @@ -276,45 +492,194 @@ char* password_func(PK11SlotInfo* slot, PRBool retry, void* arg) return ""; } -int main(int argc, char** argv) +server_info_t* findServerInfo(int portnumber) { - if (argc < 6) { - fprintf(stderr, "Error: not enough arguments\n" - "Usage: ssltunnel ( )+\n" - " Provide SSL encrypted tunnels to :\n" - " from each port specified in a , pair.\n" - " must be the nickname of a server certificate\n" - " installed in the NSS db pointed to by the .\n"); - return 1; + for (vector::iterator it = servers.begin(); + it != servers.end(); it++) + { + if (it->listen_port == portnumber) + return &(*it); } + return NULL; +} - PRNetAddr remote_addr; - if (PR_StringToNetAddr(argv[2], &remote_addr) != PR_SUCCESS) { - fprintf(stderr, "Invalid remote IP address: %s\n", argv[2]); - return 1; +int processConfigLine(char* configLine) +{ + if (*configLine == 0 || *configLine == '#') + return 0; + + char* keyword = strtok(configLine, ":"); + + // Configure usage of http/ssl tunneling proxy behavior + if (!strcmp(keyword, "httpproxy")) + { + char* value = strtok(NULL, ":"); + if (!strcmp(value, "1")) + do_http_proxy = true; + + return 0; } - int port = atoi(argv[3]); - if (port <= 0) { - fprintf(stderr, "Invalid remote port: %s\n", argv[2]); - return 1; - } - remote_addr.inet.port = PR_htons(port); - - // get our list of cert:port from the remaining args - vector servers; - for (int i=4; ihost_cert_table, hostname_copy, certnick_copy); + } + else + { + server_info_t server; + server.cert_nickname = certnick; + server.listen_port = port; + server.host_cert_table = PL_NewHashTable(0, PL_HashString, PL_CompareStrings, PL_CompareStrings, NULL, NULL); + if (!server.host_cert_table) + { + fprintf(stderr, "Internal, could not create hash table\n"); + return 1; + } + servers.push_back(server); + } + + return 0; + } + + // Configure the NSS certificate database directory + if (!strcmp(keyword, "certdbdir")) + { + nssconfigdir = strtok(NULL, "\n"); + return 0; + } + + printf("Error: keyword \"%s\" unexpected\n", keyword); + return 1; +} + +int parseConfigFile(const char* filePath) +{ + FILE* f = fopen(filePath, "r"); + if (!f) + return 1; + + char buffer[1024], *b = buffer; + while (!feof(f)) + { + char c; + fscanf(f, "%c", &c); + switch (c) + { + case '\n': + *b++ = 0; + if (processConfigLine(buffer)) + return 1; + b = buffer; + case '\r': + continue; + default: + *b++ = c; + } + } + + fclose(f); + + // Check mandatory items + if (nssconfigdir.empty()) + { + printf("Error: missing path to NSS certification database\n,use certdbdir: in the config file\n"); + return 1; + } + + if (any_host_cert_mapping && !do_http_proxy) + { + printf("Warning: any host-specific certificate configurations are ignored, add httpproxy:1 to allow them\n"); + } + + return 0; +} + +PRIntn PR_CALLBACK freeHashItems(PLHashEntry *he, PRIntn i, void *arg) +{ + delete [] (char*)he->key; + delete [] (char*)he->value; + return HT_ENUMERATE_REMOVE; +} + +int main(int argc, char** argv) +{ + char* configFilePath; + if (argc == 1) + configFilePath = "ssltunnel.cfg"; + else + configFilePath = argv[1]; + + if (parseConfigFile(configFilePath)) { + fprintf(stderr, "Error: config file \"%s\" missing or formating incorrect\n" + "Specify path to the config file as parameter to ssltunnel or \n" + "create ssltunnel.cfg in the working directory.\n\n" + "Example format of the config file:\n\n" + " # Enable http/ssl tunneling proxy-like behavior.\n" + " # If not specified ssltunnel simply does direct forward.\n" + " httpproxy:1\n\n" + " # Specify path to the certification database used.\n" + " certdbdir:/path/to/certdb\n\n" + " # Forward/proxy all requests in raw to 127.0.0.1:8888.\n" + " forward:127.0.0.1:8888\n\n" + " # Accept connections on port 4443 or 5678 resp. and authenticate\n" + " # to any host ('*') using the 'server cert' or 'server cert 2' resp.\n" + " listen:*:4443:server cert\n" + " listen:*:5678:server cert 2\n\n" + " # Accept connections on port 4443 and authenticate using\n" + " # 'a different cert' when target host is 'my.host.name:443'.\n" + " # This works only in httpproxy mode and has higher priority\n" + " # then the previews option.\n" + " listen:my.host.name:443:4443:a different cert\n", + configFilePath); + return 1; } // create a thread pool to handle connections @@ -345,9 +710,7 @@ int main(int argc, char** argv) PK11_SetPasswordFunc(password_func); // Initialize NSS - char* configdir = argv[1]; - - if (NSS_Init(configdir) != SECSuccess) { + if (NSS_Init(nssconfigdir.c_str()) != SECSuccess) { PRInt32 errorlen = PR_GetErrorTextLength(); char* err = new char[errorlen+1]; PR_GetErrorText(err); @@ -398,6 +761,14 @@ int main(int argc, char** argv) if (NSS_Shutdown() == SECFailure) { fprintf(stderr, "Leaked NSS objects!\n"); } + + for (vector::iterator it = servers.begin(); + it != servers.end(); it++) + { + PL_HashTableEnumerateEntries(it->host_cert_table, freeHashItems, NULL); + PL_HashTableDestroy(it->host_cert_table); + } + PR_Cleanup(); return 0; } diff --git a/toolkit/mozapps/installer/packager.mk b/toolkit/mozapps/installer/packager.mk index 6f513648ccf9..c456900d8b74 100644 --- a/toolkit/mozapps/installer/packager.mk +++ b/toolkit/mozapps/installer/packager.mk @@ -245,6 +245,8 @@ NO_PKG_FILES += \ res/throbber \ shlibsign* \ ssltunnel* \ + certutil* \ + pk12util* \ winEmbed.exe \ os2Embed.exe \ chrome/chrome.rdf \