1 /* Copyright (c) 2016, 2021, Oracle and/or its affiliates.
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 <mysql/plugin_keyring.h>
25 #include <my_rnd.h>
26 #include "keyring.h"
27 #include "buffered_file_io.h"
28 
29 #ifdef _WIN32
30 #define MYSQL_DEFAULT_KEYRINGFILE MYSQL_KEYRINGDIR"\\keyring"
31 #else
32 #define MYSQL_DEFAULT_KEYRINGFILE MYSQL_KEYRINGDIR"/keyring"
33 #endif
34 
35 using keyring::Buffered_file_io;
36 using keyring::Keys_container;
37 using keyring::Keys_iterator;
38 using keyring::Logger;
39 
40 mysql_rwlock_t LOCK_keyring;
41 
check_keyring_file_data(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * save,st_mysql_value * value)42 int check_keyring_file_data(MYSQL_THD thd  MY_ATTRIBUTE((unused)),
43                             struct st_mysql_sys_var *var  MY_ATTRIBUTE((unused)),
44                             void *save, st_mysql_value *value)
45 {
46   char            buff[FN_REFLEN+1];
47   const char      *keyring_filename;
48   int             len = sizeof(buff);
49   boost::movelib::unique_ptr<IKeys_container> new_keys(new Keys_container(logger.get()));
50 
51   (*(const char **) save)= NULL;
52   keyring_filename= value->val_str(value, buff, &len);
53   mysql_rwlock_wrlock(&LOCK_keyring);
54   if (create_keyring_dir_if_does_not_exist(keyring_filename))
55   {
56     mysql_rwlock_unlock(&LOCK_keyring);
57     logger->log(MY_ERROR_LEVEL, "keyring_file_data cannot be set to new value"
58       " as the keyring file cannot be created/accessed in the provided path");
59     return 1;
60   }
61   try
62   {
63     IKeyring_io *keyring_io(new Buffered_file_io(logger.get()));
64     if (new_keys->init(keyring_io, keyring_filename))
65     {
66       mysql_rwlock_unlock(&LOCK_keyring);
67       return 1;
68     }
69     *reinterpret_cast<IKeys_container **>(save)= new_keys.get();
70     new_keys.release();
71     mysql_rwlock_unlock(&LOCK_keyring);
72   }
73   catch (...)
74   {
75     mysql_rwlock_unlock(&LOCK_keyring);
76     return 1;
77   }
78   return(0);
79 }
80 
81 static char *keyring_file_data_value= NULL;
82 static MYSQL_SYSVAR_STR(
83   data,                                                        /* name       */
84   keyring_file_data_value,                                     /* value      */
85   PLUGIN_VAR_RQCMDARG,                                         /* flags      */
86   "The path to the keyring file. Must be specified",           /* comment    */
87   check_keyring_file_data,                                     /* check()    */
88   update_keyring_file_data,                                    /* update()   */
89   MYSQL_DEFAULT_KEYRINGFILE                                    /* default    */
90 );
91 static MYSQL_SYSVAR_BOOL(open_mode, keyring_open_mode,
92                          PLUGIN_VAR_INVISIBLE | PLUGIN_VAR_RQCMDARG,
93                          "Mode in which keyring file should be opened", NULL,
94                          NULL, TRUE);
95 
96 static struct st_mysql_sys_var *keyring_file_system_variables[]= {
97   MYSQL_SYSVAR(data),
98   MYSQL_SYSVAR(open_mode),
99   NULL
100 };
101 
keyring_init(MYSQL_PLUGIN plugin_info)102 static int keyring_init(MYSQL_PLUGIN plugin_info)
103 {
104   try
105   {
106 #ifdef HAVE_PSI_INTERFACE
107     keyring_init_psi_keys();
108 #endif
109 
110     DBUG_EXECUTE_IF("simulate_keyring_init_error", return TRUE;);
111 
112     if (init_keyring_locks())
113       return TRUE;
114 
115     logger.reset(new Logger(plugin_info));
116     if (create_keyring_dir_if_does_not_exist(keyring_file_data_value))
117     {
118       logger->log(MY_ERROR_LEVEL, "Could not create keyring directory "
119         "The keyring_file will stay unusable until correct path to the keyring "
120         "directory gets provided");
121       return TRUE;
122     }
123     keys.reset(new Keys_container(logger.get()));
124     IKeyring_io *keyring_io= new Buffered_file_io(logger.get());
125     if (keys->init(keyring_io, keyring_file_data_value))
126     {
127       is_keys_container_initialized = FALSE;
128       logger->log(MY_ERROR_LEVEL, "keyring_file initialization failure. Please check"
129         " if the keyring_file_data points to readable keyring file or keyring file"
130         " can be created in the specified location. "
131         "The keyring_file will stay unusable until correct path to the keyring file "
132         "gets provided");
133       return TRUE;
134     }
135     is_keys_container_initialized = TRUE;
136     return FALSE;
137   }
138   catch (...)
139   {
140     if (logger != NULL)
141       logger->log(MY_ERROR_LEVEL, "keyring_file initialization failure due to internal"
142                                   " exception inside the plugin");
143     return TRUE;
144   }
145 }
146 
keyring_deinit(void * arg MY_ATTRIBUTE ((unused)))147 int keyring_deinit(void *arg MY_ATTRIBUTE((unused)))
148 {
149   //not taking a lock here as the calls to keyring_deinit are serialized by
150   //the plugin framework
151   keys.reset();
152   logger.reset();
153   keyring_file_data.reset();
154   mysql_rwlock_destroy(&LOCK_keyring);
155   return 0;
156 }
157 
mysql_key_fetch(const char * key_id,char ** key_type,const char * user_id,void ** key,size_t * key_len)158 my_bool mysql_key_fetch(const char *key_id, char **key_type, const char *user_id,
159                         void **key, size_t *key_len)
160 {
161   return mysql_key_fetch<keyring::Key>(key_id, key_type, user_id, key, key_len,
162                                        "keyring_file");
163 }
164 
mysql_key_store(const char * key_id,const char * key_type,const char * user_id,const void * key,size_t key_len)165 my_bool mysql_key_store(const char *key_id, const char *key_type,
166                         const char *user_id, const void *key, size_t key_len)
167 {
168   return mysql_key_store<keyring::Key>(key_id, key_type, user_id, key, key_len,
169                                        "keyring_file");
170 }
171 
mysql_key_remove(const char * key_id,const char * user_id)172 my_bool mysql_key_remove(const char *key_id, const char *user_id)
173 {
174   return mysql_key_remove<keyring::Key>(key_id, user_id, "keyring_file");
175 }
176 
177 
mysql_key_generate(const char * key_id,const char * key_type,const char * user_id,size_t key_len)178 my_bool mysql_key_generate(const char *key_id, const char *key_type,
179                            const char *user_id, size_t key_len)
180 {
181   try
182   {
183     boost::movelib::unique_ptr<IKey> key_candidate(new keyring::Key(key_id, key_type, user_id, NULL, 0));
184 
185     boost::movelib::unique_ptr<uchar[]> key(new uchar[key_len]);
186     if (key.get() == NULL)
187       return TRUE;
188     memset(key.get(), 0, key_len);
189     if (is_keys_container_initialized == FALSE || check_key_for_writing(key_candidate.get(), "generating") ||
190         my_rand_buffer(key.get(), key_len))
191       return TRUE;
192 
193     return mysql_key_store(key_id, key_type, user_id, key.get(), key_len) == TRUE;
194   }
195   catch (...)
196   {
197     if (logger != NULL)
198       logger->log(MY_ERROR_LEVEL, "Failed to generate a key due to internal exception inside keyring_file plugin");
199     return TRUE;
200   }
201 }
202 
mysql_key_iterator_init(void ** key_iterator)203 static void mysql_key_iterator_init(void **key_iterator)
204 {
205   *key_iterator= new Keys_iterator(logger.get());
206   mysql_key_iterator_init<keyring::Key>(static_cast<Keys_iterator*>(*key_iterator),
207                                                "keyring_file");
208 }
209 
mysql_key_iterator_deinit(void * key_iterator)210 static void mysql_key_iterator_deinit(void *key_iterator)
211 {
212   mysql_key_iterator_deinit<keyring::Key>(static_cast<Keys_iterator*>(key_iterator),
213                                           "keyring_file");
214   delete static_cast<Keys_iterator*>(key_iterator);
215 }
216 
mysql_key_iterator_get_key(void * key_iterator,char * key_id,char * user_id)217 static bool mysql_key_iterator_get_key(void *key_iterator,
218                                        char *key_id, char *user_id)
219 {
220   return mysql_key_iterator_get_key<keyring::Key>(static_cast<Keys_iterator*>(key_iterator),
221                                                   key_id, user_id, "keyring_file");
222 }
223 
224 /* Plugin type-specific descriptor */
225 static struct st_mysql_keyring keyring_descriptor=
226 {
227   MYSQL_KEYRING_INTERFACE_VERSION,
228   mysql_key_store,
229   mysql_key_fetch,
230   mysql_key_remove,
231   mysql_key_generate,
232   mysql_key_iterator_init,
233   mysql_key_iterator_deinit,
234   mysql_key_iterator_get_key
235 };
236 
mysql_declare_plugin(keyring_file)237 mysql_declare_plugin(keyring_file)
238 {
239   MYSQL_KEYRING_PLUGIN,                                   /*   type                            */
240   &keyring_descriptor,                                    /*   descriptor                      */
241   "keyring_file",                                         /*   name                            */
242   "Oracle Corporation",                                   /*   author                          */
243   "store/fetch authentication data to/from a flat file",  /*   description                     */
244   PLUGIN_LICENSE_GPL,
245   keyring_init,                                           /*   init function (when loaded)     */
246   keyring_deinit,                                         /*   deinit function (when unloaded) */
247   0x0100,                                                 /*   version                         */
248   NULL,                                                   /*   status variables                */
249   keyring_file_system_variables,                          /*   system variables                */
250   NULL,
251   0,
252 }
253 mysql_declare_plugin_end;
254