1 /* Copyright (c) 2016, 2021, Oracle and/or its affiliates. 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License, version 2.0, 5 as published by the Free Software Foundation. 6 7 This program is also distributed with certain software (including 8 but not limited to OpenSSL) that is licensed under separate terms, 9 as designated in a particular file or component or in included license 10 documentation. The authors of MySQL hereby grant you an additional 11 permission to link the program and your derivative works with the 12 separately licensed software that they have included with MySQL. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License, version 2.0, for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; if not, write to the Free Software 21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ 22 23 #ifndef CONNECTION_DELAY_H 24 #define CONNECTION_DELAY_H 25 26 #include <lf.h> /* LF Hash */ 27 #include <my_global.h> 28 #include <my_atomic.h> /* my_atomic_* */ 29 #include <mysql_com.h> /* USERNAME_LENGTH */ 30 #include "table.h" /* TABLE_LIST */ 31 32 #include "connection_control_interfaces.h" /* Observer interface */ 33 #include "connection_delay_api.h" /* Constants */ 34 #include "connection_control_data.h" /* variables and status */ 35 #include "connection_control_memory.h" /* Connection_control_alloc */ 36 37 namespace connection_control 38 { 39 /** 40 Class to store failed attempts information for a given user. 41 */ 42 43 class Connection_event_record : public Connection_control_alloc 44 { 45 public: 46 47 /** 48 Constructor for Connection_event_record. Always initializes failed login count to 1. 49 */ Connection_event_record(const Sql_string & s)50 Connection_event_record(const Sql_string &s) 51 : m_count(1) 52 { 53 memset((void *)m_userhost, 0, sizeof(m_userhost)); 54 memcpy((void *)m_userhost, s.c_str(), s.length()); 55 m_length= s.length(); 56 m_count= 1; 57 } 58 59 /** 60 Retrives failed login count for given user entry 61 62 @returns Failed login count 63 */ get_count()64 int64 get_count() const 65 { 66 int64 result= my_atomic_load64((volatile int64*)&m_count); 67 return result; 68 } 69 70 /** Increment failed login count for given user entry by 1 */ inc_count()71 void inc_count() 72 { 73 my_atomic_add64((volatile int64*)&m_count, 1); 74 } 75 76 /** Reset failed login count for given user entry */ reset_count()77 void reset_count() 78 { 79 my_atomic_store64(&m_count, 0); 80 } 81 82 /** Get user information */ get_userhost()83 uchar * get_userhost() const 84 { 85 return const_cast<uchar *> (m_userhost); 86 } 87 88 /** Get length information */ get_length()89 size_t get_length() const 90 { 91 return m_length; 92 } 93 94 /** Destructor */ ~Connection_event_record()95 ~Connection_event_record() 96 { 97 m_count= DISABLE_THRESHOLD; 98 } 99 100 private: 101 /* '<user>'@'<host>' */ 102 uchar m_userhost[1 + USERNAME_LENGTH + 3 + HOSTNAME_LENGTH + 1 + 1]; 103 /* Length of m_userhost */ 104 size_t m_length; 105 /* connection event count */ 106 volatile int64 m_count; 107 }; 108 109 110 /** 111 Hash for a connection event. 112 Stores information in Connection_event_record object for each user. 113 */ 114 115 class Connection_delay_event : public Connection_event_records 116 { 117 public: 118 119 /** Constructor. Also initializes the hash */ 120 Connection_delay_event(); 121 122 /** Destructor. Removes all entries from hash before destroying hash */ ~Connection_delay_event()123 ~Connection_delay_event() 124 { 125 reset_all(); 126 lf_hash_destroy(&m_entries); 127 } 128 129 void fill_IS_table(THD *thd, TABLE_LIST *tables); 130 131 /* Overridden function */ 132 bool create_or_update_entry(const Sql_string &s); 133 bool remove_entry(const Sql_string &s); 134 bool match_entry(const Sql_string &s, void * value); 135 void reset_all(); 136 137 private: 138 /** Hash for storing Connection_event_record per user */ 139 LF_HASH m_entries; 140 }; 141 142 143 /** 144 Connection event action to enforce max failed login constraint 145 */ 146 147 class Connection_delay_action : public Connection_event_observer, 148 public Connection_control_alloc 149 { 150 public: 151 152 Connection_delay_action(int64 threshold, 153 int64 min_delay, 154 int64 max_delay, 155 opt_connection_control *sys_vars, 156 size_t sys_vars_size, 157 stats_connection_control *status_vars, 158 size_t status_vars_size, 159 mysql_rwlock_t *lock); 160 161 /** Destructor */ ~Connection_delay_action()162 ~Connection_delay_action() 163 { 164 deinit(); 165 m_lock= 0; 166 } 167 168 void init(Connection_event_coordinator_services *coordinator); 169 170 /** 171 Set threshold value. 172 173 @param threshold [in] New threshold value 174 175 @returns whether threshold value was changed successfully or not 176 @retval true Success 177 @retval false Failure. Invalid threshold value specified. 178 */ 179 set_threshold(int64 threshold)180 void set_threshold(int64 threshold) 181 { 182 my_atomic_store64(&m_threshold, threshold); 183 /* Clear the hash */ 184 m_userhost_hash.reset_all(); 185 } 186 187 /** Get threshold value */ get_threshold()188 int64 get_threshold() 189 { 190 int64 result= my_atomic_load64(&m_threshold); 191 return result; 192 } 193 194 /** 195 Set min/max delay 196 197 @param new_value [in] New m_min_delay/m_max_delay value 198 @param min [in] true for m_min_delay. false otherwise. 199 200 @returns whether m_min_delay/m_max_delay value was changed successfully or not 201 @retval false Success 202 @retval true Failure. Invalid value specified. 203 */ 204 set_delay(int64 new_value,bool min)205 bool set_delay(int64 new_value, bool min) 206 { 207 int64 current_max= get_max_delay(); 208 int64 current_min= get_min_delay(); 209 210 if (new_value < MIN_DELAY) 211 return true; 212 213 if ((min && new_value > current_max) || 214 (!min && new_value < current_min)) 215 return true; 216 217 else 218 min ? my_atomic_store64(&m_min_delay, new_value) : 219 my_atomic_store64(&m_max_delay, new_value); 220 return false; 221 } 222 223 /** Get max value */ get_max_delay()224 int64 get_max_delay() 225 { 226 int64 result= my_atomic_load64(&m_max_delay); 227 return result; 228 } 229 230 /** Get min value */ get_min_delay()231 int64 get_min_delay() 232 { 233 int64 result= my_atomic_load64(&m_min_delay); 234 return result; 235 } 236 237 void fill_IS_table(THD *thd, 238 TABLE_LIST *tables, 239 Item *cond); 240 241 /** Overridden functions */ 242 bool notify_event(MYSQL_THD thd, 243 Connection_event_coordinator_services *coordinator, 244 const mysql_event_connection *connection_event, 245 Error_handler *error_handler); 246 bool notify_sys_var(Connection_event_coordinator_services *coordinator, 247 opt_connection_control variable, 248 void *new_value, 249 Error_handler *error_handler); 250 251 private: 252 void deinit(); 253 void make_hash_key(MYSQL_THD thd, Sql_string &s); 254 /** 255 Generates wait time 256 257 @param count [in] Proposed delay 258 259 @returns wait time 260 */ 261 get_wait_time(int64 count)262 inline ulonglong get_wait_time(int64 count) 263 { 264 int64 max_delay= get_max_delay(); 265 int64 min_delay= get_min_delay(); 266 int64 count_mili= count*1000; 267 268 /* 269 if count < 0 (can happen in edge cases 270 we return max_delay. 271 Otherwise, following equation will be used: 272 wait_time = MIN(MIN(count, min_delay), 273 max_delay) 274 */ 275 return (static_cast<ulonglong>((count_mili >= MIN_DELAY && count_mili < max_delay) ? 276 (count_mili < min_delay ? min_delay : count_mili) : 277 max_delay)); 278 } 279 void conditional_wait(THD *thd, 280 ulonglong wait_time); 281 282 private: 283 /** Threshold value which triggers wait */ 284 volatile int64 m_threshold; 285 /** Lower cap on delay to be generated */ 286 volatile int64 m_min_delay; 287 /** Upper cap on delay to be generated */ 288 volatile int64 m_max_delay; 289 /** System variables */ 290 std::vector<opt_connection_control> m_sys_vars; 291 /** Status variables */ 292 std::vector<stats_connection_control> m_stats_vars; 293 /** Hash to store failed attempts for each user entry */ 294 Connection_delay_event m_userhost_hash; 295 /** RW lock */ 296 mysql_rwlock_t *m_lock; 297 }; 298 } 299 #endif /* !CONNECTION_DELAY_H */ 300