Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 203 additions & 42 deletions configuration.c
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,8 @@ static const enum menu_driver_enum MENU_DEFAULT_DRIVER = MENU_NULL;
enum config_bool_flags
{
CFG_BOOL_FLG_DEF_ENABLE = (1 << 0),
CFG_BOOL_FLG_HANDLE = (1 << 1)
CFG_BOOL_FLG_HANDLE = (1 << 1),
CFG_BOOL_FLG_SENSITIVE = (1 << 2)
};

struct config_bool_setting
Expand Down Expand Up @@ -882,9 +883,21 @@ struct config_path_setting
#define SETTING_OVERRIDE(override_setting) \
tmp[count-1].override = override_setting

#define SETTING_SENSITIVE() \
tmp[count-1].flags |= CFG_BOOL_FLG_SENSITIVE

#define SETTING_ARRAY_SENSITIVE(key, configval, default_enable, default_setting, handle_setting) \
SETTING_ARRAY(key, configval, default_enable, default_setting, handle_setting) \
SETTING_SENSITIVE()

#define SETTING_PATH_SENSITIVE(key, configval, default_enable, default_setting, handle_setting) \
SETTING_PATH(key, configval, default_enable, default_setting, handle_setting) \
SETTING_SENSITIVE()

/* Forward declarations */
#ifdef HAVE_CONFIGFILE
static void config_parse_file(global_t *global);
static size_t config_get_credentials_path(char *s, size_t len);
#endif

