@@ -201,6 +201,10 @@ int main(int argc, char **argv)
201201 usage (argv [0 ], 1 );
202202 }
203203 parse_connect (optarg , & request_id , & src_domain_name , & src_domain_id );
204+ if (target_refers_to_dom0 (src_domain_name ) || src_domain_id == 0 ) {
205+ warnx ("ERROR: -c cannot be used for requests to dom0" );
206+ usage (argv [0 ], 1 );
207+ }
204208 break ;
205209 case 't' :
206210 replace_chars_stdout = true;
@@ -261,22 +265,73 @@ int main(int argc, char **argv)
261265 }
262266
263267 if (target_refers_to_dom0 (domname )) {
264- if (request_id == NULL ) {
265- fprintf (stderr , "ERROR: when target domain is 'dom0', -c must be specified\n" );
266- usage (argv [0 ], 1 );
267- }
268- strncpy (svc_params .ident , request_id , sizeof (svc_params .ident ) - 1 );
269- svc_params .ident [sizeof (svc_params .ident ) - 1 ] = '\0' ;
270- if (src_domain_name == NULL ) {
271- LOG (ERROR , "internal error: src_domain_name should not be NULL here" );
272- abort ();
268+ if (request_id != NULL ) {
269+ if (request_id [0 ] == '\0' ) {
270+ warnx ("ERROR: request ID cannot be empty" );
271+ usage (argv [0 ], 1 );
272+ }
273+ strncpy (svc_params .ident , request_id , sizeof (svc_params .ident ) - 1 );
274+ svc_params .ident [sizeof (svc_params .ident ) - 1 ] = '\0' ;
275+ if (src_domain_name == NULL ) {
276+ LOG (ERROR , "internal error: src_domain_name should not be NULL here" );
277+ abort ();
278+ }
279+ rc = run_qrexec_to_dom0 (& svc_params ,
280+ src_domain_id ,
281+ src_domain_name ,
282+ remote_cmdline ,
283+ connection_timeout ,
284+ exit_with_code );
285+ } else {
286+ /* dom0 -> dom0 fake service call */
287+ assert (src_domain_id == 0 );
288+ /*
289+ * Normally calls to dom0 omit the username, but in this case
290+ * that would require the caller to pass the user if and only if the target is _not_
291+ * dom0, and that's annoying. In the past, qrexec-client was called by qrexec-daemon
292+ * which got it right, but now the main caller of qrexec-client is Python scripts
293+ * that don't have access to the C target_refers_to_dom0() function.
294+ * Therefore, parse the username and fail if it is not "DEFAULT".
295+ */
296+ #define DEFAULT_USER "DEFAULT"
297+ if (strncmp (remote_cmdline , DEFAULT_USER ":" , sizeof (DEFAULT_USER )) != 0 ) {
298+ errx (QREXEC_EXIT_PROBLEM , "dom0 -> dom0 commands must be prefixed with " DEFAULT_USER ":" );
299+ }
300+ remote_cmdline += sizeof (DEFAULT_USER );
301+ struct qrexec_parsed_command * command = parse_qubes_rpc_command (remote_cmdline , false);
302+ int prepare_ret ;
303+ char file_path [QUBES_SOCKADDR_UN_MAX_PATH_LEN ];
304+ struct buffer buf = { .data = file_path , .buflen = (int )sizeof (file_path ) };
305+ if (command == NULL ) {
306+ prepare_ret = -2 ;
307+ } else if (command -> service_descriptor == NULL ) {
308+ LOG (ERROR , "For dom0 -> dom0 commands, only proper qrexec calls are allowed" );
309+ prepare_ret = -2 ;
310+ } else if (!wait_for_session_maybe (command )) {
311+ LOG (ERROR , "Cannot load service configuration, or forking process failed" );
312+ prepare_ret = -2 ;
313+ } else {
314+ prepare_ret = find_qrexec_service (command , NULL , NULL , & buf );
315+ }
316+ switch (prepare_ret ) {
317+ case -2 :
318+ rc = QREXEC_EXIT_PROBLEM ;
319+ break ;
320+ case -1 :
321+ rc = QREXEC_EXIT_SERVICE_NOT_FOUND ;
322+ break ;
323+ case 0 :
324+ assert (command -> username == NULL );
325+ assert (command -> command );
326+ /* qrexec-client is always in a login session. */
327+ exec_qubes_rpc2 (buf .data , command -> command , environ , false);
328+ /* not reached */
329+ default :
330+ assert (false);
331+ rc = QREXEC_EXIT_PROBLEM ;
332+ break ;
333+ }
273334 }
274- rc = run_qrexec_to_dom0 (& svc_params ,
275- src_domain_id ,
276- src_domain_name ,
277- remote_cmdline ,
278- connection_timeout ,
279- exit_with_code );
280335 } else {
281336 if (request_id ) {
282337 bool const use_uuid = strncmp (domname , "uuid:" , 5 ) == 0 ;
0 commit comments