1 /* Copyright (c) 2017, 2019, 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 "log.h"
24 #include "my_default.h" // my_getopt_use_args_separator
25 #include "migrate_keyring.h"
26 #include "mysqld.h"
27 #include "mysqld_error.h"
28 #include "sql_plugin.h" // plugin_early_load_one
29 #include "violite.h"
30
31 /**
32 Standard constructor
33 */
34
Migrate_keyring()35 Migrate_keyring::Migrate_keyring()
36 {
37 m_source_plugin_handle= NULL;
38 m_destination_plugin_handle= NULL;
39 mysql= NULL;
40 }
41
42 /**
43 This function does the following:
44 1. Validate all keyring migration specific options.
45 2. Get a connection handle by connecting to server if connection
46 specific options are set.
47
48 @param [in] argc Pointer to argc of original program
49 @param [in] argv Pointer to argv of original program
50 @param [in] source_plugin Pointer to source plugin option
51 @param [in] destination_plugin Pointer to destination plugin option
52 @param [in] user User to login to server
53 @param [in] host Host on which to connect to server
54 @param [in] password Password used to connect to server
55 @param [in] socket The socket file to use for connection
56 @param [in] port Port number to use for connection
57
58 @return 0 Success
59 @return 1 Failure
60
61 */
init(int argc,char ** argv,char * source_plugin,char * destination_plugin,char * user,char * host,char * password,char * socket,ulong port)62 bool Migrate_keyring::init(int argc,
63 char **argv,
64 char *source_plugin,
65 char *destination_plugin,
66 char *user, char *host,
67 char *password, char *socket,
68 ulong port)
69 {
70 DBUG_ENTER("Migrate_keyring::init");
71
72 std::size_t found= std::string::npos;
73 string equal("=");
74 string so(".so");
75 string dll(".dll");
76
77 if (!source_plugin)
78 {
79 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
80 "Invalid --keyring-migration-source option.");
81 DBUG_RETURN(true);
82 }
83 if (!destination_plugin)
84 {
85 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
86 "Invalid --keyring-migration-destination option.");
87 DBUG_RETURN(true);
88 }
89 m_source_plugin_option= source_plugin;
90 m_destination_plugin_option= destination_plugin;
91
92 /* extract plugin name from the specified source plugin option */
93 if ((found= m_source_plugin_option.find(equal)) != std::string::npos)
94 m_source_plugin_name= m_source_plugin_option.substr(0, found);
95 else if ((found= m_source_plugin_option.find(so)) != std::string::npos)
96 m_source_plugin_name= m_source_plugin_option.substr(0, found);
97 else if ((found= m_source_plugin_option.find(dll)) != std::string::npos)
98 m_source_plugin_name= m_source_plugin_option.substr(0, found);
99 else
100 {
101 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
102 "Invalid source plugin option value.");
103 DBUG_RETURN(true);
104 }
105
106 /* extract plugin name from the specified destination plugin option */
107 if ((found= m_destination_plugin_option.find(equal)) != std::string::npos)
108 m_destination_plugin_name= m_destination_plugin_option.substr(0, found);
109 else if ((found= m_destination_plugin_option.find(so)) != std::string::npos)
110 m_destination_plugin_name= m_destination_plugin_option.substr(0, found);
111 else if ((found= m_destination_plugin_option.find(dll)) != std::string::npos)
112 m_destination_plugin_name= m_destination_plugin_option.substr(0, found);
113 else
114 {
115 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
116 "Invalid destination plugin option value.");
117 DBUG_RETURN(true);
118 }
119
120 /* if connect options are provided then initiate connection */
121 if (migrate_connect_options)
122 {
123 ssl_start();
124 /* initiate connection */
125 mysql= mysql_init(NULL);
126
127 enum mysql_ssl_mode ssl_mode= SSL_MODE_REQUIRED;
128 mysql_options(mysql, MYSQL_OPT_SSL_MODE, &ssl_mode);
129 mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
130 mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "program_name",
131 "mysqld");
132 mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_role",
133 "keyring_migration_tool");
134
135 if (!mysql_real_connect(mysql, host, user, password, "",
136 port, socket, 0))
137 {
138 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
139 "Connection to server failed. Please check connection specific "
140 "option values.");
141 DBUG_RETURN(true);
142 }
143 }
144
145 m_argc= argc;
146 m_argv= new char *[m_argc + 2]; // 1st for extra option and 2nd for NULL
147 for (int cnt= 0; cnt < m_argc; ++cnt) {
148 m_argv[cnt]= argv[cnt];
149 }
150 /* add --loose_<plugin_name>_open_mode=1 option */
151 m_internal_option= "--loose_" + m_source_plugin_name + "_open_mode=1";
152 m_argv[m_argc]= const_cast<char *>(m_internal_option.c_str());
153 /* update m_argc, m_argv */
154 m_argv[++m_argc]= NULL;
155 DBUG_RETURN(false);
156 }
157
158 /**
159 This function does the following in sequence:
160 1. Load source plugin.
161 2. Load destination plugin.
162 3. Disable access to keyring service APIs.
163 4. Fetch all keys from source plugin and upon
164 sucess store in destination plugin.
165 5. Enable access to keyring service APIs.
166 6. Unload source plugin.
167 7. Unload destination plugin.
168
169 NOTE: In case there is any error while fetching keys from source plugin,
170 this function would remove all keys stored as part of fetch.
171
172 @return 0 Success
173 @return 1 Failure
174 */
execute()175 bool Migrate_keyring::execute()
176 {
177 DBUG_ENTER("Migrate_keyring::execute");
178
179 char **tmp_m_argv;
180
181 /* Disable access to keyring service APIs */
182 if (migrate_connect_options && disable_keyring_operations())
183 goto error;
184
185 /* Load source plugin. */
186 if (load_plugin(SOURCE_PLUGIN))
187 {
188 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
189 "Failed to initialize source keyring");
190 goto error;
191 }
192
193 /* Load destination source plugin. */
194 if (load_plugin(DESTINATION_PLUGIN))
195 {
196 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
197 "Failed to initialize destination keyring");
198 goto error;
199 }
200
201 /* skip program name */
202 m_argc--;
203 /* We use a tmp ptr instead of m_argv since if the latter gets changed, we
204 * lose access to the alloced mem and hence there would be leak */
205 tmp_m_argv= m_argv + 1;
206 /* check for invalid options */
207 if (m_argc > 1)
208 {
209 struct my_option no_opts[]=
210 {
211 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
212 };
213 my_getopt_skip_unknown= 0;
214 my_getopt_use_args_separator= true;
215 if (handle_options(&m_argc, &tmp_m_argv, no_opts, NULL))
216 unireg_abort(MYSQLD_ABORT_EXIT);
217
218 if (m_argc > 1)
219 {
220 sql_print_error("Please specify options specific to keyring migration. Any "
221 "additional options can be ignored. NOTE: Although some options "
222 "are valid, migration tool can still report error example: plugin "
223 "variables for which plugin is not loaded yet.");
224 unireg_abort(MYSQLD_ABORT_EXIT);
225 }
226 }
227
228 /* Fetch all keys from source plugin and store into destination plugin. */
229 if (fetch_and_store_keys())
230 goto error;
231
232 /* Enable access to keyring service APIs */
233 if (migrate_connect_options)
234 enable_keyring_operations();
235
236 DBUG_RETURN(false);
237
238 error:
239 /*
240 Enable keyring_operations in case of error
241 */
242 if (migrate_connect_options)
243 enable_keyring_operations();
244 DBUG_RETURN(true);
245 }
246
247 /**
248 Load plugin.
249
250 @param [in] plugin_type Indicates what plugin to be loaded
251
252 @return 0 Success
253 @return 1 Failure
254 */
load_plugin(enum_plugin_type plugin_type)255 bool Migrate_keyring::load_plugin(enum_plugin_type plugin_type)
256 {
257 DBUG_ENTER("Migrate_keyring::load_plugin");
258
259 char* keyring_plugin= NULL;
260 char* plugin_name= NULL;
261 bool is_source_plugin= 0;
262
263 if (plugin_type == SOURCE_PLUGIN)
264 is_source_plugin= 1;
265
266 if (is_source_plugin)
267 {
268 keyring_plugin= const_cast<char *>(m_source_plugin_option.c_str());
269 plugin_name= const_cast<char *>(m_source_plugin_name.c_str());
270 }
271 else
272 {
273 keyring_plugin= const_cast<char *>(m_destination_plugin_option.c_str());
274 plugin_name= const_cast<char *>(m_destination_plugin_name.c_str());
275 }
276
277 if (plugin_early_load_one(&m_argc, m_argv, keyring_plugin))
278 goto error;
279 else
280 {
281 /* set plugin handle */
282 plugin_ref plugin;
283 plugin= my_plugin_lock_by_name(0, to_lex_cstring(plugin_name),
284 MYSQL_KEYRING_PLUGIN);
285 if (plugin == NULL)
286 goto error;
287
288 if (is_source_plugin)
289 m_source_plugin_handle= (st_mysql_keyring*)plugin_decl(plugin)->info;
290 else
291 m_destination_plugin_handle= (st_mysql_keyring*)plugin_decl(plugin)->info;
292
293 plugin_unlock(0, plugin);
294 }
295 DBUG_RETURN(false);
296
297 error:
298 if (is_source_plugin)
299 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
300 "Failed to load source keyring plugin.");
301 else
302 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
303 "Failed to load destination keyring plugin.");
304 DBUG_RETURN(true);
305 }
306
307 /**
308 This function does the following in sequence:
309 1. Initialize key iterator which will make iterator to position itself
310 inorder to fetch a key.
311 2. Using iterator get key ID and user name.
312 3. Fetch the key information using key ID and user name.
313 4. Store the fetched key into destination plugin.
314 5. In case of errors remove keys from destination plugin.
315
316 @return 0 Success
317 @return 1 Failure
318 */
fetch_and_store_keys()319 bool Migrate_keyring::fetch_and_store_keys()
320 {
321 DBUG_ENTER("Migrate_keyring::fetch_keys");
322
323 bool error= FALSE;
324 char key_id[MAX_KEY_LEN]= { 0 };
325 char user_id[USERNAME_LENGTH]= { 0 };
326 void *key=NULL;
327 size_t key_len= 0;
328 char *key_type= NULL;
329 void *key_iterator= NULL;
330
331 m_source_plugin_handle->mysql_key_iterator_init(&key_iterator);
332 if (key_iterator == NULL)
333 {
334 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
335 "Initializing source keyring iterator failed.");
336 DBUG_RETURN(true);
337 }
338 while(!error)
339 {
340 if (m_source_plugin_handle->
341 mysql_key_iterator_get_key(key_iterator, key_id, user_id))
342 break;
343
344 /* using key_info metadata fetch the actual key */
345 if (m_source_plugin_handle->mysql_key_fetch(key_id,
346 &key_type,
347 user_id,
348 &key,
349 &key_len))
350 {
351 /* fetch failed */
352 string errmsg= "Fetching key (" + string(key_id) +
353 ") from source plugin failed.";
354 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
355 errmsg.c_str());
356 error= TRUE;
357 }
358 else /* store the fetched key into destination plugin */
359 {
360 if (m_destination_plugin_handle->mysql_key_store(key_id, key_type,
361 user_id, key, key_len))
362 {
363 string errmsg= "Storing key (" + string(key_id) +
364 ") into destination plugin failed.";
365 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
366 errmsg.c_str());
367 error= TRUE;
368 }
369 else
370 {
371 /*
372 keep track of keys stored in successfully so that they can be
373 removed in case of error.
374 */
375 Key_info ki(key_id, user_id);
376 m_source_keys.push_back(ki);
377 }
378 }
379 if (key)
380 my_free((char*)key);
381 if (key_type)
382 my_free(key_type);
383 }
384 if (error)
385 {
386 /* something went wrong remove keys from destination plugin. */
387 while (m_source_keys.size())
388 {
389 Key_info ki= m_source_keys.back();
390 if (m_destination_plugin_handle->mysql_key_remove(
391 ki.m_key_id, ki.m_user_id))
392 {
393 string errmsg= "Removing key (" + string(ki.m_key_id) +
394 ") from destination plugin failed.";
395 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
396 errmsg.c_str());
397 }
398 m_source_keys.pop_back();
399 }
400 }
401 m_source_plugin_handle->mysql_key_iterator_deinit(key_iterator);
402 DBUG_RETURN(error);
403 }
404
405 /**
406 Disable variable @@keyring_operations.
407
408 @return 0 Success
409 @return 1 Failure
410 */
disable_keyring_operations()411 bool Migrate_keyring::disable_keyring_operations()
412 {
413 DBUG_ENTER("Migrate_keyring::disable_keyring_operations");
414 const char query[]= "SET GLOBAL KEYRING_OPERATIONS=0";
415 if (mysql && mysql_real_query(mysql, query, strlen(query)))
416 {
417 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
418 "Failed to disable keyring_operations variable.");
419 DBUG_RETURN(true);
420 }
421 DBUG_RETURN(false);
422 }
423
424 /**
425 Enable variable @@keyring_operations.
426
427 @return 0 Success
428 @return 1 Failure
429 */
enable_keyring_operations()430 bool Migrate_keyring::enable_keyring_operations()
431 {
432 DBUG_ENTER("Migrate_keyring::enable_keyring_operations");
433 const char query[]= "SET GLOBAL KEYRING_OPERATIONS=1";
434 if (mysql && mysql_real_query(mysql, query, strlen(query)))
435 {
436 my_error(ER_KEYRING_MIGRATION_FAILURE, MYF(0),
437 "Failed to enable keyring_operations variable.");
438 DBUG_RETURN(true);
439 }
440 DBUG_RETURN(false);
441 }
442
443 /**
444 Standard destructor to close connection handle.
445 */
~Migrate_keyring()446 Migrate_keyring::~Migrate_keyring()
447 {
448 if (mysql)
449 {
450 delete[] m_argv;
451 m_argv= NULL;
452 mysql_close(mysql);
453 mysql= NULL;
454 if (migrate_connect_options)
455 vio_end();
456 }
457 }
458