]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tftp: pin the first used address
authorDaniel Stenberg <daniel@haxx.se>
Sun, 21 Sep 2025 09:07:31 +0000 (11:07 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 21 Sep 2025 21:01:01 +0000 (23:01 +0200)
Store the used remote address on the first receive call and then make
sure that it remains the same address on subsequent calls to reduce the
risk of tampering. Doesn't make the transfer secure because it is still
unauthenticated and clear text.

Reported in Joshua's sarif data

Closes #18658

lib/tftp.c

index 8b6246a34265a57a6cc566896d4c474e14a1cb12..736b14b673647e5fc3b58d59d7ea4975e5c535eb 100644 (file)
@@ -126,6 +126,10 @@ struct tftp_packet {
 #define CURL_META_TFTP_CONN   "meta:proto:tftp:conn"
 
 struct tftp_conn {
+  struct Curl_sockaddr_storage local_addr;
+  struct Curl_sockaddr_storage remote_addr;
+  struct tftp_packet rpacket;
+  struct tftp_packet spacket;
   tftp_state_t    state;
   tftp_mode_t     mode;
   tftp_error_t    error;
@@ -136,16 +140,13 @@ struct tftp_conn {
   int             retry_time;
   int             retry_max;
   time_t          rx_time;
-  struct Curl_sockaddr_storage   local_addr;
-  struct Curl_sockaddr_storage   remote_addr;
   curl_socklen_t  remote_addrlen;
   int             rbytes;
   size_t          sbytes;
   unsigned int    blksize;
   unsigned int    requested_blksize;
   unsigned short  block;
-  struct tftp_packet rpacket;
-  struct tftp_packet spacket;
+  BIT(remote_pinned);
 };
 
 
@@ -1094,18 +1095,31 @@ static CURLcode tftp_pollset(struct Curl_easy *data,
 static CURLcode tftp_receive_packet(struct Curl_easy *data,
                                     struct tftp_conn *state)
 {
-  curl_socklen_t        fromlen;
-  CURLcode              result = CURLE_OK;
+  CURLcode result = CURLE_OK;
+  struct Curl_sockaddr_storage remote_addr;
+  curl_socklen_t fromlen = sizeof(remote_addr);
 
   /* Receive the packet */
-  fromlen = sizeof(state->remote_addr);
   state->rbytes = (int)recvfrom(state->sockfd,
                                 (void *)state->rpacket.data,
                                 (RECV_TYPE_ARG3)state->blksize + 4,
                                 0,
-                                (struct sockaddr *)&state->remote_addr,
+                                (struct sockaddr *)&remote_addr,
                                 &fromlen);
-  state->remote_addrlen = fromlen;
+  if(state->remote_pinned) {
+    /* pinned, verify that it comes from the same address */
+    if((state->remote_addrlen != fromlen) ||
+       memcmp(&remote_addr, &state->remote_addr, fromlen)) {
+      failf(data, "Data received from another address");
+      return CURLE_RECV_ERROR;
+    }
+  }
+  else {
+    /* pin address on first use */
+    state->remote_pinned = TRUE;
+    state->remote_addrlen = fromlen;
+    memcpy(&state->remote_addr, &remote_addr, fromlen);
+  }
 
   /* Sanity check packet length */
   if(state->rbytes < 4) {