struct defaults g_defaults;
Expand Down Expand Up @@ -1593,33 +1606,33 @@ static struct config_array_setting *populate_settings_array(
SETTING_ARRAY("cloud_sync_driver", settings->arrays.cloud_sync_driver, false, NULL, true);

#ifdef HAVE_CHEEVOS
SETTING_ARRAY("cheevos_custom_host", settings->arrays.cheevos_custom_host, false, NULL, true);
SETTING_ARRAY("cheevos_username", settings->arrays.cheevos_username, false, NULL, true);
SETTING_ARRAY("cheevos_password", settings->arrays.cheevos_password, false, NULL, true);
SETTING_ARRAY("cheevos_token", settings->arrays.cheevos_token, false, NULL, true);
SETTING_ARRAY("cheevos_leaderboards_enable", settings->arrays.cheevos_leaderboards_enable, true, "", true); /* deprecated */
SETTING_ARRAY("cheevos_custom_host", settings->arrays.cheevos_custom_host, false, NULL, true);
SETTING_ARRAY_SENSITIVE("cheevos_username", settings->arrays.cheevos_username, false, NULL, true);
SETTING_ARRAY_SENSITIVE("cheevos_password", settings->arrays.cheevos_password, false, NULL, true);
SETTING_ARRAY_SENSITIVE("cheevos_token", settings->arrays.cheevos_token, false, NULL, true);
SETTING_ARRAY("cheevos_leaderboards_enable", settings->arrays.cheevos_leaderboards_enable, true, "", true); /* deprecated */
#endif

#ifdef HAVE_NETWORKING
SETTING_ARRAY("netplay_mitm_server", settings->arrays.netplay_mitm_server, false, NULL, true);
SETTING_ARRAY("webdav_url", settings->arrays.webdav_url, false, NULL, true);
SETTING_ARRAY("webdav_username", settings->arrays.webdav_username, false, NULL, true);
SETTING_ARRAY("webdav_password", settings->arrays.webdav_password, false, NULL, true);
SETTING_ARRAY("google_drive_refresh_token", settings->arrays.google_drive_refresh_token, false, NULL, true);
SETTING_ARRAY("youtube_stream_key", settings->arrays.youtube_stream_key, true, NULL, true);
SETTING_ARRAY("twitch_stream_key", settings->arrays.twitch_stream_key, true, NULL, true);
SETTING_ARRAY("facebook_stream_key", settings->arrays.facebook_stream_key, true, NULL, true);
SETTING_ARRAY("discord_app_id", settings->arrays.discord_app_id, true, DEFAULT_DISCORD_APP_ID, true);
SETTING_ARRAY("ai_service_url", settings->arrays.ai_service_url, true, DEFAULT_AI_SERVICE_URL, true);
SETTING_ARRAY("netplay_mitm_server", settings->arrays.netplay_mitm_server, false, NULL, true);
SETTING_ARRAY("webdav_url", settings->arrays.webdav_url, false, NULL, true);
SETTING_ARRAY_SENSITIVE("webdav_username", settings->arrays.webdav_username, false, NULL, true);
SETTING_ARRAY_SENSITIVE("webdav_password", settings->arrays.webdav_password, false, NULL, true);
SETTING_ARRAY_SENSITIVE("google_drive_refresh_token", settings->arrays.google_drive_refresh_token, false, NULL, true);
SETTING_ARRAY_SENSITIVE("youtube_stream_key", settings->arrays.youtube_stream_key, true, NULL, true);
SETTING_ARRAY_SENSITIVE("twitch_stream_key", settings->arrays.twitch_stream_key, true, NULL, true);
SETTING_ARRAY_SENSITIVE("facebook_stream_key", settings->arrays.facebook_stream_key, true, NULL, true);
SETTING_ARRAY("discord_app_id", settings->arrays.discord_app_id, true, DEFAULT_DISCORD_APP_ID, true);
SETTING_ARRAY("ai_service_url", settings->arrays.ai_service_url, true, DEFAULT_AI_SERVICE_URL, true);
#endif

#ifdef HAVE_SMBCLIENT
SETTING_ARRAY("smb_client_server_address", settings->arrays.smb_client_server_address, false, NULL, true);
SETTING_ARRAY("smb_client_share", settings->arrays.smb_client_share, false, NULL, true);
SETTING_ARRAY("smb_client_subdir", settings->arrays.smb_client_subdir, false, NULL, true);
SETTING_ARRAY("smb_client_username", settings->arrays.smb_client_username, false, NULL, true);
SETTING_ARRAY("smb_client_password", settings->arrays.smb_client_password, false, NULL, true);
SETTING_ARRAY("smb_client_workgroup", settings->arrays.smb_client_workgroup, false, NULL, true);
SETTING_ARRAY("smb_client_server_address", settings->arrays.smb_client_server_address, false, NULL, true);
SETTING_ARRAY("smb_client_share", settings->arrays.smb_client_share, false, NULL, true);
SETTING_ARRAY("smb_client_subdir", settings->arrays.smb_client_subdir, false, NULL, true);
SETTING_ARRAY_SENSITIVE("smb_client_username", settings->arrays.smb_client_username, false, NULL, true);
SETTING_ARRAY_SENSITIVE("smb_client_password", settings->arrays.smb_client_password, false, NULL, true);
SETTING_ARRAY("smb_client_workgroup", settings->arrays.smb_client_workgroup, false, NULL, true);
#endif

#ifdef HAVE_LAKKA
Expand Down Expand Up @@ -1715,11 +1728,11 @@ static struct config_path_setting *populate_settings_path(
#endif

#ifdef HAVE_NETWORKING
SETTING_PATH("netplay_ip_address", settings->paths.netplay_server, false, NULL, true);
SETTING_PATH("netplay_custom_mitm_server", settings->paths.netplay_custom_mitm_server, false, NULL, true);
SETTING_PATH("netplay_nickname", settings->paths.username, false, NULL, true);
SETTING_PATH("netplay_password", settings->paths.netplay_password, false, NULL, true);
SETTING_PATH("netplay_spectate_password", settings->paths.netplay_spectate_password, false, NULL, true);
SETTING_PATH("netplay_ip_address", settings->paths.netplay_server, false, NULL, true);
SETTING_PATH("netplay_custom_mitm_server", settings->paths.netplay_custom_mitm_server, false, NULL, true);
SETTING_PATH("netplay_nickname", settings->paths.username, false, NULL, true);
SETTING_PATH_SENSITIVE("netplay_password", settings->paths.netplay_password, false, NULL, true);
SETTING_PATH_SENSITIVE("netplay_spectate_password", settings->paths.netplay_spectate_password, false, NULL, true);
#endif

#ifdef _3DS
Expand Down Expand Up @@ -3955,6 +3968,27 @@ static bool config_load_file(global_t *global,
#endif
}

/* Merge credentials from separate file.
* Credentials are stored in credentials.cfg alongside
* the main config to keep sensitive data (passwords,
* tokens, keys) out of retroarch.cfg. */
{
char credentials_path[PATH_MAX_LENGTH];
credentials_path[0] = '\0';
config_get_credentials_path(credentials_path,
sizeof(credentials_path));
if (!string_is_empty(credentials_path)
&& path_is_valid(credentials_path))
{
bool result = config_append_file(conf, credentials_path);
RARCH_LOG("[Config] Merging credentials from \"%s\".\n",
credentials_path);
if (!result)
RARCH_ERR("[Config] Failed to merge credentials from \"%s\".\n",
credentials_path);
}
}

/* Special case for perfcnt_enable */
{
bool tmp = false;
Expand Down Expand Up @@ -5560,6 +5594,107 @@ bool config_save_autoconf_profile(const char *device_name, unsigned user)
return ret;
}

/**
* config_get_credentials_path:
*
* Builds the path to the credentials config file.
* Uses the same directory as the main config file.
*
* Returns: length of the string written to @s.
**/
static size_t config_get_credentials_path(char *s, size_t len)
{
char config_directory[DIR_MAX_LENGTH];
config_directory[0] = '\0';

if (path_is_empty(RARCH_PATH_CONFIG))
{
s[0] = '\0';
return 0;
}

fill_pathname_basedir(config_directory,
path_get(RARCH_PATH_CONFIG),
sizeof(config_directory));

return fill_pathname_join_special(s, config_directory,
"credentials.cfg", len);
}

/**
* config_save_credentials:
*
* Writes only sensitive settings (passwords, tokens, keys)
* to a separate credentials.cfg file.
*
* Returns: true (1) on success, otherwise returns false (0).
**/
bool config_save_credentials(void)
{
unsigned i;
bool ret = false;
char credentials_path[PATH_MAX_LENGTH];
struct config_array_setting *array_settings = NULL;
struct config_path_setting *path_settings = NULL;
settings_t *settings = config_st;
int array_settings_size = sizeof(settings->arrays) / sizeof(settings->arrays.placeholder);
int path_settings_size = sizeof(settings->paths) / sizeof(settings->paths.placeholder);
config_file_t *conf = NULL;

credentials_path[0] = '\0';
config_get_credentials_path(credentials_path, sizeof(credentials_path));

if (string_is_empty(credentials_path))
return false;

conf = config_file_new_from_path_to_string(credentials_path);
if (!conf)
conf = config_file_new_alloc();
if (!conf)
return false;

array_settings = populate_settings_array(settings, &array_settings_size);
path_settings = populate_settings_path(settings, &path_settings_size);

if (array_settings && (array_settings_size > 0))
{
for (i = 0; i < (unsigned)array_settings_size; i++)
{
if (!(array_settings[i].flags & CFG_BOOL_FLG_SENSITIVE))
continue;
config_set_string(conf,
array_settings[i].ident,
array_settings[i].ptr);
}
free(array_settings);
array_settings = NULL;
}

if (path_settings && (path_settings_size > 0))
{
for (i = 0; i < (unsigned)path_settings_size; i++)
{
if (!(path_settings[i].flags & CFG_BOOL_FLG_SENSITIVE))
continue;
config_set_path(conf,
path_settings[i].ident,
path_settings[i].ptr);
}
free(path_settings);
path_settings = NULL;
}

ret = config_file_write(conf, credentials_path, true);
config_file_free(conf);

if (ret)
RARCH_LOG("[Config] Saved credentials to \"%s\".\n", credentials_path);
else
RARCH_ERR("[Config] Failed to save credentials to \"%s\".\n", credentials_path);

return ret;
}

/**
* config_save_file:
* @path : Path that shall be written to.
Expand All @@ -5573,6 +5708,7 @@ bool config_save_file(const char *path)
float msg_color;
unsigned i = 0;
bool ret = false;
bool credentials_saved = false;
struct config_bool_setting *bool_settings = NULL;
struct config_int_setting *int_settings = NULL;
struct config_uint_setting *uint_settings = NULL;
Expand Down Expand Up @@ -5610,13 +5746,31 @@ bool config_save_file(const char *path)
array_settings = populate_settings_array (settings, &array_settings_size);
path_settings = populate_settings_path (settings, &path_settings_size);

/* Save credentials to a separate file.
* Only strip sensitive fields from retroarch.cfg
* when credentials.cfg was written successfully. */
credentials_saved = config_save_credentials();
if (!credentials_saved)
RARCH_WARN("[Config] Credentials save failed, "
"keeping sensitive fields in main config.\n");

/* Path settings */
if (path_settings && (path_settings_size > 0))
{
for (i = 0; i < (unsigned)path_settings_size; i++)
{
const char *value = path_settings[i].ptr;

/* Sensitive settings are stored in credentials.cfg.
* Unset removes stale values from existing configs.
* Only strip when credentials.cfg was written OK. */
if ( credentials_saved
&& (path_settings[i].flags & CFG_BOOL_FLG_SENSITIVE))
{
config_unset(conf, path_settings[i].ident);
continue;
}

if (path_settings[i].flags & CFG_BOOL_FLG_DEF_ENABLE)
if (string_is_empty(path_settings[i].ptr))
value = "default";
Expand All @@ -5640,11 +5794,22 @@ bool config_save_file(const char *path)
if (array_settings && (array_settings_size > 0))
{
for (i = 0; i < (unsigned)array_settings_size; i++)
{
/* Sensitive settings are stored in credentials.cfg.
* Unset removes stale values from existing configs.
* Only strip when credentials.cfg was written OK. */
if ( credentials_saved
&& (array_settings[i].flags & CFG_BOOL_FLG_SENSITIVE))
{
config_unset(conf, array_settings[i].ident);
continue;
}
if ( !array_settings[i].override
|| !retroarch_override_setting_is_set(array_settings[i].override, NULL))
config_set_string(conf,
array_settings[i].ident,
array_settings[i].ptr);
}

free(array_settings);
}
Expand Down Expand Up @@ -6004,22 +6169,14 @@ int8_t config_save_overrides(enum override_type type,
{
if (!string_is_equal(array_settings[i].ptr, array_overrides[i].ptr))
{
#ifdef HAVE_CHEEVOS
/* As authentication doesn't occur until after content is loaded,
* the achievement authentication token might only exist in the
* override set, and therefore differ from the master config set.
* Storing the achievement authentication token in an override
* is a recipe for disaster. If it expires and the user generates
* a new token, then the override will be out of date and the
* user will have to reauthenticate for each override (and also
* remember to update each override). Also exclude the username
* as it's directly tied to the token and password.
*/
if ( string_is_equal(array_settings[i].ident, "cheevos_token")
|| string_is_equal(array_settings[i].ident, "cheevos_password")
|| string_is_equal(array_settings[i].ident, "cheevos_username"))
/* Authentication tokens stored in overrides become stale
* when they expire and get regenerated in the master
* config, forcing users to reauthenticate per override.
* Originally applied to cheevos credentials, now
* generalized to all sensitive settings via
* credentials.cfg. */
if (array_settings[i].flags & CFG_BOOL_FLG_SENSITIVE)
continue;
#endif
config_set_string(conf, array_overrides[i].ident,
array_overrides[i].ptr);
RARCH_DBG("[Override] %s = \"%s\"\n",
Expand All @@ -6029,6 +6186,10 @@ int8_t config_save_overrides(enum override_type type,

for (i = 0; i < (unsigned)path_settings_size; i++)
{
/* Sensitive settings are managed via credentials.cfg */
if (path_settings[i].flags & CFG_BOOL_FLG_SENSITIVE)
continue;

if (!string_is_equal(path_settings[i].ptr, path_overrides[i].ptr))
{
#if IOS
Expand Down
10 changes: 10 additions & 0 deletions configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -1362,6 +1362,16 @@ bool config_save_autoconf_profile(const char *device_name, unsigned user);
**/
bool config_save_file(const char *path);

/**
* config_save_credentials:
*
* Writes only sensitive settings (passwords, tokens, keys)
* to a separate credentials.cfg file.
*
* Returns: true (1) on success, otherwise returns false (0).
**/
bool config_save_credentials(void);

/**
* config_save_overrides:
* @path : Path that shall be written to.
Expand Down
Loading