3
# Author: Jamie Strandboge <jamie@ubuntu.com>
4
# Author: Kees Cook <kees@ubuntu.com>
5
# Copyright (C) 2005-2016 Canonical Ltd.
7
# This script is distributed under the terms and conditions of the GNU General
8
# Public License, Version 2 or later. See http://www.gnu.org/copyleft/gpl.html
20
releases = ['upstream'] + cve_lib.all_releases
22
max_file_size = 10 * 1024 * 1024 # 10MB
23
cvedir = cve_lib.active_dir
26
parser = optparse.OptionParser()
27
parser.add_option("-p", "--package", dest="pkgs", help="Package name and optional version where package is fixed (with optional Ubuntu release and version in that release)", metavar="NAME[,VERSION[,RELEASE,RELEASE_VERSION]]", action="append")
28
parser.add_option("-b", "--bug-url", dest="bug_urls", help="Bug references", metavar="URL", action="append")
29
parser.add_option("-r", "--reference-url", dest="ref_urls", help="URL references", metavar="URL", action="append")
30
parser.add_option("-c", "--cve", dest="cve", help="CVE entry", metavar="CVE-YYYY-NNNN")
31
parser.add_option("-e", "--embargoed", dest="embargoed", help="This is an embargoed entry", action="store_true")
32
parser.add_option("-y", "--yes", dest="autoconfirm", help="Do not ask for confirmation", action="store_true")
33
parser.add_option("-P", "--public", help="Record date the CVE went public", metavar="YYYY-MM-DD")
34
(options, args) = parser.parse_args()
36
source = source_map.load()
38
def pkg_in_rel(pkg,rel):
39
if rel in ['upstream', 'product']:
43
return (pkg in source[rel])
45
def get_releases(pkgname):
51
# Handle when devel isn't open yet
52
if cve_lib.devel_release == '':
55
# handle product kernels
56
if pkgname in cve_lib.product_kernels:
61
def update_cve(cve, pkgname, fixed_in=None, fixed_in_release=None, fixed_in_release_version=None):
62
'''Update an existing CVE file'''
63
with open(os.path.join(cvedir, cve), "r") as f:
64
lines = f.read(max_file_size).split('\n')
69
tmp_releases = get_releases(pkgname)
71
# If we are using 00boilerplate.<pkgname> and the package is DNE on all
72
# current releases, then don't add the the stanza for this release. This
73
# allows us to use generic boilerplate names like 00boilerplate.gnutls or
74
# 00boilerplate.openjdk without adding useless extra stanzas.
75
# TODO: this still doesn't handle adding the contents of the boilerplate
76
# to an existing CVE (ie, ./scripts/sctive_edit -p openjdk -c CVE-YYYY-NNNN
77
# where CVE-YYYY-NNNN already exists)
78
if os.path.exists(os.path.join(boilerplates, '00boilerplate.%s' % pkgname)):
79
pkg_exists_somewhere = False
80
for r in tmp_releases:
81
if r == 'upstream' or r in cve_lib.eol_releases:
83
if pkg_in_rel(pkgname,r):
84
pkg_exists_somewhere = True
86
if not pkg_exists_somewhere:
87
os.write(sys.stderr.fileno(), "skipping '" + pkgname + "' (DNE on all current releases)\n")
91
for r in tmp_releases:
92
if r == cve_lib.devel_release or r == '':
94
if not re.match(r'^' + r + ".*:", line):
97
match = "%s_%s" % (r, pkgname)
101
if len(skipped) == 0:
102
added_lines += '\nPatches_' + pkgname + ':\n'
104
higher_not_affected = False
105
for release in tmp_releases:
107
if r == cve_lib.devel_release or r == '':
111
os.write(sys.stderr.fileno(), "skipping '" + pkgname + "' for " + r + " (already included)\n")
113
# skip eol_releases releases
114
if r in cve_lib.eol_releases:
116
state = "needs-triage"
117
if not pkg_in_rel(pkgname,release):
119
elif r == 'upstream' and fixed_in is not None:
120
state = "released (%s)" % fixed_in
121
elif fixed_in_release_version and r == fixed_in_release:
122
state = "not-affected (%s)" % fixed_in_release_version
123
higher_not_affected = True
124
elif higher_not_affected:
125
state = "not-affected"
126
added_lines += '%s_%s: %s\n' % (r, pkgname, state)
128
if len(releases) == len(skipped):
129
os.write(sys.stdout.fileno(), "\nNothing to add!\n")
132
# If we have things to add, then make sure we update lts_unsupported on them
133
if added_lines == "":
134
# Nothing added, just update the whole CVE in place
135
cve_lib.lts_unsupported(source, os.path.join(cvedir, cve), pkgname)
138
# Update the added lines only
139
added_lines = cve_lib.lts_unsupported(source, os.path.join(cvedir, cve), pkgname, added_lines)
141
if not options.autoconfirm:
142
os.write(sys.stdout.fileno(), "\n" + added_lines)
143
os.write(sys.stdout.fileno(), "\nAppend the above to " + os.path.join(cvedir, cve) + " (y|N)? ")
144
ans = sys.stdin.readline().lower()
145
os.write(sys.stdout.fileno(), "\n")
147
if options.autoconfirm or ans.startswith("y"):
148
file = open(os.path.join(cvedir, cve), "a")
149
file.write(added_lines)
152
os.write(sys.stdout.fileno(), "Aborted\n")
154
def create_cve(cve, pkgname, fixed_in=None, fixed_in_release=None, fixed_in_release_version=None):
155
'''Create a new CVE file'''
156
src = os.path.join(boilerplates, '00boilerplate')
157
if os.path.exists(src + "." + pkgname):
158
src = src + "." + pkgname
159
boiler = open(src, "r")
160
lines = boiler.read(max_file_size).splitlines()
163
cand_pat = re.compile(r'^Candidate:')
164
ref_pat = re.compile(r'^References:')
165
bugs_pat = re.compile(r'^Bugs:')
166
pkg_pat = re.compile(r'^#[a-z/\-]+_PKG')
167
patch_pat = re.compile(r'^#Patches_PKG')
169
tmp_releases = get_releases(pkgname)
170
# print(tmp_releases)
173
higher_not_affected = False
175
if (cand_pat.search(line)):
176
contents += line + os.path.basename(cve) + '\n'
177
elif line.startswith('PublicDate:'):
178
if options.embargoed:
179
contents += "CRD: <TBD>\n"
181
contents += "PublicDate: %s\n" % options.public
183
# default to today-- this will be refreshed by check-cves
184
contents += "PublicDate: %s\n" % time.strftime("%Y-%m-%d", time.gmtime())
185
elif (patch_pat.search(line)):
186
contents += '\nPatches_' + pkgname + ':\n'
187
elif (pkg_pat.search(line)):
188
for release in tmp_releases:
190
# skip eol_releases releases
191
if r in cve_lib.eol_releases:
193
if r == cve_lib.devel_release or r == '':
197
# replace '#upstream_PKG:' with 'product_PKG:'
198
rel_pat = re.compile('upstream')
200
rel_pat = re.compile(r + '_')
202
if (rel_pat.search(line)):
203
state = "needs-triage"
204
if not pkg_in_rel(pkgname,release):
206
elif r == 'upstream' and fixed_in is not None:
207
state = "released (%s)" % fixed_in
208
elif fixed_in_release_version and r == fixed_in_release:
209
state = "not-affected (%s)" % fixed_in_release_version
210
higher_not_affected = True
211
elif higher_not_affected:
212
state = "not-affected"
213
contents += "%s_%s: %s\n" % (r, pkgname, state)
214
elif ref_pat.search(line):
215
if not re.search(r'N', cve):
216
contents += line + "\n https://cve.mitre.org/cgi-bin/cvename.cgi?name=" + cve + "\n"
218
contents += line + "\n"
220
for i in options.ref_urls:
221
contents += " %s\n" % i
222
elif options.bug_urls and bugs_pat.search(line):
224
for i in options.bug_urls:
225
contents += "\n %s\n" % i
227
contents += line + '\n'
229
contents = cve_lib.lts_unsupported(source, os.path.join(cvedir, cve), pkgname, contents)
230
if not options.autoconfirm:
231
os.write(sys.stdout.fileno(), contents)
232
os.write(sys.stdout.fileno(), "\nWrite the above to " + os.path.join(cvedir, cve) + " (y|N)? ")
233
ans = sys.stdin.readline().lower()
234
os.write(sys.stdout.fileno(), "\n")
236
if options.autoconfirm or ans.startswith("y"):
237
newfile = open(os.path.join(cvedir, cve), 'w')
238
newfile.write(contents)
241
os.write(sys.stdout.fileno(), "Aborted\n")
257
if options.embargoed:
258
cvedir = cve_lib.embargoed_dir
259
pat = re.compile(r'^[\w-]*$')
261
if not os.path.islink(cvedir):
262
os.write(sys.stderr.fileno(), "embargoed/ is not a symlink. Aborting!\n")
266
if not pat.search(cve):
267
if options.embargoed:
268
os.write(sys.stderr.fileno(), "Bad embargoed entry. Should be alphanumerics and dashes\n")
270
os.write(sys.stderr.fileno(), "Bad CVE entry. Should be CVE-XXXX-XXXX\n")
274
pat = re.compile(r'\s')
281
fixed_in_release = None
282
fixed_in_release_version = None
284
fixed_in_release = tmp_p[2]
285
fixed_in_release_version = tmp_p[3]
287
if pat.search(pkgname):
288
os.write(sys.stderr.fileno(), "Bad package name\n")
291
if not os.path.isfile(os.path.join(boilerplates, "00boilerplate")):
292
os.write(sys.stderr.fileno(), "Could not find 00boilerplate in " + cvedir + "\n")
295
if (os.path.isfile(os.path.join(cvedir, cve))):
296
if not options.autoconfirm:
297
os.write(sys.stdout.fileno(), "Found existing " + cve + "...\n\n")
298
update_cve(cve, pkgname, fixed_in, fixed_in_release, fixed_in_release_version)
300
if not options.autoconfirm:
301
os.write(sys.stdout.fileno(), "Creating new " + cve + "...\n\n")
302
create_cve(cve, pkgname, fixed_in, fixed_in_release, fixed_in_release_version)