Index: trunk/src/conf.c =================================================================== --- trunk/src/conf.c (revision 11596) +++ trunk/src/conf.c (revision 11597) @@ -1874,3 +1874,65 @@ free(tmp); } + +int pcb_conf_cmd_is_safe_(const char *path_, const char *value, const char **val_out, int msg) +{ + const char *reason; + conf_native_t *nd; + char *path, *s; + + if (val_out != NULL) + *val_out = NULL; + + if (value == NULL) + goto accept; + + path = pcb_strdup(path_); + for(s = path; *s != '\0'; s++) + if (*s == '.') + *s = '/'; + nd = conf_get_field(path); + free(path); + + if (nd == NULL) { + if (msg) + pcb_message(PCB_MSG_ERROR, "pcb_conf_cmd_is_safe(): invalid node path '%s' (looks like an internal error)\n", path_); + return 0; + } + + if (nd->array_size > 1) { + if (msg) + pcb_message(PCB_MSG_ERROR, "pcb_conf_cmd_is_safe(): invalid node path '%s' (it is an array)\n", path_); + return 0; + } + + switch(conf_lookup_role(nd->prop[0].src)) { + /* these are considered safe, because: */ + case CFR_INTERNAL: /* the attacker would have access to the source code and compilation, could place system() anyway */ + case CFR_SYSTEM: /* system admin - lets trust them */ + case CFR_USER: /* user config is written by the user, attackers have no better chance to overwrite that than overwriting the the shell's rc */ + case CFR_CLI: /* command line arguments: the user specified those; who has access to that potentially has access to the shell anyway */ + goto accept; + + /* these are considered unsafe, because: */ + case CFR_DEFAULTPCB: /* the default pcb path may be manipulated; the user has the chance to specify the command path setting from safe config files */ + reason = "default pcb"; break; + case CFR_ENV: /* env vars may be inherited from who-knows-where */ + reason = "env var"; break; + case CFR_PROJECT: /* malicous file prepared by the attacker */ + reason = "project file"; break; + case CFR_DESIGN: /* malicous file prepared by the attacker */ + reason = "board file"; break; + default: + reason = "unknown source"; + } + + if (msg) + pcb_message(PCB_MSG_ERROR, "pcb_conf_cmd_is_safe(): refusing to use the value of '%s' because it is from unsafe source %s\n", path_, reason); + return 0; + + accept:; + if (val_out != NULL) + *val_out = value; + return 1; +} Index: trunk/src/conf.h =================================================================== --- trunk/src/conf.h (revision 11596) +++ trunk/src/conf.h (revision 11597) @@ -366,4 +366,10 @@ /* Determine the file name of the project file - project_fn and pcb_fn can be NULL */ const char *conf_get_project_conf_name(const char *project_fn, const char *pcb_fn, const char **out_project_fn); +/* Return 1 if the config node named in path is considered safe enough + to specify a command to execute - e.g. an attacker shouldn't be able to + inject commands in design files sent */ +int pcb_conf_cmd_is_safe_(const char *path, const char *value, const char **val_out, int msg); +#define pcb_conf_cmd_is_safe(path, val_out, msg) pcb_conf_cmd_is_safe_(#path, conf_core.path, val_out, msg) + #endif Index: trunk/src/plug_io.c =================================================================== --- trunk/src/plug_io.c (revision 11596) +++ trunk/src/plug_io.c (revision 11597) @@ -44,6 +44,7 @@ #include #include "change.h" +#include "conf.h" #include "data.h" #include "error.h" #include "plug_io.h" @@ -925,13 +926,17 @@ int result; const char *p; static gds_t command; + const char *save_cmd; if (PCB_EMPTY_STRING_P(conf_core.rc.save_command)) return pcb_write_pcb_file(Filename, thePcb, fmt, pcb_false, elem_only); + if (!pcb_conf_cmd_is_safe(rc.save_command, &save_cmd, 1)) + return -1; + /* setup commandline */ gds_truncate(&command,0); - for (p = conf_core.rc.save_command; *p; p++) { + for (p = save_cmd; *p; p++) { /* copy character if not special or add string to command */ if (!(p[0] == '%' && p[1] == 'f')) gds_append(&command, *p); Index: trunk/src_plugins/fp_fs/fp_fs.c =================================================================== --- trunk/src_plugins/fp_fs/fp_fs.c (revision 11596) +++ trunk/src_plugins/fp_fs/fp_fs.c (revision 11597) @@ -471,8 +471,10 @@ { char *basename, *params, *fullname; FILE *f = NULL; - const char *libshell = conf_core.rc.library_shell; + const char *libshell; + pcb_conf_cmd_is_safe(rc.library_shell, &libshell, 1); + fctx->field[F_IS_PARAMETRIC].i = pcb_fp_dupname(name, &basename, ¶ms); if (basename == NULL) return NULL; Index: trunk/src_plugins/import_netlist/import_netlist.c =================================================================== --- trunk/src_plugins/import_netlist/import_netlist.c (revision 11596) +++ trunk/src_plugins/import_netlist/import_netlist.c (revision 11597) @@ -55,6 +55,7 @@ int i, j, lines, kind; pcb_bool continued; int used_popen = 0; + const char *ratcmd; if (!filename) return (1); /* nothing to do */ @@ -61,7 +62,9 @@ pcb_message(PCB_MSG_INFO, _("Importing PCB netlist %s\n"), filename); - if (PCB_EMPTY_STRING_P(conf_core.rc.rat_command)) { + pcb_conf_cmd_is_safe(rc.rat_command, &ratcmd, 1); + + if (PCB_EMPTY_STRING_P(ratcmd)) { fp = pcb_fopen(filename, "r"); if (!fp) { pcb_message(PCB_MSG_ERROR, "Cannot open %s for reading", filename); Index: trunk/src_plugins/io_pcb/parse_l.c =================================================================== --- trunk/src_plugins/io_pcb/parse_l.c (revision 11596) +++ trunk/src_plugins/io_pcb/parse_l.c (revision 11597) @@ -2513,6 +2513,7 @@ int io_pcb_ParsePCB(pcb_plug_io_t *ctx, pcb_board_t *Ptr, const char *Filename, conf_role_t settings_dest) { int retval; + const char *fcmd; yy_parse_tags = 0; yyPCB = Ptr; yyData = NULL; @@ -2521,10 +2522,15 @@ yyFontkitValid = NULL; yyElement = NULL; yy_settings_dest = settings_dest; + + if (!pcb_conf_cmd_is_safe(rc.file_command, &fcmd, 1)) + return -1; + if (settings_dest != CFR_invalid) conf_reset(settings_dest, Filename); pcb_setlocale(LC_ALL, "C"); /* make sure numerics are read predictably */ - retval = Parse(NULL, conf_core.rc.file_command, conf_core.rc.file_path, Filename); + + retval = Parse(NULL, fcmd, conf_core.rc.file_path, Filename); pcb_setlocale(LC_ALL, ""); if ((settings_dest != CFR_invalid) && (retval == 0)) { /* overwrite settings from the flags, mark them not-to-save */ @@ -2590,6 +2596,7 @@ int io_pcb_ParseFont(pcb_plug_io_t *ctx, pcb_font_t *Ptr, const char *Filename) { int r = 0, valid; + const char *fcmd; yy_parse_tags = 1; yyPCB = NULL; yyFont = Ptr; @@ -2597,8 +2604,11 @@ yyElement = NULL; yyFontReset = pcb_false; + if (!pcb_conf_cmd_is_safe(rc.font_command, &fcmd, 1)) + return -1; + yy_settings_dest = CFR_invalid; - r = Parse(NULL, conf_core.rc.font_command, NULL, Filename); + r = Parse(NULL, fcmd, NULL, Filename); if (r == 0) { #ifdef DEBUG pcb_message(PCB_MSG_DEBUG, "Found %s in %s\n", Filename, conf_core.rc.font_command); Index: trunk/src_plugins/io_pcb/parse_l.l =================================================================== --- trunk/src_plugins/io_pcb/parse_l.l (revision 11596) +++ trunk/src_plugins/io_pcb/parse_l.l (revision 11597) @@ -372,6 +372,7 @@ int io_pcb_ParsePCB(pcb_plug_io_t *ctx, pcb_board_t *Ptr, const char *Filename, conf_role_t settings_dest) { int retval; + const char *fcmd; yy_parse_tags = 0; yyPCB = Ptr; yyData = NULL; @@ -380,10 +381,15 @@ yyFontkitValid = NULL; yyElement = NULL; yy_settings_dest = settings_dest; + + if (!pcb_conf_cmd_is_safe(rc.file_command, &fcmd, 1)) + return -1; + if (settings_dest != CFR_invalid) conf_reset(settings_dest, Filename); pcb_setlocale(LC_ALL, "C"); /* make sure numerics are read predictably */ - retval = Parse(NULL, conf_core.rc.file_command, conf_core.rc.file_path, Filename); + + retval = Parse(NULL, fcmd, conf_core.rc.file_path, Filename); pcb_setlocale(LC_ALL, ""); if ((settings_dest != CFR_invalid) && (retval == 0)) { /* overwrite settings from the flags, mark them not-to-save */ @@ -449,6 +455,7 @@ int io_pcb_ParseFont(pcb_plug_io_t *ctx, pcb_font_t *Ptr, const char *Filename) { int r = 0, valid; + const char *fcmd; yy_parse_tags = 1; yyPCB = NULL; yyFont = Ptr; @@ -456,8 +463,11 @@ yyElement = NULL; yyFontReset = pcb_false; + if (!pcb_conf_cmd_is_safe(rc.font_command, &fcmd, 1)) + return -1; + yy_settings_dest = CFR_invalid; - r = Parse(NULL, conf_core.rc.font_command, NULL, Filename); + r = Parse(NULL, fcmd, NULL, Filename); if (r == 0) { #ifdef DEBUG pcb_message(PCB_MSG_DEBUG, "Found %s in %s\n", Filename, conf_core.rc.font_command);