bug 428009 - hook up ssltunnel to mochitest. r=waldo,kaie

This commit is contained in:
Honza Bambas 2008-09-05 09:35:58 -04:00
Родитель 049d891691
Коммит 779cb0331b
13 изменённых файлов: 949 добавлений и 116 удалений

Просмотреть файл

@ -79,9 +79,13 @@ browser_path = \"$(DIST)/bin/$(PROGRAM)\"
endif endif
endif endif
_CERTS_DIR = _profile/pgo/certs
AUTOMATION_PPARGS = \ AUTOMATION_PPARGS = \
-DBROWSER_PATH=$(browser_path) \ -DBROWSER_PATH=$(browser_path) \
-DXPC_BIN_PATH=\"$(DIST)/bin\" \ -DXPC_BIN_PATH=\"$(DIST)/bin\" \
-DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \
-DCERTS_DIR=\"../$(_CERTS_DIR)\" \
$(NULL) $(NULL)
ifeq ($(OS_ARCH),Darwin) ifeq ($(OS_ARCH),Darwin)

Просмотреть файл

@ -43,20 +43,27 @@ VPATH = @srcdir@
relativesrcdir = build/pgo relativesrcdir = build/pgo
include $(DEPTH)/config/autoconf.mk include $(DEPTH)/config/autoconf.mk
DIRS = \
certs \
$(NULL)
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk
# Stuff to make a build with a profile # Stuff to make a build with a profile
_PROFILE_DIR = $(DEPTH)/_profile/pgo _PROFILE_DIR = $(DEPTH)/_profile/pgo
_CERTS_DIR = $(_PROFILE_DIR)/certs
_CERTS_SRC_DIR = $(srcdir)/certs
_PGO_FILES = \ _PGO_FILES = \
automation.py \ automation.py \
profileserver.py \ profileserver.py \
genpgocert.py \
index.html \ index.html \
quit.js \ quit.js \
server-locations.txt \ server-locations.txt \
$(NULL) $(NULL)
ifeq ($(USE_SHORT_LIBNAME), 1) ifeq ($(USE_SHORT_LIBNAME), 1)
PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX) PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX)
else else
@ -80,6 +87,9 @@ endif
AUTOMATION_PPARGS = \ AUTOMATION_PPARGS = \
-DBROWSER_PATH=$(browser_path) \ -DBROWSER_PATH=$(browser_path) \
-DXPC_BIN_PATH=\"$(DIST)/bin\" \ -DXPC_BIN_PATH=\"$(DIST)/bin\" \
-DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \
-DCERTS_DIR=\"$(_CERTS_DIR)\" \
-DCERTS_SRC_DIR=\"$(_CERTS_SRC_DIR)\" \
$(NULL) $(NULL)
ifeq ($(OS_ARCH),Darwin) ifeq ($(OS_ARCH),Darwin)
@ -102,11 +112,15 @@ automation.py: automation.py.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \ $(PYTHON) $(topsrcdir)/config/Preprocessor.py \
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@ $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@
genpgocert.py: genpgocert.py.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ > $@
profileserver.py: profileserver.py.in profileserver.py: profileserver.py.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $^ > $@ $(PYTHON) $(topsrcdir)/config/Preprocessor.py $^ > $@
chmod +x $@ chmod +x $@
GARBAGE += automation.py profileserver.py GARBAGE += automation.py profileserver.py genpgocert.py
libs:: $(_PGO_FILES) libs:: $(_PGO_FILES)
$(INSTALL) $^ $(_PROFILE_DIR) $(INSTALL) $^ $(_PROFILE_DIR)

Просмотреть файл

