summary refs log tree commit diff
path: root/src/libnm-client-impl
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-client-impl')
-rw-r--r--src/libnm-client-impl/libnm.ver8
-rw-r--r--src/libnm-client-impl/meson.build8
-rw-r--r--src/libnm-client-impl/nm-client.c47
-rw-r--r--src/libnm-client-impl/nm-libnm-utils.c57
-rw-r--r--src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml2
-rw-r--r--src/libnm-client-impl/nm-property-infos-nmcli.xml10
-rw-r--r--src/libnm-client-impl/nm-settings-docs-gir.xml6
-rw-r--r--src/libnm-client-impl/nm-vpn-service-plugin.c88
-rw-r--r--src/libnm-client-impl/tests/test-libnm.c117
9 files changed, 259 insertions, 84 deletions
diff --git a/src/libnm-client-impl/libnm.ver b/src/libnm-client-impl/libnm.ver
index bb691ac2..b6d566fc 100644
--- a/src/libnm-client-impl/libnm.ver
+++ b/src/libnm-client-impl/libnm.ver
@@ -1820,4 +1820,10 @@ global:
 libnm_1_36_0 {
 global:
 	nm_setting_ovs_dpdk_get_n_rxq;
-} libnm_1_34_0;
\ No newline at end of file
+} libnm_1_34_0;
+
+libnm_1_38_0 {
+global:
+	nm_client_get_radio_flags;
+	nm_radio_flags_get_type;
+} libnm_1_36_0;
\ No newline at end of file
diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build
index 21a01e0b..2026f98d 100644
--- a/src/libnm-client-impl/meson.build
+++ b/src/libnm-client-impl/meson.build
@@ -140,6 +140,14 @@ if enable_introspection
       src_inc,
       top_inc,
     ],
+    dependencies: [
+      declare_dependency(
+        compile_args: [
+          '-UGLIB_VERSION_MIN_REQUIRED',
+          '-UGLIB_VERSION_MAX_ALLOWED',
+        ],
+      ),
+    ],
     nsversion: nm_gir_version,
     namespace: 'NM',
     identifier_prefix: nm_id_prefix,
diff --git a/src/libnm-client-impl/nm-client.c b/src/libnm-client-impl/nm-client.c
index cd2aa730..b18f47be 100644
--- a/src/libnm-client-impl/nm-client.c
+++ b/src/libnm-client-impl/nm-client.c
@@ -198,6 +198,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMClient,
                              PROP_WWAN_HARDWARE_ENABLED,
                              PROP_WIMAX_ENABLED,
                              PROP_WIMAX_HARDWARE_ENABLED,
+                             PROP_RADIO_FLAGS,
                              PROP_ACTIVE_CONNECTIONS,
                              PROP_CONNECTIVITY,
                              PROP_CONNECTIVITY_CHECK_URI,
