core: add main.auth-polkit option "root-only"

We always build with PolicyKit support enabled, because it has no
additional dependencies, beside some D-Bus calls.

However, in NetworkManager.conf the user could configure
"main.auth-polkit" to disable PolicyKit. However, previously it would
only allow to disable PolicyKit while granting access to all users.

I think it's useful to have an option that disables PolicyKit and grants
access only to root. I think we should not go too far in implementing
our own authorization mechanisms beside PolicyKit (e.g. you cannot
disable PolicyKit and grant access based on group membership of the
user). However, disabling PolicyKit can be useful sometimes, and it's
simple to implement a "root-only" setup.

Note one change is that when NetworkManager now runs without a D-Bus
connection (in initrd), it would deny all non-root requests. Previously
it would grant access. I think there should be little difference in
practice, because if we have no D-Bus we also don't have any requests to
authenticate.
This commit is contained in:
Thomas Haller 2019-12-10 08:51:03 +01:00
parent c21c6bc0be
commit 6d7446e52f
9 changed files with 125 additions and 36 deletions

View file

@ -614,18 +614,18 @@ AM_CONDITIONAL(WITH_JSON_VALIDATION, test "${enable_json_validation}" != "no")
# default configuration for main.auth-polkit. User can always enable/disable polkit
# authorization via config.
AC_ARG_ENABLE(polkit,
AS_HELP_STRING([--enable-polkit=yes|no],
AS_HELP_STRING([--enable-polkit=yes|no|root-only],
[set default value for auth-polkit configuration option. This value can be overwritten by NM configuration. 'disabled' is an alias for 'no']),
[enable_polkit=${enableval}], [enable_polkit=yes])
if (test "${enable_polkit}" != "no" -a "${enable_polkit}" != "disabled"); then
if test "${enable_polkit}" == "root-only" ; then
enable_polkit='root-only'
elif test "${enable_polkit}" != "no" -a "${enable_polkit}" != "disabled" ; then
enable_polkit=true
AC_DEFINE(NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT, "true", [The default value of the auth-polkit configuration option])
AC_SUBST(NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_TEXT, true)
else
enable_polkit=false
AC_DEFINE(NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT, "false", [The default value of the auth-polkit configuration option])
AC_SUBST(NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_TEXT, false)
fi
AC_DEFINE_UNQUOTED(NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT, "$enable_polkit", [The default value of the auth-polkit configuration option])
AC_SUBST(NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_TEXT, "$enable_polkit")
PKG_CHECK_MODULES(POLKIT, [polkit-agent-1 >= 0.97], [have_pk_agent=yes],[have_pk_agent=no])
AC_ARG_ENABLE(polkit-agent,

View file

@ -158,8 +158,11 @@ plugins-=remove-me
<varlistentry>
<term><varname>auth-polkit</varname></term>
<listitem><para>Whether the system uses PolicyKit for authorization.
If <literal>false</literal>, all requests will be allowed. If
<literal>true</literal>, non-root requests are authorized using PolicyKit.
If <literal>true</literal>, non-root requests are authorized using PolicyKit.
Requests from root (user ID zero) are always granted without asking PolicyKit.
If <literal>false</literal>, all requests will be allowed and PolicyKit is
not used. If set to <literal>root-only</literal> PolicyKit is not used and
all requests except root are denied.
The default value is <literal>&NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_TEXT;</literal>.
</para></listitem>
</varlistentry>

View file

@ -14,7 +14,7 @@ option('session_tracking_consolekit', type: 'boolean', value: true, description:
option('session_tracking', type: 'combo', choices: ['systemd', 'elogind', 'no'], value: 'systemd', description: 'Compatibility option to choose one session tracking module')
option('suspend_resume', type: 'combo', choices: ['upower', 'systemd', 'elogind', 'consolekit', 'auto'], value: 'auto', description: 'Build NetworkManager with specific suspend/resume support')
option('polkit', type: 'boolean', value: true, description: 'User auth-polkit configuration option.')
option('config_auth_polkit_default', type: 'combo', choices: ['default', 'true', 'false'], value: 'default', description: 'Default value for configuration main.auth-polkit.')
option('config_auth_polkit_default', type: 'combo', choices: ['default', 'true', 'false', 'root-only'], value: 'default', description: 'Default value for configuration main.auth-polkit.')
option('modify_system', type: 'boolean', value: false, description: 'Allow users to modify system connections')
option('polkit_agent', type: 'boolean', value: false, description: 'enable polkit agent for clients')
option('selinux', type: 'boolean', value: true, description: 'Build with SELinux')

View file

@ -410,10 +410,7 @@ main (int argc, char *argv[])
NM_UTILS_KEEP_ALIVE (config, nm_netns_get (), "NMConfig-depends-on-NMNetns");
nm_auth_manager_setup (nm_config_data_get_value_boolean (nm_config_get_data_orig (config),
NM_CONFIG_KEYFILE_GROUP_MAIN,
NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT,
NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_BOOL));
nm_auth_manager_setup (nm_config_data_get_main_auth_polkit (nm_config_get_data_orig (config)));
manager = nm_manager_setup ();

View file

@ -42,7 +42,7 @@ typedef struct {
guint changed_signal_id;
bool disposing:1;
bool shutting_down:1;
bool polkit_enabled_construct_only:1;
NMAuthPolkitMode auth_polkit_mode:3;
} NMAuthManagerPrivate;
struct _NMAuthManager {
@ -118,6 +118,7 @@ struct _NMAuthManagerCallId {
gpointer user_data;
guint64 call_numid;
guint idle_id;
bool idle_is_authorized:1;
};
#define cancellation_id_to_str_a(call_numid) \
@ -256,9 +257,10 @@ static gboolean
_call_on_idle (gpointer user_data)
{
NMAuthManagerCallId *call_id = user_data;
gboolean is_authorized = TRUE;
gboolean is_authorized;
gboolean is_challenge = FALSE;
is_authorized = call_id->idle_is_authorized;
call_id->idle_id = 0;
_LOG2T (call_id, "completed: authorized=%d, challenge=%d (simulated)",
@ -312,22 +314,25 @@ nm_auth_manager_check_authorization (NMAuthManager *self,
call_id = g_slice_new (NMAuthManagerCallId);
*call_id = (NMAuthManagerCallId) {
.self = g_object_ref (self),
.callback = callback,
.user_data = user_data,
.call_numid = ++priv->call_numid_counter,
.self = g_object_ref (self),
.callback = callback,
.user_data = user_data,
.call_numid = ++priv->call_numid_counter,
.idle_is_authorized = TRUE,
};
c_list_link_tail (&priv->calls_lst_head, &call_id->calls_lst);
if (!priv->dbus_connection) {
_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding due to polkit authorization disabled)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
call_id->idle_id = g_idle_add (_call_on_idle, call_id);
} else if (nm_auth_subject_is_internal (subject)) {
if (nm_auth_subject_is_internal (subject)) {
_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding for internal request)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
call_id->idle_id = g_idle_add (_call_on_idle, call_id);
} else if (nm_auth_subject_get_unix_process_uid (subject) == 0) {
_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (succeeding for root)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)));
call_id->idle_id = g_idle_add (_call_on_idle, call_id);
} else if (priv->auth_polkit_mode != NM_AUTH_POLKIT_MODE_USE_POLKIT) {
_LOG2T (call_id, "CheckAuthorization(%s), subject=%s (PolicyKit disabled and always %s authorization to non-root user)", action_id, nm_auth_subject_to_string (subject, subject_buf, sizeof (subject_buf)),
priv->auth_polkit_mode == NM_AUTH_POLKIT_MODE_ALLOW_ALL ? "grant" : "deny");
call_id->idle_is_authorized = (priv->auth_polkit_mode == NM_AUTH_POLKIT_MODE_ALLOW_ALL);
call_id->idle_id = g_idle_add (_call_on_idle, call_id);
} else {
GVariant *parameters;
GVariantBuilder builder;
@ -461,11 +466,17 @@ static void
set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE ((NMAuthManager *) object);
int v_int;
switch (prop_id) {
case PROP_POLKIT_ENABLED:
/* construct-only */
priv->polkit_enabled_construct_only = !!g_value_get_boolean (value);
v_int = g_value_get_int (value);
g_return_if_fail (NM_IN_SET (v_int, NM_AUTH_POLKIT_MODE_ROOT_ONLY,
NM_AUTH_POLKIT_MODE_ALLOW_ALL,
NM_AUTH_POLKIT_MODE_USE_POLKIT));
priv->auth_polkit_mode = v_int;
nm_assert (priv->auth_polkit_mode == v_int);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -481,6 +492,7 @@ nm_auth_manager_init (NMAuthManager *self)
NMAuthManagerPrivate *priv = NM_AUTH_MANAGER_GET_PRIVATE (self);
c_list_init (&priv->calls_lst_head);
priv->auth_polkit_mode = NM_AUTH_POLKIT_MODE_ROOT_ONLY;
}
static void
@ -493,8 +505,11 @@ constructed (GObject *object)
G_OBJECT_CLASS (nm_auth_manager_parent_class)->constructed (object);
if (!priv->polkit_enabled_construct_only) {
create_message = "polkit disabled";
if (priv->auth_polkit_mode != NM_AUTH_POLKIT_MODE_USE_POLKIT) {
if (priv->auth_polkit_mode == NM_AUTH_POLKIT_MODE_ROOT_ONLY)
create_message = "polkit disabled, root-only";
else
create_message = "polkit disabled, allow-all";
goto out;
}
@ -503,7 +518,8 @@ constructed (GObject *object)
if (!priv->dbus_connection) {
/* This warrants an info level message. */
logl = LOGL_INFO;
create_message = "D-Bus connection not available. Polkit is disabled and all requests are authenticated.";
create_message = "D-Bus connection not available. Polkit is disabled and only root will be authorized.";
priv->auth_polkit_mode = NM_AUTH_POLKIT_MODE_ROOT_ONLY;
goto out;
}
@ -527,14 +543,17 @@ out:
}
NMAuthManager *
nm_auth_manager_setup (gboolean polkit_enabled)
nm_auth_manager_setup (NMAuthPolkitMode auth_polkit_mode)
{
NMAuthManager *self;
g_return_val_if_fail (!singleton_instance, singleton_instance);
nm_assert (NM_IN_SET (auth_polkit_mode, NM_AUTH_POLKIT_MODE_ROOT_ONLY,
NM_AUTH_POLKIT_MODE_ALLOW_ALL,
NM_AUTH_POLKIT_MODE_USE_POLKIT));
self = g_object_new (NM_TYPE_AUTH_MANAGER,
NM_AUTH_MANAGER_POLKIT_ENABLED, polkit_enabled,
NM_AUTH_MANAGER_POLKIT_ENABLED, (int) auth_polkit_mode,
NULL);
_LOGD ("set instance");
@ -579,11 +598,11 @@ nm_auth_manager_class_init (NMAuthManagerClass *klass)
object_class->dispose = dispose;
obj_properties[PROP_POLKIT_ENABLED] =
g_param_spec_boolean (NM_AUTH_MANAGER_POLKIT_ENABLED, "", "",
FALSE,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_param_spec_int (NM_AUTH_MANAGER_POLKIT_ENABLED, "", "",
NM_AUTH_POLKIT_MODE_ROOT_ONLY, NM_AUTH_POLKIT_MODE_USE_POLKIT, NM_AUTH_POLKIT_MODE_USE_POLKIT,
G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);

View file

@ -7,6 +7,7 @@
#define NM_AUTH_MANAGER_H
#include "nm-auth-subject.h"
#include "nm-config-data.h"
/*****************************************************************************/
@ -55,7 +56,7 @@ typedef struct _NMAuthManagerClass NMAuthManagerClass;
GType nm_auth_manager_get_type (void);
NMAuthManager *nm_auth_manager_setup (gboolean polkit_enabled);
NMAuthManager *nm_auth_manager_setup (NMAuthPolkitMode auth_polkit_mode);
NMAuthManager *nm_auth_manager_get (void);
void nm_auth_manager_force_shutdown (NMAuthManager *self);

View file

@ -386,6 +386,52 @@ _nm_config_data_get_keyfile_user (const NMConfigData *self)
/*****************************************************************************/
static NMAuthPolkitMode
nm_auth_polkit_mode_from_string (const char *str)
{
int as_bool;
if (!str)
return NM_AUTH_POLKIT_MODE_UNKNOWN;
if (nm_streq (str, "root-only"))
return NM_AUTH_POLKIT_MODE_ROOT_ONLY;
as_bool = _nm_utils_ascii_str_to_bool (str, -1);
if (as_bool != -1) {
return as_bool
? NM_AUTH_POLKIT_MODE_USE_POLKIT
: NM_AUTH_POLKIT_MODE_ALLOW_ALL;
}
return NM_AUTH_POLKIT_MODE_UNKNOWN;
}
NMAuthPolkitMode
nm_config_data_get_main_auth_polkit (const NMConfigData *self)
{
NMAuthPolkitMode auth_polkit_mode;
const char *str;
str = nm_config_data_get_value (self,
NM_CONFIG_KEYFILE_GROUP_MAIN,
NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT,
NM_CONFIG_GET_VALUE_STRIP
| NM_CONFIG_GET_VALUE_NO_EMPTY);
auth_polkit_mode = nm_auth_polkit_mode_from_string (str);
if (auth_polkit_mode == NM_AUTH_POLKIT_MODE_UNKNOWN) {
auth_polkit_mode = nm_auth_polkit_mode_from_string (NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT);
if (auth_polkit_mode == NM_AUTH_POLKIT_MODE_UNKNOWN) {
nm_assert_not_reached ();
auth_polkit_mode = NM_AUTH_POLKIT_MODE_ROOT_ONLY;
}
}
return auth_polkit_mode;
}
/*****************************************************************************/
/**
* nm_config_data_get_groups:
* @self: the #NMConfigData instance

View file

@ -6,6 +6,28 @@
#ifndef NM_CONFIG_DATA_H
#define NM_CONFIG_DATA_H
/*****************************************************************************/
typedef enum {
/* an invalid mode. */
NM_AUTH_POLKIT_MODE_UNKNOWN,
/* don't use PolicyKit, but only allow root user (uid 0). */
NM_AUTH_POLKIT_MODE_ROOT_ONLY,
/* don't use PolicyKit, but allow all requests. */
NM_AUTH_POLKIT_MODE_ALLOW_ALL,
/* use PolicyKit to authorize requests. Root user (uid 0) always
* gets a free pass, without consulting PolicyKit. If PolicyKit is not
* running, authorization will fail for non root users. */
NM_AUTH_POLKIT_MODE_USE_POLKIT,
} NMAuthPolkitMode;
/*****************************************************************************/
#define NM_TYPE_CONFIG_DATA (nm_config_data_get_type ())
#define NM_CONFIG_DATA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_CONFIG_DATA, NMConfigData))
#define NM_CONFIG_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_CONFIG_DATA, NMConfigDataClass))
@ -132,6 +154,8 @@ const char *nm_config_data_get_connectivity_response (const NMConfigData *config
int nm_config_data_get_autoconnect_retries_default (const NMConfigData *config_data);
NMAuthPolkitMode nm_config_data_get_main_auth_polkit (const NMConfigData *config_data);
const char *const*nm_config_data_get_no_auto_default (const NMConfigData *config_data);
gboolean nm_config_data_get_no_auto_default_for_device (const NMConfigData *self, NMDevice *device);

View file

@ -219,7 +219,6 @@ extern char *_nm_config_match_env;
#define NM_CONFIG_DEVICE_STATE_DIR ""NMRUNDIR"/devices"
#define NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_BOOL (nm_streq (""NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT, "true"))
#define NM_CONFIG_DEFAULT_LOGGING_AUDIT_BOOL (nm_streq (""NM_CONFIG_DEFAULT_LOGGING_AUDIT, "true"))
typedef enum {