libnm: always build libnm with JSON validation

We anyway load libjansson with dlopen(), and already before it could
happen that libjansson is not available. In that case, we would not
crash, but simply proceed without json validation.

Since libnm-core no longer uses libjansson directly, but only via
"nm-glib-aux/nm-json.h", we can just always compile with that, and use
it at runtime. That means, libjansson is not a build dependency for
libnm anymore, so we don't need a compile time check.

Note that if you build without libjansson, then JANSSON_SONAME is
undefined, and loading it will still fail at runtime. So, even if
we now always build with all our code enabled, it only works if you
actually build with libjansson. Still, it's simpler to drop the
conditional build, as the only benefit is a (minimally) smaller
build.
This commit is contained in:
Thomas Haller 2020-07-01 18:38:22 +02:00
parent 3814467b88
commit bbb1f5df2f
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
10 changed files with 124 additions and 154 deletions

View file

@ -212,9 +212,6 @@
/* Define if JANSSON is enabled */
#mesondefine WITH_JANSSON
/* Define if JSON validation in libnm is enabled */
#mesondefine WITH_JSON_VALIDATION
/* Define if you have libpsl */
#mesondefine WITH_LIBPSL

View file

@ -596,6 +596,8 @@ if test "$have_jansson" = "yes"; then
fi
AC_DEFINE_UNQUOTED(JANSSON_SONAME, "$JANSSON_SONAME", [Define to path to the Jansson shared library])
else
have_jansson=no
JANSSON_SONAME=
AC_DEFINE(WITH_JANSSON, 0, [Define if JANSSON is enabled])
fi
AM_CONDITIONAL(WITH_JANSSON, test "${have_jansson}" = "yes")
@ -622,21 +624,6 @@ if test "${enable_teamdctl}" = "yes"; then
fi
AM_CONDITIONAL(WITH_TEAMDCTL, test "${enable_teamdctl}" = "yes")
# Jansson for team configuration validation
AC_ARG_ENABLE(json-validation,
AS_HELP_STRING([--enable-json-validation], [Enable JSON validation in libnm]),
[enable_json_validation=${enableval}],
[enable_json_validation=${have_jansson}])
if test "${enable_json_validation}" = "no"; then
AC_DEFINE(WITH_JSON_VALIDATION, 0, [Define if JSON validation in libnm is enabled])
else
if test "$have_jansson" = "no"; then
AC_MSG_ERROR([jansson is needed for team configuration validation. Use --disable-json-validation to build without it.])
fi
AC_DEFINE(WITH_JSON_VALIDATION, 1, [Define if JSON validation in libnm is enabled])
fi
AM_CONDITIONAL(WITH_JSON_VALIDATION, test "${enable_json_validation}" != "no")
# we usually compile with polkit support. --enable-polkit=yes|no only sets the
# default configuration for main.auth-polkit. User can always enable/disable polkit
# authorization via config.
@ -1366,6 +1353,7 @@ echo " nmcli: $build_nmcli"
echo " nmtui: $build_nmtui"
echo " nm-cloud-setup: $with_nm_cloud_setup"
echo " iwd: $ac_with_iwd"
echo " jansson: $have_jansson${JANSSON_SONAME:+ (soname: $JANSSON_SONAME)}"
echo
echo "Configuration plugins (main.plugins=${config_plugins_default})"
@ -1398,7 +1386,6 @@ echo " valgrind: $with_valgrind $with_valgrind_suppressions"
echo " code coverage: $enable_code_coverage"
echo " LTO: $enable_lto"
echo " linker garbage collection: $enable_ld_gc"
echo " JSON validation for libnm: $enable_json_validation"
echo " crypto: $with_crypto (have-gnutls: $have_crypto_gnutls, have-nss: $have_crypto_nss)"
echo " sanitizers: $sanitizers"
echo " Mozilla Public Suffix List: $with_libpsl"

View file

