diff options
author | Marta Rybczynska <rybczynska@gmail.com> | 2023-01-03 15:03:59 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-01-06 12:03:47 +0000 |
commit | f71fe538c8823f00d7fb2c3d4e78e5b18a41b98b (patch) | |
tree | 541129767bc7058f1e2d0a8d50f8a53d9e90a679 /meta/recipes-core | |
parent | 22374efbac642332801462c76b7e48f41ebdcb8b (diff) | |
download | poky-f71fe538c8823f00d7fb2c3d4e78e5b18a41b98b.tar.gz |
cve-update-db-native: avoid incomplete updates
The database update has been done on the original file. In case of
network connection issues, temporary outage of the NVD server or
a similar situation, the function could exit with incomplete data
in the database. This patch solves the issue by performing the update
on a copy of the database. It replaces the main one only if the whole
update was successful.
See https://bugzilla.yoctoproject.org/show_bug.cgi?id=14929
Reported-by: Alberto Pianon <alberto@pianon.eu>
(From OE-Core rev: 8efe99214d8b005f0ecac690ce5ba17b31758f92)
Signed-off-by: Marta Rybczynska <marta.rybczynska@linaro.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-core')
-rw-r--r-- | meta/recipes-core/meta/cve-update-db-native.bb | 83 |
1 files changed, 61 insertions, 22 deletions
diff --git a/meta/recipes-core/meta/cve-update-db-native.bb b/meta/recipes-core/meta/cve-update-db-native.bb index 9b9dbbd75f..079f062f79 100644 --- a/meta/recipes-core/meta/cve-update-db-native.bb +++ b/meta/recipes-core/meta/cve-update-db-native.bb | |||
@@ -21,6 +21,8 @@ CVE_DB_UPDATE_INTERVAL ?= "86400" | |||
21 | # Timeout for blocking socket operations, such as the connection attempt. | 21 | # Timeout for blocking socket operations, such as the connection attempt. |
22 | CVE_SOCKET_TIMEOUT ?= "60" | 22 | CVE_SOCKET_TIMEOUT ?= "60" |
23 | 23 | ||
24 | CVE_DB_TEMP_FILE ?= "${CVE_CHECK_DB_DIR}/temp_nvdcve_1.1.db" | ||
25 | |||
24 | python () { | 26 | python () { |
25 | if not bb.data.inherits_class("cve-check", d): | 27 | if not bb.data.inherits_class("cve-check", d): |
26 | raise bb.parse.SkipRecipe("Skip recipe when cve-check class is not loaded.") | 28 | raise bb.parse.SkipRecipe("Skip recipe when cve-check class is not loaded.") |
@@ -32,25 +34,15 @@ python do_fetch() { | |||
32 | """ | 34 | """ |
33 | import bb.utils | 35 | import bb.utils |
34 | import bb.progress | 36 | import bb.progress |
35 | import sqlite3, urllib, urllib.parse, gzip | 37 | import shutil |
36 | from datetime import date | ||
37 | 38 | ||
38 | bb.utils.export_proxies(d) | 39 | bb.utils.export_proxies(d) |
39 | 40 | ||
40 | YEAR_START = 2002 | ||
41 | |||
42 | db_file = d.getVar("CVE_CHECK_DB_FILE") | 41 | db_file = d.getVar("CVE_CHECK_DB_FILE") |
43 | db_dir = os.path.dirname(db_file) | 42 | db_dir = os.path.dirname(db_file) |
43 | db_tmp_file = d.getVar("CVE_DB_TEMP_FILE") | ||
44 | 44 | ||
45 | cve_socket_timeout = int(d.getVar("CVE_SOCKET_TIMEOUT")) | 45 | cleanup_db_download(db_file, db_tmp_file) |
46 | |||
47 | if os.path.exists("{0}-journal".format(db_file)): | ||
48 | # If a journal is present the last update might have been interrupted. In that case, | ||
49 | # just wipe any leftovers and force the DB to be recreated. | ||
50 | os.remove("{0}-journal".format(db_file)) | ||
51 | |||
52 | if os.path.exists(db_file): | ||
53 | os.remove(db_file) | ||
54 | 46 | ||
55 | # The NVD database changes once a day, so no need to update more frequently | 47 | # The NVD database changes once a day, so no need to update more frequently |
56 | # Allow the user to force-update | 48 | # Allow the user to force-update |
@@ -68,9 +60,60 @@ python do_fetch() { | |||
68 | pass | 60 | pass |
69 | 61 | ||
70 | bb.utils.mkdirhier(db_dir) | 62 | bb.utils.mkdirhier(db_dir) |
63 | if os.path.exists(db_file): | ||
64 | shutil.copy2(db_file, db_tmp_file) | ||
65 | |||
66 | if update_db_file(db_tmp_file, d) == True: | ||
67 | # Update downloaded correctly, can swap files | ||
68 | shutil.move(db_tmp_file, db_file) | ||
69 | else: | ||
70 | # Update failed, do not modify the database | ||
71 | bb.note("CVE database update failed") | ||
72 | os.remove(db_tmp_file) | ||
73 | } | ||
74 | |||
75 | do_fetch[lockfiles] += "${CVE_CHECK_DB_FILE_LOCK}" | ||
76 | do_fetch[file-checksums] = "" | ||
77 | do_fetch[vardeps] = "" | ||
78 | |||
79 | def cleanup_db_download(db_file, db_tmp_file): | ||
80 | """ | ||
81 | Cleanup the download space from possible failed downloads | ||
82 | """ | ||
83 | |||
84 | # Clean up the updates done on the main file | ||
85 | # Remove it only if a journal file exists - it means a complete re-download | ||
86 | if os.path.exists("{0}-journal".format(db_file)): | ||
87 | # If a journal is present the last update might have been interrupted. In that case, | ||
88 | # just wipe any leftovers and force the DB to be recreated. | ||
89 | os.remove("{0}-journal".format(db_file)) | ||
90 | |||
91 | if os.path.exists(db_file): | ||
92 | os.remove(db_file) | ||
93 | |||
94 | # Clean-up the temporary file downloads, we can remove both journal | ||
95 | # and the temporary database | ||
96 | if os.path.exists("{0}-journal".format(db_tmp_file)): | ||
97 | # If a journal is present the last update might have been interrupted. In that case, | ||
98 | # just wipe any leftovers and force the DB to be recreated. | ||
99 | os.remove("{0}-journal".format(db_tmp_file)) | ||
100 | |||
101 | if os.path.exists(db_tmp_file): | ||
102 | os.remove(db_tmp_file) | ||
103 | |||
104 | def update_db_file(db_tmp_file, d): | ||
105 | """ | ||
106 | Update the given database file | ||
107 | """ | ||
108 | import bb.utils, bb.progress | ||
109 | from datetime import date | ||
110 | import urllib, gzip, sqlite3 | ||
111 | |||
112 | YEAR_START = 2002 | ||
113 | cve_socket_timeout = int(d.getVar("CVE_SOCKET_TIMEOUT")) | ||
71 | 114 | ||
72 | # Connect to database | 115 | # Connect to database |
73 | conn = sqlite3.connect(db_file) | 116 | conn = sqlite3.connect(db_tmp_file) |
74 | initialize_db(conn) | 117 | initialize_db(conn) |
75 | 118 | ||
76 | with bb.progress.ProgressHandler(d) as ph, open(os.path.join(d.getVar("TMPDIR"), 'cve_check'), 'a') as cve_f: | 119 | with bb.progress.ProgressHandler(d) as ph, open(os.path.join(d.getVar("TMPDIR"), 'cve_check'), 'a') as cve_f: |
@@ -88,7 +131,7 @@ python do_fetch() { | |||
88 | except urllib.error.URLError as e: | 131 | except urllib.error.URLError as e: |
89 | cve_f.write('Warning: CVE db update error, Unable to fetch CVE data.\n\n') | 132 | cve_f.write('Warning: CVE db update error, Unable to fetch CVE data.\n\n') |
90 | bb.warn("Failed to fetch CVE data (%s)" % e.reason) | 133 | bb.warn("Failed to fetch CVE data (%s)" % e.reason) |
91 | return | 134 | return False |
92 | 135 | ||
93 | if response: | 136 | if response: |
94 | for l in response.read().decode("utf-8").splitlines(): | 137 | for l in response.read().decode("utf-8").splitlines(): |
@@ -98,7 +141,7 @@ python do_fetch() { | |||
98 | break | 141 | break |
99 | else: | 142 | else: |
100 | bb.warn("Cannot parse CVE metadata, update failed") | 143 | bb.warn("Cannot parse CVE metadata, update failed") |
101 | return | 144 | return False |
102 | 145 | ||
103 | # Compare with current db last modified date | 146 | # Compare with current db last modified date |
104 | cursor = conn.execute("select DATE from META where YEAR = ?", (year,)) | 147 | cursor = conn.execute("select DATE from META where YEAR = ?", (year,)) |
@@ -119,7 +162,7 @@ python do_fetch() { | |||
119 | except urllib.error.URLError as e: | 162 | except urllib.error.URLError as e: |
120 | cve_f.write('Warning: CVE db update error, CVE data is outdated.\n\n') | 163 | cve_f.write('Warning: CVE db update error, CVE data is outdated.\n\n') |
121 | bb.warn("Cannot parse CVE data (%s), update failed" % e.reason) | 164 | bb.warn("Cannot parse CVE data (%s), update failed" % e.reason) |
122 | return | 165 | return False |
123 | else: | 166 | else: |
124 | bb.debug(2, "Already up to date (last modified %s)" % last_modified) | 167 | bb.debug(2, "Already up to date (last modified %s)" % last_modified) |
125 | # Update success, set the date to cve_check file. | 168 | # Update success, set the date to cve_check file. |
@@ -128,11 +171,7 @@ python do_fetch() { | |||
128 | 171 | ||
129 | conn.commit() | 172 | conn.commit() |
130 | conn.close() | 173 | conn.close() |
131 | } | 174 | return True |
132 | |||
133 | do_fetch[lockfiles] += "${CVE_CHECK_DB_FILE_LOCK}" | ||
134 | do_fetch[file-checksums] = "" | ||
135 | do_fetch[vardeps] = "" | ||
136 | 175 | ||
137 | def initialize_db(conn): | 176 | def initialize_db(conn): |
138 | with conn: | 177 | with conn: |