@@ -307,6 +308,7 @@ typedef struct {
         guint32           connectivity;
         guint32           state;
         guint32           metered;
+        guint32           radio_flags;
         bool              connectivity_check_available;
         bool              connectivity_check_enabled;
         bool              networking_enabled;
@@ -4236,6 +4238,24 @@ nm_client_wireless_hardware_get_enabled(NMClient *client)
 }
 
 /**
+ * nm_client_get_radio_flags:
+ * @client: a #NMClient
+ *
+ * Get radio flags.
+ *
+ * Returns: the #NMRadioFlags.
+ *
+ * Since: 1.38
+ **/
+NMRadioFlags
+nm_client_get_radio_flags(NMClient *client)
+{
+    g_return_val_if_fail(NM_IS_CLIENT(client), NM_RADIO_FLAG_NONE);
+
+    return NM_CLIENT_GET_PRIVATE(client)->nm.radio_flags;
+}
+
+/**
  * nm_client_wwan_get_enabled:
  * @client: a #NMClient
  *
@@ -7349,7 +7369,10 @@ _init_start_with_bus(NMClient *self)
                                    NULL);
         if (id == 0) {
             priv->init_data->cancel_on_idle_source =
-                nm_g_idle_source_new(G_PRIORITY_DEFAULT, _init_start_cancel_on_idle_cb, self, NULL);
+                nm_g_idle_source_new(G_PRIORITY_DEFAULT_IDLE,
+                                     _init_start_cancel_on_idle_cb,
+                                     self,
+                                     NULL);
             g_source_attach(priv->init_data->cancel_on_idle_source, priv->main_context);
             return;
         }
@@ -7451,6 +7474,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_WIRELESS_HARDWARE_ENABLED:
         g_value_set_boolean(value, nm_client_wireless_hardware_get_enabled(self));
         break;
+    case PROP_RADIO_FLAGS:
+        g_value_set_uint(value, priv->nm.radio_flags);
+        break;
     case PROP_WWAN_ENABLED:
         g_value_set_boolean(value, nm_client_wwan_get_enabled(self));
         break;
@@ -8043,6 +8069,10 @@ const NMLDBusMetaIface _nml_dbus_meta_iface_nm = NML_DBUS_META_IFACE_INIT_PROP(
                                            _priv.nm.property_o[PROPERTY_O_IDX_NM_PRIMAY_CONNECTION],
                                            nm_active_connection_get_type),
         NML_DBUS_META_PROPERTY_INIT_IGNORE("PrimaryConnectionType", "s"),
+        NML_DBUS_META_PROPERTY_INIT_U("RadioFlags",
+                                      PROP_RADIO_FLAGS,
+                                      NMClient,
+                                      _priv.nm.radio_flags),
         NML_DBUS_META_PROPERTY_INIT_B("Startup", PROP_STARTUP, NMClient, _priv.nm.startup),
         NML_DBUS_META_PROPERTY_INIT_U("State", PROP_STATE, NMClient, _priv.nm.state),
         NML_DBUS_META_PROPERTY_INIT_S("Version", PROP_VERSION, NMClient, _priv.nm.version),
@@ -8311,6 +8341,21 @@ nm_client_class_init(NMClientClass *client_class)
                              G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
     /**
+     * NMClient:radio-flags:
+     *
+     * Flags for radio interfaces. See #NMRadioFlags.
+     *
+     * Since: 1.38
+     **/
+    obj_properties[PROP_RADIO_FLAGS] = g_param_spec_uint(NM_CLIENT_RADIO_FLAGS,
+                                                         "",
+                                                         "",
+                                                         0,
+                                                         G_MAXUINT32,
+                                                         NM_RADIO_FLAG_NONE,
+                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+    /**
      * NMClient:active-connections: (type GPtrArray(NMActiveConnection))
      *
      * The active connections.
diff --git a/src/libnm-client-impl/nm-libnm-utils.c b/src/libnm-client-impl/nm-libnm-utils.c
index d3d429eb..951db1bc 100644
--- a/src/libnm-client-impl/nm-libnm-utils.c
+++ b/src/libnm-client-impl/nm-libnm-utils.c
@@ -10,7 +10,9 @@
 
 #include "libnm-glib-aux/nm-time-utils.h"
 #include "libnm-core-aux-intern/nm-common-macros.h"
+#include "libnm-crypto/nm-crypto.h"
 #include "nm-object.h"
+#include "nm-utils.h"
 
 /*****************************************************************************/
 
@@ -914,3 +916,58 @@ nm_utils_print(int output_mode, const char *msg)
     else
         g_return_if_reached();
 }
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_file_is_certificate:
+ * @filename: name of the file to test
+ *
+ * Tests if @filename has a valid extension for an X.509 certificate file
+ * (".cer", ".crt", ".der", or ".pem"), and contains a certificate in a format
+ * recognized by NetworkManager.
+ *
+ * Returns: %TRUE if the file is a certificate, %FALSE if it is not
+ **/
+gboolean
+nm_utils_file_is_certificate(const char *filename)
+{
+    g_return_val_if_fail(filename != NULL, FALSE);
+
+    return nm_crypto_utils_file_is_certificate(filename);
+}
+
+/**
+ * nm_utils_file_is_private_key:
+ * @filename: name of the file to test
+ * @out_encrypted: (out): on return, whether the file is encrypted
+ *
+ * Tests if @filename has a valid extension for an X.509 private key file
+ * (".der", ".key", ".pem", or ".p12"), and contains a private key in a format
+ * recognized by NetworkManager.
+ *
+ * Returns: %TRUE if the file is a private key, %FALSE if it is not
+ **/
+gboolean
+nm_utils_file_is_private_key(const char *filename, gboolean *out_encrypted)
+{
+    g_return_val_if_fail(filename != NULL, FALSE);
+
+    return nm_crypto_utils_file_is_private_key(filename, out_encrypted);
+}
+
+/**
+ * nm_utils_file_is_pkcs12:
+ * @filename: name of the file to test
+ *
+ * Tests if @filename is a PKCS#<!-- -->12 file.
+ *
+ * Returns: %TRUE if the file is PKCS#<!-- -->12, %FALSE if it is not
+ **/
+gboolean
+nm_utils_file_is_pkcs12(const char *filename)
+{
+    g_return_val_if_fail(filename != NULL, FALSE);
+
+    return nm_crypto_is_pkcs12_file(filename, NULL);
+}
diff --git a/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml b/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml
index 91d04e13..d051725d 100644
--- a/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml
+++ b/src/libnm-client-impl/nm-property-infos-ifcfg-rh.xml
@@ -31,7 +31,7 @@
 </setting><setting name="ppp">
 </setting><setting name="pppoe">
 </setting><setting name="proxy">