@ -1116,7 +1116,6 @@ _link_watcher_to_json (const NMTeamLinkWatcher *link_watcher,
g_string_append (gstr, " }");
}
#if WITH_JSON_VALIDATION
static NMTeamLinkWatcher *
_link_watcher_from_json (const NMJsonVt *vt,
const nm_json_t *root_js_obj,
@ -1219,7 +1218,6 @@ fail:
*out_unrecognized_content = TRUE;
return NULL;
}
#endif
/*****************************************************************************/
@ -1642,7 +1640,6 @@ nm_team_setting_config_get (const NMTeamSetting *self)
/*****************************************************************************/
#if WITH_JSON_VALIDATION
static gboolean
_attr_data_match_keys (const TeamAttrData *attr_data,
const char *const*keys,
@ -1701,6 +1698,8 @@ _js_parse_locate_keys (const NMJsonVt *vt,
nm_json_t *cur_val2;
nm_json_t *cur_val3;
nm_assert (vt);
#define _handle(_self, _cur_key, _cur_val, _keys, _level, _found_keys, _out_unrecognized_content) \
({ \
const TeamAttrData *_attr_data; \
@ -1750,6 +1749,8 @@ _js_parse_unpack (const NMJsonVt *vt,
{
const TeamAttrData *attr_data;
nm_assert (vt);
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) {
NMValueTypUnion *p_out_val;
gboolean valid = FALSE;
@ -1838,11 +1839,11 @@ _js_parse_unpack (const NMJsonVt *vt,
*out_unrecognized_content = TRUE;
}
}
#endif
guint32
nm_team_setting_config_set (NMTeamSetting *self, const char *js_str)
{
const NMJsonVt *vt;
guint32 changed_flags = 0;
gboolean do_set_default = TRUE;
gboolean new_js_str_invalid = FALSE;
@ -1872,14 +1873,10 @@ nm_team_setting_config_set (NMTeamSetting *self, const char *js_str)
} else
changed_flags |= nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG);
#if WITH_JSON_VALIDATION
{
if ((vt = nm_json_vt ())) {
nm_auto_decref_json nm_json_t *root_js_obj = NULL;
const NMJsonVt *vt;
if ((vt = nm_json_vt ()))
root_js_obj = vt->nm_json_loads (js_str, 0, NULL);
root_js_obj = vt->nm_json_loads (js_str, 0, NULL);
if ( !root_js_obj
|| !nm_json_is_object (root_js_obj))
new_js_str_invalid = TRUE;
@ -1915,8 +1912,6 @@ nm_team_setting_config_set (NMTeamSetting *self, const char *js_str)
}
}
#endif
if (do_set_default)
changed_flags |= _team_setting_set_default (self);
@ -2231,6 +2226,7 @@ nm_team_setting_reset_from_dbus (NMTeamSetting *self,
GVariantIter iter;
const char *v_key;
GVariant *v_val;
const NMJsonVt *vt;
*out_changed = 0;
@ -2280,10 +2276,12 @@ nm_team_setting_reset_from_dbus (NMTeamSetting *self,
variants[attr_data->team_attr] = g_steal_pointer (&v_val_free);
}
vt = nm_json_vt ();
if (variants[NM_TEAM_ATTRIBUTE_LINK_WATCHERS]) {
if ( variants[NM_TEAM_ATTRIBUTE_CONFIG]
&& WITH_JSON_VALIDATION
&& vt
&& !NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
/* we don't require the content of the "link-watchers" and we also
* don't perform strict validation. No need to parse it. */
@ -2291,7 +2289,7 @@ nm_team_setting_reset_from_dbus (NMTeamSetting *self,
gs_free_error GError *local = NULL;
/* We might need the parsed v_link_watchers array below (because there is no JSON
* "config" present or because we don't build WITH_JSON_VALIDATION).
* "config" present or because we don't have json support).
*
* Or we might run with NM_SETTING_PARSE_FLAGS_STRICT. In that mode, we may not necessarily
* require that the entire setting as a whole validates (if a JSON config is present and
@ -2319,7 +2317,7 @@ nm_team_setting_reset_from_dbus (NMTeamSetting *self,
? g_variant_get_string (variants[NM_TEAM_ATTRIBUTE_CONFIG], NULL)
: NULL);
if ( WITH_JSON_VALIDATION
if ( vt
&& variants[NM_TEAM_ATTRIBUTE_CONFIG]) {
/* for team settings, the JSON must be able to express all possible options. That means,
* if the GVariant contains both the JSON "config" and other options, then the other options

View file

@ -5490,7 +5490,6 @@ _nm_utils_is_json_object_no_validation (const char *str, GError **error)
gboolean
nm_utils_is_json_object (const char *str, GError **error)
{
#if WITH_JSON_VALIDATION
nm_auto_decref_json nm_json_t *json = NULL;
const NMJsonVt *vt;
nm_json_error_t jerror;
@ -5530,19 +5529,6 @@ nm_utils_is_json_object (const char *str, GError **error)
}
return TRUE;
#else /* !WITH_JSON_VALIDATION */
g_return_val_if_fail (!error || !*error, FALSE);
if (!str || !str[0]) {
g_set_error_literal (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
str ? _("value is NULL") : _("value is empty"));
return FALSE;
}
return _nm_utils_is_json_object_no_validation (str, error);
#endif
}
static char *

View file

@ -14,6 +14,7 @@
#include "nm-std-aux/c-list-util.h"
#include "nm-glib-aux/nm-enum-utils.h"
#include "nm-glib-aux/nm-str-buf.h"
#include "nm-glib-aux/nm-json.h"
#include "systemd/nm-sd-utils-shared.h"
#include "nm-utils.h"
@ -7125,17 +7126,19 @@ test_nm_utils_check_valid_json (void)
{
_json_config_check_valid (NULL, FALSE);
_json_config_check_valid ("", FALSE);
#if WITH_JSON_VALIDATION
_json_config_check_valid ("{ }", TRUE);
_json_config_check_valid ("{ \"a\" : 1 }", TRUE);
_json_config_check_valid ("{ \"a\" : }", FALSE);
#else
/* Without JSON library everything except empty string is considered valid */
nmtst_json_vt_reset (FALSE);
_json_config_check_valid ("{ }", TRUE);
_json_config_check_valid ("{'%!-a1} ", TRUE);
_json_config_check_valid (" {'%!-a1}", TRUE);
_json_config_check_valid ("{'%!-a1", FALSE);
#endif
if (nmtst_json_vt_reset (TRUE)) {
_json_config_check_valid ("{ }", TRUE);
_json_config_check_valid ("{ \"a\" : 1 }", TRUE);
_json_config_check_valid ("{ \"a\" : }", FALSE);
}
}
static void
@ -7172,86 +7175,96 @@ _team_config_equal_check (const char *conf1,
static void
test_nm_utils_team_config_equal (void)
{
_team_config_equal_check ("",
"",
TRUE,
TRUE);
_team_config_equal_check ("",
" ",
TRUE,
TRUE);
_team_config_equal_check ("{}",
"{ }",
TRUE,
TRUE);
_team_config_equal_check ("{}",
"{",
TRUE,
TRUE);
_team_config_equal_check ("{ \"a\": 1 }",
"{ \"a\": 1 }",
TRUE,
TRUE);
_team_config_equal_check ("{ \"a\": 1 }",
"{ \"a\": 1 }",
TRUE,
TRUE);
int with_json_vt;
/* team config */
_team_config_equal_check ("{ }",
"{ \"runner\" : { \"name\" : \"random\"} }",
FALSE,
!WITH_JSON_VALIDATION);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }",
"{ \"runner\" : { \"name\" : \"random\"} }",
FALSE,
!WITH_JSON_VALIDATION);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"} }",
"{ \"runner\" : { \"name\" : \"random\"} }",
FALSE,
TRUE);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"loadbalance\"} }",
"{ \"runner\" : { \"name\" : \"loadbalance\"} }",
FALSE,
TRUE);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"}, \"ports\" : { \"eth0\" : {} } }",
"{ \"runner\" : { \"name\" : \"random\"}, \"ports\" : { \"eth1\" : {} } }",
FALSE,
TRUE);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"lacp\"} }",
"{ \"runner\" : { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }",
FALSE,
!WITH_JSON_VALIDATION);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }",
"{ \"runner\" : { \"name\" : \"roundrobin\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }",
FALSE,
!WITH_JSON_VALIDATION);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"lacp\"} }",
"{ \"runner\" : { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\" ] } }",
FALSE,
!WITH_JSON_VALIDATION);
for (with_json_vt = 0; with_json_vt < 2; with_json_vt++) {
const NMJsonVt *vt;
/* team port config */
_team_config_equal_check ("{ }",
"{ \"link_watch\" : { \"name\" : \"ethtool\"} }",
TRUE,
!WITH_JSON_VALIDATION);
_team_config_equal_check ("{ }",
"{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
TRUE,
TRUE);
_team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"ethtool\"} }",
"{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
TRUE,
!WITH_JSON_VALIDATION);
_team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
"{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
TRUE,
TRUE);
_team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth0\" : {} } }",
"{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth1\" : {} } }",
TRUE,
TRUE);
vt = nmtst_json_vt_reset (!!with_json_vt);
_team_config_equal_check ("",
"",
TRUE,
TRUE);
_team_config_equal_check ("",
" ",
TRUE,
TRUE);
_team_config_equal_check ("{}",
"{ }",
TRUE,
TRUE);
_team_config_equal_check ("{}",
"{",
TRUE,
TRUE);
_team_config_equal_check ("{ \"a\": 1 }",
"{ \"a\": 1 }",
TRUE,
TRUE);
_team_config_equal_check ("{ \"a\": 1 }",
"{ \"a\": 1 }",
TRUE,
TRUE);
/* team config */
_team_config_equal_check ("{ }",
"{ \"runner\" : { \"name\" : \"random\"} }",
FALSE,
!vt);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }",
"{ \"runner\" : { \"name\" : \"random\"} }",
FALSE,
!vt);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"} }",
"{ \"runner\" : { \"name\" : \"random\"} }",
FALSE,
TRUE);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"loadbalance\"} }",
"{ \"runner\" : { \"name\" : \"loadbalance\"} }",
FALSE,
TRUE);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"}, \"ports\" : { \"eth0\" : {} } }",
"{ \"runner\" : { \"name\" : \"random\"}, \"ports\" : { \"eth1\" : {} } }",
FALSE,
TRUE);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"lacp\"} }",
"{ \"runner\" : { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }",
FALSE,
!vt);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }",
"{ \"runner\" : { \"name\" : \"roundrobin\", \"tx_hash\" : [ \"eth\", \"ipv4\", \"ipv6\" ] } }",
FALSE,
!vt);
_team_config_equal_check ("{ \"runner\" : { \"name\" : \"lacp\"} }",
"{ \"runner\" : { \"name\" : \"lacp\", \"tx_hash\" : [ \"eth\" ] } }",
FALSE,
!vt);
/* team port config */
_team_config_equal_check ("{ }",
"{ \"link_watch\" : { \"name\" : \"ethtool\"} }",
TRUE,
!vt);
_team_config_equal_check ("{ }",
"{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
TRUE,
TRUE);
_team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"ethtool\"} }",
"{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
TRUE,
!vt);
_team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
"{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
TRUE,
TRUE);
_team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth0\" : {} } }",
"{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth1\" : {} } }",
TRUE,
TRUE);
}
nmtst_json_vt_reset (TRUE);
}
/*****************************************************************************/

