@@ -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,77 @@ 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+ if (local_cmdline != NULL ) {
289+ warnx ("dom0 -> dom0 qrexec calls with LOCAL_COMMAND not yet implemented" );
290+ errx (QREXEC_EXIT_PROBLEM , "please file an issue if you need this" );
291+ }
292+ /*
293+ * Normally calls to dom0 omit the username, but in this case
294+ * that would require the caller to pass the user if and only if the target is _not_
295+ * dom0, and that's annoying. In the past, qrexec-client was called by qrexec-daemon
296+ * which got it right, but now the main caller of qrexec-client is Python scripts
297+ * that don't have access to the C target_refers_to_dom0() function.
298+ * Therefore, parse the username and fail if it is not "DEFAULT".
299+ */
300+ #define DEFAULT_USER "DEFAULT"
301+ if (strncmp (remote_cmdline , DEFAULT_USER ":" , sizeof (DEFAULT_USER )) != 0 ) {
302+ errx (QREXEC_EXIT_PROBLEM , "dom0 -> dom0 commands must be prefixed with " DEFAULT_USER ":" );
303+ }
304+ remote_cmdline += sizeof (DEFAULT_USER );
305+ struct qrexec_parsed_command * command = parse_qubes_rpc_command (remote_cmdline , false);
306+ int prepare_ret ;
307+ char file_path [QUBES_SOCKADDR_UN_MAX_PATH_LEN ];
308+ struct buffer buf = { .data = file_path , .buflen = (int )sizeof (file_path ) };
309+ if (command == NULL ) {
310+ prepare_ret = -2 ;
311+ } else if (command -> service_descriptor == NULL ) {
312+ LOG (ERROR , "For dom0 -> dom0 commands, only proper qrexec calls are allowed" );
313+ prepare_ret = -2 ;
314+ } else if (!wait_for_session_maybe (command )) {
315+ LOG (ERROR , "Cannot load service configuration, or forking process failed" );
316+ prepare_ret = -2 ;
317+ } else {
318+ prepare_ret = find_qrexec_service (command , NULL , NULL , & buf );
319+ }
320+ switch (prepare_ret ) {
321+ case -2 :
322+ rc = QREXEC_EXIT_PROBLEM ;
323+ break ;
324+ case -1 :
325+ rc = QREXEC_EXIT_SERVICE_NOT_FOUND ;
326+ break ;
327+ case 0 :
328+ assert (command -> username == NULL );
329+ assert (command -> command );
330+ /* qrexec-client is always in a login session. */
331+ exec_qubes_rpc2 (buf .data , command -> command , environ , false);
332+ /* not reached */
333+ default :
334+ assert (false);
335+ rc = QREXEC_EXIT_PROBLEM ;
336+ break ;
337+ }
273338 }
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 );
280339 } else {
281340 if (request_id ) {
282341 bool const use_uuid = strncmp (domname , "uuid:" , 5 ) == 0 ;
0 commit comments