1 /* Copyright (c) 2017, 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 "security_context_imp.h"
24 
25 #include <mysql/components/minimal_chassis.h>
26 #include "sql/auth/auth_acls.h"
27 #include "sql/current_thd.h"
28 #include "sql/sql_class.h"
29 #include "sql/sql_thd_internal_api.h"  // create_thd
30 
31 /**
32   Gets the security context for the thread.
33 
34   @param[in] _thd      The thread to get the context from
35   @param[out] out_ctx  placeholder for the security context handle
36   @retval true    failure
37   @retval false   success
38 */
39 DEFINE_BOOL_METHOD(mysql_security_context_imp::get,
40                    (void *_thd, Security_context_handle *out_ctx)) {
41   THD *thd = reinterpret_cast<THD *>(_thd);
42 
43   if (!out_ctx) return true;
44 
45   try {
46     *out_ctx =
47         reinterpret_cast<Security_context_handle>(thd->security_context());
48     return false;
49   } catch (...) {
50     mysql_components_handle_std_exception(__func__);
51   }
52   return true;
53 }
54 
55 /**
56   Sets a new security context for the thread.
57 
58   @param[in] _thd    The thread to set the context to
59   @param[in] in_ctx  The handle of the new security context
60   @retval true    failure
61   @retval false   success
62 */
63 DEFINE_BOOL_METHOD(mysql_security_context_imp::set,
64                    (void *_thd, Security_context_handle in_ctx)) {
65   THD *thd = reinterpret_cast<THD *>(_thd);
66 
67   if (!in_ctx) return true;
68 
69   try {
70     Security_context *in_sctx = reinterpret_cast<Security_context *>(in_ctx);
71     if (in_sctx) {
72       thd->set_security_context(in_sctx);
73       in_sctx->set_thd(thd);
74 
75       // Turn ON the flag in THD iff the user is granted SYSTEM_USER privilege
76       set_system_user_flag(thd);
77     }
78     return false;
79   } catch (...) {
80     mysql_components_handle_std_exception(__func__);
81   }
82   return true;
83 }
84 
85 /**
86   Creates a new security context and initializes it with the defaults
87   (no access, no user etc).
88 
89   @param[out] out_ctx  placeholder for the newly created security context
90                        handle
91   @retval true    failure
92   @retval false   success
93 */
94 DEFINE_BOOL_METHOD(mysql_security_context_imp::create,
95                    (Security_context_handle * out_ctx)) {
96   try {
97     *out_ctx =
98         reinterpret_cast<Security_context_handle>(new Security_context());
99     return false;
100   } catch (...) {
101     mysql_components_handle_std_exception(__func__);
102   }
103   return true;
104 }
105 
106 /**
107   Deallocates a security context.
108 
109   @param[in] ctx  The handle of the security context to destroy
110   @retval true    failure
111   @retval false   success
112 */
113 DEFINE_BOOL_METHOD(mysql_security_context_imp::destroy,
114                    (Security_context_handle ctx)) {
115   try {
116     delete reinterpret_cast<Security_context *>(ctx);
117     return false;
118   } catch (...) {
119     mysql_components_handle_std_exception(__func__);
120   }
121   return true;
122 }
123 
124 /**
125   Duplicates a security context.
126 
127   @param[in]  in_ctx  The handle of the security context to copy
128   @param[out] out_ctx  placeholder for the handle of the copied
129                        security context
130   @retval true    failure
131   @retval false   success
132 */
133 DEFINE_BOOL_METHOD(mysql_security_context_imp::copy,
134                    (Security_context_handle in_ctx,
135                     Security_context_handle *out_ctx)) {
136   try {
137     if (out_ctx) {
138       *out_ctx =
139           reinterpret_cast<Security_context_handle>(new Security_context());
140       if (in_ctx && *out_ctx)
141         *out_ctx = reinterpret_cast<Security_context_handle>(in_ctx);
142       else
143         return true;
144       return false;
145     }
146   } catch (...) {
147     mysql_components_handle_std_exception(__func__);
148   }
149   return true;
150 }
151 
152 /**
153   Looks up in the defined user accounts an account based on
154   the user\@host[ip] combo supplied and checks if the user
155   has access to the database requested.
156   The lookup is done in exactly the same way as at login time.
157   The new security context need to checkout additional privileges using
158   the checkout_acl method.
159   @param[in]  ctx   The handle of the security context to update
160   @param[in]  user  The user name to look up, the name has to be in utf8 charset
161   @param[in]  host  The host name to look up, the name has to be in utf8 charset
162   @param[in]  ip    The ip of the incoming connection
163   @param[in]  db    The database to check access to
164   @retval true    failure
165   @retval false   success
166 */
167 DEFINE_BOOL_METHOD(mysql_security_context_imp::lookup,
168                    (Security_context_handle ctx, const char *user,
169                     const char *host, const char *ip, const char *db)) {
170   try {
171     THD *tmp_thd = nullptr;
172     bool retval;
173     if (current_thd == nullptr) {
174       tmp_thd = create_thd(false, true, false, PSI_NOT_INSTRUMENTED);
175       if (!tmp_thd) return true;
176     }
177 
178     retval = acl_getroot(tmp_thd ? tmp_thd : current_thd,
179                          reinterpret_cast<Security_context *>(ctx), user, host,
180                          ip, db)
181                  ? true
182                  : false;
183 
184     /*
185       If it is not a new security context then update the
186       system_user flag in its referenced THD.
187     */
188     Security_context *sctx = reinterpret_cast<Security_context *>(ctx);
189     THD *sctx_thd = sctx->get_thd();
190     if (sctx_thd) set_system_user_flag(sctx_thd);
191 
192     if (tmp_thd) {
193       destroy_thd(tmp_thd);
194       tmp_thd = nullptr;
195     }
196     return retval;
197   } catch (...) {
198     mysql_components_handle_std_exception(__func__);
199   }
200   return true;
201 }
202 
203 /**
204   Reads a named security context attribute and retuns its value.
205   Currently defined names are:
206 
207   - user  MYSQL_LEX_CSTRING *  login user (a.k.a. the user's part of USER())
208   - host  MYSQL_LEX_CSTRING *  login host (a.k.a. the host's part of USER())
209   - ip    MYSQL_LEX_CSTRING *  login client ip
210   - host_or_ip MYSQL_LEX_CSTRING *  host, if present, ip if not.
211   - priv_user  MYSQL_LEX_CSTRING *  authenticated user
212                (a.k.a. the user's part of CURRENT_USER())
213   - priv_host  MYSQL_LEX_CSTRING * authenticated host
214                (a.k.a. the host's part of CURRENT_USER())
215   - proxy_user  MYSQL_LEX_CSTRING *  the proxy user used in authenticating
216 
217   - privilege_super DECLARE_BOOL_METHOD *  1 if the user account has
218     supper privilege, 0 otherwise
219   - privilege_execute DECLARE_BOOL_METHOD *  1 if the user account has
220     execute privilege, 0 otherwise
221 
222   @param[in]  ctx_h   The handle of the security context to read from
223   @param[in]  name  The option name to read
224   @param[out] inout_pvalue The value of the option. Type depends on the name.
225   @retval true    failure
226   @retval false   success
227 */
228 DEFINE_BOOL_METHOD(mysql_security_context_imp::get,
229                    (Security_context_handle ctx_h, const char *name,
230                     void *inout_pvalue)) {
231   try {
232     Security_context *ctx = reinterpret_cast<Security_context *>(ctx_h);
233     if (inout_pvalue) {
234       if (!strcmp(name, "user")) {
235         *((MYSQL_LEX_CSTRING *)inout_pvalue) = ctx->user();
236       } else if (!strcmp(name, "host")) {
237         *((MYSQL_LEX_CSTRING *)inout_pvalue) = ctx->host();
238       } else if (!strcmp(name, "ip")) {
239         *((MYSQL_LEX_CSTRING *)inout_pvalue) = ctx->ip();
240       } else if (!strcmp(name, "host_or_ip")) {
241         *((MYSQL_LEX_CSTRING *)inout_pvalue) = ctx->host_or_ip();
242       } else if (!strcmp(name, "priv_user")) {
243         *((MYSQL_LEX_CSTRING *)inout_pvalue) = ctx->priv_user();
244       } else if (!strcmp(name, "priv_host")) {
245         *((MYSQL_LEX_CSTRING *)inout_pvalue) = ctx->priv_host();
246       } else if (!strcmp(name, "proxy_user")) {
247         *((MYSQL_LEX_CSTRING *)inout_pvalue) = ctx->proxy_user();
248       } else if (!strcmp(name, "external_user")) {
249         *((MYSQL_LEX_CSTRING *)inout_pvalue) = ctx->external_user();
250       } else if (!strcmp(name, "privilege_super")) {
251         bool checked = ctx->check_access(SUPER_ACL);
252         *((bool *)inout_pvalue) = checked ? true : false;
253       } else if (!strcmp(name, "privilege_execute")) {
254         bool checked = ctx->check_access(EXECUTE_ACL);
255         *((bool *)inout_pvalue) = checked ? true : false;
256       } else
257         return true; /* invalid option */
258     }
259     return false;
260   } catch (...) {
261     mysql_components_handle_std_exception(__func__);
262   }
263   return true;
264 }
265 
266 /**
267   Sets a value for a named security context attribute
268   Currently defined names are:
269 
270   - user  MYSQL_LEX_CSTRING *  login user (a.k.a. the user's part of USER())
271   - host  MYSQL_LEX_CSTRING *  login host (a.k.a. the host's part of USER())
272   - ip    MYSQL_LEX_CSTRING *  login client ip
273   - priv_user  MYSQL_LEX_CSTRING *  authenticated user
274                (a.k.a. the user's part of CURRENT_USER())
275   - priv_host  MYSQL_LEX_CSTRING *  authenticated host
276                (a.k.a. the host's part of CURRENT_USER())
277   - proxy_user MYSQL_LEX_CSTRING *  the proxy user used in authenticating
278 
279   - privilege_super  DECLARE_BOOL_METHOD * 1 if the user account has
280                      supper privilege, 0 otherwise
281   - privilege_execute DECLARE_BOOL_METHOD *  1 if the user account has
282     execute privilege, 0 otherwise
283 
284   @param[in]  ctx_h   The handle of the security context to set into
285   @param[in]  name  The option name to set
286   @param[in]  pvalue The value of the option. Type depends on the name.
287   @retval true    failure
288   @retval false   success
289 */
290 DEFINE_BOOL_METHOD(mysql_security_context_imp::set,
291                    (Security_context_handle ctx_h, const char *name,
292                     void *pvalue)) {
293   try {
294     Security_context *ctx = reinterpret_cast<Security_context *>(ctx_h);
295     if (!strcmp(name, "user")) {
296       LEX_CSTRING *value = (LEX_CSTRING *)pvalue;
297       ctx->assign_user(value->str, value->length);
298     } else if (!strcmp(name, "host")) {
299       LEX_CSTRING *value = (LEX_CSTRING *)pvalue;
300       ctx->assign_host(value->str, value->length);
301     } else if (!strcmp(name, "ip")) {
302       LEX_CSTRING *value = (LEX_CSTRING *)pvalue;
303       ctx->assign_ip(value->str, value->length);
304     } else if (!strcmp(name, "priv_user")) {
305       LEX_CSTRING *value = (LEX_CSTRING *)pvalue;
306       ctx->assign_priv_user(value->str, value->length);
307     } else if (!strcmp(name, "priv_host")) {
308       LEX_CSTRING *value = (LEX_CSTRING *)pvalue;
309       ctx->assign_priv_host(value->str, value->length);
310     } else if (!strcmp(name, "proxy_user")) {
311       LEX_CSTRING *value = (LEX_CSTRING *)pvalue;
312       ctx->assign_proxy_user(value->str, value->length);
313     } else if (!strcmp(name, "privilege_super")) {
314       char value = *(char *)pvalue;
315       if (value)
316         ctx->set_master_access(ctx->master_access() | (SUPER_ACL),
317                                ctx->restrictions());
318       else
319         ctx->set_master_access(ctx->master_access() & ~(SUPER_ACL),
320                                ctx->restrictions());
321     } else if (!strcmp(name, "privilege_execute")) {
322       char value = *(char *)pvalue;
323       if (value)
324         ctx->set_master_access(ctx->master_access() | (EXECUTE_ACL),
325                                ctx->restrictions());
326       else
327         ctx->set_master_access(ctx->master_access() & ~(EXECUTE_ACL),
328                                ctx->restrictions());
329     } else
330       return true; /* invalid option */
331     return false;
332   } catch (...) {
333     mysql_components_handle_std_exception(__func__);
334   }
335   return true;
336 }
337