Skip to content

Commit 3f09037

Browse files
committed
fix: harden proxy wasm HTTP callouts
1 parent 2d7ed02 commit 3f09037

4 files changed

Lines changed: 622 additions & 152 deletions

File tree

src/http_pool.c

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,81 @@ conn_is_alive(int fd)
119119
return (1); /* Data available: might be stale response */
120120
}
121121

122+
int
123+
vwasm_http_addr_is_private(const struct sockaddr *sa)
124+
{
125+
if (sa == NULL)
126+
return (1);
127+
128+
if (sa->sa_family == AF_INET) {
129+
const struct sockaddr_in *sin;
130+
uint32_t ip;
131+
132+
sin = (const struct sockaddr_in *)sa;
133+
ip = ntohl(sin->sin_addr.s_addr);
134+
135+
/* 127.0.0.0/8 — loopback */
136+
if ((ip >> 24) == 127)
137+
return (1);
138+
/* 10.0.0.0/8 — RFC1918 */
139+
if ((ip >> 24) == 10)
140+
return (1);
141+
/* 172.16.0.0/12 — RFC1918 */
142+
if ((ip >> 20) == (172 << 4 | 1))
143+
return (1);
144+
/* 192.168.0.0/16 — RFC1918 */
145+
if ((ip >> 16) == ((192 << 8) | 168))
146+
return (1);
147+
/* 169.254.0.0/16 — link-local */
148+
if ((ip >> 16) == ((169 << 8) | 254))
149+
return (1);
150+
/* 0.0.0.0/8 — "this" network */
151+
if ((ip >> 24) == 0)
152+
return (1);
153+
/* 100.64.0.0/10 — shared address space (CGN) */
154+
if ((ip >> 22) == (100 << 2 | 1))
155+
return (1);
156+
/* 192.0.0.0/24 — IETF protocol assignments */
157+
if ((ip >> 8) == ((192 << 16) | 0))
158+
return (1);
159+
/* 198.18.0.0/15 — benchmarking */
160+
if ((ip >> 17) == ((198 << 7) | 9))
161+
return (1);
162+
/* 240.0.0.0/4 — reserved (includes broadcast) */
163+
if ((ip >> 28) == 15)
164+
return (1);
165+
} else if (sa->sa_family == AF_INET6) {
166+
const struct sockaddr_in6 *sin6;
167+
const uint8_t *b;
168+
169+
sin6 = (const struct sockaddr_in6 *)sa;
170+
b = sin6->sin6_addr.s6_addr;
171+
172+
/* ::1/128 — loopback */
173+
if (memcmp(b, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1", 16) == 0)
174+
return (1);
175+
/* ::/128 — unspecified */
176+
if (memcmp(b, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0)
177+
return (1);
178+
/* fc00::/7 — unique local (RFC4193) */
179+
if ((b[0] & 0xfe) == 0xfc)
180+
return (1);
181+
/* fe80::/10 — link-local */
182+
if (b[0] == 0xfe && (b[1] & 0xc0) == 0x80)
183+
return (1);
184+
/* ::ffff:0:0/96 — IPv4-mapped, check inner IPv4 */
185+
if (memcmp(b, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12) == 0) {
186+
struct sockaddr_in inner;
187+
memset(&inner, 0, sizeof(inner));
188+
inner.sin_family = AF_INET;
189+
memcpy(&inner.sin_addr.s_addr, b + 12, 4);
190+
return (vwasm_http_addr_is_private(
191+
(const struct sockaddr *)&inner));
192+
}
193+
}
194+
return (0);
195+
}
196+
122197
/*
123198
* Connect to host:port with timeout.
124199
*/
@@ -538,7 +613,7 @@ vwasm_http_pool_cb_failure(struct vwasm_http_pool *pool,
538613

539614
int
540615
vwasm_http_pool_acquire(struct vwasm_http_pool *pool,
541-
const char *host, uint16_t port, uint32_t timeout_ms,
616+
const char *host, uint16_t port, uint32_t timeout_ms, int ssrf_exempt,
542617
struct vwasm_http_conn **conn_out)
543618
{
544619
struct sockaddr_storage addr;
@@ -565,6 +640,8 @@ vwasm_http_pool_acquire(struct vwasm_http_pool *pool,
565640
for (i = 0; i < pool->max_conns; i++) {
566641
struct vwasm_http_conn *c = &pool->conns[i];
567642

643+
if (!ssrf_exempt)
644+
continue;
568645
if (c->fd < 0 || c->in_use)
569646
continue;
570647
if (strcmp(c->host, host) != 0 || c->port != port)
@@ -608,6 +685,9 @@ vwasm_http_pool_acquire(struct vwasm_http_pool *pool,
608685
/* Resolve DNS */
609686
if (vwasm_http_pool_resolve(pool, host, port, &addr, &addrlen) != 0)
610687
return (-1);
688+
if (!ssrf_exempt &&
689+
vwasm_http_addr_is_private((const struct sockaddr *)&addr))
690+
return (-1);
611691

612692
/* Connect with timeout */
613693
fd = connect_with_timeout(&addr, addrlen, timeout_ms);

src/http_pool.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,12 @@ void vwasm_http_pool_destroy(struct vwasm_http_pool **poolp);
160160
* Otherwise creates a new connection.
161161
*
162162
* timeout_ms: connect timeout (0 = use pool default).
163+
* ssrf_exempt: allow private/internal resolved addresses.
163164
* Returns FD >= 0 on success, -1 on failure.
164165
* On success, conn_out is set (caller must release when done).
165166
*/
166167
int vwasm_http_pool_acquire(struct vwasm_http_pool *pool,
167-
const char *host, uint16_t port, uint32_t timeout_ms,
168+
const char *host, uint16_t port, uint32_t timeout_ms, int ssrf_exempt,
168169
struct vwasm_http_conn **conn_out);
169170

170171
/*
@@ -245,4 +246,10 @@ void vwasm_http_pool_cache_put(struct vwasm_http_pool *pool,
245246
*/
246247
char *vwasm_http_pool_stats_json(const struct vwasm_http_pool *pool);
247248

249+
/*
250+
* Return true for private/internal addresses that proxy_http_call must not
251+
* reach unless the upstream was explicitly allowlisted.
252+
*/
253+
int vwasm_http_addr_is_private(const struct sockaddr *sa);
254+
248255
#endif /* VWASM_HTTP_POOL_H */

0 commit comments

Comments
 (0)