-<property name="method" variable="PROXY_METHOD(+)" format="" values="none, auto" default="none" example="" description="Method for proxy configuration. For &quot;auto&quot;, WPAD is used for proxy configuration, or set the PAC file via PAC_URL or PAC_SCRIPT." /><property name="browser-only" variable="BROWSER_ONLY(+)" format="" values="" default="no" example="" description="Whether the proxy configuration is for browser only." /><property name="pac-url" variable="PAC_URL(+)" format="" values="" default="" example="PAC_URL=http://wpad.mycompany.com/wpad.dat" description="URL for PAC file." /><property name="pac-script" variable="PAC_SCRIPT(+)" format="" values="" default="" example="PAC_SCRIPT=/home/joe/proxy.pac" description="Path of the PAC script." /></setting><setting name="serial">
+<property name="method" variable="PROXY_METHOD(+)" format="" values="none, auto" default="none" example="" description="Method for proxy configuration. For &quot;auto&quot;, WPAD is used for proxy configuration, or set the PAC file via PAC_URL or PAC_SCRIPT." /><property name="browser-only" variable="BROWSER_ONLY(+)" format="" values="" default="no" example="" description="Whether the proxy configuration is for browser only." /><property name="pac-url" variable="PAC_URL(+)" format="" values="" default="" example="PAC_URL=http://wpad.mycompany.com/wpad.dat" description="URL for PAC file." /><property name="pac-script" variable="PAC_SCRIPT(+)" format="" values="" default="" example="PAC_SCRIPT=&quot;function FindProxyForURL (url, host) { return 'PROXY proxy.example.com:8080; DIRECT'; }&quot;" description="The PAC script. This is an UTF-8 encoded javascript code that defines a FindProxyForURL() function." /></setting><setting name="serial">
 </setting><setting name="sriov">
 <property name="total-vfs" variable="SRIOV_TOTAL_VFS(+)" format="" values="" default="" example="SRIOV_TOTAL_VFS=16" description="The total number of virtual functions to create" /><property name="vfs" variable="SRIOV_VF1(+), SRIOV_VF2(+), ..." format="" values="" default="" example="SRIOV_VF10=&quot;mac=00:11:22:33:44:55&quot;, ..." description="SR-IOV virtual function descriptors" /><property name="autoprobe-drivers" variable="SRIOV_AUTOPROBE_DRIVERS(+)" format="" values="" default="missing variable means global default" example="SRIOV_AUTOPROBE_DRIVERS=0,1" description="Whether to autoprobe virtual functions by a compatible driver" /></setting><setting name="tc">
 <property name="qdiscs" variable="QDISC1(+), QDISC2(+), ..., TC_COMMIT(+)" format="" values="" default="" example="QDISC1=ingress, QDISC2=&quot;root handle 1234: fq_codel&quot;" description="Queueing disciplines to set on the interface. When no QDISC1, QDISC2, ..., FILTER1, FILTER2, ... keys are present, NetworkManager doesn't touch qdiscs and filters present on the interface, unless TC_COMMIT is set to 'yes'." /><property name="qdiscs" variable="FILTER1(+), FILTER2(+), ..., TC_COMMIT(+)" format="" values="" default="" example="FILTER1=&quot;parent ffff: matchall action simple sdata Input&quot;, ..." description="Traffic filters to set on the interface. When no QDISC1, QDISC2, ..., FILTER1, FILTER2, ... keys are present, NetworkManager doesn't touch qdiscs and filters present on the interface, unless TC_COMMIT is set to 'yes'." /></setting><setting name="team-port">
diff --git a/src/libnm-client-impl/nm-property-infos-nmcli.xml b/src/libnm-client-impl/nm-property-infos-nmcli.xml
index 3d067745..8e39a7a2 100644
--- a/src/libnm-client-impl/nm-property-infos-nmcli.xml
+++ b/src/libnm-client-impl/nm-property-infos-nmcli.xml
@@ -71,7 +71,8 @@ Various attributes are supported:
 </listitem>
 <listitem>
 <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>,
-<literal>unavailable</literal>, <literal>prohibit</literal>. The default is <literal>unicast</literal>.</para>
+<literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>.
+The default is <literal>unicast</literal>.</para>
 </listitem>
 <listitem>
 <para><literal>"window"</literal> - an unsigned 32 bit integer.</para>
@@ -90,7 +91,7 @@ fixed priority.
 <para>
 Example: <literal>priority 5 from 192.167.4.0/24 table 45</literal>
 </para></description-docbook></property></setting><setting name="ipv6">
-<property name="addresses" variable="addresses" format="a comma separated list of addresses" values="" default="" example="" description="A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example &quot;2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64&quot;. The addresses are listed in increasing priority, meaning the last address will be the primary address." /><property name="routes" variable="routes" format="a comma separated list of routes" values="" default="" example="" description=""><description-docbook>
+<property name="addresses" variable="addresses" format="a comma separated list of addresses" values="" default="" example="" description="A list of IPv6 addresses and their prefix length. Multiple addresses can be separated by comma. For example &quot;2001:db8:85a3::8a2e:370:7334/64, 2001:db8:85a3::5/64&quot;. The addresses are listed in decreasing priority, meaning the first address will be the primary address. This can make a difference with IPv6 source address selection (RFC 6724, section 5)." /><property name="routes" variable="routes" format="a comma separated list of routes" values="" default="" example="" description=""><description-docbook>
 <para>
 A list of IPv6 destination addresses, prefix length, optional IPv6
 next hop addresses, optional route metric, optional attribute. The valid syntax is:
@@ -140,7 +141,8 @@ Various attributes are supported:
 </listitem>
 <listitem>
 <para><literal>"type"</literal> - one of <literal>unicast</literal>, <literal>local</literal>, <literal>blackhole</literal>,
