summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarta Rybczynska <rybczynska@gmail.com>2023-01-03 15:03:59 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2023-01-06 12:03:47 +0000
commitf71fe538c8823f00d7fb2c3d4e78e5b18a41b98b (patch)
tree541129767bc7058f1e2d0a8d50f8a53d9e90a679
parent22374efbac642332801462c76b7e48f41ebdcb8b (diff)
downloadpoky-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>
-rw-r--r--meta/recipes-core/meta/cve-update-db-native.bb83
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.
22CVE_SOCKET_TIMEOUT ?= "60" 22CVE_SOCKET_TIMEOUT ?= "60"
23 23
24CVE_DB_TEMP_FILE ?= "${CVE_CHECK_DB_DIR}/temp_nvdcve_1.1.db"
25
24python () { 26python () {
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
75do_fetch[lockfiles] += "${CVE_CHECK_DB_FILE_LOCK}"
76do_fetch[file-checksums] = ""
77do_fetch[vardeps] = ""
78
79def 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
104def 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
133do_fetch[lockfiles] += "${CVE_CHECK_DB_FILE_LOCK}"
134do_fetch[file-checksums] = ""
135do_fetch[vardeps] = ""
136 175
137def initialize_db(conn): 176def initialize_db(conn):
138 with conn: 177 with conn: