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