-<literal>unavailable</literal>, <literal>prohibit</literal>. The default is <literal>unicast</literal>.</para>
+<literal>unavailable</literal>, <literal>prohibit</literal>, <literal>throw</literal>.
+The default is <literal>unicast</literal>.</para>
 </listitem>
 <listitem>
 <para><literal>"window"</literal> - an unsigned 32 bit integer.</para>
@@ -171,7 +173,7 @@ Example: <literal>priority 5 from 1:2:3::5/128 table 45</literal>
 </setting><setting name="ppp">
 </setting><setting name="pppoe">
 </setting><setting name="proxy">
-</setting><setting name="serial">
+<property name="pac-script" variable="pac-script" format="" values="" default="" example="" description="The PAC script. In the profile this must be an UTF-8 encoded javascript code that defines a FindProxyForURL() function. When setting the property in nmcli, a filename is accepted too. In that case, nmcli will read the content of the file and set the script. The prefixes &quot;file://&quot; and &quot;js://&quot; are supported to explicitly differentiate between the two." /></setting><setting name="serial">
 </setting><setting name="sriov">
 </setting><setting name="tc">
 <property name="qdiscs" variable="qdiscs" format="GPtrArray(NMTCQdisc)" values="" default="" example="" description=""><description-docbook>
diff --git a/src/libnm-client-impl/nm-settings-docs-gir.xml b/src/libnm-client-impl/nm-settings-docs-gir.xml
index d8022132..77a95b51 100644
--- a/src/libnm-client-impl/nm-settings-docs-gir.xml
+++ b/src/libnm-client-impl/nm-settings-docs-gir.xml
@@ -5,7 +5,7 @@
 <nm-setting-docs>
   <setting name="connection" description="General Connection Profile Settings" name_upper="CONNECTION" >
     <property name="auth-retries" name_upper="AUTH_RETRIES" type="int32" default="-1" description="The number of retries for the authentication. Zero means to try indefinitely; -1 means to use a global default. If the global default is not set, the authentication retries for 3 times before failing the connection. Currently, this only applies to 802-1x authentication." />
-    <property name="autoconnect" name_upper="AUTOCONNECT" type="boolean" default="TRUE" description='Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles.' />
+    <property name="autoconnect" name_upper="AUTOCONNECT" type="boolean" default="TRUE" description='Whether or not the connection should be automatically connected by NetworkManager when the resources for the connection are available. TRUE to automatically activate the connection, FALSE to require manual intervention to activate the connection. Autoconnect happens when the circumstances are suitable. That means for example that the device is currently managed and not active. Autoconnect thus never replaces or competes with an already active profile. Note that autoconnect is not implemented for VPN profiles. See "secondaries" as an alternative to automatically connect VPN profiles. If multiple profiles are ready to autoconnect on the same device, the one with the better "connection.autoconnect-priority" is chosen. If the priorities are equal, then the most recently connected profile is activated. If the profiles were not connected earlier or their "connection.timestamp" is identical, the choice is undefined. Depending on "connection.multi-connect", a profile can (auto)connect only once at a time or multiple times.' />
     <property name="autoconnect-priority" name_upper="AUTOCONNECT_PRIORITY" type="int32" default="0" description="The autoconnect priority in range -999 to 999. If the connection is set to autoconnect, connections with higher priority will be preferred. The higher number means higher priority. Defaults to 0. Note that this property only matters if there are more than one candidate profile to select for autoconnect. In case of equal priority, the profile used most recently is chosen." />
     <property name="autoconnect-retries" name_upper="AUTOCONNECT_RETRIES" type="int32" default="-1" description="The number of times a connection should be tried when autoactivating before giving up. Zero means forever, -1 means the global default (4 times if not overridden). Setting this to 1 means to try activation only once before blocking autoconnect. Note that after a timeout, NetworkManager will try to autoconnect again." />
     <property name="autoconnect-slaves" name_upper="AUTOCONNECT_SLAVES" type="NMSettingConnectionAutoconnectSlaves (int32)" description='Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties "autoconnect", "autoconnect-priority" and "autoconnect-retries" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0.' />
@@ -24,7 +24,7 @@
     <property name="read-only" name_upper="READ_ONLY" type="boolean" default="FALSE" description="FALSE if the connection can be modified using the provided settings service's D-Bus interface with the right privileges, or TRUE if the connection is read-only and cannot be modified." />
     <property name="secondaries" name_upper="SECONDARIES" type="array of string" description="List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported." />
     <property name="slave-type" name_upper="SLAVE_TYPE" type="string" description="Setting name of the device type of this slave's master connection (eg, &quot;bond&quot;), or NULL if this connection is not a slave." />
