From 0c10e4f8b6ebe7a668b40e987a5646472a6c9511 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 26 Jul 2021 23:58:23 +0200 Subject: [PATCH 1/6] dhcp: escape control characters in DHCP options Control characters (DEL (=127) or those below 32) could cause undesired effects when a client displays or parses DHCP options. Escape them. --- src/core/dhcp/nm-dhcp-nettools.c | 5 ++++- src/core/dhcp/nm-dhcp-options.c | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index 584d99e28a..f0518ab0de 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -694,7 +694,10 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, /* https://tools.ietf.org/html/rfc2132#section-8.1 */ - v_str = nm_utils_buf_utf8safe_escape((char *) l_data, l_data_len, 0, &to_free); + v_str = nm_utils_buf_utf8safe_escape((char *) l_data, + l_data_len, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NIS_DOMAIN, v_str ?: ""); nm_ip4_config_set_nis_domain(ip4_config, v_str ?: ""); diff --git a/src/core/dhcp/nm-dhcp-options.c b/src/core/dhcp/nm-dhcp-options.c index 8f0d7408a9..7756572600 100644 --- a/src/core/dhcp/nm-dhcp-options.c +++ b/src/core/dhcp/nm-dhcp-options.c @@ -404,7 +404,10 @@ nm_dhcp_option_add_option_utf8safe_escape(GHashTable * options, gs_free char *to_free = NULL; const char * escaped; - escaped = nm_utils_buf_utf8safe_escape((char *) data, n_data, 0, &to_free); + escaped = nm_utils_buf_utf8safe_escape((char *) data, + n_data, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); nm_dhcp_option_add_option(options, addr_family, option, escaped ?: ""); } From 0994a444e5fb8483af7193821b6811b3bb80a677 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Sun, 18 Jul 2021 18:09:26 +0200 Subject: [PATCH 2/6] dhcp: add internal option for the boot file name Add an internal option that specifies the boot file name from the DHCP header. The option name 'filename' is the same as exposed by dhclient. --- src/core/dhcp/nm-dhcp-options.c | 3 ++- src/core/dhcp/nm-dhcp-options.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/dhcp/nm-dhcp-options.c b/src/core/dhcp/nm-dhcp-options.c index 7756572600..011f23e4e2 100644 --- a/src/core/dhcp/nm-dhcp-options.c +++ b/src/core/dhcp/nm-dhcp-options.c @@ -169,6 +169,7 @@ const NMDhcpOption _nm_dhcp_option_dhcp4_options[] = { REQ(NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, "ip_address", FALSE), REQ(NM_DHCP_OPTION_DHCP4_NM_EXPIRY, "expiry", FALSE), REQ(NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER, "next_server", FALSE), + REQ(NM_DHCP_OPTION_DHCP4_NM_FILENAME, "filename", FALSE), }; static const NMDhcpOption *const _sorted_options_4[G_N_ELEMENTS(_nm_dhcp_option_dhcp4_options)] = { @@ -184,7 +185,7 @@ static const NMDhcpOption *const _sorted_options_4[G_N_ELEMENTS(_nm_dhcp_option_ A(98), A(99), A(100), A(101), A(102), A(103), A(104), A(105), A(106), A(107), A(108), A(109), A(110), A(111), A(112), A(113), A(114), A(115), A(116), A(117), A(118), A(119), A(120), A(121), A(122), A(123), A(124), A(125), A(126), A(127), A(128), A(129), A(130), A(131), A(132), A(133), - A(134), A(15), A(135), A(136), A(16), A(137), A(138), A(139), A(140), A(141), + A(134), A(15), A(135), A(136), A(16), A(137), A(138), A(139), A(140), A(141), A(142), #undef A }; diff --git a/src/core/dhcp/nm-dhcp-options.h b/src/core/dhcp/nm-dhcp-options.h index 585f118795..8ed2039d40 100644 --- a/src/core/dhcp/nm-dhcp-options.h +++ b/src/core/dhcp/nm-dhcp-options.h @@ -153,6 +153,7 @@ typedef enum { NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS = 1024, NM_DHCP_OPTION_DHCP4_NM_EXPIRY = 1025, NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER = 1026, + NM_DHCP_OPTION_DHCP4_NM_FILENAME = 1027, /* 'file' DHCP header */ } NMDhcpOptionDhcp4Options; typedef enum { @@ -185,7 +186,7 @@ typedef struct { bool include; } NMDhcpOption; -extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[142]; +extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[143]; extern const NMDhcpOption _nm_dhcp_option_dhcp6_options[16]; static inline const char * From be6997d1279c01e215383fe2022422c920372a18 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 19 Jul 2021 15:14:29 +0200 Subject: [PATCH 3/6] n-dhcp4: fix n_dhcp4_client_lease_get_server_identifier() N_DHCP4_E_MALFORMED is an internal error code, replace it with N_DHCP4_E_INTERNAL. Also, remove extra spaces. --- src/n-dhcp4/src/n-dhcp4-c-lease.c | 8 +++++--- src/n-dhcp4/src/n-dhcp4.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/n-dhcp4/src/n-dhcp4-c-lease.c b/src/n-dhcp4/src/n-dhcp4-c-lease.c index 515814d4e6..6564dbe811 100644 --- a/src/n-dhcp4/src/n-dhcp4-c-lease.c +++ b/src/n-dhcp4/src/n-dhcp4-c-lease.c @@ -249,9 +249,11 @@ _c_public_ void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint * Gets the address contained in the server-identifier DHCP option, in network * byte order. * - * Return: 0 on success, negative error code on failure. + * Return: 0 on success, + * N_DHCP4_E_UNSET if the lease doesn't contain a server-identifier, or + * N_DCHP4_E_INTERNAL if the server-identifier is not valid. */ -_c_public_ int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *lease, struct in_addr *addr) { +_c_public_ int n_dhcp4_client_lease_get_server_identifier(NDhcp4ClientLease *lease, struct in_addr *addr) { uint8_t *data; size_t n_data; int r; @@ -260,7 +262,7 @@ _c_public_ int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *le if (r) return r; if (n_data < sizeof(struct in_addr)) - return N_DHCP4_E_MALFORMED; + return N_DHCP4_E_INTERNAL; memcpy(addr, data, sizeof(struct in_addr)); diff --git a/src/n-dhcp4/src/n-dhcp4.h b/src/n-dhcp4/src/n-dhcp4.h index 435c5600d8..0be86e9fab 100644 --- a/src/n-dhcp4/src/n-dhcp4.h +++ b/src/n-dhcp4/src/n-dhcp4.h @@ -171,7 +171,7 @@ void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *s void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep); void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint64_t *ns_lifetimep); int n_dhcp4_client_lease_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap); -int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *lease, struct in_addr *addr); +int n_dhcp4_client_lease_get_server_identifier(NDhcp4ClientLease *lease, struct in_addr *addr); int n_dhcp4_client_lease_select(NDhcp4ClientLease *lease); int n_dhcp4_client_lease_accept(NDhcp4ClientLease *lease); From 6a1349c5fb1968f67cd536b4a0135a8712bc66f6 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 14 Jul 2021 11:57:59 +0200 Subject: [PATCH 4/6] n-dhcp4: add an accessor for the file name in the lease The name of the boot file can be either in option 67 or in a field of the DHCP header. Add an accessor for the one from the DHCP header. --- src/n-dhcp4/src/libndhcp4.sym | 1 + src/n-dhcp4/src/n-dhcp4-c-lease.c | 36 +++++++++++++++++++++++++++++++ src/n-dhcp4/src/n-dhcp4.h | 1 + 3 files changed, 38 insertions(+) diff --git a/src/n-dhcp4/src/libndhcp4.sym b/src/n-dhcp4/src/libndhcp4.sym index 6c06e2fd28..4d11734e4d 100644 --- a/src/n-dhcp4/src/libndhcp4.sym +++ b/src/n-dhcp4/src/libndhcp4.sym @@ -37,6 +37,7 @@ global: n_dhcp4_client_lease_get_siaddr; n_dhcp4_client_lease_get_lifetime; n_dhcp4_client_lease_get_server_identifier; + n_dhcp4_client_lease_get_file; n_dhcp4_client_lease_query; n_dhcp4_client_lease_select; n_dhcp4_client_lease_accept; diff --git a/src/n-dhcp4/src/n-dhcp4-c-lease.c b/src/n-dhcp4/src/n-dhcp4-c-lease.c index 6564dbe811..bae2f674cd 100644 --- a/src/n-dhcp4/src/n-dhcp4-c-lease.c +++ b/src/n-dhcp4/src/n-dhcp4-c-lease.c @@ -269,6 +269,42 @@ _c_public_ int n_dhcp4_client_lease_get_server_identifier(NDhcp4ClientLease *lea return 0; } +/** + * n_dhcp4_client_lease_get_file() - query the lease for the boot file name + * @lease: the lease to operate on + * @file: return argument for the file name + * + * Query the lease for the boot file name from the DHCP header. The file name + * is returned as a NULL-terminated string. + * + * Return: 0 on success, + * N_DHCP4_E_UNSET if the lease does not contain a file name, or + * N_DCHP4_E_INTERNAL if the file name is invalid. + */ +_c_public_ int n_dhcp4_client_lease_get_file(NDhcp4ClientLease *lease, const char **file) { + NDhcp4Message *message; + + if (lease->message->options[N_DHCP4_OPTION_OVERLOAD].size > 0 + && ((*lease->message->options[N_DHCP4_OPTION_OVERLOAD].value) & N_DHCP4_OVERLOAD_FILE)) { + /* The field is overloaded to contain other options */ + return N_DHCP4_E_UNSET; + } + + message = &lease->message->message; + + if (message->file[0] == '\0') + return N_DHCP4_E_UNSET; + + if (!memchr(message->file, '\0', sizeof(message->file))) { + /* The field is NULL-terminated (RFC 2131 section 2) */ + return N_DHCP4_E_INTERNAL; + } + + *file = (const char *) message->file; + + return 0; +} + /** * n_dhcp4_client_lease_query() - query the lease for an option * @lease: the lease to operate on diff --git a/src/n-dhcp4/src/n-dhcp4.h b/src/n-dhcp4/src/n-dhcp4.h index 0be86e9fab..8493a487dd 100644 --- a/src/n-dhcp4/src/n-dhcp4.h +++ b/src/n-dhcp4/src/n-dhcp4.h @@ -172,6 +172,7 @@ void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_ba void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint64_t *ns_lifetimep); int n_dhcp4_client_lease_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap); int n_dhcp4_client_lease_get_server_identifier(NDhcp4ClientLease *lease, struct in_addr *addr); +int n_dhcp4_client_lease_get_file(NDhcp4ClientLease *lease, const char **file); int n_dhcp4_client_lease_select(NDhcp4ClientLease *lease); int n_dhcp4_client_lease_accept(NDhcp4ClientLease *lease); From 3c79944e15064b2fb7b4c8dbc09019827dd36806 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 14 Jul 2021 16:50:21 +0200 Subject: [PATCH 5/6] dhcp: nettools: parse the filename and the bootfile-name option --- src/core/dhcp/nm-dhcp-nettools.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index f0518ab0de..72f677ed55 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -703,6 +703,31 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, nm_ip4_config_set_nis_domain(ip4_config, v_str ?: ""); } + r = n_dhcp4_client_lease_get_file(lease, &v_str); + if (r == 0) { + gs_free char *to_free = NULL; + + v_str = nm_utils_buf_utf8safe_escape(v_str, + -1, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); + nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NM_FILENAME, v_str ?: ""); + } + + r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME, &l_data, &l_data_len); + if (r == 0 && nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { + gs_free char *to_free = NULL; + + v_str = nm_utils_buf_utf8safe_escape((char *) l_data, + l_data_len, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); + nm_dhcp_option_add_option(options, + AF_INET, + NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME, + v_str ?: ""); + } + lease_parse_address_list(lease, ip4_config, NM_DHCP_OPTION_DHCP4_NIS_SERVERS, options, &sbuf); lease_parse_address_list(lease, From 9a09c02012dd20edc052d5646b1044a0b137a3cd Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 14 Jul 2021 09:49:42 +0200 Subject: [PATCH 6/6] core: persist the bootfile from DHCP The bootfile location is needed by the anaconda dracut module; write it to the device state file. --- src/core/nm-config.c | 21 ++++++++++++++++----- src/core/nm-config.h | 3 ++- src/core/nm-manager.c | 15 ++++++++++----- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/core/nm-config.c b/src/core/nm-config.c index fdc450a0e6..94ec7f381b 100644 --- a/src/core/nm-config.c +++ b/src/core/nm-config.c @@ -2343,8 +2343,9 @@ _nm_config_state_set(NMConfig *self, gboolean allow_persist, gboolean force_pers "route-metric-default-aspired" #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE \ "route-metric-default-effective" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH "root-path" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER "next-server" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH "root-path" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER "next-server" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_DHCP_BOOTFILE "dhcp-bootfile" static NM_UTILS_LOOKUP_STR_DEFINE( _device_state_managed_type_to_str, @@ -2565,7 +2566,8 @@ nm_config_device_state_write(int ifindex, guint32 route_metric_default_aspired, guint32 route_metric_default_effective, const char * next_server, - const char * root_path) + const char * root_path, + const char * dhcp_bootfile) { char path[NM_STRLEN(NM_CONFIG_DEVICE_STATE_DIR "/") + DEVICE_STATE_FILENAME_LEN_MAX + 1]; GError *local = NULL; @@ -2632,6 +2634,12 @@ nm_config_device_state_write(int ifindex, DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH, root_path); } + if (dhcp_bootfile) { + g_key_file_set_string(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_DHCP_BOOTFILE, + dhcp_bootfile); + } if (!g_key_file_save_to_file(kf, path, &local)) { _LOGW("device-state: write #%d (%s) failed: %s", ifindex, path, local->message); @@ -2639,7 +2647,9 @@ nm_config_device_state_write(int ifindex, return FALSE; } _LOGT("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, " - "route-metric-default=%" G_GUINT32_FORMAT "-%" G_GUINT32_FORMAT "%s%s%s%s%s%s", + "route-metric-default=%" G_GUINT32_FORMAT "-%" G_GUINT32_FORMAT "%s%s%s" + "%s%s%s" + "%s%s%s", ifindex, path, _device_state_managed_type_to_str(managed), @@ -2648,7 +2658,8 @@ nm_config_device_state_write(int ifindex, route_metric_default_aspired, route_metric_default_effective, NM_PRINT_FMT_QUOTED(next_server, ", next-server=", next_server, "", ""), - NM_PRINT_FMT_QUOTED(root_path, ", root-path=", root_path, "", "")); + NM_PRINT_FMT_QUOTED(root_path, ", root-path=", root_path, "", ""), + NM_PRINT_FMT_QUOTED(dhcp_bootfile, ", dhcp-bootfile=", dhcp_bootfile, "", "")); return TRUE; } diff --git a/src/core/nm-config.h b/src/core/nm-config.h index 7f1c0c4c57..8279f4d264 100644 --- a/src/core/nm-config.h +++ b/src/core/nm-config.h @@ -185,7 +185,8 @@ gboolean nm_config_device_state_write(int guint32 route_metric_default_aspired, guint32 route_metric_default_effective, const char * next_server, - const char * root_path); + const char * root_path, + const char * dhcp_bootfile); void nm_config_device_state_prune_stale(GHashTable *preserve_ifindexes, NMPlatform *preserve_in_platform); diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index b66f7fbb37..c1c8034a77 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -6803,8 +6803,9 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde guint32 route_metric_default_effective; NMTernary nm_owned; NMDhcpConfig * dhcp_config; - const char * next_server = NULL; - const char * root_path = NULL; + const char * next_server = NULL; + const char * root_path = NULL; + const char * dhcp_bootfile = NULL; NM_SET_OUT(out_ifindex, 0); @@ -6848,8 +6849,11 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde dhcp_config = nm_device_get_dhcp_config(device, AF_INET); if (dhcp_config) { - root_path = nm_dhcp_config_get_option(dhcp_config, "root_path"); - next_server = nm_dhcp_config_get_option(dhcp_config, "next_server"); + root_path = nm_dhcp_config_get_option(dhcp_config, "root_path"); + next_server = nm_dhcp_config_get_option(dhcp_config, "next_server"); + dhcp_bootfile = nm_dhcp_config_get_option(dhcp_config, "filename"); + if (!dhcp_bootfile) + dhcp_bootfile = nm_dhcp_config_get_option(dhcp_config, "bootfile_name"); } if (!nm_config_device_state_write(ifindex, @@ -6860,7 +6864,8 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde route_metric_default_aspired, route_metric_default_effective, next_server, - root_path)) + root_path, + dhcp_bootfile)) return FALSE; NM_SET_OUT(out_ifindex, ifindex);