@@ -779,7 +779,8 @@ static const enum menu_driver_enum MENU_DEFAULT_DRIVER = MENU_NULL;
779779enum config_bool_flags
780780{
781781 CFG_BOOL_FLG_DEF_ENABLE = (1 << 0 ),
782- CFG_BOOL_FLG_HANDLE = (1 << 1 )
782+ CFG_BOOL_FLG_HANDLE = (1 << 1 ),
783+ CFG_BOOL_FLG_SENSITIVE = (1 << 2 )
783784};
784785
785786struct config_bool_setting
@@ -882,9 +883,21 @@ struct config_path_setting
882883#define SETTING_OVERRIDE (override_setting ) \
883884 tmp[count-1].override = override_setting
884885
886+ #define SETTING_SENSITIVE () \
887+ tmp[count-1].flags |= CFG_BOOL_FLG_SENSITIVE
888+
889+ #define SETTING_ARRAY_SENSITIVE (key , configval , default_enable , default_setting , handle_setting ) \
890+ SETTING_ARRAY(key, configval, default_enable, default_setting, handle_setting) \
891+ SETTING_SENSITIVE()
892+
893+ #define SETTING_PATH_SENSITIVE (key , configval , default_enable , default_setting , handle_setting ) \
894+ SETTING_PATH(key, configval, default_enable, default_setting, handle_setting) \
895+ SETTING_SENSITIVE()
896+
885897/* Forward declarations */
886898#ifdef HAVE_CONFIGFILE
887899static void config_parse_file (global_t * global );
900+ static size_t config_get_credentials_path (char * s , size_t len );
888901#endif
889902
890903struct defaults g_defaults ;
@@ -1593,33 +1606,33 @@ static struct config_array_setting *populate_settings_array(
15931606 SETTING_ARRAY ("cloud_sync_driver" , settings -> arrays .cloud_sync_driver , false, NULL , true);
15941607
15951608#ifdef HAVE_CHEEVOS
1596- SETTING_ARRAY ("cheevos_custom_host" , settings -> arrays .cheevos_custom_host , false, NULL , true);
1597- SETTING_ARRAY ("cheevos_username" , settings -> arrays .cheevos_username , false, NULL , true);
1598- SETTING_ARRAY ("cheevos_password" , settings -> arrays .cheevos_password , false, NULL , true);
1599- SETTING_ARRAY ("cheevos_token" , settings -> arrays .cheevos_token , false, NULL , true);
1600- SETTING_ARRAY ("cheevos_leaderboards_enable" , settings -> arrays .cheevos_leaderboards_enable , true, "" , true); /* deprecated */
1609+ SETTING_ARRAY ("cheevos_custom_host" , settings -> arrays .cheevos_custom_host , false, NULL , true);
1610+ SETTING_ARRAY_SENSITIVE ("cheevos_username" , settings -> arrays .cheevos_username , false, NULL , true);
1611+ SETTING_ARRAY_SENSITIVE ("cheevos_password" , settings -> arrays .cheevos_password , false, NULL , true);
1612+ SETTING_ARRAY_SENSITIVE ("cheevos_token" , settings -> arrays .cheevos_token , false, NULL , true);
1613+ SETTING_ARRAY ("cheevos_leaderboards_enable" , settings -> arrays .cheevos_leaderboards_enable , true, "" , true); /* deprecated */
16011614#endif
16021615
16031616#ifdef HAVE_NETWORKING
1604- SETTING_ARRAY ("netplay_mitm_server" , settings -> arrays .netplay_mitm_server , false, NULL , true);
1605- SETTING_ARRAY ("webdav_url" , settings -> arrays .webdav_url , false, NULL , true);
1606- SETTING_ARRAY ("webdav_username" , settings -> arrays .webdav_username , false, NULL , true);
1607- SETTING_ARRAY ("webdav_password" , settings -> arrays .webdav_password , false, NULL , true);
1608- SETTING_ARRAY ("google_drive_refresh_token" , settings -> arrays .google_drive_refresh_token , false, NULL , true);
1609- SETTING_ARRAY ("youtube_stream_key" , settings -> arrays .youtube_stream_key , true, NULL , true);
1610- SETTING_ARRAY ("twitch_stream_key" , settings -> arrays .twitch_stream_key , true, NULL , true);
1611- SETTING_ARRAY ("facebook_stream_key" , settings -> arrays .facebook_stream_key , true, NULL , true);
1612- SETTING_ARRAY ("discord_app_id" , settings -> arrays .discord_app_id , true, DEFAULT_DISCORD_APP_ID , true);
1613- SETTING_ARRAY ("ai_service_url" , settings -> arrays .ai_service_url , true, DEFAULT_AI_SERVICE_URL , true);
1617+ SETTING_ARRAY ("netplay_mitm_server" , settings -> arrays .netplay_mitm_server , false, NULL , true);
1618+ SETTING_ARRAY ("webdav_url" , settings -> arrays .webdav_url , false, NULL , true);
1619+ SETTING_ARRAY_SENSITIVE ("webdav_username" , settings -> arrays .webdav_username , false, NULL , true);
1620+ SETTING_ARRAY_SENSITIVE ("webdav_password" , settings -> arrays .webdav_password , false, NULL , true);
1621+ SETTING_ARRAY_SENSITIVE ("google_drive_refresh_token" , settings -> arrays .google_drive_refresh_token , false, NULL , true);
1622+ SETTING_ARRAY_SENSITIVE ("youtube_stream_key" , settings -> arrays .youtube_stream_key , true, NULL , true);
1623+ SETTING_ARRAY_SENSITIVE ("twitch_stream_key" , settings -> arrays .twitch_stream_key , true, NULL , true);
1624+ SETTING_ARRAY_SENSITIVE ("facebook_stream_key" , settings -> arrays .facebook_stream_key , true, NULL , true);
1625+ SETTING_ARRAY ("discord_app_id" , settings -> arrays .discord_app_id , true, DEFAULT_DISCORD_APP_ID , true);
1626+ SETTING_ARRAY ("ai_service_url" , settings -> arrays .ai_service_url , true, DEFAULT_AI_SERVICE_URL , true);
16141627#endif
16151628
16161629#ifdef HAVE_SMBCLIENT
1617- SETTING_ARRAY ("smb_client_server_address" , settings -> arrays .smb_client_server_address , false, NULL , true);
1618- SETTING_ARRAY ("smb_client_share" , settings -> arrays .smb_client_share , false, NULL , true);
1619- SETTING_ARRAY ("smb_client_subdir" , settings -> arrays .smb_client_subdir , false, NULL , true);
1620- SETTING_ARRAY ("smb_client_username" , settings -> arrays .smb_client_username , false, NULL , true);
1621- SETTING_ARRAY ("smb_client_password" , settings -> arrays .smb_client_password , false, NULL , true);
1622- SETTING_ARRAY ("smb_client_workgroup" , settings -> arrays .smb_client_workgroup , false, NULL , true);
1630+ SETTING_ARRAY ("smb_client_server_address" , settings -> arrays .smb_client_server_address , false, NULL , true);
1631+ SETTING_ARRAY ("smb_client_share" , settings -> arrays .smb_client_share , false, NULL , true);
1632+ SETTING_ARRAY ("smb_client_subdir" , settings -> arrays .smb_client_subdir , false, NULL , true);
1633+ SETTING_ARRAY_SENSITIVE ("smb_client_username" , settings -> arrays .smb_client_username , false, NULL , true);
1634+ SETTING_ARRAY_SENSITIVE ("smb_client_password" , settings -> arrays .smb_client_password , false, NULL , true);
1635+ SETTING_ARRAY ("smb_client_workgroup" , settings -> arrays .smb_client_workgroup , false, NULL , true);
16231636#endif
16241637
16251638#ifdef HAVE_LAKKA
@@ -1715,11 +1728,11 @@ static struct config_path_setting *populate_settings_path(
17151728#endif
17161729
17171730#ifdef HAVE_NETWORKING
1718- SETTING_PATH ("netplay_ip_address" , settings -> paths .netplay_server , false, NULL , true);
1719- SETTING_PATH ("netplay_custom_mitm_server" , settings -> paths .netplay_custom_mitm_server , false, NULL , true);
1720- SETTING_PATH ("netplay_nickname" , settings -> paths .username , false, NULL , true);
1721- SETTING_PATH ("netplay_password" , settings -> paths .netplay_password , false, NULL , true);
1722- SETTING_PATH ("netplay_spectate_password" , settings -> paths .netplay_spectate_password , false, NULL , true);
1731+ SETTING_PATH ("netplay_ip_address" , settings -> paths .netplay_server , false, NULL , true);
1732+ SETTING_PATH ("netplay_custom_mitm_server" , settings -> paths .netplay_custom_mitm_server , false, NULL , true);
1733+ SETTING_PATH ("netplay_nickname" , settings -> paths .username , false, NULL , true);
1734+ SETTING_PATH_SENSITIVE ("netplay_password" , settings -> paths .netplay_password , false, NULL , true);
1735+ SETTING_PATH_SENSITIVE ("netplay_spectate_password" , settings -> paths .netplay_spectate_password , false, NULL , true);
17231736#endif
17241737
17251738#ifdef _3DS
@@ -3955,6 +3968,27 @@ static bool config_load_file(global_t *global,
39553968#endif
39563969 }
39573970
3971+ /* Merge credentials from separate file.
3972+ * Credentials are stored in credentials.cfg alongside
3973+ * the main config to keep sensitive data (passwords,
3974+ * tokens, keys) out of retroarch.cfg. */
3975+ {
3976+ char credentials_path [PATH_MAX_LENGTH ];
3977+ credentials_path [0 ] = '\0' ;
3978+ config_get_credentials_path (credentials_path ,
3979+ sizeof (credentials_path ));
3980+ if (!string_is_empty (credentials_path )
3981+ && path_is_valid (credentials_path ))
3982+ {
3983+ bool result = config_append_file (conf , credentials_path );
3984+ RARCH_LOG ("[Config] Merging credentials from \"%s\".\n" ,
3985+ credentials_path );
3986+ if (!result )
3987+ RARCH_ERR ("[Config] Failed to merge credentials from \"%s\".\n" ,
3988+ credentials_path );
3989+ }
3990+ }
3991+
39583992 /* Special case for perfcnt_enable */
39593993 {
39603994 bool tmp = false;
@@ -5560,6 +5594,107 @@ bool config_save_autoconf_profile(const char *device_name, unsigned user)
55605594 return ret ;
55615595}
55625596
5597+ /**
5598+ * config_get_credentials_path:
5599+ *
5600+ * Builds the path to the credentials config file.
5601+ * Uses the same directory as the main config file.
5602+ *
5603+ * Returns: length of the string written to @s.
5604+ **/
5605+ static size_t config_get_credentials_path (char * s , size_t len )
5606+ {
5607+ char config_directory [DIR_MAX_LENGTH ];
5608+ config_directory [0 ] = '\0' ;
5609+
5610+ if (path_is_empty (RARCH_PATH_CONFIG ))
5611+ {
5612+ s [0 ] = '\0' ;
5613+ return 0 ;
5614+ }
5615+
5616+ fill_pathname_basedir (config_directory ,
5617+ path_get (RARCH_PATH_CONFIG ),
5618+ sizeof (config_directory ));
5619+
5620+ return fill_pathname_join_special (s , config_directory ,
5621+ "credentials.cfg" , len );
5622+ }
5623+
5624+ /**
5625+ * config_save_credentials:
5626+ *
5627+ * Writes only sensitive settings (passwords, tokens, keys)
5628+ * to a separate credentials.cfg file.
5629+ *
5630+ * Returns: true (1) on success, otherwise returns false (0).
5631+ **/
5632+ bool config_save_credentials (void )
5633+ {
5634+ unsigned i ;
5635+ bool ret = false;
5636+ char credentials_path [PATH_MAX_LENGTH ];
5637+ struct config_array_setting * array_settings = NULL ;
5638+ struct config_path_setting * path_settings = NULL ;
5639+ settings_t * settings = config_st ;
5640+ int array_settings_size = sizeof (settings -> arrays ) / sizeof (settings -> arrays .placeholder );
5641+ int path_settings_size = sizeof (settings -> paths ) / sizeof (settings -> paths .placeholder );
5642+ config_file_t * conf = NULL ;
5643+
5644+ credentials_path [0 ] = '\0' ;
5645+ config_get_credentials_path (credentials_path , sizeof (credentials_path ));
5646+
5647+ if (string_is_empty (credentials_path ))
5648+ return false;
5649+
5650+ conf = config_file_new_from_path_to_string (credentials_path );
5651+ if (!conf )
5652+ conf = config_file_new_alloc ();
5653+ if (!conf )
5654+ return false;
5655+
5656+ array_settings = populate_settings_array (settings , & array_settings_size );
5657+ path_settings = populate_settings_path (settings , & path_settings_size );
5658+
5659+ if (array_settings && (array_settings_size > 0 ))
5660+ {
5661+ for (i = 0 ; i < (unsigned )array_settings_size ; i ++ )
5662+ {
5663+ if (!(array_settings [i ].flags & CFG_BOOL_FLG_SENSITIVE ))
5664+ continue ;
5665+ config_set_string (conf ,
5666+ array_settings [i ].ident ,
5667+ array_settings [i ].ptr );
5668+ }
5669+ free (array_settings );
5670+ array_settings = NULL ;
5671+ }
5672+
5673+ if (path_settings && (path_settings_size > 0 ))
5674+ {
5675+ for (i = 0 ; i < (unsigned )path_settings_size ; i ++ )
5676+ {
5677+ if (!(path_settings [i ].flags & CFG_BOOL_FLG_SENSITIVE ))
5678+ continue ;
5679+ config_set_path (conf ,
5680+ path_settings [i ].ident ,
5681+ path_settings [i ].ptr );
5682+ }
5683+ free (path_settings );
5684+ path_settings = NULL ;
5685+ }
5686+
5687+ ret = config_file_write (conf , credentials_path , true);
5688+ config_file_free (conf );
5689+
5690+ if (ret )
5691+ RARCH_LOG ("[Config] Saved credentials to \"%s\".\n" , credentials_path );
5692+ else
5693+ RARCH_ERR ("[Config] Failed to save credentials to \"%s\".\n" , credentials_path );
5694+
5695+ return ret ;
5696+ }
5697+
55635698/**
55645699 * config_save_file:
55655700 * @path : Path that shall be written to.
@@ -5573,6 +5708,7 @@ bool config_save_file(const char *path)
55735708 float msg_color ;
55745709 unsigned i = 0 ;
55755710 bool ret = false;
5711+ bool credentials_saved = false;
55765712 struct config_bool_setting * bool_settings = NULL ;
55775713 struct config_int_setting * int_settings = NULL ;
55785714 struct config_uint_setting * uint_settings = NULL ;
@@ -5610,13 +5746,31 @@ bool config_save_file(const char *path)
56105746 array_settings = populate_settings_array (settings , & array_settings_size );
56115747 path_settings = populate_settings_path (settings , & path_settings_size );
56125748
5749+ /* Save credentials to a separate file.
5750+ * Only strip sensitive fields from retroarch.cfg
5751+ * when credentials.cfg was written successfully. */
5752+ credentials_saved = config_save_credentials ();
5753+ if (!credentials_saved )
5754+ RARCH_WARN ("[Config] Credentials save failed, "
5755+ "keeping sensitive fields in main config.\n" );
5756+
56135757 /* Path settings */
56145758 if (path_settings && (path_settings_size > 0 ))
56155759 {
56165760 for (i = 0 ; i < (unsigned )path_settings_size ; i ++ )
56175761 {
56185762 const char * value = path_settings [i ].ptr ;
56195763
5764+ /* Sensitive settings are stored in credentials.cfg.
5765+ * Unset removes stale values from existing configs.
5766+ * Only strip when credentials.cfg was written OK. */
5767+ if ( credentials_saved
5768+ && (path_settings [i ].flags & CFG_BOOL_FLG_SENSITIVE ))
5769+ {
5770+ config_unset (conf , path_settings [i ].ident );
5771+ continue ;
5772+ }
5773+
56205774 if (path_settings [i ].flags & CFG_BOOL_FLG_DEF_ENABLE )
56215775 if (string_is_empty (path_settings [i ].ptr ))
56225776 value = "default" ;
@@ -5640,11 +5794,22 @@ bool config_save_file(const char *path)
56405794 if (array_settings && (array_settings_size > 0 ))
56415795 {
56425796 for (i = 0 ; i < (unsigned )array_settings_size ; i ++ )
5797+ {
5798+ /* Sensitive settings are stored in credentials.cfg.
5799+ * Unset removes stale values from existing configs.
5800+ * Only strip when credentials.cfg was written OK. */
5801+ if ( credentials_saved
5802+ && (array_settings [i ].flags & CFG_BOOL_FLG_SENSITIVE ))
5803+ {
5804+ config_unset (conf , array_settings [i ].ident );
5805+ continue ;
5806+ }
56435807 if ( !array_settings [i ].override
56445808 || !retroarch_override_setting_is_set (array_settings [i ].override , NULL ))
56455809 config_set_string (conf ,
56465810 array_settings [i ].ident ,
56475811 array_settings [i ].ptr );
5812+ }
56485813
56495814 free (array_settings );
56505815 }
@@ -6004,22 +6169,14 @@ int8_t config_save_overrides(enum override_type type,
60046169 {
60056170 if (!string_is_equal (array_settings [i ].ptr , array_overrides [i ].ptr ))
60066171 {
6007- #ifdef HAVE_CHEEVOS
6008- /* As authentication doesn't occur until after content is loaded,
6009- * the achievement authentication token might only exist in the
6010- * override set, and therefore differ from the master config set.
6011- * Storing the achievement authentication token in an override
6012- * is a recipe for disaster. If it expires and the user generates
6013- * a new token, then the override will be out of date and the
6014- * user will have to reauthenticate for each override (and also
6015- * remember to update each override). Also exclude the username
6016- * as it's directly tied to the token and password.
6017- */
6018- if ( string_is_equal (array_settings [i ].ident , "cheevos_token" )
6019- || string_is_equal (array_settings [i ].ident , "cheevos_password" )
6020- || string_is_equal (array_settings [i ].ident , "cheevos_username" ))
6172+ /* Authentication tokens stored in overrides become stale
6173+ * when they expire and get regenerated in the master
6174+ * config, forcing users to reauthenticate per override.
6175+ * Originally applied to cheevos credentials, now
6176+ * generalized to all sensitive settings via
6177+ * credentials.cfg. */
6178+ if (array_settings [i ].flags & CFG_BOOL_FLG_SENSITIVE )
60216179 continue ;
6022- #endif
60236180 config_set_string (conf , array_overrides [i ].ident ,
60246181 array_overrides [i ].ptr );
60256182 RARCH_DBG ("[Override] %s = \"%s\"\n" ,
@@ -6029,6 +6186,10 @@ int8_t config_save_overrides(enum override_type type,
60296186
60306187 for (i = 0 ; i < (unsigned )path_settings_size ; i ++ )
60316188 {
6189+ /* Sensitive settings are managed via credentials.cfg */
6190+ if (path_settings [i ].flags & CFG_BOOL_FLG_SENSITIVE )
6191+ continue ;
6192+
60326193 if (!string_is_equal (path_settings [i ].ptr , path_overrides [i ].ptr ))
60336194 {
60346195#if IOS
0 commit comments