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