1 /* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "plugin/keyring/checker/checker.h"
24
25 #include <mysql/psi/mysql_file.h>
26 #include <memory>
27
28 #include "my_compiler.h"
29
30 namespace keyring {
31
32 const my_off_t Checker::EOF_TAG_SIZE = 3;
33 const std::string Checker::eofTAG = "EOF";
34
35 /**
36 checks if keyring file structure is invalid
37 @param[in] file - file handle to be checked
38 @param[in] file_size - total file size
39 @param[in] digest - file digest value
40 @param[out] arch - (optional) architecture type of file int lengths
41
42 @retval false - file structure is valid
43 @retval true - file structure is invalid
44 */
check_file_structure(File file,size_t file_size,Digest * digest,Converter::Arch * arch)45 bool Checker::check_file_structure(File file, size_t file_size, Digest *digest,
46 Converter::Arch *arch) {
47 // should we detect file architecture
48 if (arch != nullptr) {
49 // detect architecture, leave on error
50 *arch = detect_architecture(file, file_size);
51 if (*arch == Converter::Arch::UNKNOWN) return true;
52 }
53
54 // default assumptions
55 bool is_invalid = true;
56
57 // file size affects required validation steps
58 if (file_size == 0)
59 is_invalid = !is_empty_file_correct(digest);
60 else
61 is_invalid = !is_file_size_correct(file_size) ||
62 !is_file_tag_correct(file) || !is_file_version_correct(file) ||
63 !is_dgst_correct(file, digest);
64
65 return is_invalid;
66 }
67
is_empty_file_correct(Digest * digest)68 bool Checker::is_empty_file_correct(Digest *digest) {
69 return strlen(dummy_digest) == digest->length &&
70 strncmp(dummy_digest, reinterpret_cast<const char *>(digest->value),
71 std::min(static_cast<unsigned int>(strlen(dummy_digest)),
72 digest->length)) == 0;
73 }
74
is_file_tag_correct(File file)75 bool Checker::is_file_tag_correct(File file) {
76 uchar tag[EOF_TAG_SIZE + 1];
77 mysql_file_seek(file, 0, MY_SEEK_END, MYF(0));
78 if (unlikely(mysql_file_tell(file, MYF(0)) < EOF_TAG_SIZE))
79 return false; // File does not contain tag
80
81 if (file_seek_to_tag(file) ||
82 unlikely(mysql_file_read(file, tag, EOF_TAG_SIZE, MYF(0)) !=
83 EOF_TAG_SIZE))
84 return false;
85 tag[3] = '\0';
86 mysql_file_seek(file, 0, MY_SEEK_SET, MYF(0));
87 return eofTAG == reinterpret_cast<char *>(tag);
88 }
89
is_file_version_correct(File file)90 bool Checker::is_file_version_correct(File file) {
91 std::unique_ptr<uchar[]> version(new uchar[file_version.length() + 1]);
92 version.get()[file_version.length()] = '\0';
93 mysql_file_seek(file, 0, MY_SEEK_SET, MYF(0));
94 if (unlikely(mysql_file_read(file, version.get(), file_version.length(),
95 MYF(0)) != file_version.length() ||
96 file_version != reinterpret_cast<char *>(version.get())))
97 return false;
98
99 mysql_file_seek(file, 0, MY_SEEK_SET, MYF(0));
100 return true;
101 }
102
103 /**
104 detects machine architecture of serialized key data length
105 */
detect_architecture(File file,size_t file_size)106 Converter::Arch Checker::detect_architecture(File file, size_t file_size) {
107 // empty file should use default integer format
108 auto native_arch = Converter::get_native_arch();
109 if (file_size == 0 || file_size == file_version.length() + eof_size())
110 return native_arch;
111
112 // determine detection order for candidates
113 Converter::Arch detection_order[] = {
114 Converter::Arch::LE_64, Converter::Arch::LE_32, Converter::Arch::BE_64,
115 Converter::Arch::BE_32};
116
117 // length conversion variables
118 uchar src[8] = {0};
119 char dst[8] = {0};
120 size_t length[5] = {0};
121
122 for (auto arch : detection_order) {
123 size_t location = file_version.length();
124 bool skip_arch = false;
125
126 // determine new word width, rewind the keyring file
127 auto arch_width = Converter::get_width(arch);
128 if (mysql_file_seek(file, location, MY_SEEK_SET, MYF(0)) ==
129 MY_FILEPOS_ERROR)
130 return Converter::Arch::UNKNOWN;
131
132 // we'll read if there's at least one key worth of data ahead
133 while (location + 5 * arch_width + eof_size() <= file_size) {
134 // load and calculate sizes
135 for (size_t i = 0; i < 5; i++) {
136 // failure to read is detection failure
137 if (mysql_file_read(file, src, arch_width, MYF(0)) != arch_width)
138 return Converter::Arch::UNKNOWN;
139
140 // conversion must be possible
141 if (!Converter::convert(reinterpret_cast<char *>(src), dst, arch,
142 native_arch)) {
143 skip_arch = true;
144 break;
145 }
146
147 // store dimensions, increase position
148 length[i] = Converter::native_value(dst);
149 location += arch_width;
150 }
151
152 if (skip_arch) break;
153
154 // key size has to be memory aligned
155 if (length[0] % arch_width != 0) {
156 skip_arch = true;
157 break;
158 }
159
160 // verify that native values add up within padding size
161 auto total =
162 5 * arch_width + length[1] + length[2] + length[3] + length[4];
163 if (total > length[0] || total + arch_width < length[0]) {
164 skip_arch = true;
165 break;
166 }
167
168 // we move location according to total size
169 location += length[0] - 5 * arch_width;
170 mysql_file_seek(file, location, MY_SEEK_SET, MYF(0));
171 }
172
173 if (skip_arch) continue;
174
175 // there were no errors - detection is successful
176 if (location == file_size - eof_size()) return arch;
177 }
178
179 return Converter::Arch::UNKNOWN;
180 }
181
182 } // namespace keyring
183