|
1 #!/usr/bin/env python |
|
2 # This Source Code Form is subject to the terms of the Mozilla Public |
|
3 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
5 |
|
6 # This script exists to generate the Certificate Authority and server |
|
7 # certificates used for SSL testing in Mochitest. The already generated |
|
8 # certs are located at $topsrcdir/build/pgo/certs/ . |
|
9 |
|
10 import mozinfo |
|
11 import os |
|
12 import random |
|
13 import re |
|
14 import shutil |
|
15 import subprocess |
|
16 import sys |
|
17 import tempfile |
|
18 |
|
19 from mozbuild.base import MozbuildObject |
|
20 from mozfile import NamedTemporaryFile |
|
21 from mozprofile.permissions import ServerLocations |
|
22 |
|
23 dbFiles = [ |
|
24 re.compile("^cert[0-9]+\.db$"), |
|
25 re.compile("^key[0-9]+\.db$"), |
|
26 re.compile("^secmod\.db$") |
|
27 ] |
|
28 |
|
29 def unlinkDbFiles(path): |
|
30 for root, dirs, files in os.walk(path): |
|
31 for name in files: |
|
32 for dbFile in dbFiles: |
|
33 if dbFile.match(name) and os.path.exists(os.path.join(root, name)): |
|
34 os.unlink(os.path.join(root, name)) |
|
35 |
|
36 def dbFilesExist(path): |
|
37 for root, dirs, files in os.walk(path): |
|
38 for name in files: |
|
39 for dbFile in dbFiles: |
|
40 if dbFile.match(name) and os.path.exists(os.path.join(root, name)): |
|
41 return True |
|
42 return False |
|
43 |
|
44 |
|
45 def runUtil(util, args, inputdata = None): |
|
46 env = os.environ.copy() |
|
47 if mozinfo.os == "linux": |
|
48 pathvar = "LD_LIBRARY_PATH" |
|
49 app_path = os.path.dirname(util) |
|
50 if pathvar in env: |
|
51 env[pathvar] = "%s%s%s" % (app_path, os.pathsep, env[pathvar]) |
|
52 else: |
|
53 env[pathvar] = app_path |
|
54 proc = subprocess.Popen([util] + args, env=env, |
|
55 stdin=subprocess.PIPE if inputdata else None) |
|
56 proc.communicate(inputdata) |
|
57 return proc.returncode |
|
58 |
|
59 |
|
60 def createRandomFile(randomFile): |
|
61 for count in xrange(0, 2048): |
|
62 randomFile.write(chr(random.randint(0, 255))) |
|
63 |
|
64 |
|
65 def createCertificateAuthority(build, srcDir): |
|
66 certutil = build.get_binary_path(what="certutil") |
|
67 pk12util = build.get_binary_path(what="pk12util") |
|
68 |
|
69 #TODO: mozfile.TemporaryDirectory |
|
70 tempDbDir = tempfile.mkdtemp() |
|
71 with NamedTemporaryFile() as pwfile, NamedTemporaryFile() as rndfile: |
|
72 pgoCAModulePathSrc = os.path.join(srcDir, "pgoca.p12") |
|
73 pgoCAPathSrc = os.path.join(srcDir, "pgoca.ca") |
|
74 |
|
75 pwfile.write("\n") |
|
76 |
|
77 # Create temporary certification database for CA generation |
|
78 status = runUtil(certutil, ["-N", "-d", tempDbDir, "-f", pwfile.name]) |
|
79 if status: |
|
80 return status |
|
81 |
|
82 createRandomFile(rndfile) |
|
83 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", pwfile.name, "-z", rndfile.name], "Y\n0\nN\n") |
|
84 if status: |
|
85 return status |
|
86 |
|
87 status = runUtil(certutil, ["-L", "-d", tempDbDir, "-n", "pgo temporary ca", "-a", "-o", pgoCAPathSrc, "-f", pwfile.name]) |
|
88 if status: |
|
89 return status |
|
90 |
|
91 status = runUtil(pk12util, ["-o", pgoCAModulePathSrc, "-n", "pgo temporary ca", "-d", tempDbDir, "-w", pwfile.name, "-k", pwfile.name]) |
|
92 if status: |
|
93 return status |
|
94 |
|
95 shutil.rmtree(tempDbDir) |
|
96 return 0 |
|
97 |
|
98 |
|
99 def createSSLServerCertificate(build, srcDir): |
|
100 certutil = build.get_binary_path(what="certutil") |
|
101 pk12util = build.get_binary_path(what="pk12util") |
|
102 |
|
103 with NamedTemporaryFile() as pwfile, NamedTemporaryFile() as rndfile: |
|
104 pgoCAPath = os.path.join(srcDir, "pgoca.p12") |
|
105 |
|
106 pwfile.write("\n") |
|
107 |
|
108 if not dbFilesExist(srcDir): |
|
109 # Make sure all DB files from src are really deleted |
|
110 unlinkDbFiles(srcDir) |
|
111 |
|
112 # Create certification database for ssltunnel |
|
113 status = runUtil(certutil, ["-N", "-d", srcDir, "-f", pwfile.name]) |
|
114 if status: |
|
115 return status |
|
116 |
|
117 status = runUtil(pk12util, ["-i", pgoCAPath, "-w", pwfile.name, "-d", srcDir, "-k", pwfile.name]) |
|
118 if status: |
|
119 return status |
|
120 |
|
121 # Generate automatic certificate |
|
122 locations = ServerLocations(os.path.join(build.topsrcdir, |
|
123 "build", "pgo", |
|
124 "server-locations.txt")) |
|
125 iterator = iter(locations) |
|
126 |
|
127 # Skips the first entry, I don't know why: bug 879740 |
|
128 iterator.next() |
|
129 |
|
130 locationsParam = "" |
|
131 firstLocation = "" |
|
132 for loc in iterator: |
|
133 if loc.scheme == "https" and "nocert" not in loc.options: |
|
134 customCertOption = False |
|
135 customCertRE = re.compile("^cert=(?:\w+)") |
|
136 for option in loc.options: |
|
137 match = customCertRE.match(option) |
|
138 if match: |
|
139 customCertOption = True |
|
140 break |
|
141 |
|
142 if not customCertOption: |
|
143 if len(locationsParam) > 0: |
|
144 locationsParam += "," |
|
145 locationsParam += loc.host |
|
146 |
|
147 if firstLocation == "": |
|
148 firstLocation = loc.host |
|
149 |
|
150 if not firstLocation: |
|
151 print "Nothing to generate, no automatic secure hosts specified" |
|
152 else: |
|
153 createRandomFile(rndfile) |
|
154 |
|
155 runUtil(certutil, ["-D", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfile.name, "-f", pwfile.name]) |
|
156 # Ignore the result, the certificate may not be present when new database is being built |
|
157 |
|
158 status = runUtil(certutil, ["-S", "-s", "CN=%s" % firstLocation, "-t", "Pu,,", "-c", "pgo temporary ca", "-m", "2", "-8", locationsParam, "-v", "120", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfile.name, "-f", pwfile.name]) |
|
159 if status: |
|
160 return status |
|
161 |
|
162 return 0 |
|
163 |
|
164 if len(sys.argv) == 1: |
|
165 print "Specify --gen-server or --gen-ca" |
|
166 sys.exit(1) |
|
167 |
|
168 build = MozbuildObject.from_environment() |
|
169 certdir = os.path.join(build.topsrcdir, "build", "pgo", "certs") |
|
170 if sys.argv[1] == "--gen-server": |
|
171 certificateStatus = createSSLServerCertificate(build, certdir) |
|
172 if certificateStatus: |
|
173 print "TEST-UNEXPECTED-FAIL | SSL Server Certificate generation" |
|
174 |
|
175 sys.exit(certificateStatus) |
|
176 |
|
177 if sys.argv[1] == "--gen-ca": |
|
178 certificateStatus = createCertificateAuthority(build, certdir) |
|
179 if certificateStatus: |
|
180 print "TEST-UNEXPECTED-FAIL | Certificate Authority generation" |
|
181 else: |
|
182 print "\n\n" |
|
183 print "===================================================" |
|
184 print " IMPORTANT:" |
|
185 print " To use this new certificate authority in tests" |
|
186 print " run 'make' at testing/mochitest" |
|
187 print "===================================================" |
|
188 |
|
189 sys.exit(certificateStatus) |
|
190 |
|
191 print "Invalid option specified" |
|
192 sys.exit(1) |