I Use This!
High Activity

News

Analyzed about 6 hours ago. based on code collected about 16 hours ago.
Posted over 2 years ago by Frederic Descamps
This post is the seventh post of a series of articles on extending MySQL with the Component Infrastructure, the list above will be updated as new articles are published: Extending MySQL using the Component Infrastructure – part 1Extending MySQL ... [More] using the Component Infrastructure – part 2: building the serverExtending MySQL using the Component Infrastructure – part 3: component servicesExtending MySQL using the Component Infrastructure – part 4: error loggingExtending MySQL using the Component Infrastructure – part 5: privilegesExtending MySQL using the Component Infrastructure – part 6: functionsExtending MySQL using the Component Infrastructure – part 7: messages to usersIn the previous post, we coded the check for the right privilege to be able to run our function. We were able to log a message in error log but the user didn’t receive any message. Additionally, logging a message in error log is only recommended when we want an admin to see it, now we want to user to get the message. This is exactly what we gonna fix in this article. As usual, the component infrastructure provides a service we can use to also print an error to the user: the MySQL Runtime Error Service included in mysql/components/services/mysql_runtime_error_service.h. Let’s modify our scan.h to include that file and add the required service place holder: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #include #include /* LogComponentErr */ #include /* Errors */ #include #include #include #include #include #include #include #include extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins); extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); extern REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); extern REQUIRES_SERVICE_PLACEHOLDER(udf_registration); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context); extern REQUIRES_SERVICE_PLACEHOLDER(global_grants_check); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_runtime_error); extern SERVICE_TYPE(log_builtins) * log_bi; extern SERVICE_TYPE(log_builtins_string) * log_bs; view raw scan.h hosted with ❤ by GitHub This is what we did on lines 29 and 44. Now we can also update scan.cc to require that service and update our viruscan_udf function to use that service using the right error message (ER_SPECIFIC_ACCESS_DENIED_ERROR) to replace the message in error log on line 132 (you can find those message in include/mysqld_ername.h): This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #define NO_SIGNATURE_CHANGE 0 #define SIGNATURE_CHANGE 1 #include REQUIRES_SERVICE_PLACEHOLDER(log_builtins); REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); REQUIRES_SERVICE_PLACEHOLDER(udf_registration); REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context); REQUIRES_SERVICE_PLACEHOLDER(global_grants_check); REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader); REQUIRES_SERVICE_PLACEHOLDER(mysql_runtime_error); SERVICE_TYPE(log_builtins) * log_bi; SERVICE_TYPE(log_builtins_string) * log_bs; static const char *SCAN_PRIVILEGE_NAME = "VIRUS_SCAN"; class udf_list { typedef std::list udf_list_t; public: ~udf_list() { unregister(); } bool add_scalar(const char *func_name, enum Item_result return_type, Udf_func_any func, Udf_func_init init_func = NULL, Udf_func_deinit deinit_func = NULL) { if (!mysql_service_udf_registration->udf_register( func_name, return_type, func, init_func, deinit_func)) { set.push_back(func_name); return false; } return true; } bool unregister() { udf_list_t delete_set; /* try to unregister all of the udfs */ for (auto udf : set) { int was_present = 0; if (!mysql_service_udf_registration->udf_unregister(udf.c_str(), &was_present) || !was_present) delete_set.push_back(udf); } /* remove the unregistered ones from the list */ for (auto udf : delete_set) set.remove(udf); /* success: empty set */ if (set.empty()) return false; /* failure: entries still in the set */ return true; } private: udf_list_t set; } * list; namespace udf_impl { bool have_virus_scan_privilege(void *opaque_thd) { // get the security context of the thread Security_context_handle ctx = nullptr; if (mysql_service_mysql_thd_security_context->get(opaque_thd, &ctx) || !ctx) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "problem trying to get security context"); return false; } if (mysql_service_global_grants_check->has_global_grant( ctx, SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) return true; return false; } const char *udf_init = "udf_init", *my_udf = "my_udf", *my_udf_clear = "my_clear", *my_udf_add = "my_udf_add"; static bool viruscan_udf_init(UDF_INIT *initid, UDF_ARGS *, char *) { const char* name = "utf8mb4"; char *value = const_cast(name); initid->ptr = const_cast(udf_init); if (mysql_service_mysql_udf_metadata->result_set( initid, "charset", const_cast(value))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "failed to set result charset"); return false; } return 0; } static void viruscan_udf_deinit(__attribute__((unused)) UDF_INIT *initid) { assert(initid->ptr == udf_init || initid->ptr == my_udf); } const char *viruscan_udf(UDF_INIT *initid, UDF_ARGS *args, char *outp, unsigned long *length, char *is_null, char *error) { MYSQL_THD thd; mysql_service_mysql_current_thread_reader->get(&thd); if(!have_virus_scan_privilege(thd)) { mysql_error_service_printf( ER_SPECIFIC_ACCESS_DENIED_ERROR, 0, SCAN_PRIVILEGE_NAME); return 0; } strcpy(outp, "we do nothing yet"); *length = strlen(outp); return const_cast(outp); } } /* namespace udf_impl */ static mysql_service_status_t viruscan_service_init() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "initializing…"); // Registration of the privilege if (mysql_service_dynamic_privilege_register->register_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not register privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "new privilege 'VIRUS_SCAN' has been registered successfully."); } list = new udf_list(); if (list->add_scalar("virus_scan", Item_result::STRING_RESULT, (Udf_func_any)udf_impl::viruscan_udf, udf_impl::viruscan_udf_init, udf_impl::viruscan_udf_deinit)) { delete list; return 1; /* failure: one of the UDF registrations failed */ } return result; } static mysql_service_status_t viruscan_service_deinit() { mysql_service_status_t result = 0; if (mysql_service_dynamic_privilege_register->unregister_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not unregister privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "privilege 'VIRUS_SCAN' has been unregistered successfully."); } if (list->unregister()) return 1; /* failure: some UDFs still in use */ delete list; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "uninstalled."); return result; } BEGIN_COMPONENT_PROVIDES(viruscan_service) END_COMPONENT_PROVIDES(); BEGIN_COMPONENT_REQUIRES(viruscan_service) REQUIRES_SERVICE(log_builtins), REQUIRES_SERVICE(log_builtins_string), REQUIRES_SERVICE(dynamic_privilege_register), REQUIRES_SERVICE(mysql_udf_metadata), REQUIRES_SERVICE(udf_registration), REQUIRES_SERVICE(mysql_thd_security_context), REQUIRES_SERVICE(global_grants_check), REQUIRES_SERVICE(mysql_current_thread_reader), REQUIRES_SERVICE(mysql_runtime_error), END_COMPONENT_REQUIRES(); /* A list of metadata to describe the Component. */ BEGIN_COMPONENT_METADATA(viruscan_service) METADATA("mysql.author", "Oracle Corporation"), METADATA("mysql.license", "GPL"), METADATA("mysql.dev", "lefred"), END_COMPONENT_METADATA(); /* Declaration of the Component. */ DECLARE_COMPONENT(viruscan_service, "mysql:viruscan_service") viruscan_service_init, viruscan_service_deinit END_DECLARE_COMPONENT(); /* Defines list of Components contained in this library. Note that for now we assume that library will have exactly one Component. */ DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(viruscan_service) END_DECLARE_LIBRARY_COMPONENTS view raw scan.cc hosted with ❤ by GitHub Let’s test it after compiling the new code and running mtr again (see part 2): Excellent, this is exactly what we were looking for. Extra On lines 91 and 92 we get the security context. I would like to show that from that security context we can for example get the user and host: Security_context_handle ctx = nullptr; mysql_service_mysql_thd_security_context->get(thd, &ctx); MYSQL_LEX_CSTRING user; MYSQL_LEX_CSTRING host; mysql_service_mysql_security_context_options->get(ctx, "priv_user", &user); mysql_service_mysql_security_context_options->get(ctx, "priv_host", &host); Conclusion In this article we saw how we can return messages to the user using the appropriate component service. The next article will cover how to use libclamav from our component. Stay tuned, happy coding and enjoy MySQL ! [Less]
Posted over 2 years ago by Frederic Descamps
For my presentation for the next FOSDEM MySQL Devroom, I needed to create some load. As usual, for this task, what better than sysbench ? The “problem” was that my system was already setup and I used OL7 on the always free Ampere compute ... [More] instance on OCI. As you may know, this is an ARM architecture (aarch64) and there was not sysbench rpm package available for this OS and architecture. I also wanted to have sysbench compiled and linked with MySQL 8.0 libraries [1], [2]. And this is why I built this package that requires also luajit-2.10. Here are the packages in case you are interested. sysbench-1.0.20-6.el7.aarch64Download luajit-2.1.0-1.16beta3.el7.aarch64Download Enjoy MySQL and enjoy Sysbench ! [Less]
Posted over 2 years ago by Codership
There are plenty of articles on the Internet that tell you to setup a Galera Cluster by disabling an OS based firewall and also disabling SELinux. While we agree that this might be the fastest way to get your Galera Cluster setup, it is not ... [More] necessarily good security hygiene, and we would prefer if you started 2022 with a bit more secure Galera Cluster! What is SELinux? Is is Security-Enhanced Linux that allows administrators to allow who has more control over the system. It has permissive and enforcing mode, and is turned on by default in Red Hat Enterprise Linux and derivatives. It is important to remember that if you install Galera Cluster via a package that we provide, we have provided all the necessary contexts for it. You effectively do not have to disable SELinux to get started. However, if you are using the rsync method for a physical state snapshot (SST), then you can test to see if having SELinux on makes sense. To test it, on a donor node, type: setenforce 1 (this should be the default, for what it is worth). Then on a joiner node, trigger an SST (presuming you set it up to use rsync), by doing: service mysql stop rm -rf /var/lib/mysql/* service mysql start Look at the mysqld.log (you should always have error logging enabled; from a MySQL standpoint, this is where your generated password is stored for first login; and from a Galera Cluster standpoint this is where all kinds of important messages are written) and you’ll see it is failing to complete the SST. You can fix this quickly by doing setenforce 0 on the donor node. Now, the better thing to do is to write SELinux contexts, or just switch from using the rsync method to XtraBackup or the new CLONE SST method. What about a firewall? We require TCP for 3306 (it is MySQL’s port), TCP & UDP for 4567 (cluster replication traffic), TCP for 4568 for Incremental State Transfers and TCP for 4444 for State Snapshot Transfers. We have extensive documentation of using firewalld, and iptables. For what it is worth, Galera Cluster also runs well on FreeBSD (and other BSD derivatives), so there is also documentation on pf. How do you test if the firewall is the problem? On the joiner node, do: iptables -A INPUT -p tcp --destination-port 4444 -j DROP Now, attempt an SST just like above where you remove the data directory. Look at the log files. The fix, is of course simple: iptables -D INPUT -p tcp --destination-port 4444 -j DROP So here we hope you try running a more secure Galera Cluster environment this year. The Galera Manager configures this automatically for you, so you might also consider using it. We have a very active support group on GitHub issues going on as well. Do try it and check it out. [Less]
Posted over 2 years ago by Oracle MySQL Group
MySQL Database Service with HeatWave is now available globally in 30 regions Oracle Cloud Infrastructure commercial regions. The most recent addition is Johannesburg in South Africa. 
Posted over 2 years ago by Frederic Descamps
Hello, Due to a complete misunderstanding about the available slots, FOSDEM’s organization had to remove 2 talks from the previous planned agenda. However, we didn’t want to announce to previously accepted speakers that their talks were ... [More] cancelled ! Therefore, we decided, with FOSDEM’s approval, to extend the FOSDEM 2022 MySQL Devroom, opening our room on Saturday afternoon. Two talks were then moved to Saturday and the two backup talks were immediately added to the schedule ! More work for us and our speakers but more pleasure for all the MySQL fans ! Don’t miss FOSDEM MySQL Devroom’s 2022 edition, free online conference. Here is the new updated schedule (the official one is available here): Saturday, Feb 5th TitleSpeakerStartEncrypting binary (and relay) logs in MySQLMatthias C14:00Hash join in MySQL 8.0Øystein Grøvlen14:30ProxySQL 2021 Dev SubmitJavier Jaramago Fernández15:00Backup/Restore tools performance comparisonVinicius Grippa, Jean Da Silva11:00Sunday, Feb 6th TitleSpeakerStartFlame Graphs for MySQL DBAsValerii Kravchuk10:00The relational model in the modern development ageShlomi Noach10:30ProxySQL Cluster: challenges and solutions to synchronizeconfigurationacross multiple decentralized cluster nodesRené Cannaò11:00Percona XtraDB Cluster(PXC) Non blocking operations, what you need to know to avoid pitfallsMarco Tusa11:30MySQL InnoDB ClusterSet – The integrated solution for disaster tolerance of InnoDB ClustersMiguel Araújo & Kenny Gryp12:00MySQL on Kubernetes demystifiedMarco Tusa13:00MySQL Operator for KubernetesJohannes Schlüter13:30Newest MySQL component services features or how to make an HTTP server accessing table data in a componentJoro Kodinov14:30MySQL 8.0: Logical Backups, Snapshots and PITR like a rockstarlefred15:10Why your backup strategy is wrong?Pep Pla15:55Efficient MySQL Performance – A Book in 40 MinutesDaniel Nichter16:10MySQL Performance on Modern CPU: Intel vs AMD vs ARMPeter Zaitsev17:00Release Note Highlights from 2021 – Recap ’23 to ’27Dave Stokes17:30See you there, don’t miss the event online as you will have the possibility to chat and video talk to all speakers ! Happy FOSDEM ! [Less]
Posted over 2 years ago by Frederic Descamps
This post is the sixth post of a series of articles on extending MySQL with the Component Infrastructure, the list above will be updated as new articles are published: Extending MySQL using the Component Infrastructure – part 1Extending MySQL using ... [More] the Component Infrastructure – part 2: building the serverExtending MySQL using the Component Infrastructure – part 3: component servicesExtending MySQL using the Component Infrastructure – part 4: error loggingExtending MySQL using the Component Infrastructure – part 5: privilegesExtending MySQL using the Component Infrastructure – part 6: functionsOur component is nicely evolving. In this article we will start the creation of our first function. We will check the previously created privilege to access the function. We again start with scan.h where we add the header for the udf registration service we will use to register our first function on line 26 and the place holder on line 34: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #include #include /* LogComponentErr */ #include /* Errors */ #include #include #include #include #include extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins); extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); extern REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); extern REQUIRES_SERVICE_PLACEHOLDER(udf_registration); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); extern SERVICE_TYPE(log_builtins) * log_bi; extern SERVICE_TYPE(log_builtins_string) * log_bs; view raw scan.h hosted with ❤ by GitHub As our function will return a string, we also need to specify the character set otherwise it will return binary. Therefore, we need to use udf metadata service (line 25 and 35). We also need some other standards headers on lines 28 and 29. Creating a function For the code of scan.cc, we will split it in two parts. First we will create the function and after we will add the check regarding the privilege. Let’s start by the function creation: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #define NO_SIGNATURE_CHANGE 0 #define SIGNATURE_CHANGE 1 #include REQUIRES_SERVICE_PLACEHOLDER(log_builtins); REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); REQUIRES_SERVICE_PLACEHOLDER(udf_registration); REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); SERVICE_TYPE(log_builtins) * log_bi; SERVICE_TYPE(log_builtins_string) * log_bs; static const char *SCAN_PRIVILEGE_NAME = "VIRUS_SCAN"; class udf_list { typedef std::list udf_list_t; public: ~udf_list() { unregister(); } bool add_scalar(const char *func_name, enum Item_result return_type, Udf_func_any func, Udf_func_init init_func = NULL, Udf_func_deinit deinit_func = NULL) { if (!mysql_service_udf_registration->udf_register( func_name, return_type, func, init_func, deinit_func)) { set.push_back(func_name); return false; } return true; } bool unregister() { udf_list_t delete_set; /* try to unregister all of the udfs */ for (auto udf : set) { int was_present = 0; if (!mysql_service_udf_registration->udf_unregister(udf.c_str(), &was_present) || !was_present) delete_set.push_back(udf); } /* remove the unregistered ones from the list */ for (auto udf : delete_set) set.remove(udf); /* success: empty set */ if (set.empty()) return false; /* failure: entries still in the set */ return true; } private: udf_list_t set; } * list; namespace udf_impl { const char *udf_init = "udf_init", *my_udf = "my_udf", *my_udf_clear = "my_clear", *my_udf_add = "my_udf_add"; static bool viruscan_udf_init(UDF_INIT *initid, UDF_ARGS *, char *) { const char* name = "utf8mb4"; char *value = const_cast(name); initid->ptr = const_cast(udf_init); if (mysql_service_mysql_udf_metadata->result_set( initid, "charset", const_cast(value))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "failed to set result charset"); return false; } return 0; } static void viruscan_udf_deinit(__attribute__((unused)) UDF_INIT *initid) { assert(initid->ptr == udf_init || initid->ptr == my_udf); } const char *viruscan_udf(UDF_INIT *initid, UDF_ARGS *args, char *outp, unsigned long *length, char *is_null, char *error) { strcpy(outp, "we do nothing yet"); *length = strlen(outp); return const_cast(outp); } } /* namespace udf_impl */ static mysql_service_status_t viruscan_service_init() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "initializing…"); // Registration of the privilege if (mysql_service_dynamic_privilege_register->register_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not register privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "new privilege 'VIRUS_SCAN' has been registered successfully."); } list = new udf_list(); if (list->add_scalar("virus_scan", Item_result::STRING_RESULT, (Udf_func_any)udf_impl::viruscan_udf, udf_impl::viruscan_udf_init, udf_impl::viruscan_udf_deinit)) { delete list; return 1; /* failure: one of the UDF registrations failed */ } return result; } static mysql_service_status_t viruscan_service_deinit() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; if (mysql_service_dynamic_privilege_register->unregister_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not unregister privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "privilege 'VIRUS_SCAN' has been unregistered successfully."); } if (list->unregister()) return 1; /* failure: some UDFs still in use */ delete list; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "uninstalled."); return result; } BEGIN_COMPONENT_PROVIDES(viruscan_service) END_COMPONENT_PROVIDES(); BEGIN_COMPONENT_REQUIRES(viruscan_service) REQUIRES_SERVICE(log_builtins), REQUIRES_SERVICE(log_builtins_string), REQUIRES_SERVICE(dynamic_privilege_register), REQUIRES_SERVICE(mysql_udf_metadata), REQUIRES_SERVICE(udf_registration), END_COMPONENT_REQUIRES(); /* A list of metadata to describe the Component. */ BEGIN_COMPONENT_METADATA(viruscan_service) METADATA("mysql.author", "Oracle Corporation"), METADATA("mysql.license", "GPL"), METADATA("mysql.dev", "lefred"), END_COMPONENT_METADATA(); /* Declaration of the Component. */ DECLARE_COMPONENT(viruscan_service, "mysql:viruscan_service") viruscan_service_init, viruscan_service_deinit END_DECLARE_COMPONENT(); /* Defines list of Components contained in this library. Note that for now we assume that library will have exactly one Component. */ DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(viruscan_service) END_DECLARE_LIBRARY_COMPONENTS view raw scan.cc hosted with ❤ by GitHub On line 34 and 35 we added the place holder and we declare the requirement for our component on lines 178 and 179. As we plan to have more than one user function with our component, the easiest is to create a list of a udf class. This is what we do from line 41 to 80. The class has public methods to register (add) and remove (unregister) the function. From line 86 to 110, we implement the functions called during initialization, uninitialization and call of the new component function. We set the charsert on line 90 via mysql_service_mysql_udf_metadata->result_set. We use the list class from line 134 to 145. On lines 147 and 169 we remove the function when the component is uninstalled. We can compile it ( make component_viruscan), we will get some warnings as we don’t use all parameters (we will use them later). We can already test this version: Checking the privilege Now that our function is working, we can of course extend it. The first thing we gonna add is the check for the privilege we created. For this we will use 3 new services: mysql_thd_security_contextglobal_grants_checkmysql_current_thread_readerWe start by adding them in scan.h, the are part of two new includes (lines 26 and 27): This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #include #include /* LogComponentErr */ #include /* Errors */ #include #include #include #include #include #include #include extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins); extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); extern REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); extern REQUIRES_SERVICE_PLACEHOLDER(udf_registration); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context); extern REQUIRES_SERVICE_PLACEHOLDER(global_grants_check); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader); extern SERVICE_TYPE(log_builtins) * log_bi; extern SERVICE_TYPE(log_builtins_string) * log_bs; view raw scan.h hosted with ❤ by GitHub We added the requires service place holder as usual and we will add them in scan.cc too: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #define NO_SIGNATURE_CHANGE 0 #define SIGNATURE_CHANGE 1 #include REQUIRES_SERVICE_PLACEHOLDER(log_builtins); REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); REQUIRES_SERVICE_PLACEHOLDER(udf_registration); REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context); REQUIRES_SERVICE_PLACEHOLDER(global_grants_check); REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader); SERVICE_TYPE(log_builtins) * log_bi; SERVICE_TYPE(log_builtins_string) * log_bs; static const char *SCAN_PRIVILEGE_NAME = "VIRUS_SCAN"; class udf_list { typedef std::list udf_list_t; public: ~udf_list() { unregister(); } bool add_scalar(const char *func_name, enum Item_result return_type, Udf_func_any func, Udf_func_init init_func = NULL, Udf_func_deinit deinit_func = NULL) { if (!mysql_service_udf_registration->udf_register( func_name, return_type, func, init_func, deinit_func)) { set.push_back(func_name); return false; } return true; } bool unregister() { udf_list_t delete_set; /* try to unregister all of the udfs */ for (auto udf : set) { int was_present = 0; if (!mysql_service_udf_registration->udf_unregister(udf.c_str(), &was_present) || !was_present) delete_set.push_back(udf); } /* remove the unregistered ones from the list */ for (auto udf : delete_set) set.remove(udf); /* success: empty set */ if (set.empty()) return false; /* failure: entries still in the set */ return true; } private: udf_list_t set; } * list; namespace udf_impl { bool have_virus_scan_privilege(void *opaque_thd) { // get the security context of the thread Security_context_handle ctx = nullptr; if (mysql_service_mysql_thd_security_context->get(opaque_thd, &ctx) || !ctx) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "problem trying to get security context"); return false; } if (mysql_service_global_grants_check->has_global_grant( ctx, SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) return true; return false; } const char *udf_init = "udf_init", *my_udf = "my_udf", *my_udf_clear = "my_clear", *my_udf_add = "my_udf_add"; static bool viruscan_udf_init(UDF_INIT *initid, UDF_ARGS *, char *) { const char* name = "utf8mb4"; char *value = const_cast(name); initid->ptr = const_cast(udf_init); if (mysql_service_mysql_udf_metadata->result_set( initid, "charset", const_cast(value))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "failed to set result charset"); return false; } return 0; } static void viruscan_udf_deinit(__attribute__((unused)) UDF_INIT *initid) { assert(initid->ptr == udf_init || initid->ptr == my_udf); } const char *viruscan_udf(UDF_INIT *initid, UDF_ARGS *args, char *outp, unsigned long *length, char *is_null, char *error) { MYSQL_THD thd; mysql_service_mysql_current_thread_reader->get(&thd); if(!have_virus_scan_privilege(thd)) { //to handle better next time LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "Wrong privilege"); return 0; } strcpy(outp, "we do nothing yet"); *length = strlen(outp); return const_cast(outp); } } /* namespace udf_impl */ static mysql_service_status_t viruscan_service_init() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "initializing…"); // Registration of the privilege if (mysql_service_dynamic_privilege_register->register_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not register privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "new privilege 'VIRUS_SCAN' has been registered successfully."); } list = new udf_list(); if (list->add_scalar("virus_scan", Item_result::STRING_RESULT, (Udf_func_any)udf_impl::viruscan_udf, udf_impl::viruscan_udf_init, udf_impl::viruscan_udf_deinit)) { delete list; return 1; /* failure: one of the UDF registrations failed */ } return result; } static mysql_service_status_t viruscan_service_deinit() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; if (mysql_service_dynamic_privilege_register->unregister_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not unregister privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "privilege 'VIRUS_SCAN' has been unregistered successfully."); } if (list->unregister()) return 1; /* failure: some UDFs still in use */ delete list; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "uninstalled."); return result; } BEGIN_COMPONENT_PROVIDES(viruscan_service) END_COMPONENT_PROVIDES(); BEGIN_COMPONENT_REQUIRES(viruscan_service) REQUIRES_SERVICE(log_builtins), REQUIRES_SERVICE(log_builtins_string), REQUIRES_SERVICE(dynamic_privilege_register), REQUIRES_SERVICE(mysql_udf_metadata), REQUIRES_SERVICE(udf_registration), REQUIRES_SERVICE(mysql_thd_security_context), REQUIRES_SERVICE(global_grants_check), REQUIRES_SERVICE(mysql_current_thread_reader), END_COMPONENT_REQUIRES(); /* A list of metadata to describe the Component. */ BEGIN_COMPONENT_METADATA(viruscan_service) METADATA("mysql.author", "Oracle Corporation"), METADATA("mysql.license", "GPL"), METADATA("mysql.dev", "lefred"), END_COMPONENT_METADATA(); /* Declaration of the Component. */ DECLARE_COMPONENT(viruscan_service, "mysql:viruscan_service") viruscan_service_init, viruscan_service_deinit END_DECLARE_COMPONENT(); /* Defines list of Components contained in this library. Note that for now we assume that library will have exactly one Component. */ DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(viruscan_service) END_DECLARE_LIBRARY_COMPONENTS view raw scan.cc hosted with ❤ by GitHub On line 88 we created a new boolean function that checks if the user in the pointed thd has the required privilege (VIRUS_SCAN) or not. Then on line 130 we extend the function viruscan_udf to use our new have_virus_scan_privilege function. You can notice that we need to pass the thd we get on line 128. As usual we need to update the required services for our component on lines 210 to 212. We can compile the component and test it: Conclusion In this article we covered how to create a function that we can call in MySQL. We also saw how to check for a privilege. Currently we only print a message in error log, on the next article, we will extend this by printing a better message in error log but also return a message to the user. Keep coding and enjoy MySQL ! [Less]
Posted over 2 years ago by Frederic Descamps
This post is the sixth post of a series of articles on extending MySQL with the Component Infrastructure, the list above will be updated as new articles are published: Extending MySQL using the Component Infrastructure – part 1Extending MySQL using ... [More] the Component Infrastructure – part 2: building the serverExtending MySQL using the Component Infrastructure – part 3: component servicesExtending MySQL using the Component Infrastructure – part 4: error loggingExtending MySQL using the Component Infrastructure – part 5: privilegesExtending MySQL using the Component Infrastructure – part 6: functionsExtending MySQL using the Component Infrastructure – part 7: messages to usersOur component is nicely evolving. In this article we will start the creation of our first function. We will check the previously created privilege to access the function. We again start with scan.h where we add the header for the udf registration service we will use to register our first function on line 26 and the place holder on line 34: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #include #include /* LogComponentErr */ #include /* Errors */ #include #include #include #include #include extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins); extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); extern REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); extern REQUIRES_SERVICE_PLACEHOLDER(udf_registration); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); extern SERVICE_TYPE(log_builtins) * log_bi; extern SERVICE_TYPE(log_builtins_string) * log_bs; view raw scan.h hosted with ❤ by GitHub As our function will return a string, we also need to specify the character set otherwise it will return binary. Therefore, we need to use udf metadata service (line 25 and 35). We also need some other standards headers on lines 28 and 29. Creating a function For the code of scan.cc, we will split it in two parts. First we will create the function and after we will add the check regarding the privilege. Let’s start by the function creation: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #define NO_SIGNATURE_CHANGE 0 #define SIGNATURE_CHANGE 1 #include REQUIRES_SERVICE_PLACEHOLDER(log_builtins); REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); REQUIRES_SERVICE_PLACEHOLDER(udf_registration); REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); SERVICE_TYPE(log_builtins) * log_bi; SERVICE_TYPE(log_builtins_string) * log_bs; static const char *SCAN_PRIVILEGE_NAME = "VIRUS_SCAN"; class udf_list { typedef std::list udf_list_t; public: ~udf_list() { unregister(); } bool add_scalar(const char *func_name, enum Item_result return_type, Udf_func_any func, Udf_func_init init_func = NULL, Udf_func_deinit deinit_func = NULL) { if (!mysql_service_udf_registration->udf_register( func_name, return_type, func, init_func, deinit_func)) { set.push_back(func_name); return false; } return true; } bool unregister() { udf_list_t delete_set; /* try to unregister all of the udfs */ for (auto udf : set) { int was_present = 0; if (!mysql_service_udf_registration->udf_unregister(udf.c_str(), &was_present) || !was_present) delete_set.push_back(udf); } /* remove the unregistered ones from the list */ for (auto udf : delete_set) set.remove(udf); /* success: empty set */ if (set.empty()) return false; /* failure: entries still in the set */ return true; } private: udf_list_t set; } * list; namespace udf_impl { const char *udf_init = "udf_init", *my_udf = "my_udf", *my_udf_clear = "my_clear", *my_udf_add = "my_udf_add"; static bool viruscan_udf_init(UDF_INIT *initid, UDF_ARGS *, char *) { const char* name = "utf8mb4"; char *value = const_cast(name); initid->ptr = const_cast(udf_init); if (mysql_service_mysql_udf_metadata->result_set( initid, "charset", const_cast(value))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "failed to set result charset"); return false; } return 0; } static void viruscan_udf_deinit(__attribute__((unused)) UDF_INIT *initid) { assert(initid->ptr == udf_init || initid->ptr == my_udf); } const char *viruscan_udf(UDF_INIT *initid, UDF_ARGS *args, char *outp, unsigned long *length, char *is_null, char *error) { strcpy(outp, "we do nothing yet"); *length = strlen(outp); return const_cast(outp); } } /* namespace udf_impl */ static mysql_service_status_t viruscan_service_init() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "initializing…"); // Registration of the privilege if (mysql_service_dynamic_privilege_register->register_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not register privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "new privilege 'VIRUS_SCAN' has been registered successfully."); } list = new udf_list(); if (list->add_scalar("virus_scan", Item_result::STRING_RESULT, (Udf_func_any)udf_impl::viruscan_udf, udf_impl::viruscan_udf_init, udf_impl::viruscan_udf_deinit)) { delete list; return 1; /* failure: one of the UDF registrations failed */ } return result; } static mysql_service_status_t viruscan_service_deinit() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; if (mysql_service_dynamic_privilege_register->unregister_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not unregister privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "privilege 'VIRUS_SCAN' has been unregistered successfully."); } if (list->unregister()) return 1; /* failure: some UDFs still in use */ delete list; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "uninstalled."); return result; } BEGIN_COMPONENT_PROVIDES(viruscan_service) END_COMPONENT_PROVIDES(); BEGIN_COMPONENT_REQUIRES(viruscan_service) REQUIRES_SERVICE(log_builtins), REQUIRES_SERVICE(log_builtins_string), REQUIRES_SERVICE(dynamic_privilege_register), REQUIRES_SERVICE(mysql_udf_metadata), REQUIRES_SERVICE(udf_registration), END_COMPONENT_REQUIRES(); /* A list of metadata to describe the Component. */ BEGIN_COMPONENT_METADATA(viruscan_service) METADATA("mysql.author", "Oracle Corporation"), METADATA("mysql.license", "GPL"), METADATA("mysql.dev", "lefred"), END_COMPONENT_METADATA(); /* Declaration of the Component. */ DECLARE_COMPONENT(viruscan_service, "mysql:viruscan_service") viruscan_service_init, viruscan_service_deinit END_DECLARE_COMPONENT(); /* Defines list of Components contained in this library. Note that for now we assume that library will have exactly one Component. */ DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(viruscan_service) END_DECLARE_LIBRARY_COMPONENTS view raw scan.cc hosted with ❤ by GitHub On line 34 and 35 we added the place holder and we declare the requirement for our component on lines 178 and 179. As we plan to have more than one user function with our component, the easiest is to create a list of a udf class. This is what we do from line 41 to 80. The class has public methods to register (add) and remove (unregister) the function. From line 86 to 110, we implement the functions called during initialization, uninitialization and call of the new component function. We set the charsert on line 90 via mysql_service_mysql_udf_metadata->result_set. We use the list class from line 134 to 145. On lines 147 and 169 we remove the function when the component is uninstalled. We can compile it ( make component_viruscan), we will get some warnings as we don’t use all parameters (we will use them later). We can already test this version: Checking the privilege Now that our function is working, we can of course extend it. The first thing we gonna add is the check for the privilege we created. For this we will use 3 new services: mysql_thd_security_contextglobal_grants_checkmysql_current_thread_readerWe start by adding them in scan.h, the are part of two new includes (lines 26 and 27): This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #include #include /* LogComponentErr */ #include /* Errors */ #include #include #include #include #include #include #include extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins); extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); extern REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); extern REQUIRES_SERVICE_PLACEHOLDER(udf_registration); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context); extern REQUIRES_SERVICE_PLACEHOLDER(global_grants_check); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader); extern SERVICE_TYPE(log_builtins) * log_bi; extern SERVICE_TYPE(log_builtins_string) * log_bs; view raw scan.h hosted with ❤ by GitHub We added the requires service place holder as usual and we will add them in scan.cc too: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #define NO_SIGNATURE_CHANGE 0 #define SIGNATURE_CHANGE 1 #include REQUIRES_SERVICE_PLACEHOLDER(log_builtins); REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); REQUIRES_SERVICE_PLACEHOLDER(udf_registration); REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context); REQUIRES_SERVICE_PLACEHOLDER(global_grants_check); REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader); SERVICE_TYPE(log_builtins) * log_bi; SERVICE_TYPE(log_builtins_string) * log_bs; static const char *SCAN_PRIVILEGE_NAME = "VIRUS_SCAN"; class udf_list { typedef std::list udf_list_t; public: ~udf_list() { unregister(); } bool add_scalar(const char *func_name, enum Item_result return_type, Udf_func_any func, Udf_func_init init_func = NULL, Udf_func_deinit deinit_func = NULL) { if (!mysql_service_udf_registration->udf_register( func_name, return_type, func, init_func, deinit_func)) { set.push_back(func_name); return false; } return true; } bool unregister() { udf_list_t delete_set; /* try to unregister all of the udfs */ for (auto udf : set) { int was_present = 0; if (!mysql_service_udf_registration->udf_unregister(udf.c_str(), &was_present) || !was_present) delete_set.push_back(udf); } /* remove the unregistered ones from the list */ for (auto udf : delete_set) set.remove(udf); /* success: empty set */ if (set.empty()) return false; /* failure: entries still in the set */ return true; } private: udf_list_t set; } * list; namespace udf_impl { bool have_virus_scan_privilege(void *opaque_thd) { // get the security context of the thread Security_context_handle ctx = nullptr; if (mysql_service_mysql_thd_security_context->get(opaque_thd, &ctx) || !ctx) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "problem trying to get security context"); return false; } if (mysql_service_global_grants_check->has_global_grant( ctx, SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) return true; return false; } const char *udf_init = "udf_init", *my_udf = "my_udf", *my_udf_clear = "my_clear", *my_udf_add = "my_udf_add"; static bool viruscan_udf_init(UDF_INIT *initid, UDF_ARGS *, char *) { const char* name = "utf8mb4"; char *value = const_cast(name); initid->ptr = const_cast(udf_init); if (mysql_service_mysql_udf_metadata->result_set( initid, "charset", const_cast(value))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "failed to set result charset"); return false; } return 0; } static void viruscan_udf_deinit(__attribute__((unused)) UDF_INIT *initid) { assert(initid->ptr == udf_init || initid->ptr == my_udf); } const char *viruscan_udf(UDF_INIT *initid, UDF_ARGS *args, char *outp, unsigned long *length, char *is_null, char *error) { MYSQL_THD thd; mysql_service_mysql_current_thread_reader->get(&thd); if(!have_virus_scan_privilege(thd)) { //to handle better next time LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "Wrong privilege"); return 0; } strcpy(outp, "we do nothing yet"); *length = strlen(outp); return const_cast(outp); } } /* namespace udf_impl */ static mysql_service_status_t viruscan_service_init() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "initializing…"); // Registration of the privilege if (mysql_service_dynamic_privilege_register->register_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not register privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "new privilege 'VIRUS_SCAN' has been registered successfully."); } list = new udf_list(); if (list->add_scalar("virus_scan", Item_result::STRING_RESULT, (Udf_func_any)udf_impl::viruscan_udf, udf_impl::viruscan_udf_init, udf_impl::viruscan_udf_deinit)) { delete list; return 1; /* failure: one of the UDF registrations failed */ } return result; } static mysql_service_status_t viruscan_service_deinit() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; if (mysql_service_dynamic_privilege_register->unregister_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not unregister privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "privilege 'VIRUS_SCAN' has been unregistered successfully."); } if (list->unregister()) return 1; /* failure: some UDFs still in use */ delete list; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "uninstalled."); return result; } BEGIN_COMPONENT_PROVIDES(viruscan_service) END_COMPONENT_PROVIDES(); BEGIN_COMPONENT_REQUIRES(viruscan_service) REQUIRES_SERVICE(log_builtins), REQUIRES_SERVICE(log_builtins_string), REQUIRES_SERVICE(dynamic_privilege_register), REQUIRES_SERVICE(mysql_udf_metadata), REQUIRES_SERVICE(udf_registration), REQUIRES_SERVICE(mysql_thd_security_context), REQUIRES_SERVICE(global_grants_check), REQUIRES_SERVICE(mysql_current_thread_reader), END_COMPONENT_REQUIRES(); /* A list of metadata to describe the Component. */ BEGIN_COMPONENT_METADATA(viruscan_service) METADATA("mysql.author", "Oracle Corporation"), METADATA("mysql.license", "GPL"), METADATA("mysql.dev", "lefred"), END_COMPONENT_METADATA(); /* Declaration of the Component. */ DECLARE_COMPONENT(viruscan_service, "mysql:viruscan_service") viruscan_service_init, viruscan_service_deinit END_DECLARE_COMPONENT(); /* Defines list of Components contained in this library. Note that for now we assume that library will have exactly one Component. */ DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(viruscan_service) END_DECLARE_LIBRARY_COMPONENTS view raw scan.cc hosted with ❤ by GitHub On line 88 we created a new boolean function that checks if the user in the pointed thd has the required privilege (VIRUS_SCAN) or not. Then on line 130 we extend the function viruscan_udf to use our new have_virus_scan_privilege function. You can notice that we need to pass the thd we get on line 128. As usual we need to update the required services for our component on lines 210 to 212. We can compile the component and test it: Conclusion In this article we covered how to create a function that we can call in MySQL. We also saw how to check for a privilege. Currently we only print a message in error log, on the next article, we will extend this by printing a better message in error log but also return a message to the user. Keep coding and enjoy MySQL ! [Less]
Posted over 2 years ago by Frederic Descamps
This post is the sixth post of a series of articles on extending MySQL with the Component Infrastructure, the list above will be updated as new articles are published: Extending MySQL using the Component Infrastructure – part 1Extending MySQL using ... [More] the Component Infrastructure – part 2: building the serverExtending MySQL using the Component Infrastructure – part 3: component servicesExtending MySQL using the Component Infrastructure – part 4: error loggingExtending MySQL using the Component Infrastructure – part 5: privilegesExtending MySQL using the Component Infrastructure – part 6: functionsExtending MySQL using the Component Infrastructure – part 7: messages to usersOur component is nicely evolving. In this article we will start the creation of our first function. We will check the previously created privilege to access the function. We again start with scan.h where we add the header for the udf registration service we will use to register our first function on line 26 and the place holder on line 34: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #include #include /* LogComponentErr */ #include /* Errors */ #include #include #include #include #include extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins); extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); extern REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); extern REQUIRES_SERVICE_PLACEHOLDER(udf_registration); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); extern SERVICE_TYPE(log_builtins) * log_bi; extern SERVICE_TYPE(log_builtins_string) * log_bs; view raw scan.h hosted with ❤ by GitHub As our function will return a string, we also need to specify the character set otherwise it will return binary. Therefore, we need to use udf metadata service (line 25 and 35). We also need some other standards headers on lines 28 and 29. Creating a function For the code of scan.cc, we will split it in two parts. First we will create the function and after we will add the check regarding the privilege. Let’s start by the function creation: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #define NO_SIGNATURE_CHANGE 0 #define SIGNATURE_CHANGE 1 #include REQUIRES_SERVICE_PLACEHOLDER(log_builtins); REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); REQUIRES_SERVICE_PLACEHOLDER(udf_registration); REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); SERVICE_TYPE(log_builtins) * log_bi; SERVICE_TYPE(log_builtins_string) * log_bs; static const char *SCAN_PRIVILEGE_NAME = "VIRUS_SCAN"; class udf_list { typedef std::list udf_list_t; public: ~udf_list() { unregister(); } bool add_scalar(const char *func_name, enum Item_result return_type, Udf_func_any func, Udf_func_init init_func = NULL, Udf_func_deinit deinit_func = NULL) { if (!mysql_service_udf_registration->udf_register( func_name, return_type, func, init_func, deinit_func)) { set.push_back(func_name); return false; } return true; } bool unregister() { udf_list_t delete_set; /* try to unregister all of the udfs */ for (auto udf : set) { int was_present = 0; if (!mysql_service_udf_registration->udf_unregister(udf.c_str(), &was_present) || !was_present) delete_set.push_back(udf); } /* remove the unregistered ones from the list */ for (auto udf : delete_set) set.remove(udf); /* success: empty set */ if (set.empty()) return false; /* failure: entries still in the set */ return true; } private: udf_list_t set; } * list; namespace udf_impl { const char *udf_init = "udf_init", *my_udf = "my_udf", *my_udf_clear = "my_clear", *my_udf_add = "my_udf_add"; static bool viruscan_udf_init(UDF_INIT *initid, UDF_ARGS *, char *) { const char* name = "utf8mb4"; char *value = const_cast(name); initid->ptr = const_cast(udf_init); if (mysql_service_mysql_udf_metadata->result_set( initid, "charset", const_cast(value))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "failed to set result charset"); return false; } return 0; } static void viruscan_udf_deinit(__attribute__((unused)) UDF_INIT *initid) { assert(initid->ptr == udf_init || initid->ptr == my_udf); } const char *viruscan_udf(UDF_INIT *initid, UDF_ARGS *args, char *outp, unsigned long *length, char *is_null, char *error) { strcpy(outp, "we do nothing yet"); *length = strlen(outp); return const_cast(outp); } } /* namespace udf_impl */ static mysql_service_status_t viruscan_service_init() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "initializing…"); // Registration of the privilege if (mysql_service_dynamic_privilege_register->register_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not register privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "new privilege 'VIRUS_SCAN' has been registered successfully."); } list = new udf_list(); if (list->add_scalar("virus_scan", Item_result::STRING_RESULT, (Udf_func_any)udf_impl::viruscan_udf, udf_impl::viruscan_udf_init, udf_impl::viruscan_udf_deinit)) { delete list; return 1; /* failure: one of the UDF registrations failed */ } return result; } static mysql_service_status_t viruscan_service_deinit() { mysql_service_status_t result = 0; if (mysql_service_dynamic_privilege_register->unregister_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not unregister privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "privilege 'VIRUS_SCAN' has been unregistered successfully."); } if (list->unregister()) return 1; /* failure: some UDFs still in use */ delete list; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "uninstalled."); return result; } BEGIN_COMPONENT_PROVIDES(viruscan_service) END_COMPONENT_PROVIDES(); BEGIN_COMPONENT_REQUIRES(viruscan_service) REQUIRES_SERVICE(log_builtins), REQUIRES_SERVICE(log_builtins_string), REQUIRES_SERVICE(dynamic_privilege_register), REQUIRES_SERVICE(mysql_udf_metadata), REQUIRES_SERVICE(udf_registration), END_COMPONENT_REQUIRES(); /* A list of metadata to describe the Component. */ BEGIN_COMPONENT_METADATA(viruscan_service) METADATA("mysql.author", "Oracle Corporation"), METADATA("mysql.license", "GPL"), METADATA("mysql.dev", "lefred"), END_COMPONENT_METADATA(); /* Declaration of the Component. */ DECLARE_COMPONENT(viruscan_service, "mysql:viruscan_service") viruscan_service_init, viruscan_service_deinit END_DECLARE_COMPONENT(); /* Defines list of Components contained in this library. Note that for now we assume that library will have exactly one Component. */ DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(viruscan_service) END_DECLARE_LIBRARY_COMPONENTS view raw scan.cc hosted with ❤ by GitHub On line 34 and 35 we added the place holder and we declare the requirement for our component on lines 178 and 179. As we plan to have more than one user function with our component, the easiest is to create a list of a udf class. This is what we do from line 41 to 80. The class has public methods to register (add) and remove (unregister) the function. From line 86 to 110, we implement the functions called during initialization, uninitialization and call of the new component function. We set the charsert on line 90 via mysql_service_mysql_udf_metadata->result_set. We use the list class from line 134 to 145. On lines 147 and 166 we remove the function when the component is uninstalled. We can compile it ( make component_viruscan), we will get some warnings as we don’t use all parameters (we will use them later). We can already test this version: Checking the privilege Now that our function is working, we can of course extend it. The first thing we gonna add is the check for the privilege we created. For this we will use 3 new services: mysql_thd_security_contextglobal_grants_checkmysql_current_thread_readerWe start by adding them in scan.h, the are part of two new includes (lines 26 and 27): This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #include #include /* LogComponentErr */ #include /* Errors */ #include #include #include #include #include #include #include extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins); extern REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); extern REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); extern REQUIRES_SERVICE_PLACEHOLDER(udf_registration); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context); extern REQUIRES_SERVICE_PLACEHOLDER(global_grants_check); extern REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader); extern SERVICE_TYPE(log_builtins) * log_bi; extern SERVICE_TYPE(log_builtins_string) * log_bs; view raw scan.h hosted with ❤ by GitHub We added the requires service place holder as usual and we will add them in scan.cc too: This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define LOG_COMPONENT_TAG "viruscan" #define NO_SIGNATURE_CHANGE 0 #define SIGNATURE_CHANGE 1 #include REQUIRES_SERVICE_PLACEHOLDER(log_builtins); REQUIRES_SERVICE_PLACEHOLDER(log_builtins_string); REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); REQUIRES_SERVICE_PLACEHOLDER(udf_registration); REQUIRES_SERVICE_PLACEHOLDER(mysql_udf_metadata); REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context); REQUIRES_SERVICE_PLACEHOLDER(global_grants_check); REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader); SERVICE_TYPE(log_builtins) * log_bi; SERVICE_TYPE(log_builtins_string) * log_bs; static const char *SCAN_PRIVILEGE_NAME = "VIRUS_SCAN"; class udf_list { typedef std::list udf_list_t; public: ~udf_list() { unregister(); } bool add_scalar(const char *func_name, enum Item_result return_type, Udf_func_any func, Udf_func_init init_func = NULL, Udf_func_deinit deinit_func = NULL) { if (!mysql_service_udf_registration->udf_register( func_name, return_type, func, init_func, deinit_func)) { set.push_back(func_name); return false; } return true; } bool unregister() { udf_list_t delete_set; /* try to unregister all of the udfs */ for (auto udf : set) { int was_present = 0; if (!mysql_service_udf_registration->udf_unregister(udf.c_str(), &was_present) || !was_present) delete_set.push_back(udf); } /* remove the unregistered ones from the list */ for (auto udf : delete_set) set.remove(udf); /* success: empty set */ if (set.empty()) return false; /* failure: entries still in the set */ return true; } private: udf_list_t set; } * list; namespace udf_impl { bool have_virus_scan_privilege(void *opaque_thd) { // get the security context of the thread Security_context_handle ctx = nullptr; if (mysql_service_mysql_thd_security_context->get(opaque_thd, &ctx) || !ctx) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "problem trying to get security context"); return false; } if (mysql_service_global_grants_check->has_global_grant( ctx, SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) return true; return false; } const char *udf_init = "udf_init", *my_udf = "my_udf", *my_udf_clear = "my_clear", *my_udf_add = "my_udf_add"; static bool viruscan_udf_init(UDF_INIT *initid, UDF_ARGS *, char *) { const char* name = "utf8mb4"; char *value = const_cast(name); initid->ptr = const_cast(udf_init); if (mysql_service_mysql_udf_metadata->result_set( initid, "charset", const_cast(value))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "failed to set result charset"); return false; } return 0; } static void viruscan_udf_deinit(__attribute__((unused)) UDF_INIT *initid) { assert(initid->ptr == udf_init || initid->ptr == my_udf); } const char *viruscan_udf(UDF_INIT *initid, UDF_ARGS *args, char *outp, unsigned long *length, char *is_null, char *error) { MYSQL_THD thd; mysql_service_mysql_current_thread_reader->get(&thd); if(!have_virus_scan_privilege(thd)) { //to handle better next time LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "Wrong privilege"); return 0; } strcpy(outp, "we do nothing yet"); *length = strlen(outp); return const_cast(outp); } } /* namespace udf_impl */ static mysql_service_status_t viruscan_service_init() { mysql_service_status_t result = 0; log_bi = mysql_service_log_builtins; log_bs = mysql_service_log_builtins_string; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "initializing…"); // Registration of the privilege if (mysql_service_dynamic_privilege_register->register_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not register privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "new privilege 'VIRUS_SCAN' has been registered successfully."); } list = new udf_list(); if (list->add_scalar("virus_scan", Item_result::STRING_RESULT, (Udf_func_any)udf_impl::viruscan_udf, udf_impl::viruscan_udf_init, udf_impl::viruscan_udf_deinit)) { delete list; return 1; /* failure: one of the UDF registrations failed */ } return result; } static mysql_service_status_t viruscan_service_deinit() { mysql_service_status_t result = 0; if (mysql_service_dynamic_privilege_register->unregister_privilege(SCAN_PRIVILEGE_NAME, strlen(SCAN_PRIVILEGE_NAME))) { LogComponentErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "could not unregister privilege 'VIRUS_SCAN'."); result = 1; } else { LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "privilege 'VIRUS_SCAN' has been unregistered successfully."); } if (list->unregister()) return 1; /* failure: some UDFs still in use */ delete list; LogComponentErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "uninstalled."); return result; } BEGIN_COMPONENT_PROVIDES(viruscan_service) END_COMPONENT_PROVIDES(); BEGIN_COMPONENT_REQUIRES(viruscan_service) REQUIRES_SERVICE(log_builtins), REQUIRES_SERVICE(log_builtins_string), REQUIRES_SERVICE(dynamic_privilege_register), REQUIRES_SERVICE(mysql_udf_metadata), REQUIRES_SERVICE(udf_registration), REQUIRES_SERVICE(mysql_thd_security_context), REQUIRES_SERVICE(global_grants_check), REQUIRES_SERVICE(mysql_current_thread_reader), END_COMPONENT_REQUIRES(); /* A list of metadata to describe the Component. */ BEGIN_COMPONENT_METADATA(viruscan_service) METADATA("mysql.author", "Oracle Corporation"), METADATA("mysql.license", "GPL"), METADATA("mysql.dev", "lefred"), END_COMPONENT_METADATA(); /* Declaration of the Component. */ DECLARE_COMPONENT(viruscan_service, "mysql:viruscan_service") viruscan_service_init, viruscan_service_deinit END_DECLARE_COMPONENT(); /* Defines list of Components contained in this library. Note that for now we assume that library will have exactly one Component. */ DECLARE_LIBRARY_COMPONENTS &COMPONENT_REF(viruscan_service) END_DECLARE_LIBRARY_COMPONENTS view raw scan.cc hosted with ❤ by GitHub On line 88 we created a new boolean function that checks if the user in the pointed thd has the required privilege (VIRUS_SCAN) or not. Then on line 130 we extend the function viruscan_udf to use our new have_virus_scan_privilege function. You can notice that we need to pass the thd we get on line 128. As usual we need to update the required services for our component on lines 210 to 212. We can compile the component and test it: Conclusion In this article we covered how to create a function that we can call in MySQL. We also saw how to check for a privilege. Currently we only print a message in error log, on the next article, we will extend this by printing a better message in error log but also return a message to the user. Keep coding and enjoy MySQL ! [Less]
Posted over 2 years ago by Joshua Otwell
I’m resharing a post I first published over on my Medium account for interested readers here. As you know, SQL is my passion and sweet spot. But, I also have an affinity for PHP and CodeIgniter. Continue reading and learn how I prototype Query ... [More] Builder patterns in CodeIgniter 4… Self-Promotion: If you enjoy the content written here, by all means, share this blog and your favorite post(s) with others who may benefit from or like it as well. Since coffee is my favorite drink, you can even buy me one if you would like! The Newsletter for PHP and MySQL Developers CodeIgniter 4 Query Helper methods I use the CodeIgniter 4 Query Helper, $db->getLastQuery() method a great deal to learn just how Query Builder methods are mapping to the actual SQL code executed. I wrote all about it in the post, CodeIgniter 4 Query Helper $db->getLastQuery() method for SQL prototyping. Be sure and check this post out if you are interested. Are you a Medium member? If so, receive an email notification each time I publish a blog post if you prefer the Medium platform. Not a member? No worries! Use my sign-up link (I will get a commission at no extra cost to you) and join. I really enjoy reading all the great content there and I know you will too!!! Related content I’ve written several other PHP/MySQL-related blog posts that I’m listing below. Please share them with others who will get value out of them as well. Thank you! Use MySQL to generate MySQLImport CSV data with phpMyAdminLast Insert ID with CodeIgniter 4Fun with MySQL SUBSTRING()The Newsletter for PHP and MySQL Developers How can I help you? Are you thinking of starting up a blog? I use WordPress for my blog. Let’s both save money on the plans offered. Grab a Gmail HTML Email Signature template from my Etsy shop and make your emails pop and stand out. Need hosting for your next web application or WordPress site? I use and highly recommend Hostinger. They have great pricing and service.I enjoy reading Refind: The essence of the web, every morning in your inbox. Subscribe for free. Help me get a premium subscription by signing up yourself with my referral link.Grab a free pack of mobile Creator wallpapers.Like what you have read? See anything incorrect? Please comment below and thank you for reading!!! A Call To Action! Thank you for taking the time to read this post. I truly hope you discovered something interesting and enlightening. Please share your findings here, with someone else you know who would get the same value out of it as well. Visit the Portfolio-Projects page to see blog posts/technical writing I have completed for clients. To receive email notifications (Never Spam) from this blog (“Digital Owl’s Prose”) for the latest blog posts as they are published, please subscribe (of your own volition) by clicking the ‘Click To Subscribe!’ button in the sidebar on the homepage! (Feel free at any time to review the Digital Owl’s Prose Privacy Policy Page for any questions you may have about: email updates, opt-in, opt-out, contact forms, etc…) Be sure and visit the “Best Of” page for a collection of my best blog posts. Josh Otwell has a passion to study and grow as a SQL Developer and blogger. Other favorite activities find him with his nose buried in a good book, article, or the Linux command line. Among those, he shares a love of tabletop RPG games, reading fantasy novels, and spending time with his wife and two daughters. Disclaimer: The examples presented in this post are hypothetical ideas of how to achieve similar types of results. They are not the utmost best solution(s). The majority, if not all, of the examples provided, are performed on a personal development/learning workstation environment and should not be considered production quality or ready. Your particular goals and needs may vary. Use those practices that best benefit your needs and goals. Opinions are my own. Disclosure: Some of the services and products links in this post are affiliate links. At no additional cost to you, should you make a purchase by clicking through one of them, I will receive a commission. The post CodeIgniter 4 Query Helper getLastQuery() method – MySQL Prototyping appeared first on Digital Owl's Prose. [Less]
Posted over 2 years ago by Joshua Otwell
I’m resharing a post I first published over on my Medium account for interested readers here. As you know, SQL is my passion and sweet spot. But, I also have an affinity for PHP and CodeIgniter. Continue reading and learn how I prototype Query ... [More] Builder patterns in CodeIgniter 4… Self-Promotion: If you enjoy the content written here, by all means, share this blog and your favorite post(s) with others who may benefit from or like it as well. Since coffee is my favorite drink, you can even buy me one if you would like! The Newsletter for PHP and MySQL Developers CodeIgniter 4 Query Helper methods I use the CodeIgniter 4 Query Helper, $db->getLastQuery() method a great deal to learn just how Query Builder methods are mapping to the actual SQL code executed. I wrote all about it in the post, CodeIgniter 4 Query Helper $db->getLastQuery() method for SQL prototyping. Be sure and check this post out if you are interested. Are you a Medium member? If so, receive an email notification each time I publish a blog post if you prefer the Medium platform. Not a member? No worries! Use my sign-up link (I will get a commission at no extra cost to you) and join. I really enjoy reading all the great content there and I know you will too!!! Related content I’ve written several other PHP/MySQL-related blog posts that I’m listing below. Please share them with others who will get value out of them as well. Thank you! Use MySQL to generate MySQLImport CSV data with phpMyAdminLast Insert ID with CodeIgniter 4Fun with MySQL SUBSTRING()The Newsletter for PHP and MySQL Developers How can I help you? Are you thinking of starting up a blog? I use WordPress for my blog. Let’s both save money on the plans offered. Grab a Gmail HTML Email Signature template from my Etsy shop and make your emails pop and stand out. Need hosting for your next web application or WordPress site? I use and highly recommend Hostinger. They have great pricing and service.I enjoy reading Refind: The essence of the web, every morning in your inbox. Subscribe for free. Help me get a premium subscription by signing up yourself with my referral link.Grab a free pack of mobile Creator wallpapers.Just getting started or wanting to learn MySQL? Find out about my premium blog posts and MySQL Beginner Series here.Like what you have read? See anything incorrect? Please comment below and thank you for reading!!! A Call To Action! Thank you for taking the time to read this post. I truly hope you discovered something interesting and enlightening. Please share your findings here, with someone else you know who would get the same value out of it as well. Visit the Portfolio-Projects page to see blog posts/technical writing I have completed for clients. To receive email notifications (Never Spam) from this blog (“Digital Owl’s Prose”) for the latest blog posts as they are published, please subscribe (of your own volition) by clicking the ‘Click To Subscribe!’ button in the sidebar on the homepage! (Feel free at any time to review the Digital Owl’s Prose Privacy Policy Page for any questions you may have about: email updates, opt-in, opt-out, contact forms, etc…) Be sure and visit the “Best Of” page for a collection of my best blog posts. Josh Otwell has a passion to study and grow as a SQL Developer and blogger. Other favorite activities find him with his nose buried in a good book, article, or the Linux command line. Among those, he shares a love of tabletop RPG games, reading fantasy novels, and spending time with his wife and two daughters. Disclaimer: The examples presented in this post are hypothetical ideas of how to achieve similar types of results. They are not the utmost best solution(s). The majority, if not all, of the examples provided, are performed on a personal development/learning workstation environment and should not be considered production quality or ready. Your particular goals and needs may vary. Use those practices that best benefit your needs and goals. Opinions are my own. Disclosure: Some of the services and products links in this post are affiliate links. At no additional cost to you, should you make a purchase by clicking through one of them, I will receive a commission. The post CodeIgniter 4 Query Helper getLastQuery() method – MySQL Prototyping appeared first on Digital Owl's Prose. [Less]