View file

@ -5,6 +5,7 @@
#include "nm-default.h"
#include "nm-glib-aux/nm-json.h"
#include "nm-keyfile/nm-keyfile-utils.h"
#include "nm-keyfile/nm-keyfile-internal.h"
#include "nm-simple-connection.h"
@ -627,7 +628,7 @@ test_team_conf_read_invalid (void)
gs_unref_object NMConnection *con = NULL;
NMSettingTeam *s_team;
if (!WITH_JSON_VALIDATION) {
if (!nm_json_vt ()) {
g_test_skip ("team test requires JSON validation");
return;
}

View file

@ -8,6 +8,7 @@
#include <linux/pkt_sched.h>
#include <net/if.h>
#include "nm-glib-aux/nm-json.h"
#include "nm-core-internal.h"
#include "nm-utils.h"
#include "nm-utils-private.h"
@ -32,15 +33,6 @@
/*****************************************************************************/
/* assert that the define is just a plain integer (boolean). */
G_STATIC_ASSERT ( (WITH_JSON_VALIDATION) == 1
|| (WITH_JSON_VALIDATION) == 0);
_nm_unused static const int _with_json_validation = WITH_JSON_VALIDATION;
/*****************************************************************************/
/* converts @dict to a connection. In this case, @dict must be good, without warnings, so that
* NM_SETTING_PARSE_FLAGS_STRICT and NM_SETTING_PARSE_FLAGS_BEST_EFFORT yield the exact same results. */
static NMConnection *
@ -1000,7 +992,7 @@ _test_team_config_sync (const char *team_config,
guint i, j;
gboolean found;
if (!WITH_JSON_VALIDATION) {
if (!nm_json_vt ()) {
g_test_skip ("team test requires JSON validation");
return;
}
@ -1265,7 +1257,7 @@ _test_team_port_config_sync (const char *team_port_config,
guint i, j;
gboolean found;
if (!WITH_JSON_VALIDATION) {
if (!nm_json_vt ()) {
g_test_skip ("team test requires JSON validation");
return;
}
@ -1397,7 +1389,7 @@ _check_team_setting (NMSetting *setting)
: nm_setting_team_get_config (NM_SETTING_TEAM (setting)),
NULL);
if (WITH_JSON_VALIDATION)
if (nm_json_vt ())
nmtst_assert_setting_is_equal (setting, setting2, NM_SETTING_COMPARE_FLAG_EXACT);
g_clear_object (&setting2);

View file

@ -256,6 +256,7 @@ libndp_dep = dependency('libndp')
jansson_dep = dependency('jansson', version: '>= 2.5', required: false)
config_h.set10('WITH_JANSSON', jansson_dep.found())
jansson_msg = 'no'
if jansson_dep.found()
jansson_libdir = jansson_dep.get_pkgconfig_variable('libdir')
res = run_command(find_program('eu-readelf', 'readelf'), '-d', join_paths(jansson_libdir, 'libjansson.so'))
@ -267,6 +268,7 @@ if jansson_dep.found()
endforeach
assert(jansson_soname != '', 'Unable to determine Jansson SONAME')
config_h.set_quoted('JANSSON_SONAME', jansson_soname)
jansson_msg = 'yes (soname: ' + jansson_soname + ')'
endif
libsystemd_dep = dependency('libsystemd', version: '>= 209', required: false)
@ -497,12 +499,6 @@ if enable_teamdctl
assert(libteamdctl_dep.found(), 'You must have libteamdctl installed to build. Use -Dteamdctl=false to disable it')
endif
enable_json_validation = get_option('json_validation')
if enable_json_validation
assert(jansson_dep.found(), 'jansson is needed for team configuration validation. Use -Djson_validation=false to disable it')
endif
config_h.set10('WITH_JSON_VALIDATION', enable_json_validation)
# polkit
enable_polkit = get_option('polkit')
if enable_polkit
@ -1013,6 +1009,7 @@ if enable_ppp
output += ' ' + pppd_path + ' plugins:' + pppd_plugin_dir
endif
output += '\n'
output += ' jansson: ' + jansson_msg + '\n'
output += ' modemmanager-1: ' + enable_modem_manager.to_string() + '\n'
output += ' ofono: ' + enable_ofono.to_string() + '\n'
output += ' concheck: ' + enable_concheck.to_string() + '\n'
@ -1047,7 +1044,6 @@ output += '\n'
output += ' code coverage: ' + get_option('b_coverage').to_string() + '\n'
output += ' LTO: ' + enable_lto.to_string() + '\n'
output += ' Linker garbage collection: ' + enable_ld_gc.to_string() + '\n'
output += ' JSON validation for libnm: ' + enable_json_validation.to_string() + '\n'
output += ' crypto: ' + crypto + '\n'
output += ' sanitizers: ' + get_option('b_sanitize') + '\n'
output += ' Mozilla Public Suffix List: ' + enable_libpsl.to_string() + '\n'

View file

@ -69,6 +69,5 @@ option('valgrind', type: 'array', value: ['no'], description: 'Use valgrind to m
option('valgrind_suppressions', type: 'string', value: '', description: 'Use specific valgrind suppression file')
option('ld_gc', type: 'boolean', value: true, description: 'Enable garbage collection of unused symbols on linking')
option('libpsl', type: 'boolean', value: true, description: 'Link against libpsl')
option('json_validation', type: 'boolean', value: true, description: 'Enable JSON validation in libnm')
option('crypto', type: 'combo', choices: ['nss', 'gnutls'], value: 'nss', description: 'Cryptography library to use for certificate and key operations')
option('qt', type: 'boolean', value: true, description: 'enable Qt examples')

View file

@ -15,6 +15,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include "nm-glib-aux/nm-json.h"
#include "nm-utils.h"
#include "nm-setting-connection.h"
#include "nm-setting-wired.h"
@ -9129,7 +9130,7 @@ test_read_team_master_invalid (gconstpointer user_data)
gs_free_error GError *error = NULL;
gs_unref_object NMConnection *connection = NULL;
if (WITH_JSON_VALIDATION) {
if (nm_json_vt ()) {
_connection_from_file_fail (PATH_NAME, NULL, TYPE_ETHERNET, &error);
g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY);