1 /* Copyright (c) 2015, 2017, 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 "my_global.h"
24 #include "sql_bootstrap.h"
25 #include "sql_initialize.h"
26 #include "my_rnd.h"
27 #include "m_ctype.h"
28 #include "mysqld.h"
29 #include <my_sys.h>
30 #include "sql_authentication.h"
31 #include "log.h"
32 #include "sql_class.h"
33 #include "sql_show.h"
34
35 #include "../scripts/sql_commands_system_tables.h"
36 #include "../scripts/sql_commands_system_data.h"
37 #include "../scripts/sql_commands_help_data.h"
38 #include "../scripts/sql_commands_sys_schema.h"
39
40 static const char *initialization_cmds[] =
41 {
42 "CREATE DATABASE mysql;\n",
43 "USE mysql;\n",
44 NULL
45 };
46
47 #define INSERT_USER_CMD "CREATE USER root@localhost IDENTIFIED BY '%s' PASSWORD EXPIRE;\n"
48 #define INSERT_USER_CMD_INSECURE "CREATE USER root@localhost;\n"
49 #define GENERATED_PASSWORD_LENGTH 12
50
51 char insert_user_buffer[sizeof(INSERT_USER_CMD) + GENERATED_PASSWORD_LENGTH * 2];
52
53 my_bool opt_initialize_insecure= FALSE;
54
55 static const char *initialization_data[] =
56 {
57 "FLUSH PRIVILEGES",
58 insert_user_buffer,
59 "GRANT ALL PRIVILEGES ON *.* TO root@localhost WITH GRANT OPTION;\n",
60 "GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION;\n",
61 NULL
62 };
63
64 static const char *session_service_initialization_data[] =
65 {
66 "CREATE USER 'mysql.session'@localhost IDENTIFIED "
67 "WITH mysql_native_password AS '*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE' "
68 "ACCOUNT LOCK;\n",
69 "REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'mysql.session'@localhost;\n",
70 "GRANT SELECT ON mysql.user TO 'mysql.session'@localhost;\n",
71 "GRANT SELECT ON performance_schema.* TO 'mysql.session'@localhost;\n",
72 "GRANT SUPER ON *.* TO 'mysql.session'@localhost;\n",
73 NULL
74 };
75
76 static const char** cmds[]=
77 {
78 initialization_cmds,
79 mysql_system_tables,
80 initialization_data,
81 mysql_system_data,
82 fill_help_tables,
83 session_service_initialization_data,
84 mysql_sys_schema,
85 NULL
86 };
87
88 /** keep in sync with the above array */
89 static const char *cmd_descs[]=
90 {
91 "Creating the system database",
92 "Creating the system tables",
93 "Filling in the system tables, part 1",
94 "Filling in the system tables, part 2",
95 "Filling in the mysql.help table",
96 "Creating user for internal session service",
97 "Creating the sys schema",
98 NULL
99 };
100
101
generate_password(char * password,int size)102 static void generate_password(char *password, int size)
103 {
104
105 #define UPCHARS "QWERTYUIOPASDFGHJKLZXCVBNM"
106 #define LOWCHARS "qwertyuiopasdfghjklzxcvbnm"
107 #define NUMCHARS "1234567890"
108 #define SYMCHARS ",.-+*;:_!#%&/()=?><"
109 #define rnd_of(x) x[((int) (my_rnd_ssl(&srnd) * 100)) % \
110 (sizeof(x) - 1)]
111
112 static const char g_allowed_pwd_chars[]=
113 LOWCHARS SYMCHARS UPCHARS NUMCHARS;
114 static const char g_upper_case_chars[]= UPCHARS;
115 static const char g_lower_case_chars[]= LOWCHARS;
116 static const char g_numeric_chars[]= NUMCHARS;
117 static const char g_special_chars[]= SYMCHARS;
118 rand_struct srnd;
119 char *ptr= password;
120 bool had_upper= false, had_lower= false,
121 had_numeric= false, had_special= false;
122
123 for (; size > 0; --size)
124 {
125 char ch= rnd_of(g_allowed_pwd_chars);
126
127 /*
128 Ensure we have a password that conforms to the strong
129 password validation plugin ploicy by re-drawing specially
130 the last 4 chars if there's need.
131 */
132 if (size == 4 && !had_lower)
133 {
134 ch= rnd_of(g_lower_case_chars);
135 had_lower= true;
136 }
137 else if (size == 3 && !had_numeric)
138 {
139 ch= rnd_of(g_numeric_chars);
140 had_numeric= true;
141 }
142 else if (size == 2 && !had_special)
143 {
144 ch= rnd_of(g_special_chars);
145 had_special= true;
146 }
147 else if (size == 1 && !had_upper)
148 {
149 ch= rnd_of(g_upper_case_chars);
150 had_upper= true;
151 }
152
153 if (!had_upper && strchr(g_upper_case_chars, ch))
154 had_upper= true;
155 else if (!had_lower && strchr(g_lower_case_chars, ch))
156 had_lower= true;
157 else if (!had_numeric && strchr(g_numeric_chars, ch))
158 had_numeric= true;
159 else if (!had_special && strchr(g_special_chars, ch))
160 had_special= true;
161
162 *ptr++= ch;
163
164 }
165 }
166
167
168 /* these globals don't need protection since it's single-threaded execution */
169 static int cmds_ofs=0, cmd_ofs= 0;
170 static File_command_iterator *init_file_iter= NULL;
171
begin(void)172 void Compiled_in_command_iterator::begin(void)
173 {
174 cmds_ofs= cmd_ofs= 0;
175
176 is_active= true;
177 sql_print_information("%s", cmd_descs[cmds_ofs]);
178 if (opt_initialize_insecure)
179 {
180 strcpy(insert_user_buffer, INSERT_USER_CMD_INSECURE);
181 sql_print_warning("root@localhost is created with an empty password ! "
182 "Please consider switching off the --initialize-insecure option.");
183 }
184 else
185 {
186 char password[GENERATED_PASSWORD_LENGTH + 1];
187 char escaped_password[GENERATED_PASSWORD_LENGTH * 2 + 1];
188 ulong saved_verbosity= log_error_verbosity;
189
190 generate_password(password, GENERATED_PASSWORD_LENGTH);
191 password[GENERATED_PASSWORD_LENGTH]= 0;
192
193 /*
194 Temporarily bump verbosity to print the password.
195 It's safe to do it since we're the sole process running.
196 */
197 log_error_verbosity= 3;
198 sql_print_information(
199 "A temporary password is generated for root@localhost: %s", password);
200 log_error_verbosity= saved_verbosity;
201
202 escape_string_for_mysql(&my_charset_bin,
203 escaped_password, sizeof(escaped_password),
204 password, GENERATED_PASSWORD_LENGTH);
205
206 sprintf(insert_user_buffer, INSERT_USER_CMD, escaped_password);
207 }
208 }
209
210
next(std::string & query,int * read_error,int * query_source)211 int Compiled_in_command_iterator::next(std::string &query, int *read_error,
212 int *query_source)
213 {
214 if (init_file_iter)
215 return init_file_iter->next(query, read_error, query_source);
216
217 *query_source= QUERY_SOURCE_COMPILED;
218 while (cmds[cmds_ofs] != NULL && cmds[cmds_ofs][cmd_ofs] == NULL)
219 {
220 cmds_ofs++;
221 if (cmds[cmds_ofs] != NULL)
222 sql_print_information("%s", cmd_descs[cmds_ofs]);
223 cmd_ofs= 0;
224 }
225
226 if (cmds[cmds_ofs] == NULL)
227 {
228 if (opt_init_file)
229 {
230 /* need to allow error reporting */
231 THD *thd= current_thd;
232 thd->get_stmt_da()->set_overwrite_status(true);
233 init_file_iter= new File_command_iterator(opt_init_file);
234 if (!init_file_iter->has_file())
235 {
236 sql_print_error("Failed to open the bootstrap file %s", opt_init_file);
237 /* in case of error in open */
238 delete init_file_iter;
239 init_file_iter= NULL;
240 return READ_BOOTSTRAP_ERROR;
241 }
242 init_file_iter->begin();
243 return init_file_iter->next(query, read_error, query_source);
244 }
245
246 return READ_BOOTSTRAP_EOF;
247 }
248
249 query.assign(cmds[cmds_ofs][cmd_ofs++]);
250 return READ_BOOTSTRAP_SUCCESS;
251 }
252
end(void)253 void Compiled_in_command_iterator::end(void)
254 {
255 if (init_file_iter)
256 {
257 init_file_iter->end();
258 delete init_file_iter;
259 init_file_iter= NULL;
260 }
261 if (is_active)
262 {
263 sql_print_information("Bootstrapping complete");
264 is_active= false;
265 }
266 }
267
268
269 /**
270 Create the data directory
271
272 Creates the data directory when --initialize is specified.
273 The directory is created when it does not exist.
274 If it exists, is empty and the process can write into it
275 no action is taken and the directory is accepted.
276 Otherwise an error is thrown.
277 "Empty" means no files other than the ones starting with "."
278 or in the --ignore-db list.
279
280 @param data_home the normalized path to the data directory
281 @return status
282 @retval true failed to create. Error printed.
283 @retval false success
284 */
initialize_create_data_directory(const char * data_home)285 bool initialize_create_data_directory(const char *data_home)
286 {
287 MY_DIR *dir;
288 int flags=
289 #ifdef _WIN32
290 0
291 #else
292 S_IRWXU | S_IRGRP | S_IXGRP
293 #endif
294 ;
295
296 if (NULL != (dir= my_dir(data_home, MYF(MY_DONT_SORT))))
297 {
298 bool no_files= true;
299 char path[FN_REFLEN];
300 File fd;
301
302 /*
303 Ignore files starting with . and in the --ignore-db list.
304 This is exactly how find_files() in sql_show.cc operates.
305 */
306 for (uint i=0; i < dir->number_off_files; i++)
307 {
308 FILEINFO *file= dir->dir_entry + i;
309 if (file->name[0] != '.' &&
310 !is_in_ignore_db_dirs_list(file->name))
311 {
312 no_files= false;
313 break;
314 }
315 }
316
317 my_dirend(dir);
318
319 if (!no_files)
320 {
321 sql_print_error("--initialize specified but the data directory"
322 " has files in it. Aborting.");
323 return true; /* purecov: inspected */
324 }
325
326 sql_print_information("--initialize specifed on an existing data directory.");
327
328 if (NULL == fn_format(path, "is_writable", data_home, "",
329 MY_UNPACK_FILENAME | MY_SAFE_PATH))
330 {
331 sql_print_error("--initialize specified but the data directory"
332 " exists and the path is too long. Aborting.");
333 return true; /* purecov: inspected */
334
335 }
336 if (-1 != (fd= my_create(path, 0, flags, MYF(MY_WME))))
337 {
338 my_close(fd, MYF(MY_WME));
339 my_delete(path, MYF(MY_WME));
340 }
341 else
342 {
343 sql_print_error("--initialize specified but the data directory"
344 " exists and is not writable. Aborting.");
345 return true; /* purecov: inspected */
346 }
347
348 /* the data dir found is usable */
349 return false;
350 }
351
352 sql_print_information("Creating the data directory %s", data_home);
353 if (my_mkdir(data_home, flags, MYF(MY_WME)))
354 return true; /* purecov: inspected */
355
356 return false;
357 }
358