1 /* $NetBSD: proxyp.c,v 1.2 2021/08/14 16:14:58 christos Exp $ */
2
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 2000-2020 The OpenLDAP Foundation.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17
18 #include <sys/cdefs.h>
19 __RCSID("$NetBSD: proxyp.c,v 1.2 2021/08/14 16:14:58 christos Exp $");
20
21 #include "portable.h"
22 #include "slap.h"
23
24 #ifdef HAVE_STDINT_H
25 #include <stdint.h>
26 #endif
27 #ifdef HAVE_INTTYPES_H
28 #include <inttypes.h>
29 #endif
30
31 #include <lber_types.h>
32 #include <ac/string.h>
33 #include <ac/errno.h>
34
35 typedef struct {
36 uint8_t sig[12]; /* hex 0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a */
37 uint8_t ver_cmd; /* protocol version and command */
38 uint8_t fam; /* protocol family and address */
39 uint16_t len; /* length of address data */
40 } proxyp_header;
41
42 typedef union {
43 struct { /* for TCP/UDP over IPv4, len = 12 */
44 uint32_t src_addr;
45 uint32_t dst_addr;
46 uint16_t src_port;
47 uint16_t dst_port;
48 } ip4;
49 struct { /* for TCP/UDP over IPv6, len = 36 */
50 uint8_t src_addr[16];
51 uint8_t dst_addr[16];
52 uint16_t src_port;
53 uint16_t dst_port;
54 } ip6;
55 struct { /* for AF_UNIX sockets, len = 216 */
56 uint8_t src_addr[108];
57 uint8_t dst_addr[108];
58 } unx;
59 } proxyp_addr;
60
61 static const uint8_t proxyp_sig[12] = {
62 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a,
63 };
64
65 int
proxyp(ber_socket_t sfd,Sockaddr * from)66 proxyp( ber_socket_t sfd, Sockaddr *from ) {
67 proxyp_header pph;
68 proxyp_addr ppa;
69 char peername[LDAP_IPADDRLEN];
70 struct berval peerbv = BER_BVC(peername);
71 /* Maximum size of header minus static component size is max option size */
72 uint8_t proxyp_options[536 - 16];
73 int pph_len;
74 int ret;
75
76 peername[0] = '\0';
77
78 do {
79 ret = tcp_read( SLAP_FD2SOCK( sfd ), &pph, sizeof(pph) );
80 } while ( ret == -1 && errno == EINTR );
81
82 if ( ret == -1 ) {
83 char ebuf[128];
84 int save_errno = errno;
85 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
86 "header read failed %d (%s)\n",
87 (long)sfd, save_errno,
88 AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
89 return 0;
90 } else if ( ret != sizeof(pph) ) {
91 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
92 "header read insufficient data %d\n",
93 (long)sfd, ret );
94 return 0;
95 }
96
97 if ( memcmp( pph.sig, proxyp_sig, 12 ) != 0 ) {
98 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
99 "invalid header signature\n", (long)sfd );
100 return 0;
101 }
102
103 if ( ( pph.ver_cmd & 0xF0 ) != 0x20 ) {
104 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
105 "invalid header version %x\n",
106 (long)sfd, pph.ver_cmd & 0xF0 );
107 return 0;
108 }
109
110 pph_len = ntohs( pph.len );
111 if ( ( pph.ver_cmd & 0x0F ) == 0x01 ) { /* PROXY command */
112 int addr_len;
113 switch ( pph.fam ) {
114 case 0x11: /* TCPv4 */
115 addr_len = sizeof( ppa.ip4 );
116 break;
117 case 0x21: /* TCPv6 */
118 addr_len = sizeof( ppa.ip6 );
119 break;
120 default:
121 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
122 "unsupported protocol %x\n",
123 (long)sfd, pph.fam );
124 return 0;
125 }
126
127 if ( pph_len < addr_len ) {
128 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
129 "address length %d too small, expecting %d\n",
130 (long)sfd, pph_len, addr_len );
131 return 0;
132 }
133
134 do {
135 ret = tcp_read( SLAP_FD2SOCK (sfd), &ppa, addr_len );
136 } while ( ret == -1 && errno == EINTR );
137
138 if ( ret == -1 ) {
139 char ebuf[128];
140 int save_errno = errno;
141 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
142 "address read failed %d (%s)\n",
143 (long)sfd, save_errno,
144 AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
145 return 0;
146 } else if ( ret != addr_len ) {
147 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
148 "address read insufficient data, expecting %d, read %d\n",
149 (long)sfd, addr_len, ret );
150 return 0;
151 }
152
153 pph_len -= addr_len;
154 }
155
156 switch ( pph.ver_cmd & 0x0F ) {
157 case 0x01: /* PROXY command */
158 switch ( pph.fam ) {
159 case 0x11: /* TCPv4 */
160 ldap_pvt_sockaddrstr( from, &peerbv );
161 Debug( LDAP_DEBUG_STATS, "proxyp(%ld): via %s\n",
162 (long)sfd, peername );
163
164 from->sa_in_addr.sin_family = AF_INET;
165 from->sa_in_addr.sin_addr.s_addr = ppa.ip4.src_addr;
166 from->sa_in_addr.sin_port = ppa.ip4.src_port;
167 break;
168
169 case 0x21: /* TCPv6 */
170 #ifdef LDAP_PF_INET6
171 ldap_pvt_sockaddrstr( from, &peerbv );
172 Debug( LDAP_DEBUG_STATS, "proxyp(%ld): via %s\n",
173 (long)sfd, peername );
174 from->sa_in6_addr.sin6_family = AF_INET6;
175 memcpy( &from->sa_in6_addr.sin6_addr, ppa.ip6.src_addr,
176 sizeof(ppa.ip6.src_addr) );
177 from->sa_in6_addr.sin6_port = ppa.ip6.src_port;
178 #else
179 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
180 "IPv6 proxied addresses disabled\n",
181 (long)sfd );
182 return 0;
183 #endif
184 break;
185 }
186
187 break;
188
189 case 0x00: /* LOCAL command */
190 Debug( LDAP_DEBUG_CONNS, "proxyp(%ld): "
191 "local connection, ignoring proxy data\n",
192 (long)sfd );
193 break;
194
195 default:
196 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): invalid command %x\n",
197 (long)sfd, pph.ver_cmd & 0x0F );
198 return 0;
199 }
200
201 /* Clear out any options left in proxy packet */
202 if ( pph_len > 0 ) {
203 if (pph_len > sizeof( proxyp_options ) ) {
204 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
205 "options size %d too big\n",
206 (long)sfd, pph_len );
207 return 0;
208 }
209
210 do {
211 ret = tcp_read( SLAP_FD2SOCK (sfd), &proxyp_options, pph_len );
212 } while ( ret == -1 && errno == EINTR );
213
214 if ( ret == -1 ) {
215 char ebuf[128];
216 int save_errno = errno;
217 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
218 "options read failed %d (%s)\n",
219 (long)sfd, save_errno,
220 AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
221 return 0;
222 } else if ( ret != pph_len ) {
223 Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
224 "options read insufficient data, expecting %d, read %d\n",
225 (long)sfd, pph_len, ret );
226 return 0;
227 }
228 }
229
230 return 1;
231 }
232