Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 30 Apr 2026 21:07:00 +0000
From:      Dag-Erling=?utf-8?Q? Sm=C3=B8rg?=rav <des@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: 252f603d1704 - stable/15 - dhclient: Improve server and filename validation
Message-ID:  <69f3c474.44db8.65c9d45a@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch stable/15 has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=252f603d1704044defb7695e707bc87c7f75483a

commit 252f603d1704044defb7695e707bc87c7f75483a
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2026-04-30 16:45:35 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2026-04-30 21:06:54 +0000

    dhclient: Improve server and filename validation
    
    * Don't iterate over each string three times; once is enough.
    
    * Reject control characters (anything below space) in addition to the
      double quote and backslash.
    
    * If an unsafe character is encountered, discard the string instead of
      rejecting the entire lease.
    
    * If backslashes are encountered in the file name option, convert them
      to forward slashes instead of rejecting the option.
    
    * Tweak the warning messages a bit.  Looking through the rest of the
      code, it seems to me that notes generally end with a period while
      warnings generally don't.
    
    Fixes:          8008e4b88daf ("dhclient: Check for unexpected characters in some DHCP server options")
    PR:             294886
    MFC after:      1 week
    Reviewed by:    brooks, markj
    Differential Revision:  https://reviews.freebsd.org/D56740
    
    (cherry picked from commit 873a195ba63575e46686cfd6ea9670a0ca340fa0)
---
 sbin/dhclient/dhclient.c | 75 ++++++++++++++++++++++++++++++++++--------------
 1 file changed, 54 insertions(+), 21 deletions(-)

diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
index f671b0ab9bed..695451088231 100644
--- a/sbin/dhclient/dhclient.c
+++ b/sbin/dhclient/dhclient.c
@@ -1161,7 +1161,7 @@ packet_to_lease(struct packet *packet)
 	lease = malloc(sizeof(struct client_lease));
 
 	if (!lease) {
-		warning("dhcpoffer: no memory to record lease.");
+		warning("dhcpoffer: no memory to record lease");
 		return (NULL);
 	}
 
@@ -1211,7 +1211,7 @@ packet_to_lease(struct packet *packet)
 
 	/* If the server name was filled out, copy it.
 	   Do not attempt to validate the server name as a host name.
-	   RFC 2131 merely states that sname is NUL-terminated (which do
+	   RFC 2131 merely states that sname is NUL-terminated (which we
 	   do not assume) and that it is the server's host name.  Since
 	   the ISC client and server allow arbitrary characters, we do
 	   as well. */
@@ -1219,39 +1219,72 @@ packet_to_lease(struct packet *packet)
 	    !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) &&
 	    packet->raw->sname[0]) {
 		lease->server_name = malloc(DHCP_SNAME_LEN + 1);
-		if (!lease->server_name) {
-			warning("dhcpoffer: no memory for server name.");
+		if (lease->server_name == NULL) {
+			warning("dhcpoffer: no memory for server name");
 			free_client_lease(lease);
 			return (NULL);
 		}
-		memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN);
-		lease->server_name[DHCP_SNAME_LEN]='\0';
-		if (strchr(lease->server_name, '"') != NULL ||
-		    strchr(lease->server_name, '\\') != NULL) {
-			warning("dhcpoffer: server name contains invalid characters.");
-			free_client_lease(lease);
-			return (NULL);
+		for (i = 0; i < DHCP_SNAME_LEN; i++) {
+			if (packet->raw->sname[i] == '\0') {
+				break;
+			}
+			if (packet->raw->sname[i] < ' ' ||
+			    packet->raw->sname[i] == '"' ||
+			    packet->raw->sname[i] == '\\') {
+				warning("dhcpoffer: server name contains "
+				    "unsafe characters");
+				free(lease->server_name);
+				lease->server_name = NULL;
+				break;
+			}
+			lease->server_name[i] = packet->raw->sname[i];
+		}
+		/* Terminate and zero-pad */
+		if (lease->server_name != NULL) {
+			while (i < DHCP_SNAME_LEN + 1) {
+				lease->server_name[i++] = '\0';
+			}
 		}
 	}
 
-	/* Ditto for the filename. */
+	/* Ditto for the file name. */
 	if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
 	    !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) &&
 	    packet->raw->file[0]) {
 		/* Don't count on the NUL terminator. */
 		lease->filename = malloc(DHCP_FILE_LEN + 1);
-		if (!lease->filename) {
-			warning("dhcpoffer: no memory for filename.");
+		if (lease->filename == NULL) {
+			warning("dhcpoffer: no memory for file name");
 			free_client_lease(lease);
 			return (NULL);
 		}
-		memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN);
-		lease->filename[DHCP_FILE_LEN]='\0';
-		if (strchr(lease->filename, '"') != NULL ||
-		    strchr(lease->filename, '\\') != NULL) {
-			warning("dhcpoffer: filename contains invalid characters.");
-			free_client_lease(lease);
-			return (NULL);
+		for (i = 0; i < DHCP_FILE_LEN; i++) {
+			if (packet->raw->file[i] == '\0') {
+				break;
+			}
+			if (packet->raw->file[i] < ' ' ||
+			    packet->raw->file[i] == '"') {
+				warning("dhcpoffer: file name contains "
+				    "unsafe characters");
+				free(lease->filename);
+				lease->filename = NULL;
+				break;
+			}
+			if (packet->raw->file[i] == '\\') {
+				/*
+				 * This is common in Windows-centric
+				 * environments.  Instead of rejecting,
+				 * silently convert to forward slash.
+				 */
+				packet->raw->file[i] = '/';
+			}
+			lease->filename[i] = packet->raw->file[i];
+		}
+		/* Terminate and zero-pad */
+		if (lease->filename != NULL) {
+			while (i < DHCP_FILE_LEN + 1) {
+				lease->filename[i++] = '\0';
+			}
 		}
 	}
 	return lease;


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69f3c474.44db8.65c9d45a>