-    <property name="stable-id" name_upper="STABLE_ID" type="string" description="This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret-key. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are &quot;${CONNECTION}&quot;, &quot;${DEVICE}&quot;, &quot;${MAC}&quot;, &quot;${BOOT}&quot;, &quot;${RANDOM}&quot;. These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that &quot;${DEVICE}&quot; corresponds to the interface name of the device and &quot;${MAC}&quot; is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as &quot;$$&quot;. For example, set it to &quot;${CONNECTION}-${BOOT}-${DEVICE}&quot; to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to &quot;${CONNECTION}&quot; and uses a unique, fixed ID for the connection." />
+    <property name="stable-id" name_upper="STABLE_ID" type="string" description="This represents the identity of the connection used for various purposes. It allows to configure multiple profiles to share the identity. Also, the stable-id can contain placeholders that are substituted dynamically and deterministically depending on the context. The stable-id is used for generating IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the generated cloned MAC address for ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. It is also used as DHCP client identifier with ipv4.dhcp-client-id=stable and to derive the DHCP DUID with ipv6.dhcp-duid=stable-[llt,ll,uuid]. Note that depending on the context where it is used, other parameters are also seeded into the generation algorithm. For example, a per-host key is commonly also included, so that different systems end up generating different IDs. Or with ipv6.addr-gen-mode=stable-privacy, also the device's name is included, so that different interfaces yield different addresses. The per-host key is the identity of your machine and stored in /var/lib/NetworkManager/secret_key. See NetworkManager(8) manual about the secret-key and the host identity. The '$' character is treated special to perform dynamic substitutions at runtime. Currently, supported are &quot;${CONNECTION}&quot;, &quot;${DEVICE}&quot;, &quot;${MAC}&quot;, &quot;${BOOT}&quot;, &quot;${RANDOM}&quot;. These effectively create unique IDs per-connection, per-device, per-boot, or every time. Note that &quot;${DEVICE}&quot; corresponds to the interface name of the device and &quot;${MAC}&quot; is the permanent MAC address of the device. Any unrecognized patterns following '$' are treated verbatim, however are reserved for future use. You are thus advised to avoid '$' or escape it as &quot;$$&quot;. For example, set it to &quot;${CONNECTION}-${BOOT}-${DEVICE}&quot; to create a unique id for this connection that changes with every reboot and differs depending on the interface where the profile activates. If the value is unset, a global connection default is consulted. If the value is still unset, the default is similar to &quot;${CONNECTION}&quot; and uses a unique, fixed ID for the connection." />
     <property name="timestamp" name_upper="TIMESTAMP" type="uint64" default="0" description="The time, in seconds since the Unix Epoch, that the connection was last _successfully_ fully activated. NetworkManager updates the connection timestamp periodically when the connection is active to ensure that an active connection has the latest timestamp. The property is only meant for reading (changes to this property will not be preserved)." />
     <property name="type" name_upper="TYPE" type="string" description='Base type of the connection. For hardware-dependent connections, should contain the setting name of the hardware-type specific setting (ie, "802-3-ethernet" or "802-11-wireless" or "bluetooth", etc), and for non-hardware dependent connections like VPN or otherwise, should contain the setting name of that setting type (ie, "vpn" or "bridge", etc).' />
     <property name="uuid" name_upper="UUID" type="string" description='A universally unique identifier for the connection, for example generated with libuuid.  It should be assigned when the connection is created, and never changed as long as the connection still applies to the same network.  For example, it should not be changed when the "id" property or NMSettingIP4Config changes, but might need to be re-created when the Wi-Fi SSID, mobile broadband network provider, or "type" property changes. The UUID must be in the format "2815492f-7e56-435e-b2e9-246bd7cdc664" (ie, contains only hexadecimal characters and "-").' />
@@ -353,7 +353,7 @@
   <setting name="proxy" description="WWW Proxy Settings" name_upper="PROXY" >
     <property name="browser-only" name_upper="BROWSER_ONLY" type="boolean" default="FALSE" description="Whether the proxy configuration is for browser only." />
     <property name="method" name_upper="METHOD" type="int32" default="0" description="Method for proxy configuration, Default is NM_SETTING_PROXY_METHOD_NONE (0)" />
-    <property name="pac-script" name_upper="PAC_SCRIPT" type="string" description="PAC script for the connection." />
+    <property name="pac-script" name_upper="PAC_SCRIPT" type="string" description="PAC script for the connection. This is an UTF-8 encoded javascript code that defines a FindProxyForURL() function." />
     <property name="pac-url" name_upper="PAC_URL" type="string" description="PAC URL for obtaining PAC file." />
   </setting>
   <setting name="serial" description="Serial Link Settings" name_upper="SERIAL" >
