From 94eb1b4eb1d66425eca73ea74ad7477e71010fef Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 6 Aug 2015 11:45:55 +0300 Subject: [PATCH] Added service and monitor permission checks. --- server/core/config.c | 1 + server/core/dbusers.c | 104 +++++++++++++++++++++++++++++++++++++++ server/core/monitor.c | 58 ++++++++++++++++++++++ server/core/service.c | 15 +++++- server/include/dbusers.h | 1 + server/include/monitor.h | 1 + server/include/service.h | 1 + 7 files changed, 180 insertions(+), 1 deletion(-) diff --git a/server/core/config.c b/server/core/config.c index dee089a49..41bd25540 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1107,6 +1107,7 @@ process_config_context(CONFIG_CONTEXT *context) monitorAddUser(obj->element, user, passwd); + valid_monitor_permissions(obj->element); } else if (obj->element && user) { diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 57594f63d..a7abe4872 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -2315,3 +2315,107 @@ int add_wildcard_users(USERS *users, char* name, char* host, char* password, cha return rval; } + +/** + * Check if the service user has all required permissions to operate properly. + * this checks for SELECT permissions on mysql.user and mysql.db tables and for + * SHOW DATABASES permissions. If permissions are not adequate, an error message + * is logged. + * @param service Service to inspect + */ +void valid_service_permissions(SERVICE* service) +{ + MYSQL* mysql; + MYSQL_RES* res; + char *user,*password,*dpasswd; + SERVER_REF* server; + int conn_timeout = 1; + + if(service_is_internal(service)) + return; + + if(service->dbref == NULL) + { + skygw_log_write(LE,"[%s] Error: Service is missing the servers parameter.",service->name); + return; + } + + server = service->dbref; + + if (serviceGetUser(service, &user, &password) == 0) + { + skygw_log_write(LE,"[%s] Error: Service %s is missing the user credentials for authentication.", + __FUNCTION__,service->name); + return; + } + + dpasswd = decryptPassword(password); + + if((mysql = mysql_init(NULL)) == NULL) + { + skygw_log_write(LE,"[%s] Error: MySQL connection initialization failed.",__FUNCTION__); + free(dpasswd); + return; + } + + mysql_options(mysql,MYSQL_OPT_USE_REMOTE_CONNECTION,NULL); + mysql_options(mysql,MYSQL_OPT_CONNECT_TIMEOUT,&conn_timeout); + /** Connect to the first server. This assumes all servers have identical + * user permissions. */ + + if(mysql_real_connect(mysql,server->server->name,user,dpasswd,NULL,server->server->port,NULL,0) == NULL) + { + skygw_log_write(LE,"[%s] Error: Failed to connect to server %s(%s:%d) when" + " checking authentication user credentials and permissions.", + service->name, + server->server->unique_name, + server->server->name, + server->server->port); + mysql_close(mysql); + free(dpasswd); + return; + } + + if(mysql_query(mysql,"select * from mysql.user limit 1") != 0) + { + skygw_log_write(LE,"[%s] Error: Failed to query from mysql.user table. MySQL error message: %s",service->name,mysql_error(mysql)); + mysql_close(mysql); + free(dpasswd); + return; + } + + mysql_free_result(mysql_use_result(mysql)); + + if(mysql_query(mysql,"select * from mysql.db limit 1") != 0) + { + skygw_log_write(LM|LE,"The user '%s' for service '%s' does not have" + " SELECT permissions on the mysql.db table. MaxScale will not use the database in authentication. MySQL error message: %s", + user,service->name,mysql_error(mysql)); + mysql_close(mysql); + free(dpasswd); + return; + } + else + { + mysql_free_result(mysql_use_result(mysql)); + } + + if(mysql_query(mysql,LOAD_MYSQL_DATABASE_NAMES) != 0) + { + skygw_log_write(LE,"[%s] Error: Failed to query for SHOW DATABASES permissions. MySQL error message: %s.",service->name,mysql_error(mysql)); + } + else + { + res = mysql_use_result(mysql); + if(mysql_num_rows(res) == 0) + { + skygw_log_write(LM|LE,"The user '%s' for service '%s' does not have" + " SHOW DATABASES permissions. MaxScale will not use the database in authentication.", + user,service->name); + } + mysql_free_result(res); + } + + mysql_close(mysql); + free(dpasswd); +} \ No newline at end of file diff --git a/server/core/monitor.c b/server/core/monitor.c index f422b4f6b..debbd67a9 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -40,6 +40,7 @@ #include #include #include +#include /** Defined in log_manager.cc */ extern int lm_enabled_logfiles_bitmask; @@ -49,6 +50,8 @@ extern __thread log_info_t tls_log_info; static MONITOR *allMonitors = NULL; static SPINLOCK monLock = SPINLOCK_INIT; +void valid_monitor_permissions(MONITOR* monitor); + /** * Allocate a new monitor, load the associated module for the monitor * and start execution on the monitor. @@ -448,3 +451,58 @@ int *data; return set; } + +/** + * Check if the monitor user has all required permissions to operate properly. + * this checks for ... + * @param service Monitor to inspect + */ +void valid_monitor_permissions(MONITOR* monitor) +{ + MYSQL* mysql; + MYSQL_RES* res; + char *user,*dpasswd; + SERVER* server; + int conn_timeout = 1; + + user = monitor->user; + dpasswd = decryptPassword(monitor->password); + + if((mysql = mysql_init(NULL)) == NULL) + { + skygw_log_write(LE,"[%s] Error: MySQL connection initialization failed.",__FUNCTION__); + free(dpasswd); + return; + } + + server = monitor->databases->server; + mysql_options(mysql,MYSQL_OPT_USE_REMOTE_CONNECTION,NULL); + mysql_options(mysql,MYSQL_OPT_CONNECT_TIMEOUT,&conn_timeout); + + /** Connect to the first server. This assumes all servers have identical + * user permissions. */ + if(mysql_real_connect(mysql,server->name,user,dpasswd,NULL,server->port,NULL,0) == NULL) + { + skygw_log_write(LE,"[%s] Error: Failed to connect to server %s(%s:%d) when" + " checking monitor user credentials and permissions.", + monitor->name, + server->unique_name, + server->name, + server->port); + mysql_close(mysql); + free(dpasswd); + return; + } + + if(mysql_query(mysql,"show slave status") != 0) + { + skygw_log_write(LE,"[%s] Error: Monitor failed to query for slave status. MySQL error message: %s",monitor->name,mysql_error(mysql)); + mysql_close(mysql); + free(dpasswd); + return; + } + + mysql_free_result(mysql_use_result(mysql)); + mysql_close(mysql); + free(dpasswd); +} \ No newline at end of file diff --git a/server/core/service.c b/server/core/service.c index 012fe5d13..4257b74f2 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -438,6 +438,8 @@ if(service->ssl_mode != SSL_DISABLED) return 0; } + valid_service_permissions(service); + port = service->ports; while (!service->svc_do_shutdown && port) { @@ -456,7 +458,7 @@ if(service->ssl_mode != SSL_DISABLED) hktask_add("connection_timeout",session_close_timeouts,NULL,5); } - return listeners; + return listeners; } /** @@ -2042,3 +2044,14 @@ int serviceInitSSL(SERVICE* service) } return 0; } + +/** + * Check if the service is an internal service. Internal services are special + * services which do not require the servers parameter. + * @param service Service to check + * @return True if this service is used only internally + */ +bool service_is_internal(SERVICE* service) +{ + return strcmp(service->routerModule,"cli") == 0 || strcmp(service->routerModule,"debugcli") == 0; +} \ No newline at end of file diff --git a/server/include/dbusers.h b/server/include/dbusers.h index f0367a227..0d78cd548 100644 --- a/server/include/dbusers.h +++ b/server/include/dbusers.h @@ -68,4 +68,5 @@ extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key); extern int replace_mysql_users(SERVICE *service); extern int dbusers_save(USERS *, char *); extern int dbusers_load(USERS *, char *); +void valid_service_permissions(SERVICE* service); #endif diff --git a/server/include/monitor.h b/server/include/monitor.h index 442efb27f..e3a46cffc 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -169,4 +169,5 @@ extern void monitorList(DCB *); extern void monitorSetInterval (MONITOR *, unsigned long); extern void monitorSetNetworkTimeout(MONITOR *, int, int); extern RESULTSET *monitorGetList(); +void valid_monitor_permissions(MONITOR* monitor); #endif diff --git a/server/include/service.h b/server/include/service.h index b64270b9e..2deac6984 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -246,4 +246,5 @@ void service_shutdown(); extern int serviceSessionCountAll(); extern RESULTSET *serviceGetList(); extern RESULTSET *serviceGetListenerList(); +bool service_is_internal(SERVICE* service); #endif