1 /* 2 * Copyright (c) 2020 Darren Tucker <dtucker@openbsd.org> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <sys/socket.h> 18 #include <sys/types.h> 19 20 #include <limits.h> 21 #include <netdb.h> 22 #include <stdio.h> 23 #include <string.h> 24 25 #include "addr.h" 26 #include "canohost.h" 27 #include "log.h" 28 #include "misc.h" 29 #include "srclimit.h" 30 #include "xmalloc.h" 31 32 static int max_children, max_persource, ipv4_masklen, ipv6_masklen; 33 34 /* Per connection state, used to enforce unauthenticated connection limit. */ 35 static struct child_info { 36 int id; 37 struct xaddr addr; 38 } *child; 39 40 void 41 srclimit_init(int max, int persource, int ipv4len, int ipv6len) 42 { 43 int i; 44 45 max_children = max; 46 ipv4_masklen = ipv4len; 47 ipv6_masklen = ipv6len; 48 max_persource = persource; 49 if (max_persource == INT_MAX) /* no limit */ 50 return; 51 debug("%s: max connections %d, per source %d, masks %d,%d", __func__, 52 max, persource, ipv4len, ipv6len); 53 if (max <= 0) 54 fatal("%s: invalid number of sockets: %d", __func__, max); 55 child = xcalloc(max_children, sizeof(*child)); 56 for (i = 0; i < max_children; i++) 57 child[i].id = -1; 58 } 59 60 /* returns 1 if connection allowed, 0 if not allowed. */ 61 int 62 srclimit_check_allow(int sock, int id) 63 { 64 struct xaddr xa, xb, xmask; 65 struct sockaddr_storage addr; 66 socklen_t addrlen = sizeof(addr); 67 struct sockaddr *sa = (struct sockaddr *)&addr; 68 int i, bits, first_unused, count = 0; 69 char xas[NI_MAXHOST]; 70 71 if (max_persource == INT_MAX) /* no limit */ 72 return 1; 73 74 debug("%s: sock %d id %d limit %d", __func__, sock, id, max_persource); 75 if (getpeername(sock, sa, &addrlen) != 0) 76 return 1; /* not remote socket? */ 77 if (addr_sa_to_xaddr(sa, addrlen, &xa) != 0) 78 return 1; /* unknown address family? */ 79 80 /* Mask address off address to desired size. */ 81 bits = xa.af == AF_INET ? ipv4_masklen : ipv6_masklen; 82 if (addr_netmask(xa.af, bits, &xmask) != 0 || 83 addr_and(&xb, &xa, &xmask) != 0) { 84 debug3("%s: invalid mask %d bits", __func__, bits); 85 return 1; 86 } 87 88 first_unused = max_children; 89 /* Count matching entries and find first unused one. */ 90 for (i = 0; i < max_children; i++) { 91 if (child[i].id == -1) { 92 if (i < first_unused) 93 first_unused = i; 94 } else if (addr_cmp(&child[i].addr, &xb) == 0) { 95 count++; 96 } 97 } 98 if (addr_ntop(&xa, xas, sizeof(xas)) != 0) { 99 debug3("%s: addr ntop failed", __func__); 100 return 1; 101 } 102 debug3("%s: new unauthenticated connection from %s/%d, at %d of %d", 103 __func__, xas, bits, count, max_persource); 104 105 if (first_unused == max_children) { /* no free slot found */ 106 debug3("%s: no free slot", __func__); 107 return 0; 108 } 109 if (first_unused < 0 || first_unused >= max_children) 110 fatal("%s: internal error: first_unused out of range", 111 __func__); 112 113 if (count >= max_persource) 114 return 0; 115 116 /* Connection allowed, store masked address. */ 117 child[first_unused].id = id; 118 memcpy(&child[first_unused].addr, &xb, sizeof(xb)); 119 return 1; 120 } 121 122 void 123 srclimit_done(int id) 124 { 125 int i; 126 127 if (max_persource == INT_MAX) /* no limit */ 128 return; 129 130 debug("%s: id %d", __func__, id); 131 /* Clear corresponding state entry. */ 132 for (i = 0; i < max_children; i++) { 133 if (child[i].id == id) { 134 child[i].id = -1; 135 return; 136 } 137 } 138 } 139