1 /*        $NetBSD: ratelimit.c,v 1.2 2021/10/12 22:51:28 rillig Exp $ */
2 
3 /*-
4  * Copyright (c) 2021 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by James Browning, Gabe Coffland, Alex Gavin, and Solomon Ritzow.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: ratelimit.c,v 1.2 2021/10/12 22:51:28 rillig Exp $");
33 
34 #include <sys/queue.h>
35 
36 #include <arpa/inet.h>
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <syslog.h>
41 #include <unistd.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <stddef.h>
45 
46 #include "inetd.h"
47 
48 union addr {
49           struct in_addr      ipv4_addr;
50           /* ensure aligned for comparison in rl_ipv6_eq (already is on 64-bit) */
51 #ifdef INET6
52           struct in6_addr     ipv6_addr __attribute__((aligned(16)));
53 #endif
54           char                other_addr[NI_MAXHOST];
55 };
56 
57 static void         rl_reset(struct servtab *, time_t);
58 static time_t       rl_time(void);
59 static void         rl_get_name(struct servtab *, int, union addr *);
60 static void         rl_drop_connection(struct servtab *, int);
61 static struct rl_ip_node      *rl_add(struct servtab *, union addr *);
62 static struct rl_ip_node      *rl_try_get_ip(struct servtab *, union addr *);
63 static bool         rl_ip_eq(struct servtab *, union addr *, struct rl_ip_node *);
64 #ifdef INET6
65 static bool         rl_ipv6_eq(struct in6_addr *, struct in6_addr *);
66 #endif
67 #ifdef DEBUG_ENABLE
68 static void         rl_print_found_node(struct servtab *, struct rl_ip_node *);
69 #endif
70 static void         rl_log_address_exceed(struct servtab *, struct rl_ip_node *);
71 static const char   *rl_node_tostring(struct servtab *, struct rl_ip_node *, char[NI_MAXHOST]);
72 static bool         rl_process_service_max(struct servtab *, int, time_t *);
73 static bool         rl_process_ip_max(struct servtab *, int, time_t *);
74 
75 /* Return 0 on allow, -1 if connection should be blocked */
76 int
rl_process(struct servtab * sep,int ctrl)77 rl_process(struct servtab *sep, int ctrl)
78 {
79           time_t now = -1;
80 
81           DPRINTF(SERV_FMT ": processing rate-limiting",
82               SERV_PARAMS(sep));
83           DPRINTF(SERV_FMT ": se_service_max "
84               "%zu and se_count %zu", SERV_PARAMS(sep),
85               sep->se_service_max, sep->se_count);
86 
87           if (sep->se_count == 0) {
88                     now = rl_time();
89                     sep->se_time = now;
90           }
91 
92           if (!rl_process_service_max(sep, ctrl, &now)
93               || !rl_process_ip_max(sep, ctrl, &now)) {
94                     return -1;
95           }
96 
97           DPRINTF(SERV_FMT ": running service ", SERV_PARAMS(sep));
98 
99           /* se_count is only incremented if rl_process will return 0 */
100           sep->se_count++;
101           return 0;
102 }
103 
104 /*
105  * Get the identifier for the remote peer based on sep->se_socktype and
106  * sep->se_family
107  */
108 static void
rl_get_name(struct servtab * sep,int ctrl,union addr * out)109 rl_get_name(struct servtab *sep, int ctrl, union addr *out)
110 {
111           union {
112                     struct sockaddr_storage ss;
113                     struct sockaddr sa;
114                     struct sockaddr_in sin;
115                     struct sockaddr_in6 sin6;
116           } addr;
117 
118           /* Get the sockaddr of socket ctrl */
119           switch (sep->se_socktype) {
120           case SOCK_STREAM: {
121                     socklen_t len = sizeof(struct sockaddr_storage);
122                     if (getpeername(ctrl, &addr.sa, &len) == -1) {
123                               /* error, log it and skip ip rate limiting */
124                               syslog(LOG_ERR,
125                                   SERV_FMT " failed to get peer name of the "
126                                   "connection", SERV_PARAMS(sep));
127                               exit(EXIT_FAILURE);
128                     }
129                     break;
130           }
131           case SOCK_DGRAM: {
132                     struct msghdr header = {
133                               .msg_name = &addr.sa,
134                               .msg_namelen = sizeof(struct sockaddr_storage),
135                               /* scatter/gather and control info is null */
136                     };
137                     ssize_t count;
138 
139                     /* Peek so service can still get the packet */
140                     count = recvmsg(ctrl, &header, MSG_PEEK);
141                     if (count == -1) {
142                               syslog(LOG_ERR,
143                                   "failed to get dgram source address: %s; exiting",
144                                   strerror(errno));
145                               exit(EXIT_FAILURE);
146                     }
147                     break;
148           }
149           default:
150                     DPRINTF(SERV_FMT ": ip_max rate limiting not supported for "
151                         "socktype", SERV_PARAMS(sep));
152                     syslog(LOG_ERR, SERV_FMT
153                         ": ip_max rate limiting not supported for socktype",
154                         SERV_PARAMS(sep));
155                     exit(EXIT_FAILURE);
156           }
157 
158           /* Convert addr to to rate limiting address */
159           switch (sep->se_family) {
160                     case AF_INET:
161                               out->ipv4_addr = addr.sin.sin_addr;
162                               break;
163 #ifdef INET6
164                     case AF_INET6:
165                               out->ipv6_addr = addr.sin6.sin6_addr;
166                               break;
167 #endif
168                     default: {
169                               int res = getnameinfo(&addr.sa,
170                                   (socklen_t)addr.sa.sa_len,
171                                   out->other_addr, NI_MAXHOST,
172                                   NULL, 0,
173                                   NI_NUMERICHOST
174                               );
175                               if (res != 0) {
176                                         syslog(LOG_ERR,
177                                             SERV_FMT ": failed to get name info of "
178                                             "the incoming connection: %s; exiting",
179                                             SERV_PARAMS(sep), gai_strerror(res));
180                                         exit(EXIT_FAILURE);
181                               }
182                               break;
183                     }
184           }
185 }
186 
187 static void
rl_drop_connection(struct servtab * sep,int ctrl)188 rl_drop_connection(struct servtab *sep, int ctrl)
189 {
190 
191           if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM) {
192                     /*
193                      * If the fd isn't a listen socket,
194                      * close the individual connection too.
195                      */
196                     close(ctrl);
197                     return;
198           }
199           if (sep->se_socktype != SOCK_DGRAM) {
200                     return;
201           }
202           /*
203            * Drop the single datagram the service would have
204            * consumed if nowait. If this is a wait service, this
205            * will consume 1 datagram, and further received packets
206            * will be removed in the same way.
207            */
208           struct msghdr header = {
209                     /* All fields null, just consume one message */
210           };
211           ssize_t count;
212 
213           count = recvmsg(ctrl, &header, 0);
214           if (count == -1) {
215                     syslog(LOG_ERR,
216                         SERV_FMT ": failed to consume nowait dgram: %s",
217                         SERV_PARAMS(sep), strerror(errno));
218                     exit(EXIT_FAILURE);
219           }
220           DPRINTF(SERV_FMT ": dropped dgram message",
221               SERV_PARAMS(sep));
222 }
223 
224 static time_t
rl_time(void)225 rl_time(void)
226 {
227           struct timespec ts;
228           if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) {
229                     syslog(LOG_ERR, "clock_gettime for rate limiting failed: %s; "
230                         "exiting", strerror(errno));
231                     /* Exit inetd if rate limiting fails */
232                     exit(EXIT_FAILURE);
233           }
234           return ts.tv_sec;
235 }
236 
237 /* Add addr to IP tracking or return NULL if malloc fails */
238 static struct rl_ip_node *
rl_add(struct servtab * sep,union addr * addr)239 rl_add(struct servtab *sep, union addr *addr)
240 {
241 
242           struct rl_ip_node *node;
243           size_t node_size, bufsize;
244 #ifdef DEBUG_ENABLE
245           char buffer[NI_MAXHOST];
246 #endif
247 
248           switch(sep->se_family) {
249           case AF_INET:
250                     /* ip_node to end of IPv4 address */
251                     node_size = offsetof(struct rl_ip_node, ipv4_addr)
252                         + sizeof(struct in_addr);
253                     break;
254           case AF_INET6:
255                     /* ip_node to end of IPv6 address */
256                     node_size = offsetof(struct rl_ip_node, ipv6_addr)
257                         + sizeof(struct in6_addr);
258                     break;
259           default:
260                     /* ip_node to other_addr plus size of string + NULL */
261                     bufsize = strlen(addr->other_addr) + sizeof(char);
262                     node_size = offsetof(struct rl_ip_node, other_addr) + bufsize;
263                     break;
264           }
265 
266           node = malloc(node_size);
267           if (node == NULL) {
268                     if (errno == ENOMEM) {
269                               return NULL;
270                     } else {
271                               syslog(LOG_ERR, "malloc failed unexpectedly: %s",
272                                   strerror(errno));
273                               exit(EXIT_FAILURE);
274                     }
275           }
276 
277           node->count = 0;
278 
279           /* copy the data into the new allocation */
280           switch(sep->se_family) {
281           case AF_INET:
282                     node->ipv4_addr = addr->ipv4_addr;
283                     break;
284           case AF_INET6:
285                     /* Hopefully this is inlined, means the same thing as memcpy */
286                     __builtin_memcpy(&node->ipv6_addr, &addr->ipv6_addr,
287                         sizeof(struct in6_addr));
288                     break;
289           default:
290                     strlcpy(node->other_addr, addr->other_addr, bufsize);
291                     break;
292           }
293 
294           /* initializes 'entries' member to NULL automatically */
295           SLIST_INSERT_HEAD(&sep->se_rl_ip_list, node, entries);
296 
297           DPRINTF(SERV_FMT ": add '%s' to rate limit tracking (%zu byte record)",
298               SERV_PARAMS(sep), rl_node_tostring(sep, node, buffer), node_size);
299 
300           return node;
301 }
302 
303 static void
rl_reset(struct servtab * sep,time_t now)304 rl_reset(struct servtab *sep, time_t now)
305 {
306           DPRINTF(SERV_FMT ": %ji seconds passed; resetting rate limiting ",
307               SERV_PARAMS(sep), (intmax_t)(now - sep->se_time));
308 
309           sep->se_count = 0;
310           sep->se_time = now;
311           if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) {
312                     rl_clear_ip_list(sep);
313           }
314 }
315 
316 void
rl_clear_ip_list(struct servtab * sep)317 rl_clear_ip_list(struct servtab *sep)
318 {
319           while (!SLIST_EMPTY(&sep->se_rl_ip_list)) {
320                     struct rl_ip_node *node = SLIST_FIRST(&sep->se_rl_ip_list);
321                     SLIST_REMOVE_HEAD(&sep->se_rl_ip_list, entries);
322                     free(node);
323           }
324 }
325 
326 /* Get the node associated with addr, or NULL */
327 static struct rl_ip_node *
rl_try_get_ip(struct servtab * sep,union addr * addr)328 rl_try_get_ip(struct servtab *sep, union addr *addr)
329 {
330 
331           struct rl_ip_node *cur;
332           SLIST_FOREACH(cur, &sep->se_rl_ip_list, entries) {
333                     if (rl_ip_eq(sep, addr, cur)) {
334                               return cur;
335                     }
336           }
337 
338           return NULL;
339 }
340 
341 /* Return true if passed service rate limiting checks, false if blocked */
342 static bool
rl_process_service_max(struct servtab * sep,int ctrl,time_t * now)343 rl_process_service_max(struct servtab *sep, int ctrl, time_t *now)
344 {
345           if (sep->se_count >= sep->se_service_max) {
346                     if (*now == -1) {
347                               /* Only get the clock time if we didn't already */
348                               *now = rl_time();
349                     }
350 
351                     if (*now - sep->se_time > CNT_INTVL) {
352                               rl_reset(sep, *now);
353                     } else {
354                               syslog(LOG_ERR, SERV_FMT
355                                   ": max spawn rate (%zu in %ji seconds) "
356                                   "already met; closing for %ju seconds",
357                                   SERV_PARAMS(sep),
358                                   sep->se_service_max,
359                                   (intmax_t)CNT_INTVL,
360                                   (uintmax_t)RETRYTIME);
361                               DPRINTF(SERV_FMT
362                                   ": max spawn rate (%zu in %ji seconds) "
363                                   "already met; closing for %ju seconds",
364                                   SERV_PARAMS(sep),
365                                   sep->se_service_max,
366                                   (intmax_t)CNT_INTVL,
367                                   (uintmax_t)RETRYTIME);
368 
369                               rl_drop_connection(sep, ctrl);
370 
371                               /* Close the server for 10 minutes */
372                               close_sep(sep);
373                               if (!timingout) {
374                                         timingout = true;
375                                         alarm(RETRYTIME);
376                               }
377 
378                               return false;
379                     }
380           }
381           return true;
382 }
383 
384 /* Return true if passed IP rate limiting checks, false if blocked */
385 static bool
rl_process_ip_max(struct servtab * sep,int ctrl,time_t * now)386 rl_process_ip_max(struct servtab *sep, int ctrl, time_t *now) {
387           if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) {
388                     struct rl_ip_node *node;
389                     union addr addr;
390 
391                     rl_get_name(sep, ctrl, &addr);
392                     node = rl_try_get_ip(sep, &addr);
393                     if (node == NULL) {
394                               node = rl_add(sep, &addr);
395                               if (node == NULL) {
396                                         /* If rl_add can't allocate, reject request */
397                                         DPRINTF("Cannot allocate rl_ip_node");
398                                         return false;
399                               }
400                     }
401 #ifdef DEBUG_ENABLE
402                     else {
403                               /*
404                                * in a separate function to prevent large stack
405                                * frame
406                                */
407                               rl_print_found_node(sep, node);
408                     }
409 #endif
410 
411                     DPRINTF(
412                         SERV_FMT ": se_ip_max %zu and ip_count %zu",
413                         SERV_PARAMS(sep), sep->se_ip_max, node->count);
414 
415                     if (node->count >= sep->se_ip_max) {
416                               if (*now == -1) {
417                                         *now = rl_time();
418                               }
419 
420                               if (*now - sep->se_time > CNT_INTVL) {
421                                         rl_reset(sep, *now);
422                                         node = rl_add(sep, &addr);
423                                         if (node == NULL) {
424                                                   DPRINTF("Cannot allocate rl_ip_node");
425                                                   return false;
426                                         }
427                               } else {
428                                         if (debug && node->count == sep->se_ip_max) {
429                                                   /*
430                                                    * Only log first failed request to
431                                                    * prevent DoS attack writing to system
432                                                    * log
433                                                    */
434                                                   rl_log_address_exceed(sep, node);
435                                         } else {
436                                                   DPRINTF(SERV_FMT
437                                                       ": service not started",
438                                                       SERV_PARAMS(sep));
439                                         }
440 
441                                         rl_drop_connection(sep, ctrl);
442                                         /*
443                                          * Increment so debug-syslog message will
444                                          * trigger only once
445                                          */
446                                         if (node->count < SIZE_MAX) {
447                                                   node->count++;
448                                         }
449                                         return false;
450                               }
451                     }
452                     node->count++;
453           }
454           return true;
455 }
456 
457 static bool
rl_ip_eq(struct servtab * sep,union addr * addr,struct rl_ip_node * cur)458 rl_ip_eq(struct servtab *sep, union addr *addr, struct rl_ip_node *cur) {
459           switch(sep->se_family) {
460           case AF_INET:
461                     if (addr->ipv4_addr.s_addr == cur->ipv4_addr.s_addr) {
462                               return true;
463                     }
464                     break;
465 #ifdef INET6
466           case AF_INET6:
467                     if (rl_ipv6_eq(&addr->ipv6_addr, &cur->ipv6_addr)) {
468                               return true;
469                     }
470                     break;
471 #endif
472           default:
473                     if (strncmp(cur->other_addr, addr->other_addr, NI_MAXHOST)
474                         == 0) {
475                               return true;
476                     }
477                     break;
478           }
479           return false;
480 }
481 
482 #ifdef INET6
483 static bool
rl_ipv6_eq(struct in6_addr * a,struct in6_addr * b)484 rl_ipv6_eq(struct in6_addr *a, struct in6_addr *b)
485 {
486 #if UINTMAX_MAX >= UINT64_MAX
487           { /* requires 8 byte aligned structs */
488                     uint64_t *ap = (uint64_t *)a->s6_addr;
489                     uint64_t *bp = (uint64_t *)b->s6_addr;
490                     return (ap[0] == bp[0]) & (ap[1] == bp[1]);
491           }
492 #else
493           { /* requires 4 byte aligned structs */
494                     uint32_t *ap = (uint32_t *)a->s6_addr;
495                     uint32_t *bp = (uint32_t *)b->s6_addr;
496                     return ap[0] == bp[0] && ap[1] == bp[1] &&
497                               ap[2] == bp[2] && ap[3] == bp[3];
498           }
499 #endif
500 }
501 #endif
502 
503 static const char *
rl_node_tostring(struct servtab * sep,struct rl_ip_node * node,char buffer[NI_MAXHOST])504 rl_node_tostring(struct servtab *sep, struct rl_ip_node *node,
505     char buffer[NI_MAXHOST])
506 {
507           switch (sep->se_family) {
508           case AF_INET:
509 #ifdef INET6
510           case AF_INET6:
511 #endif
512                     /* ipv4_addr/ipv6_addr share same address */
513                     return inet_ntop(sep->se_family, (void*)&node->ipv4_addr,
514                         (char*)buffer, NI_MAXHOST);
515           default:
516                     return (char *)&node->other_addr;
517           }
518 }
519 
520 #ifdef DEBUG_ENABLE
521 /* Separate function due to large buffer size */
522 static void
rl_print_found_node(struct servtab * sep,struct rl_ip_node * node)523 rl_print_found_node(struct servtab *sep, struct rl_ip_node *node)
524 {
525           char buffer[NI_MAXHOST];
526           DPRINTF(SERV_FMT ": found record for address '%s'",
527               SERV_PARAMS(sep), rl_node_tostring(sep, node, buffer));
528 }
529 #endif
530 
531 /* Separate function due to large buffer sie */
532 static void
rl_log_address_exceed(struct servtab * sep,struct rl_ip_node * node)533 rl_log_address_exceed(struct servtab *sep, struct rl_ip_node *node)
534 {
535           char buffer[NI_MAXHOST];
536           const char * name = rl_node_tostring(sep, node, buffer);
537           syslog(LOG_ERR, SERV_FMT
538               ": max ip spawn rate (%zu in "
539               "%ji seconds) for "
540               "'%." TOSTRING(NI_MAXHOST) "s' "
541               "already met; service not started",
542               SERV_PARAMS(sep),
543               sep->se_ip_max,
544               (intmax_t)CNT_INTVL,
545               name);
546           DPRINTF(SERV_FMT
547               ": max ip spawn rate (%zu in "
548               "%ji seconds) for "
549               "'%." TOSTRING(NI_MAXHOST) "s' "
550               "already met; service not started",
551               SERV_PARAMS(sep),
552               sep->se_ip_max,
553               (intmax_t)CNT_INTVL,
554               name);
555 }
556