@ -53,6 +53,8 @@ Runs the browser from a script, and provides useful utilities
for setting up the browser environment. for setting up the browser environment.
""" """
SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
__all__ = [ __all__ = [
"UNIXISH", "UNIXISH",
"IS_WIN32", "IS_WIN32",
@ -62,6 +64,7 @@ __all__ = [
"initializeProfile", "initializeProfile",
"DIST_BIN", "DIST_BIN",
"DEFAULT_APP", "DEFAULT_APP",
"environment",
] ]
# These are generated in mozilla/build/Makefile.in # These are generated in mozilla/build/Makefile.in
@ -74,10 +77,12 @@ __all__ = [
IS_CYGWIN = False IS_CYGWIN = False
#endif #endif
#expand IS_CAMINO = __IS_CAMINO__ != 0 #expand IS_CAMINO = __IS_CAMINO__ != 0
#expand BIN_SUFFIX = __BIN_SUFFIX__
UNIXISH = not IS_WIN32 and not IS_MAC UNIXISH = not IS_WIN32 and not IS_MAC
#expand DEFAULT_APP = "./" + __BROWSER_PATH__ #expand DEFAULT_APP = "./" + __BROWSER_PATH__
#expand CERTS_DIR = __CERTS_DIR__
########### ###########
# LOGGING # # LOGGING #
@ -103,7 +108,7 @@ class Process:
non-Windows platforms. :-( 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 Creates a process representing the execution of the given command, which
must be an absolute path, with the given arguments in the given environment. must be an absolute path, with the given arguments in the given environment.
@ -111,24 +116,40 @@ class Process:
""" """
command = os.path.abspath(command) command = os.path.abspath(command)
if IS_WIN32: if IS_WIN32:
import tempfile
import subprocess import subprocess
if inputdata:
inputfile = tempfile.TemporaryFile()
inputfile.write(inputdata)
inputfile.seek(0)
else:
inputfile = None
cmd = [command] cmd = [command]
cmd.extend(args) cmd.extend(args)
p = subprocess.Popen(cmd, env = env, p = subprocess.Popen(cmd, env = env,
stdout = subprocess.PIPE, stdout = subprocess.PIPE,
stderr = subprocess.STDOUT) stderr = subprocess.STDOUT,
stdin = inputfile)
self._out = p.stdout self._out = p.stdout
else: else:
import popen2 import popen2
cmd = [] cmd = []
for (k, v) in env.iteritems(): if env:
cmd.append(k + "='" + v + "' ") for (k, v) in env.iteritems():
cmd.append(k + "='" + v + "' ")
cmd.append("'" + command + "'") cmd.append("'" + command + "'")
cmd.extend(map(lambda x: "'" + x + "'", args)) cmd.extend(map(lambda x: "'" + x + "'", args))
cmd = " ".join(cmd) cmd = " ".join(cmd)
p = popen2.Popen4(cmd) p = popen2.Popen4(cmd)
self._out = p.fromchild self._out = p.fromchild
if inputdata:
p.tochild.write(inputdata)
p.tochild.close()
self._process = p self._process = p
self.pid = p.pid self.pid = p.pid
@ -165,8 +186,13 @@ class Process:
def kill(self): def kill(self):
"Kills this process." "Kills this process."
try: try:
if not IS_WIN32: # XXX if not IS_WIN32:
os.kill(self._process.pid, signal.SIGKILL) os.kill(self._process.pid, signal.SIGKILL)
else:
import subprocess
pid = "%i" % self.pid
process = subprocess.Popen(["taskkill", "/F", "/PID", pid])
process.wait()
except: except:
pass pass
@ -201,13 +227,13 @@ class Location:
self.options = options self.options = options
def readLocations(): def readLocations(locationsPath = "server-locations.txt"):
""" """
Reads the locations at which the Mochitest HTTP server is available from Reads the locations at which the Mochitest HTTP server is available from
server-locations.txt. 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 # 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 # we get exactly the format we want. See server-locations.txt for the exact
@ -224,7 +250,7 @@ def readLocations():
r"(?P<port>\d+)" r"(?P<port>\d+)"
r"(?:" r"(?:"
r"\s+" r"\s+"
r"(?P<options>\w+(?:,\w+)*)" r"(?P<options>\S+(?:,\S+)*)"
r")?$") r")?$")
locations = [] locations = []
lineno = 0 lineno = 0
@ -320,13 +346,20 @@ function FindProxyForURL(url, host)
if (!matches) if (!matches)
return 'DIRECT'; return 'DIRECT';
var isHttp = matches[1] == 'http'; var isHttp = matches[1] == 'http';
var isHttps = matches[1] == 'https';
if (!matches[3]) 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]; var origin = matches[1] + '://' + matches[2] + ':' + matches[3];
if (origins.indexOf(origin) < 0) if (origins.indexOf(origin) < 0)
return 'DIRECT'; return 'DIRECT';
if (isHttp) if (isHttp)
return 'PROXY localhost:8888'; return 'PROXY 127.0.0.1:8888';
if (isHttps)
return 'PROXY 127.0.0.1:4443';
return 'DIRECT'; return 'DIRECT';
}""" % { "origins": origins } }""" % { "origins": origins }
pacURL = "".join(pacURL.splitlines()) 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.write("".join(prefs))
prefsFile.close() 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<nickname>[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 # # RUN THE APP #
############### ###############
def runApp(testURL, env, app, profileDir, extraArgs): 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." "Run the app, returning the time at which it was started."
# mark the start # mark the start
start = datetime.now() start = datetime.now()
@ -375,10 +477,12 @@ def runApp(testURL, env, app, profileDir, extraArgs):
else: else:
args.append((testURL)) args.append((testURL))
args.extend(extraArgs) args.extend(extraArgs)
proc = Process(cmd, args, env = env) proc = Process(cmd, args, env = environment(env))
log.info("Application pid: %d", proc.pid) log.info("Application pid: %d", proc.pid)
status = proc.wait() status = proc.wait()
if status != 0: if status != 0:
log.info("ERROR FAIL Exited with code %d during test run", status) log.info("ERROR FAIL Exited with code %d during test run", status)
ssltunnelProcess.kill()
return start return start

Просмотреть файл

@ -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 <honzab@firemni.cz>
#
# 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)

15
build/pgo/certs/pgoca.ca Normal file
Просмотреть файл

@ -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-----

Двоичные данные
build/pgo/certs/pgoca.p12 Normal file

Двоичный файл не отображается.

214
build/pgo/genpgocert.py.in Normal file
Просмотреть файл

@ -0,0 +1,214 @@
#
# ***** 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.org 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 <honzab@firemni.cz>
#
# 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)

Просмотреть файл

@ -52,11 +52,24 @@
# number is the default for the protocol. # number is the default for the protocol.
# #
# Unrecognized options are ignored. Recognized options are "primary" and # 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 # 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" # otherwise identify a particular origin (e.g. HTTP/1.0 requests).
# denotes a location which should have the ability to request elevated #
# privileges; the default is no privileges. # "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.test1.example.com:80 privileged
http://sub2.test2.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>. # 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://xn--exmple-cua.test:80 privileged
http://sub1.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. # 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://sub.sectest2.example.org:80 privileged
http://sectest2.example.org:80 http://sectest2.example.org:80
http://sub.sectest1.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

Просмотреть файл

@ -281,8 +281,16 @@ ifndef MOZ_NATIVE_NSS
$(MAKE) -C $(topsrcdir)/security/coreconf $(DEFAULT_GMAKE_FLAGS) $(MAKE) -C $(topsrcdir)/security/coreconf $(DEFAULT_GMAKE_FLAGS)
$(MAKE) -C $(topsrcdir)/security/dbm $(DEFAULT_GMAKE_FLAGS) $(MAKE) -C $(topsrcdir)/security/dbm $(DEFAULT_GMAKE_FLAGS)
$(MAKE) -C $(topsrcdir)/security/nss/lib $(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/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) $(MAKE) -C $(topsrcdir)/security/nss/cmd/shlibsign $(DEFAULT_GMAKE_FLAGS)
endif endif
$(INSTALL) -m 755 $(DIST)/lib/$(LOADABLE_ROOT_MODULE) $(DIST)/bin $(INSTALL) -m 755 $(DIST)/lib/$(LOADABLE_ROOT_MODULE) $(DIST)/bin

Просмотреть файл

@ -74,6 +74,7 @@ _SERV_FILES = \
_DEST_DIR = $(DEPTH)/_tests/$(relativesrcdir) _DEST_DIR = $(DEPTH)/_tests/$(relativesrcdir)
_CERTS_DIR = $(DEPTH)/_profile/pgo/certs
ifeq ($(USE_SHORT_LIBNAME), 1) ifeq ($(USE_SHORT_LIBNAME), 1)
PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX) PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX)
@ -99,6 +100,8 @@ endif
TEST_DRIVER_PPARGS = \ TEST_DRIVER_PPARGS = \
-DBROWSER_PATH=$(browser_path) \ -DBROWSER_PATH=$(browser_path) \
-DXPC_BIN_PATH=\"../$(DIST)/bin\" \ -DXPC_BIN_PATH=\"../$(DIST)/bin\" \
-DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \
-DCERTS_DIR=\"../$(_CERTS_DIR)\" \
$(NULL) $(NULL)
ifeq ($(OS_ARCH),Darwin) ifeq ($(OS_ARCH),Darwin)
@ -129,3 +132,6 @@ GARBAGE += runtests.py automation.py
libs:: $(_SERV_FILES) libs:: $(_SERV_FILES)
$(INSTALL) $^ $(_DEST_DIR) $(INSTALL) $^ $(_DEST_DIR)
libs::
$(PYTHON) $(DEPTH)/_profile/pgo/genpgocert.py --gen-server

