2104 lines
76 KiB
C
2104 lines
76 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
|
||
*
|
||
* Copyright (C) 2020-2023 Marco Trevisan <marco.trevisan@canonical.com>
|
||
*
|
||
* This program is free software; you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation; either version 2, or (at your option)
|
||
* any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program; if not, write to the Free Software
|
||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||
* 02110-1301, USA.
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <ctype.h>
|
||
#include <grp.h>
|
||
#include <locale.h>
|
||
#include <pwd.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <sysexits.h>
|
||
|
||
#include <glib.h>
|
||
#include <glib/gi18n.h>
|
||
#include <gio/gio.h>
|
||
|
||
#include "gdm-common.h"
|
||
|
||
#define DCONF_SYSCONFIG_PROFILES_PATH DCONF_SYSCONFIG "dconf/profile"
|
||
#define DCONF_SYSCONFIG_DB_PATH DCONF_SYSCONFIG "dconf/db"
|
||
#define DCONF_SYSTEM_DB_PREFIX "system-db:"
|
||
#define DCONF_SYSTEM_DB_DEFAULT_NAME "gdm_auth_config"
|
||
|
||
#define GDM_DEFAULT_DCONF_PROFILE \
|
||
DCONF_PROFILES_PATH "/" GDM_DCONF_PROFILE
|
||
#define GDM_CONFIG_DCONF_PROFILE \
|
||
DCONF_SYSCONFIG_PROFILES_PATH "/" GDM_DCONF_PROFILE
|
||
|
||
#define GDM_CONFIG_DCONF_DB_NAME GDM_DCONF_PROFILE
|
||
#define GDM_CONFIG_DCONF_DB DCONF_SYSTEM_DB_PREFIX GDM_CONFIG_DCONF_DB_NAME
|
||
|
||
#define GDM_CONFIG_DCONF_OVERRIDE_NAME "01_gdm-config"
|
||
#define GDM_CONFIG_DCONF_LOCKS_NAME GDM_CONFIG_DCONF_OVERRIDE_NAME "-locks"
|
||
|
||
#define LOGIN_SCHEMA "org.gnome.login-screen"
|
||
|
||
#define GSD_SC_SCHEMA "org.gnome.settings-daemon.peripherals.smartcard"
|
||
#define GSD_SC_REMOVAL_ACTION_KEY "removal-action"
|
||
|
||
#define PASSWORD_KEY "enable-password-authentication"
|
||
#define FINGERPRINT_KEY "enable-fingerprint-authentication"
|
||
#define SMARTCARD_KEY "enable-smartcard-authentication"
|
||
|
||
static int opt_enable = -1;
|
||
static int opt_disable = -1;
|
||
static int opt_required = -1;
|
||
static gboolean opt_cmd_help = FALSE;
|
||
static gboolean opt_debug = FALSE;
|
||
static gboolean opt_verbose = FALSE;
|
||
static const char *opt_removal_action = NULL;
|
||
|
||
typedef enum {
|
||
COMMAND_HELP,
|
||
COMMAND_SHOW,
|
||
COMMAND_PASSWORD,
|
||
COMMAND_FINGERPRINT,
|
||
COMMAND_SMARTCARD,
|
||
COMMAND_RESET,
|
||
COMMAND_UNKNOWN,
|
||
} GdmConfigCommand;
|
||
|
||
typedef enum {
|
||
AUTH_PASSWORD = COMMAND_PASSWORD,
|
||
AUTH_FINGERPRINT = COMMAND_FINGERPRINT,
|
||
AUTH_SMARTCARD = COMMAND_SMARTCARD,
|
||
AUTH_NONE = COMMAND_UNKNOWN,
|
||
} GdmAuthType;
|
||
|
||
typedef enum {
|
||
ACTION_UNSET,
|
||
ACTION_INVALID,
|
||
ACTION_ENABLED,
|
||
ACTION_DISABLED,
|
||
ACTION_REQUIRED,
|
||
} GdmAuthAction;
|
||
|
||
typedef struct
|
||
{
|
||
GdmConfigCommand config_command;
|
||
GPtrArray *args;
|
||
} OptionData;
|
||
|
||
typedef struct _GdmConfigCommandHandler GdmConfigCommandHandler;
|
||
typedef gboolean (*CommandHandlerFunc) (GdmConfigCommand, GError **);
|
||
|
||
typedef struct _GdmConfigCommandHandler {
|
||
GPtrArray *entries;
|
||
GOptionParseFunc post_parse_func;
|
||
CommandHandlerFunc handler_func;
|
||
CommandHandlerFunc options_handler_func;
|
||
gpointer data;
|
||
GDestroyNotify data_destroy;
|
||
} GdmConfigCommandHandler;
|
||
|
||
static const GOptionEntry generic_entries[] =
|
||
{
|
||
{
|
||
"help", 'h', 0, G_OPTION_ARG_NONE, &opt_cmd_help,
|
||
N_("Show command help"), NULL
|
||
},
|
||
{
|
||
"verbose", 'v', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &opt_verbose,
|
||
N_("Show verbose output"), NULL
|
||
},
|
||
{
|
||
"debug", 'u', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &opt_debug,
|
||
N_("Show debug output"), NULL
|
||
},
|
||
{ NULL }
|
||
};
|
||
|
||
static const GOptionEntry toggle_entries[] =
|
||
{
|
||
{
|
||
"enable", 'e', 0, G_OPTION_ARG_NONE, &opt_enable,
|
||
N_("Enable the authentication method"), NULL
|
||
},
|
||
{
|
||
"disable", 'd', 0, G_OPTION_ARG_NONE, &opt_disable,
|
||
N_("Disable the authentication method"), NULL
|
||
},
|
||
{ NULL }
|
||
};
|
||
|
||
static GOptionEntry smartcard_entries[] =
|
||
{
|
||
{
|
||
"required", 'r', 0, G_OPTION_ARG_NONE, &opt_required,
|
||
N_("Require the authentication method"), NULL
|
||
},
|
||
{
|
||
"removal-action", 'a', 0, G_OPTION_ARG_STRING, &opt_removal_action,
|
||
N_("Action to perform on smartcard removal"), NULL,
|
||
},
|
||
{ NULL }
|
||
};
|
||
|
||
static GOptionEntry reset_entries[] =
|
||
{
|
||
{
|
||
"yes", 'y', 0, G_OPTION_ARG_NONE, &opt_required,
|
||
N_("Assume yes to any answer"), NULL
|
||
},
|
||
{ NULL }
|
||
};
|
||
|
||
static const char* SC_REMOVAL_ACTIONS[] = {
|
||
"none", /* GSD_SMARTCARD_REMOVAL_ACTION_NONE */
|
||
"lock-screen", /* GSD_SMARTCARD_REMOVAL_ACTION_LOCK_SCREEN */
|
||
"force-logout", /* GSD_SMARTCARD_REMOVAL_ACTION_FORCE_LOGOUT */
|
||
"user-defined",
|
||
NULL,
|
||
};
|
||
|
||
static gboolean handle_password (GdmConfigCommand, GError **);
|
||
static gboolean handle_fingerprint (GdmConfigCommand, GError **);
|
||
static gboolean handle_smartcard (GdmConfigCommand, GError **);
|
||
static gboolean handle_smartcard_options (GdmConfigCommand, GError **);
|
||
static gboolean handle_show (GdmConfigCommand, GError **);
|
||
static gboolean handle_reset (GdmConfigCommand, GError **);
|
||
|
||
static GdmAuthAction
|
||
get_requested_action (void)
|
||
{
|
||
if (opt_enable == TRUE && opt_disable == TRUE) {
|
||
return ACTION_INVALID;
|
||
} else if (opt_required == TRUE && opt_disable == TRUE) {
|
||
return ACTION_INVALID;
|
||
}
|
||
|
||
if (opt_required == TRUE)
|
||
return ACTION_REQUIRED;
|
||
|
||
if (opt_enable == TRUE)
|
||
return ACTION_ENABLED;
|
||
|
||
if (opt_disable == TRUE)
|
||
return ACTION_DISABLED;
|
||
|
||
return ACTION_UNSET;
|
||
}
|
||
|
||
static gboolean
|
||
smartcard_option_is_valid (const char *option_key,
|
||
const char *option_value)
|
||
{
|
||
if (!option_key || !option_value)
|
||
return FALSE;
|
||
|
||
if (g_str_equal (GSD_SC_REMOVAL_ACTION_KEY, option_key)) {
|
||
return g_strv_contains (SC_REMOVAL_ACTIONS, option_value);
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static GdmConfigCommand
|
||
parse_config_command (const char *command) {
|
||
if (g_str_equal (command, "help")) {
|
||
return COMMAND_HELP;
|
||
} else if (g_str_equal (command, "password")) {
|
||
return COMMAND_PASSWORD;
|
||
} else if (g_str_equal (command, "fingerprint")) {
|
||
return COMMAND_FINGERPRINT;
|
||
} else if (g_str_equal (command, "smartcard")) {
|
||
return COMMAND_SMARTCARD;
|
||
} else if (g_str_equal (command, "show")) {
|
||
return COMMAND_SHOW;
|
||
} else if (g_str_equal (command, "reset")) {
|
||
return COMMAND_RESET;
|
||
}
|
||
|
||
return COMMAND_UNKNOWN;
|
||
}
|
||
|
||
const char *
|
||
config_command_to_string (GdmConfigCommand config_command)
|
||
{
|
||
switch (config_command) {
|
||
case COMMAND_HELP:
|
||
return "help";
|
||
case COMMAND_PASSWORD:
|
||
return "password";
|
||
case COMMAND_FINGERPRINT:
|
||
return "fingerprint";
|
||
case COMMAND_SMARTCARD:
|
||
return "smartcard";
|
||
case COMMAND_RESET:
|
||
return "reset";
|
||
case COMMAND_SHOW:
|
||
return "show";
|
||
case COMMAND_UNKNOWN:
|
||
return "unknown";
|
||
}
|
||
|
||
g_assert_not_reached ();
|
||
}
|
||
|
||
static const char *
|
||
get_command_title (GdmConfigCommand config_command)
|
||
{
|
||
switch (config_command) {
|
||
case COMMAND_PASSWORD:
|
||
return _("Configure Password Authentication.");
|
||
case COMMAND_FINGERPRINT:
|
||
return _("Configure Fingerprint Authentication.");
|
||
case COMMAND_SMARTCARD:
|
||
return _("Configure Smart Card Authentication.");
|
||
case COMMAND_RESET:
|
||
return _("Reset the GDM Authentication configuration.");
|
||
case COMMAND_SHOW:
|
||
return _("Show GDM Authentication configuration.");
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
static const char *
|
||
get_command_group_title (GdmConfigCommand config_command)
|
||
{
|
||
switch (config_command) {
|
||
case COMMAND_PASSWORD:
|
||
return _("Password options");
|
||
case COMMAND_FINGERPRINT:
|
||
return _("Fingerprint options");
|
||
case COMMAND_SMARTCARD:
|
||
return _("Smart Card options");
|
||
case COMMAND_RESET:
|
||
return _("Reset options");
|
||
case COMMAND_SHOW:
|
||
return _("Show options");
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
static GdmAuthType
|
||
config_command_to_auth_type (GdmConfigCommand config_command)
|
||
{
|
||
switch (config_command) {
|
||
case COMMAND_PASSWORD:
|
||
case COMMAND_SMARTCARD:
|
||
case COMMAND_FINGERPRINT:
|
||
return (GdmAuthType) config_command;
|
||
default:
|
||
return AUTH_NONE;
|
||
}
|
||
}
|
||
|
||
const char *
|
||
auth_type_to_string (GdmAuthType auth_type)
|
||
{
|
||
return config_command_to_string ((GdmConfigCommand) auth_type);
|
||
}
|
||
|
||
const char *
|
||
get_pam_module_missing_error (GdmAuthType auth_type)
|
||
{
|
||
switch (auth_type) {
|
||
case AUTH_PASSWORD:
|
||
return _("No PAM module available for Password authentication");
|
||
case AUTH_SMARTCARD:
|
||
return _("No PAM module available for Smart Card authentication");
|
||
case AUTH_FINGERPRINT:
|
||
return _("No PAM module available for Fingerprint authentication");
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
const char *
|
||
auth_type_to_option_key (GdmAuthType auth_type)
|
||
{
|
||
switch (auth_type) {
|
||
case AUTH_PASSWORD:
|
||
return PASSWORD_KEY;
|
||
case AUTH_FINGERPRINT:
|
||
return FINGERPRINT_KEY;
|
||
case AUTH_SMARTCARD:
|
||
return SMARTCARD_KEY;
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
toggle_option_check (GOptionContext *context,
|
||
GOptionGroup *group,
|
||
gpointer user_data,
|
||
GError **error)
|
||
{
|
||
OptionData *data = user_data;
|
||
|
||
if (data->args->len < 2) {
|
||
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
|
||
_("“%s” needs at least one parameter"),
|
||
config_command_to_string (data->config_command));
|
||
return FALSE;
|
||
}
|
||
|
||
if (get_requested_action () == ACTION_INVALID) {
|
||
|
||
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
|
||
/* TRANSLATORS: “command” can't be enabled... */
|
||
_("“%s” can't be enabled and disabled at the same time"),
|
||
config_command_to_string (data->config_command));
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
smartcard_option_check (GOptionContext *context,
|
||
GOptionGroup *group,
|
||
gpointer user_data,
|
||
GError **error)
|
||
{
|
||
OptionData *data = user_data;
|
||
|
||
if (!toggle_option_check (context, group, user_data, error))
|
||
return FALSE;
|
||
|
||
if (opt_removal_action &&
|
||
!smartcard_option_is_valid (GSD_SC_REMOVAL_ACTION_KEY, opt_removal_action)) {
|
||
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
|
||
/* TRANSLATORS: option is not a valid command “option-key” value */
|
||
_("“%s” is not a valid %s “%s” value"),
|
||
opt_removal_action,
|
||
config_command_to_string (data->config_command),
|
||
GSD_SC_REMOVAL_ACTION_KEY);
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
gdm_config_command_handler_free (GdmConfigCommandHandler *command_handler)
|
||
{
|
||
if (command_handler->data_destroy)
|
||
command_handler->data_destroy (command_handler->data);
|
||
|
||
g_clear_pointer (&command_handler->entries, g_ptr_array_unref);
|
||
g_free (command_handler);
|
||
}
|
||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdmConfigCommandHandler, gdm_config_command_handler_free);
|
||
|
||
static GdmConfigCommandHandler *
|
||
config_command_get_handler (GdmConfigCommand config_command)
|
||
{
|
||
g_autoptr (GdmConfigCommandHandler) cmd_entries = NULL;
|
||
|
||
cmd_entries = g_new0 (GdmConfigCommandHandler, 1);
|
||
cmd_entries->entries = g_ptr_array_new ();
|
||
|
||
switch (config_command) {
|
||
case COMMAND_PASSWORD:
|
||
case COMMAND_FINGERPRINT:
|
||
case COMMAND_SMARTCARD:
|
||
g_ptr_array_add (cmd_entries->entries, (gpointer) toggle_entries);
|
||
cmd_entries->post_parse_func = toggle_option_check;
|
||
break;
|
||
case COMMAND_SHOW:
|
||
cmd_entries->handler_func = handle_show;
|
||
return g_steal_pointer (&cmd_entries);
|
||
case COMMAND_RESET:
|
||
g_ptr_array_add (cmd_entries->entries, (gpointer) reset_entries);
|
||
cmd_entries->handler_func = handle_reset;
|
||
return g_steal_pointer (&cmd_entries);
|
||
case COMMAND_HELP:
|
||
case COMMAND_UNKNOWN:
|
||
return NULL;
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
|
||
if (config_command == COMMAND_PASSWORD) {
|
||
cmd_entries->handler_func = handle_password;
|
||
} else if (config_command == COMMAND_FINGERPRINT) {
|
||
cmd_entries->handler_func = handle_fingerprint;
|
||
} else if (config_command == COMMAND_SMARTCARD) {
|
||
g_autofree char *removal_args = NULL;
|
||
int i;
|
||
|
||
removal_args = g_strjoinv ("|", (GStrv) SC_REMOVAL_ACTIONS);
|
||
for (i = 0; smartcard_entries[i].long_name; ++i) {
|
||
if (!g_str_equal (smartcard_entries[i].long_name, "removal-action"))
|
||
continue;
|
||
|
||
smartcard_entries[i].arg_description = removal_args;
|
||
|
||
cmd_entries->data = g_steal_pointer (&removal_args);
|
||
cmd_entries->data_destroy = g_free;
|
||
}
|
||
g_ptr_array_add (cmd_entries->entries, smartcard_entries);
|
||
cmd_entries->post_parse_func = smartcard_option_check;
|
||
cmd_entries->handler_func = handle_smartcard;
|
||
cmd_entries->options_handler_func = handle_smartcard_options;
|
||
} else {
|
||
g_assert_not_reached ();
|
||
}
|
||
|
||
return g_steal_pointer (&cmd_entries);
|
||
}
|
||
|
||
static void
|
||
config_command_handler_add_to_group (GdmConfigCommandHandler *command_handler,
|
||
GOptionGroup *group)
|
||
{
|
||
guint i;
|
||
|
||
if (!command_handler || !command_handler->entries)
|
||
return;
|
||
|
||
for (i = 0; i < command_handler->entries->len; ++i) {
|
||
const GOptionEntry *entries;
|
||
|
||
entries = g_ptr_array_index (command_handler->entries, i);
|
||
g_option_group_add_entries (group, entries);
|
||
}
|
||
|
||
g_option_group_set_parse_hooks (group, NULL,
|
||
command_handler->post_parse_func);
|
||
}
|
||
|
||
static gboolean
|
||
switch_to_gdm_user (GError **error) {
|
||
struct passwd *pwent;
|
||
|
||
/* We don't care about forking here, as we need to do just one action
|
||
* so, once we switch, there's no point of return */
|
||
|
||
gdm_get_pwent_for_name (GDM_USERNAME, &pwent);
|
||
|
||
if (pwent == NULL) {
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||
_("Failed to switch to %s user"), GDM_USERNAME);
|
||
return FALSE;
|
||
}
|
||
|
||
g_debug ("Switching to %s user (uid:gid) %d:%d",
|
||
GDM_USERNAME, pwent->pw_uid, pwent->pw_gid);
|
||
|
||
if (setgid (pwent->pw_gid) < 0) {
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
_("Couldn’t set groupid to %d"), pwent->pw_gid);
|
||
return FALSE;
|
||
}
|
||
|
||
if (initgroups (pwent->pw_name, pwent->pw_gid) < 0) {
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
_("initgroups () failed for %s"), pwent->pw_name);
|
||
return FALSE;
|
||
}
|
||
|
||
if (setuid (pwent->pw_uid) < 0) {
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||
_("Couldn’t set userid to %u"), pwent->pw_uid);
|
||
return FALSE;
|
||
}
|
||
|
||
g_unsetenv ("XDG_RUNTIME_DIR");
|
||
g_unsetenv ("DISPLAY");
|
||
|
||
if (pwent->pw_dir != NULL && pwent->pw_dir[0] != '\0') {
|
||
g_setenv ("HOME", pwent->pw_dir, TRUE);
|
||
g_setenv ("PWD", GDM_WORKING_DIR, TRUE);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static GPtrArray *
|
||
read_file_contents_to_array (const char *file_path,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GPtrArray) array = NULL;
|
||
g_autofree char *content = NULL;
|
||
|
||
array = g_ptr_array_new_with_free_func (g_free);
|
||
|
||
if (!g_file_get_contents (file_path, &content, NULL, error)) {
|
||
return NULL;
|
||
}
|
||
|
||
if (content) {
|
||
char **lines = g_strsplit (content, "\n", -1);
|
||
int i;
|
||
|
||
if (lines && *lines) {
|
||
array->len = g_strv_length (lines) + 1;
|
||
array->pdata = (gpointer *) g_steal_pointer (&lines);
|
||
}
|
||
|
||
/* Strip the trailing empty and NULL lines */
|
||
for (i = array->len - 1; i >= 0; i--) {
|
||
char *line = g_ptr_array_index (array, i);
|
||
|
||
if (line && (g_ascii_isgraph (line[0]) || strlen (line) > 1))
|
||
break;
|
||
|
||
g_ptr_array_remove_index (array, i);
|
||
}
|
||
}
|
||
|
||
return g_steal_pointer (&array);
|
||
}
|
||
|
||
static gboolean
|
||
write_array_to_file (GPtrArray *array,
|
||
char *file_path,
|
||
GError **error)
|
||
{
|
||
g_autoptr (GPtrArray) array_copy = NULL;
|
||
g_autofree char *contents = NULL;
|
||
|
||
array_copy = g_ptr_array_copy (array, NULL, NULL);
|
||
g_ptr_array_set_free_func (array_copy, NULL);
|
||
|
||
if (array->len) {
|
||
/* Ensure final new line */
|
||
if (!g_str_equal (g_ptr_array_index (array_copy, array->len - 1), ""))
|
||
g_ptr_array_add (array_copy, "");
|
||
}
|
||
|
||
g_ptr_array_add (array_copy, NULL);
|
||
|
||
contents = g_strjoinv ("\n", (char **) array_copy->pdata);
|
||
|
||
return g_file_set_contents (file_path, contents, -1, error);
|
||
}
|
||
|
||
GPtrArray *
|
||
build_distro_hook_arguments (const char *distro_hook,
|
||
GdmConfigCommand config_command,
|
||
GPtrArray *command_args)
|
||
{
|
||
g_autoptr(GPtrArray) call_args = NULL;
|
||
GdmAuthAction action;
|
||
|
||
action = get_requested_action ();
|
||
call_args = g_ptr_array_new ();
|
||
|
||
g_ptr_array_add (call_args, (char *) distro_hook);
|
||
g_ptr_array_add (call_args, (char *) config_command_to_string (config_command));
|
||
|
||
if (command_args)
|
||
g_ptr_array_extend (call_args, command_args, NULL, NULL);
|
||
else if (action == ACTION_REQUIRED)
|
||
g_ptr_array_add (call_args, "require");
|
||
else if (action == ACTION_ENABLED)
|
||
g_ptr_array_add (call_args, "enable");
|
||
else if (action == ACTION_DISABLED)
|
||
g_ptr_array_add (call_args, "disable");
|
||
else if (config_command != COMMAND_RESET)
|
||
return NULL;
|
||
|
||
g_ptr_array_add (call_args, NULL);
|
||
|
||
if (opt_verbose) {
|
||
g_autofree char *cmdline = NULL;
|
||
|
||
cmdline = g_strjoinv (" ", (GStrv) call_args->pdata);
|
||
g_debug ("Calling hook command “%s”", cmdline);
|
||
}
|
||
|
||
return g_steal_pointer (&call_args);
|
||
}
|
||
|
||
static gboolean
|
||
try_run_distro_hook (GdmConfigCommand config_command,
|
||
GPtrArray *command_args,
|
||
char **stdout_out,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GPtrArray) call_args = NULL;
|
||
g_autoptr(GError) local_error = NULL;
|
||
g_autofree char *distro_hook = NULL;
|
||
g_autofree char *local_stdout = NULL;
|
||
g_autofree char *local_stderr = NULL;
|
||
gint exit_status;
|
||
|
||
/* Distro hooks need to follow these rules:
|
||
* - The name of the hook should be "gdm-auth-config-$DISTRO_NAME",
|
||
* - Must be executable and being placed in the GDM's libexec dir.
|
||
*
|
||
* And it will be called in this way:
|
||
* gdm-auth-config-foo [command] [enable|disable|require|$option] \
|
||
* [option-specific-params]
|
||
* or
|
||
* gdm-auth-config-foo show [command] [$option]
|
||
* or
|
||
* gdm-auth-config-foo reset [require]
|
||
*
|
||
* In set mode, if the exit code is 19 (as SIGSTOP), we won't proceed
|
||
* doing further actions, as we consider that the script already handled
|
||
* all the required changes. Otherwise if it the exit status is 0,
|
||
* then we will continue performing the default actions.
|
||
*
|
||
* When in 'show' mode it should print in stdout one of these values:
|
||
* - required
|
||
* - enabled
|
||
* - disabled
|
||
* - command's option specific output
|
||
*
|
||
* However, we'll always double-check that the returned value matches
|
||
* the system settings.
|
||
*
|
||
* For example:
|
||
* gdm-auth-config-foo show smartcard
|
||
* > expected: enabled|disabled|required
|
||
* gdm-auth-config-foo show smartcard removal-action
|
||
* > expected: 'none|log-out|lock-screen'
|
||
* gdm-auth-config-foo password disable
|
||
* gdm-auth-config-foo smartcard require
|
||
* > expected: exit status of 0 if done or 19 if we need to continue
|
||
*/
|
||
|
||
if (DISTRO[0] == '\0' || g_str_equal (DISTRO, "none")) {
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||
_("No distro detected, no hook to run"));
|
||
return FALSE;
|
||
}
|
||
|
||
distro_hook = g_build_filename (LIBEXECDIR, "gdm-auth-config-" DISTRO, NULL);
|
||
g_debug ("Looking for distro hook “%s”", distro_hook);
|
||
|
||
if (!g_file_test (distro_hook, G_FILE_TEST_IS_REGULAR |
|
||
G_FILE_TEST_IS_EXECUTABLE)) {
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
|
||
"%s is not an executable", distro_hook);
|
||
return FALSE;
|
||
}
|
||
|
||
call_args = build_distro_hook_arguments (distro_hook, config_command, command_args);
|
||
|
||
if (!call_args) {
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||
_("No valid args found to run hook “%s”"), distro_hook);
|
||
return FALSE;
|
||
}
|
||
|
||
if (opt_verbose)
|
||
g_print (_("Running distro hook “%s”\n"), distro_hook);
|
||
|
||
g_spawn_sync (NULL, (GStrv) call_args->pdata, NULL, G_SPAWN_DEFAULT,
|
||
NULL, NULL, &local_stdout, &local_stderr, &exit_status, &local_error);
|
||
|
||
if (local_stdout)
|
||
local_stdout = g_strstrip (local_stdout);
|
||
if (local_stderr)
|
||
local_stderr = g_strstrip (local_stderr);
|
||
|
||
if (!local_error) {
|
||
if (WEXITSTATUS (exit_status) == SIGSTOP) {
|
||
g_print ("%s\n", local_stdout);
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
|
||
_("Distro hook “%s” requested stopping"),
|
||
distro_hook);
|
||
return FALSE;
|
||
} else {
|
||
#if GLIB_CHECK_VERSION (2, 70, 0)
|
||
g_spawn_check_wait_status (exit_status, &local_error);
|
||
#else
|
||
g_spawn_check_exit_status (exit_status, &local_error);
|
||
#endif
|
||
}
|
||
}
|
||
|
||
if (local_error) {
|
||
g_warning (_("Distro hook failed with exit status %d and error %s:\n"
|
||
"Standard output:\n%s\n"
|
||
"Error output:\n%s"),
|
||
WEXITSTATUS (exit_status), local_error->message,
|
||
local_stderr, local_stdout);
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
g_debug ("Distro hook ran correctly\n"
|
||
"Standard output:\n%s\n"
|
||
"Error output:\n%s",
|
||
local_stdout, local_stderr);
|
||
|
||
if (stdout_out)
|
||
*stdout_out = g_steal_pointer (&local_stdout);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
set_distro_hook_config (GdmConfigCommand config_command,
|
||
const char *option_key,
|
||
const char *option_value,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GPtrArray) command_args = NULL;
|
||
g_autoptr(GError) local_error = NULL;
|
||
g_autofree char *local_stdout = NULL;
|
||
|
||
command_args = g_ptr_array_sized_new (2);
|
||
g_ptr_array_add (command_args, (char *) option_key);
|
||
g_ptr_array_add (command_args, (char *) option_value);
|
||
|
||
if (!try_run_distro_hook (config_command, command_args, &local_stdout, &local_error)) {
|
||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||
g_debug ("Distro hook for %s %s requested us to stop",
|
||
config_command_to_string (config_command), option_key);
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
|
||
g_debug ("Distro hook for command %s option-key %s not found: %s",
|
||
config_command_to_string (config_command), option_key,
|
||
local_error->message);
|
||
}
|
||
|
||
g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
|
||
/* TRANSLATORS: Failed to set command “command” option key “option-key” via distro hook */
|
||
_("Failed to set command “%s” option key “%s” via distro hook: "),
|
||
config_command_to_string (config_command),
|
||
option_key);
|
||
return FALSE;
|
||
}
|
||
|
||
g_debug ("Distro hook for %s %s completed, "
|
||
"continuing with default action...",
|
||
config_command_to_string (config_command), option_key);
|
||
g_print ("%s\n", local_stdout);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static char *
|
||
get_distro_hook_config (GdmAuthType auth_type,
|
||
const char *option_key,
|
||
GError **error)
|
||
{
|
||
g_autofree char *output = NULL;
|
||
g_autoptr(GPtrArray) command_args = NULL;
|
||
|
||
command_args = g_ptr_array_sized_new (2);
|
||
g_ptr_array_add (command_args, (char *) auth_type_to_string (auth_type));
|
||
g_ptr_array_add (command_args, (char *) option_key);
|
||
|
||
if (!try_run_distro_hook (COMMAND_SHOW, command_args, &output, error)) {
|
||
return NULL;
|
||
}
|
||
|
||
return g_steal_pointer (&output);
|
||
}
|
||
|
||
static gboolean
|
||
make_directory_with_parents (const char *path,
|
||
GError **error)
|
||
{
|
||
if (!g_file_test (path, G_FILE_TEST_IS_DIR) &&
|
||
g_mkdir_with_parents (path, 0755) != 0) {
|
||
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
|
||
_("Failed to create directory %s"), path);
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
check_distro_hook_is_required (GdmConfigCommand config_command)
|
||
{
|
||
switch (config_command) {
|
||
case COMMAND_SMARTCARD:
|
||
/* Enabling smartcard or requiring authentication might
|
||
* involve further actions that depend on distribution,
|
||
* (such as switching PAM profiles), as per this we
|
||
* always require an hook to be present.
|
||
* For the generic cases for which GDM already ships PAM
|
||
* configuration files, we may use a generic hook that
|
||
* does nothing, but leaves us to change the parameters
|
||
* that we can handle.
|
||
*/
|
||
return (opt_enable == TRUE || opt_required == TRUE);
|
||
default:
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
static const char *
|
||
get_dconf_system_profile (void)
|
||
{
|
||
const char *profile;
|
||
|
||
profile = g_getenv ("DCONF_PROFILE");
|
||
if (profile)
|
||
return profile;
|
||
|
||
return "user";
|
||
}
|
||
|
||
static char *
|
||
get_dconf_db_path (const char *db_name)
|
||
{
|
||
g_autofree char *db_dir = NULL;
|
||
|
||
db_dir = g_strdup_printf ("%s.d", db_name);
|
||
|
||
return g_build_filename (DCONF_SYSCONFIG_DB_PATH, db_dir, NULL);
|
||
}
|
||
|
||
static char *
|
||
get_dconf_system_profile_file (GError **error)
|
||
{
|
||
const char *profile;
|
||
const char * const *xdg_data_dirs;
|
||
const char *prefix;
|
||
|
||
/* This is based on what happens on dconf_engine_open_profile_file */
|
||
profile = get_dconf_system_profile ();
|
||
prefix = DCONF_SYSCONFIG;
|
||
|
||
xdg_data_dirs = g_get_system_data_dirs ();
|
||
do {
|
||
g_autofree char *filename = NULL;
|
||
|
||
filename = g_build_filename (prefix, "dconf", "profile", profile, NULL);
|
||
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
|
||
return g_steal_pointer (&filename);
|
||
}
|
||
} while ((prefix = *xdg_data_dirs++));
|
||
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||
_("dconf profile not found"));
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static char *
|
||
ensure_dconf_configurable_system_profile (GError **error)
|
||
{
|
||
g_autofree char *profile_file = NULL;
|
||
g_autofree char *db_path = NULL;
|
||
g_autoptr(GPtrArray) profile_lines = NULL;
|
||
const char *gdm_sys_db;
|
||
|
||
gdm_sys_db = DCONF_SYSTEM_DB_PREFIX DCONF_SYSTEM_DB_DEFAULT_NAME;
|
||
profile_file = g_build_filename (DCONF_SYSCONFIG_PROFILES_PATH,
|
||
get_dconf_system_profile (), NULL);
|
||
|
||
if (!g_file_test (profile_file, G_FILE_TEST_IS_REGULAR)) {
|
||
if (!make_directory_with_parents (DCONF_SYSCONFIG_PROFILES_PATH, error))
|
||
return FALSE;
|
||
|
||
profile_lines = g_ptr_array_new_with_free_func (g_free);
|
||
g_ptr_array_add (profile_lines, g_strdup ("user-db:user"));
|
||
g_ptr_array_add (profile_lines, g_strdup (gdm_sys_db));
|
||
} else {
|
||
profile_lines = read_file_contents_to_array (profile_file, error);
|
||
if (!profile_lines)
|
||
return FALSE;
|
||
|
||
if (!g_ptr_array_find_with_equal_func (profile_lines, gdm_sys_db,
|
||
g_str_equal, NULL)) {
|
||
g_ptr_array_add (profile_lines, g_strdup (gdm_sys_db));
|
||
} else {
|
||
g_clear_pointer (&profile_lines, g_ptr_array_unref);
|
||
}
|
||
}
|
||
|
||
if (profile_lines) {
|
||
if (!write_array_to_file (profile_lines, profile_file, error))
|
||
return FALSE;
|
||
}
|
||
|
||
db_path = get_dconf_db_path (DCONF_SYSTEM_DB_DEFAULT_NAME);
|
||
if (!make_directory_with_parents (db_path, error)) {
|
||
return FALSE;
|
||
}
|
||
|
||
return g_steal_pointer (&profile_file);
|
||
}
|
||
|
||
GPtrArray *
|
||
get_system_dconf_profile_contents (GError **error)
|
||
{
|
||
g_autofree char *profile_file = NULL;
|
||
|
||
profile_file = get_dconf_system_profile_file (error);
|
||
if (!profile_file)
|
||
return NULL;
|
||
|
||
return read_file_contents_to_array (profile_file, error);
|
||
}
|
||
|
||
char *
|
||
get_system_dconf_profile_db_name (const char *preferred,
|
||
GError **error)
|
||
{
|
||
g_autoptr (GPtrArray) profile_contents = NULL;
|
||
const char *db_name = NULL;
|
||
int i;
|
||
|
||
profile_contents = get_system_dconf_profile_contents (error);
|
||
if (!profile_contents)
|
||
return NULL;
|
||
|
||
for (i = 0; i < profile_contents->len; i++) {
|
||
const char *profile_line = g_ptr_array_index (profile_contents, i);
|
||
|
||
if (g_str_has_prefix (profile_line, DCONF_SYSTEM_DB_PREFIX) &&
|
||
strlen (profile_line) > sizeof (DCONF_SYSTEM_DB_PREFIX) - 1) {
|
||
db_name = profile_line + sizeof (DCONF_SYSTEM_DB_PREFIX) - 1;
|
||
|
||
if (!preferred || g_str_equal (preferred, db_name))
|
||
return g_strdup (db_name);
|
||
}
|
||
}
|
||
|
||
if (db_name)
|
||
return g_strdup (db_name);
|
||
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||
_("dconf has no system-db configured"));
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static char *
|
||
ensure_system_user_dconf_profile (GError **error)
|
||
{
|
||
g_autofree char *db_name = NULL;
|
||
g_autoptr(GError) local_error = NULL;
|
||
|
||
db_name = get_system_dconf_profile_db_name (DCONF_SYSTEM_DB_DEFAULT_NAME,
|
||
&local_error);
|
||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
|
||
g_autofree char *profile_file = NULL;
|
||
|
||
profile_file = ensure_dconf_configurable_system_profile (error);
|
||
if (!profile_file)
|
||
return FALSE;
|
||
|
||
g_debug ("Initialized dconf profile at %s", profile_file);
|
||
db_name = get_system_dconf_profile_db_name (DCONF_SYSTEM_DB_DEFAULT_NAME,
|
||
error);
|
||
if (!db_name)
|
||
return FALSE;
|
||
} else if (local_error) {
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
g_assert (db_name);
|
||
g_debug ("Found system database with name “%s”", db_name);
|
||
|
||
return g_steal_pointer (&db_name);
|
||
}
|
||
|
||
static gboolean
|
||
ensure_gdm_config_dconf_profile (GError **error)
|
||
{
|
||
g_autofree char* current_content = NULL;
|
||
gboolean needs_profile_update = TRUE;
|
||
|
||
g_debug ("Ensuring dconf profile in " GDM_CONFIG_DCONF_PROFILE);
|
||
|
||
g_file_get_contents (GDM_CONFIG_DCONF_PROFILE, ¤t_content,
|
||
NULL, NULL);
|
||
|
||
if (current_content) {
|
||
g_auto(GStrv) content_lines = NULL;
|
||
|
||
content_lines = g_strsplit (current_content, "\n", -1);
|
||
|
||
if (g_strv_contains ((const char **) content_lines,
|
||
GDM_CONFIG_DCONF_DB)) {
|
||
g_debug ("Profile doesn't need to be updated");
|
||
needs_profile_update = FALSE;
|
||
}
|
||
}
|
||
|
||
if (needs_profile_update) {
|
||
g_autofree char* default_content = NULL;
|
||
g_autofree char* profile_content = NULL;
|
||
|
||
if (!g_file_get_contents (GDM_DEFAULT_DCONF_PROFILE, &default_content,
|
||
NULL, error))
|
||
return FALSE;
|
||
|
||
profile_content = g_strdup_printf ("# This profile is managed by %s\n"
|
||
"%s\n"
|
||
"%s\n",
|
||
g_get_prgname(),
|
||
default_content,
|
||
GDM_CONFIG_DCONF_DB);
|
||
|
||
g_debug ("Creating profile file "
|
||
GDM_CONFIG_DCONF_PROFILE
|
||
" using default content from "
|
||
GDM_DEFAULT_DCONF_PROFILE " and "
|
||
GDM_CONFIG_DCONF_DB);
|
||
|
||
if (!make_directory_with_parents (DCONF_SYSCONFIG_PROFILES_PATH, error))
|
||
return FALSE;
|
||
|
||
if (!g_file_set_contents (GDM_CONFIG_DCONF_PROFILE, profile_content,
|
||
-1, error))
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static char *schema_to_key (const char *schema)
|
||
{
|
||
g_auto(GStrv) split = NULL;
|
||
|
||
split = g_strsplit (schema, ".", -1);
|
||
return g_strjoinv ("/", split);
|
||
}
|
||
|
||
static gboolean
|
||
write_dconf_setting_to_key_file (const char *db_name,
|
||
const char *file_name,
|
||
const char *schema,
|
||
const char *key,
|
||
GVariant *value,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GKeyFile) key_file = NULL;
|
||
g_autoptr(GError) local_error = NULL;
|
||
g_autoptr(GVariant) value_ref = NULL;
|
||
g_autofree char *value_str = NULL;
|
||
g_autofree char *header_comment = NULL;
|
||
g_autofree char *db_dir = NULL;
|
||
g_autofree char *file_path = NULL;
|
||
g_autofree char *setting_path = NULL;
|
||
gboolean create_new = FALSE;
|
||
|
||
db_dir = get_dconf_db_path (db_name);
|
||
file_path = g_build_filename (db_dir, file_name, NULL);
|
||
key_file = g_key_file_new ();
|
||
|
||
if (!g_key_file_load_from_file (key_file, file_path,
|
||
G_KEY_FILE_KEEP_COMMENTS, &local_error)) {
|
||
create_new = TRUE;
|
||
g_debug ("Impossible to load setting file “%s” %s", file_path,
|
||
local_error->message);
|
||
|
||
if (!value) {
|
||
/* We had no value to set anyways... */
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
header_comment = g_strdup_printf ("This file has been generated by %s\n"
|
||
"Do no edit!", g_get_prgname ());
|
||
|
||
if (create_new) {
|
||
if (!g_key_file_set_comment (key_file, NULL, NULL,
|
||
header_comment, error)) {
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
g_autofree char *file_comment = NULL;
|
||
|
||
file_comment = g_key_file_get_comment (key_file, NULL, NULL, &local_error);
|
||
file_comment = g_strstrip (file_comment);
|
||
|
||
if (local_error) {
|
||
/* TRANSLATORS: First value is a file path, second is an error message */
|
||
g_warning (_("Failed to get the “%s” header comment: %s, was it modified?"),
|
||
file_path, local_error->message);
|
||
} else if (g_strcmp0 (file_comment, header_comment) != 0) {
|
||
g_warning (_("File “%s” header comment does not match, was it modified?"),
|
||
file_path);
|
||
}
|
||
}
|
||
|
||
setting_path = schema_to_key (schema);
|
||
|
||
if (value) {
|
||
value_ref = g_variant_ref_sink (value);
|
||
value_str = g_variant_print (value_ref, FALSE);
|
||
|
||
if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
|
||
g_key_file_set_boolean (key_file, setting_path, key,
|
||
g_variant_get_boolean (value));
|
||
else
|
||
g_key_file_set_string (key_file, setting_path, key, value_str);
|
||
} else {
|
||
g_clear_error (&local_error);
|
||
g_key_file_remove_key (key_file, setting_path, key, &local_error);
|
||
|
||
if (local_error &&
|
||
!g_error_matches (local_error, G_KEY_FILE_ERROR,
|
||
G_KEY_FILE_ERROR_KEY_NOT_FOUND) &&
|
||
!g_error_matches (local_error, G_KEY_FILE_ERROR,
|
||
G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
g_debug ("Writing %s configuration “%s = %s” to file %s",
|
||
setting_path, key, value_str,
|
||
file_path);
|
||
|
||
if (create_new && !make_directory_with_parents (db_dir, error))
|
||
return FALSE;
|
||
|
||
return g_key_file_save_to_file (key_file, file_path, error);
|
||
}
|
||
|
||
static gboolean
|
||
remove_dconf_setting_key_file (const char *db_name,
|
||
const char *key_file,
|
||
GError **error)
|
||
{
|
||
g_autofree char *db_dir = NULL;
|
||
g_autofree char *key_file_path = NULL;
|
||
g_autoptr (GFile) file = NULL;
|
||
|
||
db_dir = get_dconf_db_path (db_name);
|
||
key_file_path = g_build_filename (db_dir, key_file, NULL);
|
||
file = g_file_new_for_path (key_file_path);
|
||
|
||
g_debug ("Removing setting key file %s", key_file_path);
|
||
|
||
if (g_file_delete (file, NULL, error)) {
|
||
/* Try to remove the parent dir, if empty */
|
||
g_autoptr (GFile) parent_dir = g_file_get_parent (file);
|
||
g_file_delete (parent_dir, NULL, NULL);
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static char *
|
||
get_dconf_db_lock_path (const char *db_name,
|
||
const char *lock_name)
|
||
{
|
||
g_autofree char *db_dir = NULL;
|
||
g_autofree char *db_locks_dir = NULL;
|
||
|
||
db_dir = get_dconf_db_path (db_name);
|
||
db_locks_dir = g_build_filename (db_dir, "locks", NULL);
|
||
|
||
return g_build_filename (db_locks_dir, lock_name, NULL);
|
||
}
|
||
|
||
static gboolean
|
||
set_dconf_setting_lock_full (const char *db_name,
|
||
const char *lock_name,
|
||
const char *schema,
|
||
const char *key,
|
||
gboolean add,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GPtrArray) locks_list = NULL;
|
||
g_autoptr(GError) local_error = NULL;
|
||
g_autofree char *lock_file = NULL;
|
||
g_autofree char *file_header = NULL;
|
||
g_autofree char *locked_schema = NULL;
|
||
g_autofree char *full_key = NULL;
|
||
gboolean found;
|
||
unsigned int idx;
|
||
|
||
lock_file = get_dconf_db_lock_path (db_name, lock_name);
|
||
locks_list = read_file_contents_to_array (lock_file, &local_error);
|
||
|
||
if (local_error) {
|
||
g_debug ("Impossible to read current locks: %s", local_error->message);
|
||
|
||
if (!add) {
|
||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
|
||
g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
|
||
return TRUE;
|
||
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
file_header = g_strdup_printf ("# This file is managed by %s",
|
||
g_get_prgname ());
|
||
|
||
if (!locks_list) {
|
||
g_autofree char *db_locks_dir = NULL;
|
||
|
||
g_assert (add);
|
||
locks_list = g_ptr_array_new_with_free_func (g_free);
|
||
g_ptr_array_add (locks_list, g_steal_pointer (&file_header));
|
||
g_ptr_array_add (locks_list, g_strdup ("# Do no edit!"));
|
||
|
||
db_locks_dir = g_path_get_dirname (lock_file);
|
||
if (!make_directory_with_parents (db_locks_dir, error))
|
||
return FALSE;
|
||
} else {
|
||
if (locks_list->len && !g_str_equal (g_ptr_array_index (locks_list, 0), file_header)) {
|
||
/* XXX: Fail with an error instead? */
|
||
g_warning (_("No expected header found on lock file “%s”, was it modified?"),
|
||
lock_file);
|
||
}
|
||
}
|
||
|
||
locked_schema = schema_to_key (schema);
|
||
full_key = g_strdup_printf ("/%s/%s", locked_schema, key);
|
||
found = g_ptr_array_find_with_equal_func (locks_list, full_key, g_str_equal, &idx);
|
||
|
||
if (found && add) {
|
||
g_debug ("No need to add lock %s to %s, already present",
|
||
full_key, lock_file);
|
||
return TRUE;
|
||
} else if (!found && !add) {
|
||
g_debug ("No need to remove lock %s to %s, not present",
|
||
full_key, lock_file);
|
||
return TRUE;
|
||
}
|
||
|
||
if (add) {
|
||
g_debug ("Adding key %s lock to %s", full_key, lock_file);
|
||
g_ptr_array_add (locks_list, g_strdup (full_key));
|
||
} else {
|
||
g_debug ("Removing key %s lock from %s", full_key, lock_file);
|
||
g_ptr_array_remove_index (locks_list, idx);
|
||
}
|
||
|
||
return write_array_to_file (locks_list, lock_file, error);
|
||
}
|
||
|
||
static gboolean
|
||
set_dconf_setting_lock (const char *db_name,
|
||
const char *lock_name,
|
||
const char *schema,
|
||
const char *key,
|
||
GError **error)
|
||
{
|
||
return set_dconf_setting_lock_full (db_name, lock_name, schema, key, TRUE, error);
|
||
}
|
||
|
||
static gboolean
|
||
unset_dconf_setting_lock (const char *db_name,
|
||
const char *lock_name,
|
||
const char *schema,
|
||
const char *key,
|
||
GError **error)
|
||
{
|
||
return set_dconf_setting_lock_full (db_name, lock_name, schema, key, FALSE, error);
|
||
}
|
||
|
||
static gboolean
|
||
remove_dconf_setting_lock (const char *db_name,
|
||
const char *lock_name,
|
||
GError **error)
|
||
{
|
||
g_autofree char *lock_path = NULL;
|
||
g_autoptr (GFile) file = NULL;
|
||
|
||
lock_path = get_dconf_db_lock_path (db_name, lock_name);
|
||
file = g_file_new_for_path (lock_path);
|
||
|
||
g_debug ("Removing setting lock file %s", lock_path);
|
||
|
||
if (g_file_delete (file, NULL, error)) {
|
||
/* Try to remove the parent dir, if empty */
|
||
g_autoptr (GFile) parent_dir = g_file_get_parent (file);
|
||
g_file_delete (parent_dir, NULL, NULL);
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static gboolean
|
||
update_dconf (GError **error)
|
||
{
|
||
g_auto(GStrv) environ = NULL;
|
||
const char* command[] = { "dconf", "update", NULL };
|
||
gint exit_code;
|
||
|
||
g_debug ("Updating dconf...");
|
||
|
||
environ = g_get_environ ();
|
||
environ = g_environ_setenv (environ, "G_DEBUG", "fatal-warnings", FALSE);
|
||
|
||
if (!g_spawn_sync (NULL, (GStrv) command, environ, G_SPAWN_SEARCH_PATH,
|
||
NULL, NULL, NULL, NULL, &exit_code, error))
|
||
return FALSE;
|
||
|
||
#if GLIB_CHECK_VERSION (2, 70, 0)
|
||
return g_spawn_check_wait_status (exit_code, error);
|
||
#else
|
||
return g_spawn_check_exit_status (exit_code, error);
|
||
#endif
|
||
}
|
||
|
||
static gboolean
|
||
write_system_setting_to_db (const char *db_name,
|
||
const char *schema,
|
||
const char *key,
|
||
GVariant *value,
|
||
GError **error)
|
||
{
|
||
return write_dconf_setting_to_key_file (db_name,
|
||
GDM_CONFIG_DCONF_OVERRIDE_NAME,
|
||
schema,
|
||
key,
|
||
value,
|
||
error);
|
||
}
|
||
|
||
static gboolean
|
||
write_locked_setting_to_db (const char *db_name,
|
||
const char *schema,
|
||
const char *key,
|
||
GVariant *value,
|
||
GError **error)
|
||
{
|
||
if (!write_system_setting_to_db (db_name, schema, key, value, error)) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (!set_dconf_setting_lock_full (db_name, GDM_CONFIG_DCONF_LOCKS_NAME,
|
||
schema, key, value != NULL, error)) {
|
||
return FALSE;
|
||
}
|
||
|
||
if (!update_dconf (error))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
write_locked_system_settings_to_db (const char *schema,
|
||
const char *key,
|
||
GVariant *value,
|
||
GError **error)
|
||
{
|
||
g_autofree char *db_name = NULL;
|
||
|
||
if (value) {
|
||
db_name = ensure_system_user_dconf_profile (error);
|
||
if (!db_name)
|
||
return FALSE;
|
||
} else {
|
||
g_autoptr (GError) local_error = NULL;
|
||
|
||
db_name = get_system_dconf_profile_db_name (DCONF_SYSTEM_DB_DEFAULT_NAME,
|
||
&local_error);
|
||
|
||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
|
||
return TRUE;
|
||
} else if (local_error) {
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
return write_locked_setting_to_db (db_name, schema, key, value, error);
|
||
}
|
||
|
||
static gboolean
|
||
write_gdm_auth_setting_to_db (GdmAuthType auth_type,
|
||
gboolean value,
|
||
GError **error)
|
||
{
|
||
if (!ensure_gdm_config_dconf_profile (error))
|
||
return FALSE;
|
||
|
||
return write_system_setting_to_db (GDM_CONFIG_DCONF_DB_NAME,
|
||
LOGIN_SCHEMA,
|
||
auth_type_to_option_key (auth_type),
|
||
g_variant_new_boolean (value),
|
||
error);
|
||
}
|
||
|
||
static gboolean
|
||
set_gdm_auth_setting_lock (GdmAuthType config_command,
|
||
GError **error)
|
||
{
|
||
return set_dconf_setting_lock (GDM_CONFIG_DCONF_DB_NAME,
|
||
GDM_CONFIG_DCONF_LOCKS_NAME,
|
||
LOGIN_SCHEMA,
|
||
auth_type_to_option_key (config_command),
|
||
error);
|
||
}
|
||
|
||
static gboolean
|
||
write_locked_gdm_settings_to_db (GdmAuthType auth_type,
|
||
gboolean value,
|
||
GError **error)
|
||
{
|
||
if (!write_gdm_auth_setting_to_db (auth_type, value, error))
|
||
return FALSE;
|
||
|
||
if (!set_gdm_auth_setting_lock (auth_type, error))
|
||
return FALSE;
|
||
|
||
if (!update_dconf (error))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static GPtrArray *
|
||
create_cmd_args (int argc, gchar **argv, GdmConfigCommand command)
|
||
{
|
||
GPtrArray *args;
|
||
const char *command_str;
|
||
int i;
|
||
|
||
args = g_ptr_array_new_full (argc, g_free);
|
||
command_str = config_command_to_string (command);
|
||
g_ptr_array_add (args, g_strdup_printf ("%s %s", g_get_prgname (), command_str));
|
||
|
||
for (i = 2; i < argc; i++) {
|
||
g_ptr_array_add (args, g_strdup (argv[i]));
|
||
}
|
||
|
||
return g_steal_pointer (&args);
|
||
}
|
||
|
||
static gboolean
|
||
have_pam_module (GdmAuthType auth_type)
|
||
{
|
||
g_autofree char *pam_profile = NULL;
|
||
|
||
pam_profile = g_strdup_printf(PAM_PROFILES_DIR "/gdm-%s",
|
||
auth_type_to_string(auth_type));
|
||
|
||
g_debug ("Checking for PAM profile “%s” existance", pam_profile);
|
||
|
||
return g_file_test (pam_profile, G_FILE_TEST_IS_REGULAR);
|
||
}
|
||
|
||
static gboolean
|
||
handle_config_command_options (GdmConfigCommand config_command,
|
||
GdmConfigCommandHandler *command_handler,
|
||
GError **error)
|
||
{
|
||
if (!command_handler->options_handler_func)
|
||
return TRUE;
|
||
|
||
return command_handler->options_handler_func (config_command, error);
|
||
}
|
||
|
||
static gboolean
|
||
handle_config_command (GdmConfigCommand config_command,
|
||
GdmConfigCommandHandler *command_handler,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GError) local_error = NULL;
|
||
g_autofree char *output = NULL;
|
||
GdmAuthType auth_type;
|
||
|
||
auth_type = config_command_to_auth_type (config_command);
|
||
g_assert (auth_type != AUTH_NONE);
|
||
|
||
if (!have_pam_module (auth_type)) {
|
||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED,
|
||
get_pam_module_missing_error (auth_type));
|
||
return FALSE;
|
||
}
|
||
|
||
if (try_run_distro_hook (config_command, NULL, &output, &local_error)) {
|
||
g_print ("%s", output);
|
||
return handle_config_command_options (config_command, command_handler, error);
|
||
}
|
||
|
||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||
g_debug ("Hook completed, stopping execution: %s",
|
||
local_error->message);
|
||
|
||
if (!handle_config_command_options (config_command, command_handler, error))
|
||
return FALSE;
|
||
|
||
/* If the option handle didn't fail, we still need to propagate
|
||
* the cancellation, or we will continue with default handler */
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
if (check_distro_hook_is_required (config_command)) {
|
||
g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
|
||
_("Failed to run a required distro hook: "));
|
||
return FALSE;
|
||
}
|
||
|
||
g_debug ("Failed to run optional distro hook: %s", local_error->message);
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
print_usage (int argc,
|
||
char *argv[],
|
||
GdmConfigCommand config_command)
|
||
{
|
||
g_autoptr(GOptionContext) ctx = NULL;
|
||
g_autofree char *usage = NULL;
|
||
|
||
ctx = g_option_context_new (_("COMMAND"));
|
||
g_option_context_set_help_enabled (ctx, FALSE);
|
||
|
||
g_option_context_parse (ctx, &argc, &argv, NULL);
|
||
usage = g_strdup_printf (_("Commands:\n"
|
||
" help Shows this information\n"
|
||
" password Configure the password authentication\n"
|
||
" fingerprint Configure the fingerprint authentication\n"
|
||
" smartcard Configure the smartcard authentication\n"
|
||
" reset Resets the default configuration\n"
|
||
" show Shows the current configuration\n"
|
||
"\n"
|
||
"Use “%s COMMAND --help” to get help on each command.\n"),
|
||
g_get_prgname ());
|
||
|
||
g_option_context_set_description (ctx, usage);
|
||
|
||
usage = g_option_context_get_help (ctx, FALSE, NULL);
|
||
if (config_command == COMMAND_HELP) {
|
||
g_print ("%s", usage);
|
||
return TRUE;
|
||
}
|
||
|
||
g_printerr ("%s", usage);
|
||
return FALSE;
|
||
}
|
||
|
||
static gboolean
|
||
handle_command (int argc,
|
||
char *argv[])
|
||
{
|
||
GdmConfigCommand config_command;
|
||
GdmAuthType auth_type;
|
||
g_autoptr(GOptionContext) option_cx = NULL;
|
||
g_autoptr(GdmConfigCommandHandler) command_handler = NULL;
|
||
g_autoptr(GOptionGroup) group = NULL;
|
||
g_autoptr(GPtrArray) cmd_args = NULL;
|
||
g_autoptr(GPtrArray) args_copy = NULL;
|
||
g_autoptr(GError) error = NULL;
|
||
g_autofree OptionData *options_data = NULL;
|
||
const char *title;
|
||
const char *group_title;
|
||
|
||
config_command = parse_config_command (argv[1]);
|
||
command_handler = config_command_get_handler (config_command);
|
||
|
||
if (!command_handler) {
|
||
return print_usage (argc, argv, config_command);
|
||
}
|
||
|
||
g_assert (command_handler);
|
||
title = get_command_title (config_command);
|
||
group_title = get_command_group_title (config_command);
|
||
|
||
cmd_args = create_cmd_args (argc, argv, config_command);
|
||
|
||
options_data = g_new0 (OptionData, 1);
|
||
options_data->config_command = config_command;
|
||
options_data->args = cmd_args;
|
||
|
||
option_cx = g_option_context_new (NULL);
|
||
g_option_context_set_help_enabled (option_cx, FALSE);
|
||
g_option_context_set_summary (option_cx, title);
|
||
|
||
group = g_option_group_new (config_command_to_string (config_command),
|
||
group_title,
|
||
N_("Command options"),
|
||
g_steal_pointer (&options_data), g_free);
|
||
g_option_group_set_translation_domain (group, GETTEXT_PACKAGE);
|
||
g_option_group_add_entries (group, generic_entries);
|
||
config_command_handler_add_to_group (command_handler, group);
|
||
g_option_context_add_group (option_cx, g_steal_pointer (&group));
|
||
|
||
/* We need to make a further container copy here as the parsing
|
||
* may remove some elements from the array, leading us to a leak
|
||
*/
|
||
args_copy = g_ptr_array_copy (cmd_args, NULL, NULL);
|
||
g_ptr_array_set_free_func (args_copy, NULL);
|
||
argc = args_copy->len;
|
||
argv = (char **) args_copy->pdata;
|
||
|
||
if (!g_option_context_parse (option_cx, &argc, &argv, &error) ||
|
||
opt_cmd_help || /* help requested */
|
||
argc > 1 /* More than one parameter has not been parsed */) {
|
||
g_autofree char *help = NULL;
|
||
help = g_option_context_get_help (option_cx, FALSE, NULL);
|
||
|
||
if (error && argc > 1) {
|
||
g_printerr ("%sError: %s\n", help, error->message);
|
||
} else if (opt_cmd_help) {
|
||
g_print ("%s", help);
|
||
return TRUE;
|
||
} else {
|
||
g_printerr ("%s", help);
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
if (geteuid () != 0 && !g_getenv ("UNDER_JHBUILD")) {
|
||
/* TRANSLATORS: You need to be root to use PROGRAM-NAME “command” command */
|
||
g_critical (_("You need to be root to use %s “%s” command"),
|
||
g_get_prgname(), config_command_to_string (config_command));
|
||
return FALSE;
|
||
}
|
||
|
||
if (opt_debug) {
|
||
g_setenv ("G_MESSAGES_DEBUG", G_LOG_DOMAIN, FALSE);
|
||
}
|
||
|
||
auth_type = config_command_to_auth_type (config_command);
|
||
if (auth_type != AUTH_NONE &&
|
||
!handle_config_command (config_command, command_handler, &error)) {
|
||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||
return TRUE;
|
||
|
||
g_critical ("Failed to handle “%s” command: %s",
|
||
config_command_to_string (config_command),
|
||
error->message);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!command_handler->handler_func (config_command, &error)) {
|
||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||
return FALSE;
|
||
|
||
g_critical ("Failed to run “%s” command: %s",
|
||
config_command_to_string (config_command),
|
||
error->message);
|
||
return FALSE;
|
||
}
|
||
|
||
if (opt_verbose && config_command != COMMAND_SHOW) {
|
||
handle_show (COMMAND_SHOW, NULL);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
handle_toggle_command (GdmConfigCommand config_command,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GError) local_error = NULL;
|
||
GdmAuthType auth_type = config_command_to_auth_type (config_command);
|
||
gboolean enabled = (get_requested_action () == ACTION_ENABLED);
|
||
|
||
g_assert (auth_type != AUTH_NONE);
|
||
|
||
if (!write_locked_gdm_settings_to_db (auth_type, enabled, &local_error)) {
|
||
g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
|
||
_("Failed to set %s setting: "),
|
||
config_command_to_string (config_command));
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
handle_password (GdmConfigCommand config_command,
|
||
GError **error)
|
||
{
|
||
return handle_toggle_command (config_command, error);
|
||
}
|
||
|
||
static gboolean
|
||
handle_fingerprint (GdmConfigCommand config_command,
|
||
GError **error)
|
||
{
|
||
return handle_toggle_command (config_command, error);
|
||
}
|
||
|
||
static gboolean
|
||
toggle_smartcard_settings (const char *key,
|
||
GVariant *value,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GError) local_error = NULL;
|
||
|
||
/* While this is not strictly needed we also do this for the gdm
|
||
* database so that we keep the values in sync for both profiles */
|
||
if (!write_locked_setting_to_db (GDM_CONFIG_DCONF_DB_NAME,
|
||
GSD_SC_SCHEMA,
|
||
key,
|
||
value ? g_variant_ref_sink (value) : NULL,
|
||
&local_error)) {
|
||
g_debug ("Failed to write setting to %s database: %s",
|
||
GDM_CONFIG_DCONF_DB_NAME,
|
||
local_error->message);
|
||
}
|
||
|
||
return write_locked_system_settings_to_db (GSD_SC_SCHEMA,
|
||
key,
|
||
value,
|
||
error);
|
||
}
|
||
|
||
static gboolean
|
||
handle_smartcard_options (GdmConfigCommand config_command,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GError) local_error = NULL;
|
||
GVariant *value;
|
||
|
||
if (!opt_removal_action)
|
||
return TRUE;
|
||
|
||
if (!set_distro_hook_config (config_command, GSD_SC_REMOVAL_ACTION_KEY,
|
||
opt_removal_action, &local_error)) {
|
||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||
return TRUE;
|
||
|
||
if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
g_clear_error (&local_error);
|
||
}
|
||
|
||
if (g_str_equal (opt_removal_action, "user-defined")) {
|
||
value = NULL;
|
||
} else {
|
||
value = g_variant_new_string (opt_removal_action);
|
||
}
|
||
|
||
return toggle_smartcard_settings (GSD_SC_REMOVAL_ACTION_KEY, value, error);
|
||
}
|
||
|
||
static gboolean
|
||
handle_smartcard (GdmConfigCommand config_command,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GError) local_error = NULL;
|
||
GdmAuthAction action;
|
||
|
||
action = get_requested_action ();
|
||
|
||
if (action != ACTION_UNSET) {
|
||
gboolean enabled = (action == ACTION_REQUIRED || action == ACTION_ENABLED);
|
||
if (!write_locked_gdm_settings_to_db (AUTH_SMARTCARD, enabled, &local_error)) {
|
||
g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
|
||
_("Failed to set smartcard setting"));
|
||
return FALSE;
|
||
}
|
||
|
||
if (!write_locked_gdm_settings_to_db (AUTH_PASSWORD, action != ACTION_REQUIRED, &local_error)) {
|
||
g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
|
||
_("Failed to set password setting"));
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (!handle_smartcard_options (config_command, error))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static const char *
|
||
get_boolean_string (gboolean val)
|
||
{
|
||
return val ? N_("Enabled") : N_("Disabled");
|
||
}
|
||
|
||
static const char *
|
||
get_gdm_setting_state (GdmAuthType auth_type) {
|
||
g_autoptr(GSettings) login_settings = NULL;
|
||
gboolean enabled;
|
||
|
||
if (!have_pam_module (auth_type))
|
||
return N_("Not supported");
|
||
|
||
login_settings = g_settings_new (LOGIN_SCHEMA);
|
||
enabled = g_settings_get_boolean (login_settings,
|
||
auth_type_to_option_key (auth_type));
|
||
|
||
if (enabled) {
|
||
g_autofree char *output = NULL;
|
||
|
||
output = get_distro_hook_config (auth_type, NULL, NULL);
|
||
|
||
if (g_strcmp0 (output, "enabled") == 0) {
|
||
return get_boolean_string (TRUE);
|
||
} else if (g_strcmp0 (output, "disabled") == 0) {
|
||
return get_boolean_string (FALSE);
|
||
} else if (g_strcmp0 (output, "required") == 0) {
|
||
return N_("Required");
|
||
}
|
||
}
|
||
|
||
return get_boolean_string (enabled);
|
||
}
|
||
|
||
static char *
|
||
get_smartcard_option (const char *option_key) {
|
||
g_autoptr(GSettings) smartcard_settings = NULL;
|
||
g_autofree char *option_value = NULL;
|
||
|
||
option_value = get_distro_hook_config (AUTH_SMARTCARD, option_key, NULL);
|
||
if (option_value && smartcard_option_is_valid (option_key, option_value)) {
|
||
return g_steal_pointer (&option_value);
|
||
}
|
||
|
||
smartcard_settings = g_settings_new (GSD_SC_SCHEMA);
|
||
|
||
if (g_settings_is_writable (smartcard_settings, option_key)) {
|
||
g_autoptr(GVariant) defalut_value = NULL;
|
||
|
||
defalut_value = g_settings_get_default_value (smartcard_settings,
|
||
option_key);
|
||
return g_strdup_printf ("user-defined (default is %s)",
|
||
g_variant_get_string (defalut_value, NULL));
|
||
}
|
||
|
||
return g_settings_get_string (smartcard_settings, option_key);
|
||
}
|
||
|
||
static gboolean
|
||
handle_show (GdmConfigCommand config_command,
|
||
GError **error)
|
||
{
|
||
g_autofree char *removal_setting = NULL;
|
||
g_autoptr(GError) local_error = NULL;
|
||
g_autofree char *default_dconf_profile = NULL;
|
||
|
||
/* This is not completely correct when getting the smartcard removal
|
||
* option, as that's a per-user / system setting until we don't add the
|
||
* system db to the gdm ones, but we need to set it now otherwise the
|
||
* gsettings backend won't give us proper results for auth parameters,
|
||
* as the backend isn't really ever released on unref, however the
|
||
* alternative may be just using DConfClient API directly or just
|
||
* calling gsettings tool without DCONF_PROFILE set or doing a fork
|
||
* here, but that's probably too much for this tool */
|
||
default_dconf_profile = g_strdup (g_getenv ("DCONF_PROFILE"));
|
||
g_setenv ("DCONF_PROFILE", GDM_DCONF_PROFILE, TRUE);
|
||
|
||
removal_setting = get_smartcard_option (GSD_SC_REMOVAL_ACTION_KEY);
|
||
|
||
/* While this may be not super-needed it ensures that we act as if we
|
||
* were gdm itself */
|
||
if (!g_getenv ("UNDER_JHBUILD") && !switch_to_gdm_user (&local_error)) {
|
||
g_critical ("Impossible to switch to GDM user %s: %s",
|
||
GDM_USERNAME, local_error->message);
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
|
||
g_print(_("GDM Authorization configuration\n"
|
||
"\n"
|
||
" Password authentication: %s\n"
|
||
" Fingerprint authentication: %s\n"
|
||
" Smart Card authentication: %s\n"
|
||
" Smart Card removal action: %s\n"),
|
||
get_gdm_setting_state (AUTH_PASSWORD),
|
||
get_gdm_setting_state (AUTH_FINGERPRINT),
|
||
get_gdm_setting_state (AUTH_SMARTCARD),
|
||
removal_setting
|
||
);
|
||
|
||
if (default_dconf_profile)
|
||
g_setenv ("DCONF_PROFILE", default_dconf_profile, TRUE);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
handle_reset (GdmConfigCommand config_command,
|
||
GError **error)
|
||
{
|
||
g_autoptr(GError) local_error = NULL;
|
||
g_autoptr(GFile) profile_file = NULL;
|
||
g_autofree char *system_db_name = NULL;
|
||
int i;
|
||
|
||
if (get_requested_action () != ACTION_REQUIRED) {
|
||
const char *reply_Y = C_("Interactive question", "Y");
|
||
const char *reply_y = C_("Interactive question", "y");
|
||
const char *reply_N = C_("Interactive question", "N");
|
||
const char *reply_n = C_("Interactive question", "n");
|
||
char buffer[50];
|
||
|
||
while (TRUE) {
|
||
g_print (C_("Interactive question", "Do you want to continue? [Y/n]? "));
|
||
if (!fgets(buffer, sizeof (buffer), stdin))
|
||
continue;
|
||
|
||
g_strstrip (buffer);
|
||
|
||
if (g_str_equal (buffer, reply_Y))
|
||
break;
|
||
if (g_str_equal (buffer, reply_y))
|
||
break;
|
||
if (g_str_equal (buffer, reply_N))
|
||
break;
|
||
if (g_str_equal (buffer, reply_n))
|
||
break;
|
||
}
|
||
|
||
if (!g_str_equal (buffer, reply_y) && !g_str_equal (buffer, reply_Y)) {
|
||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
|
||
_("User cancelled the request"));
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (!try_run_distro_hook (COMMAND_RESET, NULL, NULL, &local_error)) {
|
||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||
return TRUE;
|
||
|
||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
|
||
g_debug ("Ignored reset distro hook: %s", local_error->message);
|
||
g_clear_error (&local_error);
|
||
} else {
|
||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
for (i = 0; i < AUTH_NONE; i++) {
|
||
const char *option_key;
|
||
|
||
if (config_command_to_auth_type (i) == AUTH_NONE)
|
||
continue;
|
||
|
||
option_key = auth_type_to_option_key (i);
|
||
if (!write_system_setting_to_db (GDM_CONFIG_DCONF_DB_NAME,
|
||
LOGIN_SCHEMA,
|
||
option_key,
|
||
NULL,
|
||
&local_error)) {
|
||
g_propagate_prefixed_error (error, g_steal_pointer (&local_error),
|
||
_("Failed to reset %s setting: "),
|
||
config_command_to_string (config_command));
|
||
return FALSE;
|
||
}
|
||
|
||
if (!unset_dconf_setting_lock (GDM_CONFIG_DCONF_DB_NAME,
|
||
GDM_CONFIG_DCONF_LOCKS_NAME,
|
||
LOGIN_SCHEMA, option_key, error))
|
||
return FALSE;
|
||
}
|
||
|
||
if (!toggle_smartcard_settings (GSD_SC_REMOVAL_ACTION_KEY, NULL, error))
|
||
return FALSE;
|
||
|
||
system_db_name = get_system_dconf_profile_db_name (DCONF_SYSTEM_DB_DEFAULT_NAME, NULL);
|
||
if (g_strcmp0 (system_db_name, DCONF_SYSTEM_DB_DEFAULT_NAME) == 0) {
|
||
g_autofree char *profile_file = NULL;
|
||
g_autoptr(GPtrArray) profile_contents = NULL;
|
||
|
||
profile_file = get_dconf_system_profile_file (error);
|
||
profile_contents = get_system_dconf_profile_contents (NULL);
|
||
|
||
if (profile_contents) {
|
||
g_autofree char *full_line = NULL;
|
||
guint index;
|
||
|
||
full_line = g_strconcat (DCONF_SYSTEM_DB_PREFIX, system_db_name, NULL);
|
||
while (g_ptr_array_find_with_equal_func (profile_contents, full_line,
|
||
g_str_equal, &index)) {
|
||
g_debug ("Removing %s db from profile %s",
|
||
system_db_name, profile_file);
|
||
g_ptr_array_remove_index (profile_contents, index);
|
||
write_array_to_file (profile_contents, profile_file, NULL);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (system_db_name) {
|
||
if (!remove_dconf_setting_lock (system_db_name,
|
||
GDM_CONFIG_DCONF_LOCKS_NAME,
|
||
&local_error)) {
|
||
g_warning ("Failed to remove lock file for DB %s: %s",
|
||
system_db_name,
|
||
local_error->message);
|
||
g_clear_error (&local_error);
|
||
}
|
||
|
||
if (!remove_dconf_setting_key_file (system_db_name,
|
||
GDM_CONFIG_DCONF_OVERRIDE_NAME,
|
||
&local_error)) {
|
||
g_warning ("Failed to remove setting key file for DB %s: %s",
|
||
system_db_name,
|
||
local_error->message);
|
||
g_clear_error (&local_error);
|
||
}
|
||
}
|
||
|
||
if (!remove_dconf_setting_lock (GDM_CONFIG_DCONF_DB_NAME,
|
||
GDM_CONFIG_DCONF_LOCKS_NAME,
|
||
&local_error)) {
|
||
g_warning ("Failed to remove lock file for DB %s: %s",
|
||
GDM_CONFIG_DCONF_DB_NAME,
|
||
local_error->message);
|
||
g_clear_error (&local_error);
|
||
}
|
||
|
||
if (!remove_dconf_setting_key_file (GDM_CONFIG_DCONF_DB_NAME,
|
||
GDM_CONFIG_DCONF_OVERRIDE_NAME,
|
||
&local_error)) {
|
||
g_warning ("Failed to remove setting key file for DB %s: %s",
|
||
GDM_CONFIG_DCONF_DB_NAME,
|
||
local_error->message);
|
||
g_clear_error (&local_error);
|
||
}
|
||
|
||
/* finally remove the profile/gdm */
|
||
profile_file = g_file_new_for_path (GDM_CONFIG_DCONF_PROFILE);
|
||
if (!g_file_delete (profile_file, NULL, &local_error)) {
|
||
g_warning ("Failed to remove gdm-auth-config profile override: %s",
|
||
local_error->message);
|
||
g_clear_error (&local_error);
|
||
}
|
||
|
||
if (!update_dconf (error))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
int
|
||
main (int argc, char *argv[])
|
||
{
|
||
g_autofree char *program_name = NULL;
|
||
|
||
setlocale (LC_ALL, "");
|
||
textdomain (GETTEXT_PACKAGE);
|
||
|
||
program_name = g_path_get_basename (argv[0]);
|
||
g_set_prgname (program_name);
|
||
|
||
if (argc < 2) {
|
||
print_usage (argc, argv, COMMAND_UNKNOWN);
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
if (!handle_command (argc, argv))
|
||
return EXIT_FAILURE;
|
||
|
||
return EXIT_SUCCESS;
|
||
}
|