1 /* Copyright (c) 2017, MariaDB
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 as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA */
15 
16 #include <mariadb.h>
17 #include <mysql.h>
18 #include <mysql_com.h>
19 #include <mysqld_error.h>
20 #include <my_sys.h>
21 #include <m_string.h>
22 #include <my_net.h>
23 #include <violite.h>
24 #include <proxy_protocol.h>
25 #include <log.h>
26 #include <my_pthread.h>
27 
28 #define PROXY_PROTOCOL_V1_SIGNATURE "PROXY"
29 #define PROXY_PROTOCOL_V2_SIGNATURE "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
30 #define MAX_PROXY_HEADER_LEN 256
31 
32 static mysql_rwlock_t lock;
33 
34 /*
35   Parse proxy protocol version 1 header (text)
36 */
parse_v1_header(char * hdr,size_t len,proxy_peer_info * peer_info)37 static int parse_v1_header(char *hdr, size_t len, proxy_peer_info *peer_info)
38 {
39   char address_family[MAX_PROXY_HEADER_LEN + 1];
40   char client_address[MAX_PROXY_HEADER_LEN + 1];
41   char server_address[MAX_PROXY_HEADER_LEN + 1];
42   int client_port;
43   int server_port;
44 
45   int ret = sscanf(hdr, "PROXY %s %s %s %d %d",
46     address_family, client_address, server_address,
47     &client_port, &server_port);
48 
49   if (ret != 5)
50   {
51     if (ret >= 1 && !strcmp(address_family, "UNKNOWN"))
52     {
53       peer_info->is_local_command= true;
54       return 0;
55     }
56     return -1;
57   }
58 
59   if (client_port < 0 || client_port > 0xffff
60     || server_port < 0 || server_port > 0xffff)
61     return -1;
62 
63   if (!strcmp(address_family, "UNKNOWN"))
64   {
65     peer_info->is_local_command= true;
66     return 0;
67   }
68   else if (!strcmp(address_family, "TCP4"))
69   {
70     /* Initialize IPv4 peer address.*/
71     peer_info->peer_addr.ss_family= AF_INET;
72     if (!inet_pton(AF_INET, client_address,
73       &((struct sockaddr_in *)(&peer_info->peer_addr))->sin_addr))
74       return -1;
75   }
76   else if (!strcmp(address_family, "TCP6"))
77   {
78     /* Initialize IPv6 peer address.*/
79     peer_info->peer_addr.ss_family= AF_INET6;
80     if (!inet_pton(AF_INET6, client_address,
81       &((struct sockaddr_in6 *)(&peer_info->peer_addr))->sin6_addr))
82       return -1;
83   }
84   peer_info->port= client_port;
85   /* Check if server address is legal.*/
86   char addr_bin[16];
87   if (!inet_pton(peer_info->peer_addr.ss_family,
88     server_address, addr_bin))
89     return -1;
90 
91   return 0;
92 }
93 
94 
95 /*
96   Parse proxy protocol V2 (binary) header
97 */
parse_v2_header(uchar * hdr,size_t len,proxy_peer_info * peer_info)98 static int parse_v2_header(uchar *hdr, size_t len,proxy_peer_info *peer_info)
99 {
100   /* V2 Signature */
101   if (memcmp(hdr, PROXY_PROTOCOL_V2_SIGNATURE, 12))
102     return -1;
103 
104   /* version  + command */
105   uint8 ver= (hdr[12] & 0xF0);
106   if (ver != 0x20)
107     return -1; /* Wrong version*/
108 
109   uint cmd= (hdr[12] & 0xF);
110 
111   /* Address family */
112   uchar fam= hdr[13];
113 
114   if (cmd == 0)
115   {
116     /* LOCAL command*/
117     peer_info->is_local_command= true;
118     return 0;
119   }
120 
121   if (cmd != 0x01)
122   {
123     /* Not PROXY COMMAND */
124     return -1;
125   }
126 
127   struct sockaddr_in *sin= (struct sockaddr_in *)(&peer_info->peer_addr);
128   struct sockaddr_in6 *sin6= (struct sockaddr_in6 *)(&peer_info->peer_addr);
129   switch (fam)
130   {
131     case 0x11:  /* TCPv4 */
132       sin->sin_family= AF_INET;
133       memcpy(&(sin->sin_addr), hdr + 16, 4);
134       peer_info->port= (hdr[24] << 8) + hdr[25];
135       break;
136     case 0x21:  /* TCPv6 */
137       sin6->sin6_family= AF_INET6;
138       memcpy(&(sin6->sin6_addr), hdr + 16, 16);
139       peer_info->port= (hdr[48] << 8) + hdr[49];
140       break;
141     case 0x31: /* AF_UNIX, stream */
142       peer_info->peer_addr.ss_family= AF_UNIX;
143       break;
144     default:
145       return -1;
146   }
147   return 0;
148 }
149 
150 
has_proxy_protocol_header(NET * net)151 bool has_proxy_protocol_header(NET *net)
152 {
153   compile_time_assert(NET_HEADER_SIZE < sizeof(PROXY_PROTOCOL_V1_SIGNATURE));
154   compile_time_assert(NET_HEADER_SIZE < sizeof(PROXY_PROTOCOL_V2_SIGNATURE));
155 
156   const uchar *preread_bytes= net->buff + net->where_b;
157   return !memcmp(preread_bytes, PROXY_PROTOCOL_V1_SIGNATURE, NET_HEADER_SIZE)||
158       !memcmp(preread_bytes, PROXY_PROTOCOL_V2_SIGNATURE, NET_HEADER_SIZE);
159 }
160 
161 
162 /**
163   Try to parse proxy header.
164   https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
165 
166   Whenever this function is called, client is connecting, and
167   we have have pre-read 4 bytes (NET_HEADER_SIZE)  from the network already.
168   These 4 bytes did not match MySQL packet header, and (unless the client
169   is buggy), those bytes must be proxy header.
170 
171    @param[in]  net - vio and already preread bytes from the header
172    @param[out] peer_info - parsed proxy header with client host and port
173    @return 0 in case of success, -1 if error.
174 */
parse_proxy_protocol_header(NET * net,proxy_peer_info * peer_info)175 int parse_proxy_protocol_header(NET *net, proxy_peer_info *peer_info)
176 {
177   uchar hdr[MAX_PROXY_HEADER_LEN];
178   size_t pos= 0;
179 
180   DBUG_ASSERT(!net->compress);
181   const uchar *preread_bytes= net->buff + net->where_b;
182   bool have_v1_header= !memcmp(preread_bytes, PROXY_PROTOCOL_V1_SIGNATURE, NET_HEADER_SIZE);
183   bool have_v2_header=
184     !have_v1_header && !memcmp(preread_bytes, PROXY_PROTOCOL_V2_SIGNATURE, NET_HEADER_SIZE);
185   if (!have_v1_header && !have_v2_header)
186   {
187     // not a proxy protocol header
188     return -1;
189   }
190   memcpy(hdr, preread_bytes, NET_HEADER_SIZE);
191   pos= NET_HEADER_SIZE;
192   Vio *vio= net->vio;
193   memset(peer_info, 0, sizeof (*peer_info));
194 
195   if (have_v1_header)
196   {
197     /* Read until end of header (newline character)*/
198     while(pos < sizeof(hdr))
199     {
200       long len= (long)vio_read(vio, hdr + pos, 1);
201       if (len < 0)
202         return -1;
203       pos++;
204       if (hdr[pos-1] == '\n')
205         break;
206     }
207     hdr[pos]= 0;
208 
209     if (parse_v1_header((char *)hdr, pos, peer_info))
210       return -1;
211   }
212   else // if (have_v2_header)
213   {
214 #define PROXY_V2_HEADER_LEN 16
215     /* read off 16 bytes of the header.*/
216     ssize_t len= vio_read(vio, hdr + pos, PROXY_V2_HEADER_LEN - pos);
217     if (len < 0)
218       return -1;
219     // 2 last bytes are the length in network byte order of the part following header
220     ushort trail_len= ((ushort)hdr[PROXY_V2_HEADER_LEN-2] >> 8) + hdr[PROXY_V2_HEADER_LEN-1];
221     if (trail_len > sizeof(hdr) - PROXY_V2_HEADER_LEN)
222       return -1;
223     if (trail_len > 0)
224     {
225       len= vio_read(vio,  hdr + PROXY_V2_HEADER_LEN, trail_len);
226       if (len < 0)
227         return -1;
228     }
229     pos= PROXY_V2_HEADER_LEN + trail_len;
230     if (parse_v2_header(hdr, pos, peer_info))
231       return -1;
232   }
233 
234   if (peer_info->peer_addr.ss_family == AF_INET6)
235   {
236     /*
237       Normalize IPv4 compatible or mapped IPv6 addresses.
238       They will be treated as IPv4.
239     */
240     sockaddr_storage tmp;
241     memset(&tmp, 0, sizeof(tmp));
242     vio_get_normalized_ip((const struct sockaddr *)&peer_info->peer_addr,
243       sizeof(sockaddr_storage), (struct sockaddr *)&tmp);
244     memcpy(&peer_info->peer_addr, &tmp, sizeof(tmp));
245   }
246   return 0;
247 }
248 
249 
250 /**
251  CIDR address matching etc (for the proxy_protocol_networks parameter)
252 */
253 
254 /**
255   Subnetwork address in CIDR format, e.g
256   192.168.1.0/24 or 2001:db8::/32
257 */
258 struct subnet
259 {
260   char addr[16]; /* Binary representation of the address, big endian*/
261   unsigned short family; /* Address family, AF_INET or AF_INET6 */
262   unsigned short bits; /* subnetwork size */
263 };
264 
265 static  subnet* proxy_protocol_subnets;
266 size_t  proxy_protocol_subnet_count;
267 
268 #define MAX_MASK_BITS(family) (family == AF_INET ? 32 : 128)
269 
270 
271 /** Convert IPv4 that are compat or mapped IPv4 to "normal" IPv4 */
normalize_subnet(struct subnet * subnet)272 static int normalize_subnet(struct subnet *subnet)
273 {
274   unsigned char *addr= (unsigned char*)subnet->addr;
275   if (subnet->family == AF_INET6)
276   {
277     const struct in6_addr *src_ip6=(in6_addr *)addr;
278     if (IN6_IS_ADDR_V4MAPPED(src_ip6) || IN6_IS_ADDR_V4COMPAT(src_ip6))
279     {
280       /* Copy the actual IPv4 address (4 last bytes) */
281       if (subnet->bits < 96)
282         return -1;
283       subnet->family= AF_INET;
284       memcpy(addr, addr+12, 4);
285       subnet->bits -= 96;
286     }
287   }
288   return 0;
289 }
290 
291 /**
292   Convert string representation of a subnet to subnet struct.
293 */
parse_subnet(char * addr_str,struct subnet * subnet)294 static int parse_subnet(char *addr_str, struct subnet *subnet)
295 {
296   if (strchr(addr_str, ':'))
297     subnet->family= AF_INET6;
298   else if (strchr(addr_str, '.'))
299     subnet->family= AF_INET;
300   else if (!strcmp(addr_str, "localhost"))
301   {
302     subnet->family= AF_UNIX;
303     subnet->bits= 0;
304     return 0;
305   }
306 
307   char *pmask= strchr(addr_str, '/');
308   if (!pmask)
309   {
310     subnet->bits= MAX_MASK_BITS(subnet->family);
311   }
312   else
313   {
314     *pmask= 0;
315     pmask++;
316     int b= 0;
317 
318     do
319     {
320       if (*pmask < '0' || *pmask > '9')
321         return -1;
322       b= 10 * b + *pmask - '0';
323       if (b > MAX_MASK_BITS(subnet->family))
324         return -1;
325       pmask++;
326     }
327     while (*pmask);
328 
329     subnet->bits= (unsigned short)b;
330   }
331 
332   if (!inet_pton(subnet->family, addr_str, subnet->addr))
333     return -1;
334 
335   if (normalize_subnet(subnet))
336     return -1;
337 
338   return 0;
339 }
340 
341 /**
342   Parse comma separated string subnet list into subnets array,
343   which is stored in 'proxy_protocol_subnets' variable
344 
345   @param[in] subnets_str : networks in CIDR format,
346     separated by comma and/or space
347   @param[out] out_subnets : parsed subnets;
348   @param[out] out_count : number of parsed subnets
349 
350   @return 0 if success, otherwise -1
351 */
parse_networks(const char * subnets_str,subnet ** out_subnets,size_t * out_count)352 static int parse_networks(const char *subnets_str, subnet **out_subnets, size_t *out_count)
353 {
354   int ret= -1;
355   subnet *subnets= 0;
356   size_t count= 0;
357   const char *p= subnets_str;
358   size_t max_subnets;
359 
360   if (!subnets_str || !*subnets_str)
361   {
362     ret= 0;
363     goto end;
364   }
365 
366   max_subnets= MY_MAX(3,strlen(subnets_str)/2);
367   subnets= (subnet *)my_malloc(max_subnets * sizeof(subnet),MY_ZEROFILL);
368 
369   /* Check for special case '*'. */
370   if (strcmp(subnets_str, "*") == 0)
371   {
372     subnets[0].family= AF_INET;
373     subnets[1].family= AF_INET6;
374     subnets[2].family= AF_UNIX;
375     count= 3;
376     ret= 0;
377     goto end;
378   }
379 
380   char token[256];
381   for(count= 0;; count++)
382   {
383     while(*p && (*p ==',' || *p == ' '))
384       p++;
385     if (!*p)
386       break;
387 
388     size_t cnt= 0;
389     while(*p && *p != ',' && *p != ' ' && cnt < sizeof(token)-1)
390       token[cnt++]= *p++;
391 
392     token[cnt++]=0;
393     if (cnt == sizeof(token))
394       goto end;
395 
396     if (parse_subnet(token, &subnets[count]))
397     {
398       my_printf_error(ER_PARSE_ERROR,"Error parsing proxy_protocol_networks parameter, near '%s'",MYF(0),token);
399       goto end;
400     }
401   }
402 
403   ret = 0;
404 
405 end:
406   if (ret)
407   {
408     my_free(subnets);
409     *out_subnets= NULL;
410     *out_count= 0;
411     return ret;
412   }
413   *out_subnets = subnets;
414   *out_count= count;
415   return 0;
416 }
417 
418 /**
419   Check validity of proxy_protocol_networks parameter
420   @param[in] in - input string
421   @return : true, if input is list of CIDR-style networks
422     separated by command or space
423 */
proxy_protocol_networks_valid(const char * in)424 bool proxy_protocol_networks_valid(const char *in)
425 {
426   subnet *new_subnets;
427   size_t new_count;
428   int ret= parse_networks(in, &new_subnets, &new_count);
429   my_free(new_subnets);
430   return !ret;
431 }
432 
433 
434 /**
435   Set 'proxy_protocol_networks' parameter.
436 
437   @param[in] spec : networks in CIDR format,
438     separated by comma and/or space
439 
440   @return 0 if success, otherwise -1
441 */
set_proxy_protocol_networks(const char * spec)442 int set_proxy_protocol_networks(const char *spec)
443 {
444   subnet *new_subnets;
445   subnet *old_subnet = 0;
446   size_t new_count;
447 
448   int ret= parse_networks(spec, &new_subnets, &new_count);
449   if (ret)
450     return ret;
451 
452   mysql_rwlock_wrlock(&lock);
453   old_subnet = proxy_protocol_subnets;
454   proxy_protocol_subnets = new_subnets;
455   proxy_protocol_subnet_count = new_count;
456   mysql_rwlock_unlock(&lock);
457   my_free(old_subnet);
458   return ret;
459 }
460 
461 
462 /**
463    Compare memory areas, in memcmp().similar fashion.
464    The difference to memcmp() is that size parameter is the
465    bit count, not byte count.
466 */
compare_bits(const void * s1,const void * s2,int bit_count)467 static int compare_bits(const void *s1, const void *s2, int bit_count)
468 {
469   int result= 0;
470   int byte_count= bit_count / 8;
471   if (byte_count && (result= memcmp(s1, s2, byte_count)))
472     return result;
473   int rem= bit_count % 8;
474   if (rem)
475   {
476     // compare remaining bits i.e partial bytes.
477     unsigned char s1_bits= (((char *)s1)[byte_count]) >> (8 - rem);
478     unsigned char s2_bits= (((char *)s2)[byte_count]) >> (8 - rem);
479     if (s1_bits > s2_bits)
480       return 1;
481     if (s1_bits < s2_bits)
482       return -1;
483   }
484   return 0;
485 }
486 
487 /**
488   Check whether networks address matches network.
489 */
addr_matches_subnet(const sockaddr * sock_addr,const subnet * subnet)490 bool addr_matches_subnet(const sockaddr *sock_addr, const subnet *subnet)
491 {
492   DBUG_ASSERT(subnet->family == AF_UNIX ||
493     subnet->family == AF_INET ||
494     subnet->family == AF_INET6);
495 
496   if (sock_addr->sa_family != subnet->family)
497     return false;
498 
499   if (subnet->family == AF_UNIX)
500     return true;
501 
502   void *addr= (subnet->family == AF_INET) ?
503     (void *)&((struct sockaddr_in *)sock_addr)->sin_addr :
504     (void *)&((struct sockaddr_in6 *)sock_addr)->sin6_addr;
505 
506   return (compare_bits(subnet->addr, addr, subnet->bits) == 0);
507 }
508 
509 
510 /**
511   Check whether proxy header from client is allowed, as per
512   specification in 'proxy_protocol_networks' server variable.
513 
514   The non-TCP "localhost" clients (unix socket, shared memory, pipes)
515   are accepted whenever 127.0.0.1 accepted  in 'proxy_protocol_networks'
516 */
is_proxy_protocol_allowed(const sockaddr * addr)517 bool is_proxy_protocol_allowed(const sockaddr *addr)
518 {
519   if (proxy_protocol_subnet_count == 0)
520     return false;
521 
522   sockaddr_storage addr_storage;
523   struct sockaddr *normalized_addr= (struct sockaddr *)&addr_storage;
524 
525   /*
526    Non-TCP addresses (unix domain socket, windows pipe and shared memory
527    gets tranlated to TCP4 localhost address.
528 
529    Note, that vio remote addresses are initialized with binary zeros
530    for these protocols (which is AF_UNSPEC everywhere).
531   */
532   switch(addr->sa_family)
533   {
534     case AF_UNSPEC:
535     case AF_UNIX:
536       normalized_addr->sa_family= AF_UNIX;
537       break;
538     case AF_INET:
539     case AF_INET6:
540       {
541       size_t len=
542         (addr->sa_family == AF_INET)?sizeof(sockaddr_in):sizeof (sockaddr_in6);
543       vio_get_normalized_ip(addr, len,normalized_addr);
544       }
545       break;
546     default:
547       DBUG_ASSERT(0);
548   }
549 
550   bool ret= false;
551   mysql_rwlock_rdlock(&lock);
552   for (size_t i= 0; i < proxy_protocol_subnet_count; i++)
553   {
554     if (addr_matches_subnet(normalized_addr, &proxy_protocol_subnets[i]))
555     {
556       ret= true;
557       break;
558     }
559   }
560   mysql_rwlock_unlock(&lock);
561 
562   return ret;
563 }
564 
565 
init_proxy_protocol_networks(const char * spec)566 int init_proxy_protocol_networks(const char *spec)
567 {
568 #ifdef HAVE_PSI_INTERFACE
569   static PSI_rwlock_key psi_rwlock_key;
570   static PSI_rwlock_info psi_rwlock_info={ &psi_rwlock_key, "rwlock", 0 };
571   mysql_rwlock_register("proxy_proto", &psi_rwlock_info, 1);
572 #endif
573 
574   mysql_rwlock_init(psi_rwlock_key, &lock);
575   return set_proxy_protocol_networks(spec);
576 }
577 
578 
destroy_proxy_protocol_networks()579 void destroy_proxy_protocol_networks()
580 {
581   my_free(proxy_protocol_subnets);
582   mysql_rwlock_destroy(&lock);
583 }
584