Просмотреть файл

@ -222,7 +222,7 @@ function processLocations(server)
"(\\d+)" + "(\\d+)" +
"(?:" + "(?:" +
"\\s+" + "\\s+" +
"(\\w+(?:,\\w+)*)" + "(\\S+(?:,\\S+)*)" +
")?$"); ")?$");
var line = {}; var line = {};

Просмотреть файл

@ -50,6 +50,7 @@
#include "key.h" #include "key.h"
#include "keyt.h" #include "keyt.h"
#include "ssl.h" #include "ssl.h"
#include "plhash.h"
using std::string; using std::string;
using std::vector; using std::vector;
@ -57,8 +58,8 @@ using std::vector;
// Structs for passing data into jobs on the thread pool // Structs for passing data into jobs on the thread pool
typedef struct { typedef struct {
PRInt32 listen_port; PRInt32 listen_port;
PRNetAddr remote_addr;
string cert_nickname; string cert_nickname;
PLHashTable* host_cert_table;
} server_info_t; } server_info_t;
typedef struct { typedef struct {
@ -113,11 +114,16 @@ const PRInt32 DEFAULT_STACKSIZE = (512 * 1024);
const PRInt32 BUF_SIZE = 4096; const PRInt32 BUF_SIZE = 4096;
// global data // global data
string nssconfigdir;
vector<server_info_t> servers;
PRNetAddr remote_addr;
PRThreadPool* threads = NULL; PRThreadPool* threads = NULL;
PRLock* shutdown_lock = NULL; PRLock* shutdown_lock = NULL;
PRCondVar* shutdown_condvar = NULL; PRCondVar* shutdown_condvar = NULL;
// Not really used, unless something fails to start // Not really used, unless something fails to start
bool shutdown_server = false; 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. * Signal the main thread that the application should shut down.
@ -129,6 +135,89 @@ void SignalShutdown()
PR_Unlock(shutdown_lock); 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 * Handle an incoming client connection. The server thread has already
* accepted the connection, so we just need to connect to the remote * 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()); AutoFD other_sock(PR_NewTCPSocket());
bool client_done = false; bool client_done = false;
bool client_error = 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 && if (other_sock)
PR_Connect(other_sock, &ci->server_info->remote_addr, connect_timeout) {
== PR_SUCCESS) { PRInt32 numberOfSockets = 1;
PRInt32 bytes = PR_Recv(ci->client_sock, buf, BUF_SIZE, 0, short_timeout);
if (bytes > 0 && struct relayBuffer
PR_Send(other_sock, buf, bytes, 0, short_timeout) > 0) { {
bytes = PR_Recv(other_sock, buf, BUF_SIZE, 0, short_timeout); char *buffer, *bufferhead, *buffertail, *bufferend;
while (bytes > 0) { relayBuffer()
if (PR_Send(ci->client_sock, buf, bytes, 0, short_timeout) == -1) { {
client_error = true; bufferhead = buffertail = buffer = new char[BUF_SIZE];
break; bufferend = buffer + BUF_SIZE;
}
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);
} }
~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) if (!client_error)
PR_Shutdown(ci->client_sock, PR_SHUTDOWN_BOTH); PR_Shutdown(ci->client_sock, PR_SHUTDOWN_SEND);
PR_Close(ci->client_sock); PR_Close(ci->client_sock);
delete ci; delete ci;
@ -198,21 +438,6 @@ void PR_CALLBACK StartServer(void* data)
server_info_t* si = static_cast<server_info_t*>(data); server_info_t* si = static_cast<server_info_t*>(data);
//TODO: select ciphers? //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()); AutoFD listen_socket(PR_NewTCPSocket());
if (!listen_socket) { if (!listen_socket) {
fprintf(stderr, "failed to create socket\n"); fprintf(stderr, "failed to create socket\n");
@ -234,21 +459,6 @@ void PR_CALLBACK StartServer(void* data)
return; 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, printf("Server listening on port %d with cert %s\n", si->listen_port,
si->cert_nickname.c_str()); si->cert_nickname.c_str());
@ -258,6 +468,12 @@ void PR_CALLBACK StartServer(void* data)
// block waiting for connections // block waiting for connections
ci->client_sock = PR_Accept(listen_socket, &ci->client_addr, ci->client_sock = PR_Accept(listen_socket, &ci->client_addr,
PR_INTERVAL_NO_TIMEOUT); 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) if (ci->client_sock)
// Not actually using this PRJob*... // Not actually using this PRJob*...
//PRJob* job = //PRJob* job =
@ -276,45 +492,194 @@ char* password_func(PK11SlotInfo* slot, PRBool retry, void* arg)
return ""; return "";
} }
int main(int argc, char** argv) server_info_t* findServerInfo(int portnumber)
{ {
if (argc < 6) { for (vector<server_info_t>::iterator it = servers.begin();
fprintf(stderr, "Error: not enough arguments\n" it != servers.end(); it++)
"Usage: ssltunnel <NSS db path> <remote ip> <remote port> (<certname> <port>)+\n" {
" Provide SSL encrypted tunnels to <remote ip>:<remote port>\n" if (it->listen_port == portnumber)
" from each port specified in a <certname>,<port> pair.\n" return &(*it);
" <certname> must be the nickname of a server certificate\n"
" installed in the NSS db pointed to by the <NSS db path>.\n");
return 1;
} }
return NULL;
}
PRNetAddr remote_addr; int processConfigLine(char* configLine)
if (PR_StringToNetAddr(argv[2], &remote_addr) != PR_SUCCESS) { {
fprintf(stderr, "Invalid remote IP address: %s\n", argv[2]); if (*configLine == 0 || *configLine == '#')
return 1; 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]); // Configure the forward address of the target server
if (port <= 0) { if (!strcmp(keyword, "forward"))
fprintf(stderr, "Invalid remote port: %s\n", argv[2]); {
return 1; char* ipstring = strtok(NULL, ":");
} if (PR_StringToNetAddr(ipstring, &remote_addr) != PR_SUCCESS) {
remote_addr.inet.port = PR_htons(port); fprintf(stderr, "Invalid remote IP address: %s\n", ipstring);
// get our list of cert:port from the remaining args
vector<server_info_t> servers;
for (int i=4; i<argc; i++) {
server_info_t server;
memcpy(&server.remote_addr, &remote_addr, sizeof(PRNetAddr));
server.cert_nickname = argv[i++];
port = atoi(argv[i]);
if (port <= 0) {
fprintf(stderr, "Invalid port specified: %s\n", argv[i]);
return 1; return 1;
} }
server.listen_port = port; char* portstring = strtok(NULL, ":");
servers.push_back(server); int port = atoi(portstring);
if (port <= 0) {
fprintf(stderr, "Invalid remote port: %s\n", portstring);
return 1;
}
remote_addr.inet.port = PR_htons(port);
return 0;
}
// Configure all listen sockets and port+certificate bindings
if (!strcmp(keyword, "listen"))
{
char* hostname = strtok(NULL, ":");
char* hostportstring = NULL;
if (strcmp(hostname, "*"))
{
any_host_cert_mapping = true;
hostportstring = strtok(NULL, ":");
}
char* portstring = strtok(NULL, ":");
char* certnick = strtok(NULL, ":");
int port = atoi(portstring);
if (port <= 0) {
fprintf(stderr, "Invalid port specified: %s\n", portstring);
return 1;
}
if (server_info_t* existingServer = findServerInfo(port))
{
char *certnick_copy = new char[strlen(certnick)+1];
char *hostname_copy = new char[strlen(hostname)+strlen(hostportstring)+2];
strcpy(hostname_copy, hostname);
strcat(hostname_copy, ":");
strcat(hostname_copy, hostportstring);
strcpy(certnick_copy, certnick);
PL_HashTableAdd(existingServer->host_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:<path> 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 // create a thread pool to handle connections
@ -345,9 +710,7 @@ int main(int argc, char** argv)
PK11_SetPasswordFunc(password_func); PK11_SetPasswordFunc(password_func);
// Initialize NSS // Initialize NSS
char* configdir = argv[1]; if (NSS_Init(nssconfigdir.c_str()) != SECSuccess) {
if (NSS_Init(configdir) != SECSuccess) {
PRInt32 errorlen = PR_GetErrorTextLength(); PRInt32 errorlen = PR_GetErrorTextLength();
char* err = new char[errorlen+1]; char* err = new char[errorlen+1];
PR_GetErrorText(err); PR_GetErrorText(err);
@ -398,6 +761,14 @@ int main(int argc, char** argv)
if (NSS_Shutdown() == SECFailure) { if (NSS_Shutdown() == SECFailure) {
fprintf(stderr, "Leaked NSS objects!\n"); fprintf(stderr, "Leaked NSS objects!\n");
} }
for (vector<server_info_t>::iterator it = servers.begin();
it != servers.end(); it++)
{
PL_HashTableEnumerateEntries(it->host_cert_table, freeHashItems, NULL);
PL_HashTableDestroy(it->host_cert_table);
}
PR_Cleanup(); PR_Cleanup();
return 0; return 0;
} }

Просмотреть файл

@ -245,6 +245,8 @@ NO_PKG_FILES += \
res/throbber \ res/throbber \
shlibsign* \ shlibsign* \
ssltunnel* \ ssltunnel* \
certutil* \
pk12util* \
winEmbed.exe \ winEmbed.exe \
os2Embed.exe \ os2Embed.exe \
chrome/chrome.rdf \ chrome/chrome.rdf \