diff --git a/src/libnm-client-impl/nm-vpn-service-plugin.c b/src/libnm-client-impl/nm-vpn-service-plugin.c
index 2a217502..3493d1db 100644
--- a/src/libnm-client-impl/nm-vpn-service-plugin.c
+++ b/src/libnm-client-impl/nm-vpn-service-plugin.c
@@ -729,54 +729,6 @@ nm_vpn_service_plugin_secrets_required(NMVpnServicePlugin *plugin,
 
 /*****************************************************************************/
 
-typedef struct {
-    char *buf;
-    gsize n_buf;
-    int   fd;
-    bool  eof : 1;
-    char  buf_full[1024];
-} ReadFdBuf;
-
-static inline gboolean
-_read_fd_buf_c(ReadFdBuf *read_buf, char *ch)
-{
-    gssize n_read;
-
-    if (read_buf->n_buf > 0)
-        goto out_data;
-    if (read_buf->eof)
-        return FALSE;
-
-again:
-    n_read = read(read_buf->fd, read_buf->buf_full, sizeof(read_buf->buf_full));
-    if (n_read <= 0) {
-        if (n_read < 0 && errno == EAGAIN) {
-            struct pollfd pfd;
-            int           r;
-
-            memset(&pfd, 0, sizeof(pfd));
-            pfd.fd     = read_buf->fd;
-            pfd.events = POLLIN;
-
-            r = poll(&pfd, 1, -1);
-            if (r > 0)
-                goto again;
-            /* error or timeout. Fall through and set EOF. */
-        }
-        read_buf->eof = TRUE;
-        return FALSE;
-    }
-
-    read_buf->buf   = read_buf->buf_full;
-    read_buf->n_buf = n_read;
-
-out_data:
-    read_buf->n_buf--;
-    *ch = read_buf->buf[0];
-    read_buf->buf++;
-    return TRUE;
-}
-
 #define DATA_KEY_TAG   "DATA_KEY="
 #define DATA_VAL_TAG   "DATA_VAL="
 #define SECRET_KEY_TAG "SECRET_KEY="
@@ -808,7 +760,7 @@ nm_vpn_service_plugin_read_vpn_details(int fd, GHashTable **out_data, GHashTable
     nm_auto_free_gstring GString  *val     = NULL;
     nm_auto_free_gstring GString  *line    = NULL;
     GString                       *str     = NULL;
-    ReadFdBuf                      read_buf;
+    char                           c;
 
     if (out_data)
         g_return_val_if_fail(*out_data == NULL, FALSE);
@@ -819,27 +771,37 @@ nm_vpn_service_plugin_read_vpn_details(int fd, GHashTable **out_data, GHashTable
     secrets =
         g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, (GDestroyNotify) nm_free_secret);
 
-    read_buf.buf   = NULL;
-    read_buf.n_buf = 0;
-    read_buf.fd    = fd;
-    read_buf.eof   = FALSE;
-
     line = g_string_new(NULL);
 
     /* Read stdin for data and secret items until we get a DONE */
     while (1) {
-        gboolean eof;
-        char     c = '\0';
+        ssize_t nr;
+
+        nr = read(fd, &c, 1);
+        if (nr < 0) {
+            if (errno == EAGAIN) {
+                struct pollfd pfd;
+                int           r;
 
-        eof = !_read_fd_buf_c(&read_buf, &c);
+                memset(&pfd, 0, sizeof(pfd));
+                pfd.fd     = fd;
+                pfd.events = POLLIN;
+
+                r = poll(&pfd, 1, -1);
+                if (r > 0)
+                    continue;
+
+                /* error or timeout. Fall through and break. */
+            }
+            break;
+        }
 
-        if (!eof && c == '\0') {
-            /* On the first '\0' char, we also assume the data is finished. Abort. */
-            read_buf.eof = TRUE;
-            eof          = TRUE;
+        if (nr > 0 && c == '\0') {
+            /* '\0' are not supported. Replace with newline. */
+            c = '\n';
         }
 
-        if (!eof && c != '\n') {
+        if (nr > 0 && c != '\n') {
             g_string_append_c(line, c);
             if (line->len > 512 * 1024) {
                 /* we are about to read a huge line. That is not right, abort. */
@@ -893,7 +855,7 @@ nm_vpn_service_plugin_read_vpn_details(int fd, GHashTable **out_data, GHashTable
 next:
         g_string_truncate(line, 0);
 
-        if (eof)
+        if (nr == 0)
             break;
     }
 
diff --git a/src/libnm-client-impl/tests/test-libnm.c b/src/libnm-client-impl/tests/test-libnm.c
index 0f600a4a..1810e139 100644
--- a/src/libnm-client-impl/tests/test-libnm.c
+++ b/src/libnm-client-impl/tests/test-libnm.c
@@ -2272,6 +2272,12 @@ _memfd_create(const char *name)
     return -1;
 }
 
+typedef enum {
+    READ_VPN_DETAIL_TYPE_GOOD,
+    READ_VPN_DETAIL_TYPE_NO_DONE,
+    READ_VPN_DETAIL_TYPE_BROKEN,
+} ReadVpnDetailType;
+
 typedef struct {
     const char *key;
     const char *val;
@@ -2285,6 +2291,7 @@ _do_read_vpn_details_impl1(const char              *file,
                            int                      memfd,
                            char                    *mem,
                            gsize                    len,
+                           ReadVpnDetailType        detail_type,
                            const ReadVpnDetailData *expected_data,
                            guint                    expected_data_len,
                            const ReadVpnDetailData *expected_secrets,
@@ -2294,10 +2301,27 @@ _do_read_vpn_details_impl1(const char              *file,
     off_t                          lseeked;
     gs_unref_hashtable GHashTable *data    = NULL;
     gs_unref_hashtable GHashTable *secrets = NULL;
+    char                           ch;
+    gboolean                       append_quit;
+    char                           read_buf[1024];
+    gssize                         n_read;
+    gssize                         i;
 
     written = write(memfd, mem, len);
     g_assert_cmpint(written, ==, (gssize) len);
 
+    append_quit = nmtst_get_rand_bool();
+
+    if (append_quit) {
+        if (len > 0 && mem[len - 1] != '\n') {
+            ch      = '\n';
+            written = write(memfd, &ch, 1);
+            g_assert_cmpint(written, ==, 1);
+        }
+        written = write(memfd, "QUIT", 4);
+        g_assert_cmpint(written, ==, 4);
+    }
+
     lseeked = lseek(memfd, 0, SEEK_SET);
     g_assert_cmpint(lseeked, ==, 0);
 
@@ -2327,13 +2351,14 @@ _do_read_vpn_details_impl1(const char              *file,
                 if (nm_streq(_expected[_i].key, _k))                                          \
                     break;                                                                    \
             }                                                                                 \
-            if (_i >= _expected_len)                                                          \
+            if (_i >= _expected_len) {                                                        \
                 g_error("%s:%d: hash '%s' contains unexpected data key '%s' with value '%s'", \
                         file,                                                                 \
                         line,                                                                 \
                         G_STRINGIFY(hash),                                                    \
                         _k,                                                                   \
                         _v);                                                                  \
+            }                                                                                 \
         }                                                                                     \
                                                                                               \
         for (_i = 0; _i < _expected_len; _i++) {                                              \
@@ -2342,7 +2367,7 @@ _do_read_vpn_details_impl1(const char              *file,
             g_assert(_d->key);                                                                \
             g_assert(_d->val);                                                                \
             _v = g_hash_table_lookup(_hash, _d->key);                                         \
-            if (!nm_streq0(_v, _d->val))                                                      \
+            if (!nm_streq0(_v, _d->val)) {                                                    \
                 g_error("%s:%d: hash '%s' contains data key '%s' with value %s%s%s but we "   \
                         "expected '%s'",                                                      \
                         file,                                                                 \
@@ -2351,20 +2376,71 @@ _do_read_vpn_details_impl1(const char              *file,
                         _d->key,                                                              \
                         NM_PRINT_FMT_QUOTE_STRING(_v),                                        \
                         _d->val);                                                             \
+            }                                                                                 \
         }                                                                                     \
                                                                                               \
         g_assert_cmpint(g_hash_table_size(_hash), ==, _expected_len);                         \
     }                                                                                         \
     G_STMT_END
 
+    NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
     _assert_hash(data, expected_data, expected_data_len);
     _assert_hash(secrets, expected_secrets, expected_secrets_len);
+    NM_PRAGMA_WARNING_REENABLE
 
 #undef _assert_hash
+
+    n_read = read(memfd, read_buf, sizeof(read_buf));
+
+    if (0) {
+        gs_free char *ss = NULL;
+
+        g_print(">>>> n_read=%zd;  \"%s\"",
+                n_read,
+                n_read > 0 ? (
+                    ss = nm_utils_buf_utf8safe_escape_cp(read_buf,
+                                                         n_read,
+                                                         NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL))
+                           : "");
+    }
+
+    g_assert_cmpint(n_read, >=, 0);
+    g_assert_cmpint(n_read, <, sizeof(read_buf));
+
+    i = read(memfd, &ch, 1);
+    g_assert_cmpint(i, ==, 0);
+
+    switch (detail_type) {
+    case READ_VPN_DETAIL_TYPE_GOOD:
+        g_assert_cmpint(n_read, >=, 0);
+        i = 0;
+        while (i < n_read && NM_IN_SET(read_buf[i], '\n'))
+            i++;
+        if (append_quit)
+            g_assert_cmpmem("QUIT", 4, &read_buf[i], n_read - i);
+        else
+            g_assert_cmpint(n_read - i, ==, 0);
+        break;
+    case READ_VPN_DETAIL_TYPE_NO_DONE:
+        g_assert_cmpint(n_read, ==, 0);
+        break;
+    case READ_VPN_DETAIL_TYPE_BROKEN:
+        g_assert_cmpint(n_read, >, 0);
+        if (append_quit) {
+            g_assert_cmpint(n_read, >, 4);
+            g_assert(memmem(&read_buf[i], n_read + i, "QUIT", 4));
+        }
+        break;
+    default:
+        g_assert_not_reached();
+        break;
+    }
+
     return TRUE;
 }
 
 #define _do_read_vpn_details_impl0(str,                                          \
+                                   detail_type,                                  \
                                    expected_data,                                \
                                    expected_data_len,                            \
                                    expected_secrets,                             \
@@ -2385,6 +2461,7 @@ _do_read_vpn_details_impl1(const char              *file,
                                        _memfd,                                   \
                                        "" str "",                                \
                                        NM_STRLEN(str),                           \
+                                       (detail_type),                            \
                                        expected_data,                            \
                                        expected_data_len,                        \
                                        expected_secrets,                         \
@@ -2393,14 +2470,16 @@ _do_read_vpn_details_impl1(const char              *file,
     }                                                                            \
     G_STMT_END
 
-#define _do_read_vpn_details_empty(str) _do_read_vpn_details_impl0(str, NULL, 0, NULL, 0, {})
+#define _do_read_vpn_details_empty(str) \
+    _do_read_vpn_details_impl0(str, READ_VPN_DETAIL_TYPE_GOOD, NULL, 0, NULL, 0, {})
 
-#define _do_read_vpn_details(str, expected_data, expected_secrets, pre_setup_cmd) \
-    _do_read_vpn_details_impl0(str,                                               \
-                               expected_data,                                     \
-                               G_N_ELEMENTS(expected_data),                       \
-                               expected_secrets,                                  \
-                               G_N_ELEMENTS(expected_secrets),                    \
+#define _do_read_vpn_details(str, detail_type, expected_data, expected_secrets, pre_setup_cmd) \
+    _do_read_vpn_details_impl0(str,                                                            \
+                               detail_type,                                                    \
+                               expected_data,                                                  \
+                               G_N_ELEMENTS(expected_data),                                    \
+                               expected_secrets,                                               \
+                               G_N_ELEMENTS(expected_secrets),                                 \
                                pre_setup_cmd)
 
 static void
@@ -2426,6 +2505,7 @@ test_nm_vpn_service_plugin_read_vpn_details(void)
                          "DONE\n"
                          "\n"
                          "",
+                         READ_VPN_DETAIL_TYPE_GOOD,
                          READ_VPN_DETAIL_DATA({"some-key", "string"}, {"some-other-key", "val2"}, ),
                          READ_VPN_DETAIL_DATA({"some-secret", "val3"}, ), );
 
@@ -2433,6 +2513,7 @@ test_nm_vpn_service_plugin_read_vpn_details(void)
                          "DATA_KEY=some-key\n"
                          "DATA_VAL=string\n"
                          "DONE\n",
+                         READ_VPN_DETAIL_TYPE_GOOD,
                          READ_VPN_DETAIL_DATA({"some-key", "string"}, ),
                          READ_VPN_DETAIL_DATA(), );
 
@@ -2461,6 +2542,7 @@ test_nm_vpn_service_plugin_read_vpn_details(void)
         "\n"
         "DONE\n"
         "",
+        READ_VPN_DETAIL_TYPE_BROKEN,
         READ_VPN_DETAIL_DATA({"some-key", "string\ncontinued after a line break"}, ),
         READ_VPN_DETAIL_DATA({"key names\ncan have\ncontinuations too", "value"}, ), );
 
@@ -2474,6 +2556,7 @@ test_nm_vpn_service_plugin_read_vpn_details(void)
         "=continuations too\n"
         "SECRET_VAL=value\n"
         "",
+        READ_VPN_DETAIL_TYPE_NO_DONE,
         READ_VPN_DETAIL_DATA({"some-key", "string\ncontinued after a line break"}, ),
         READ_VPN_DETAIL_DATA({"key names\ncan have\ncontinuations too", "value"}, ), );
 
@@ -2507,6 +2590,7 @@ test_nm_vpn_service_plugin_read_vpn_details(void)
                          "DONE\n"
                          "\n"
                          "",
+                         READ_VPN_DETAIL_TYPE_GOOD,
                          READ_VPN_DETAIL_DATA({"some\nkey-2", "val2"},
                                               {"some-key", "string"},
                                               {"key3\nkey-2", "val3"}, ),
@@ -2546,8 +2630,15 @@ test_nm_vpn_service_plugin_read_vpn_details(void)
                          "DONE\n"
                          "\n"
                          "",
-                         READ_VPN_DETAIL_DATA({"some\nkey-2", "val2"}, {"some-key", "string"}, ),
-                         READ_VPN_DETAIL_DATA(), );
+                         READ_VPN_DETAIL_TYPE_GOOD,
+                         READ_VPN_DETAIL_DATA({"some\nkey-2", "val2"},
+                                              {"some-key", "string"},
+                                              {"key3\nkey-2", "val3"}, ),
+                         READ_VPN_DETAIL_DATA({"some-secret", "val3"},
+                                              {"key-inval", "in\xc1val"},
+                                              {"ke\xc0yx", "inval"},
+                                              {"keyx", ""},
+                                              {"", "val3"}), );
 }
 
 /*****************************************************************************/
@@ -3049,10 +3140,12 @@ check_dbus_properties:
                     break;
                 p_expected_type++;
                 if (p_expected_type >= &expected_types[G_N_ELEMENTS(expected_types)]) {
+                    NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
                     g_error("D-Bus type \"%s\" is not implemented (in property %s.%s)",
                             (const char *) mpr->dbus_type,
                             mif->dbus_iface_name,
                             mpr->dbus_property_name);
+                    NM_PRAGMA_WARNING_REENABLE
                 }
             }
 
@@ -3151,6 +3244,7 @@ check_dbus_properties:
                             break;
                     }
                     if (p_expected_type_2 >= &expected_types[G_N_ELEMENTS(expected_types)]) {
+                        NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
                         g_error("D-Bus property \"%s.%s\" (type \"%s\") maps to property \"%s\", "
                                 "but that has an unexpected property type %s (expected %s)",
                                 mif->dbus_iface_name,
@@ -3159,6 +3253,7 @@ check_dbus_properties:
                                 pspec->name,
                                 g_type_name(pspec->value_type),
                                 g_type_name(p_expected_type->default_gtype));
+                        NM_PRAGMA_WARNING_REENABLE
                     }
                 }