41
41
#include "libqrexec-utils.h"
42
42
#include "qrexec-daemon-common.h"
43
43
44
- // whether qrexec-client should replace problematic bytes with _ before printing the output
45
- static bool replace_chars_stdout = false;
46
- static bool replace_chars_stderr = false;
47
-
48
- static int exit_with_code = 1 ;
49
-
50
44
#define VCHAN_BUFFER_SIZE 65536
51
45
52
- #define QREXEC_DATA_MIN_VERSION QREXEC_PROTOCOL_V2
53
-
54
- static int local_stdin_fd , local_stdout_fd ;
55
- static pid_t local_pid = 0 ;
56
- /* flag if this is "remote" end of service call. In this case swap STDIN/STDOUT
57
- * msg types and send exit code at the end */
58
- static bool is_service = false;
59
-
60
- static volatile sig_atomic_t sigchld = 0 ;
61
-
62
46
extern char * * environ ;
63
47
64
48
static char * xstrdup (const char * arg ) {
@@ -77,62 +61,6 @@ static void set_remote_domain(const char *src_domain_name) {
77
61
}
78
62
}
79
63
80
- /* initialize data_protocol_version */
81
- static int handle_agent_handshake (libvchan_t * vchan , bool remote_send_first )
82
- {
83
- struct msg_header hdr ;
84
- struct peer_info info ;
85
- int data_protocol_version = -1 ;
86
- int who = 0 ; // even - send to remote, odd - receive from remote
87
-
88
- while (who < 2 ) {
89
- if ((who + remote_send_first ) & 1 ) {
90
- if (!read_vchan_all (vchan , & hdr , sizeof (hdr ))) {
91
- PERROR ("daemon handshake" );
92
- return -1 ;
93
- }
94
- if (hdr .type != MSG_HELLO || hdr .len != sizeof (info )) {
95
- LOG (ERROR , "Invalid daemon MSG_HELLO" );
96
- return -1 ;
97
- }
98
- if (!read_vchan_all (vchan , & info , sizeof (info ))) {
99
- PERROR ("daemon handshake" );
100
- return -1 ;
101
- }
102
-
103
- data_protocol_version = info .version < QREXEC_PROTOCOL_VERSION ?
104
- info .version : QREXEC_PROTOCOL_VERSION ;
105
- if (data_protocol_version < QREXEC_DATA_MIN_VERSION ) {
106
- LOG (ERROR , "Incompatible daemon protocol version "
107
- "(daemon %d, client %d)" ,
108
- info .version , QREXEC_PROTOCOL_VERSION );
109
- return -1 ;
110
- }
111
- } else {
112
- hdr .type = MSG_HELLO ;
113
- hdr .len = sizeof (info );
114
- info .version = QREXEC_PROTOCOL_VERSION ;
115
-
116
- if (!write_vchan_all (vchan , & hdr , sizeof (hdr ))) {
117
- LOG (ERROR , "Failed to send MSG_HELLO hdr to daemon" );
118
- return -1 ;
119
- }
120
- if (!write_vchan_all (vchan , & info , sizeof (info ))) {
121
- LOG (ERROR , "Failed to send MSG_HELLO to daemon" );
122
- return -1 ;
123
- }
124
- }
125
- who ++ ;
126
- }
127
- return data_protocol_version ;
128
- }
129
-
130
- static void sigchld_handler (int x __attribute__((__unused__ )))
131
- {
132
- sigchld = 1 ;
133
- signal (SIGCHLD , sigchld_handler );
134
- }
135
-
136
64
/* called from do_fork_exec */
137
65
static _Noreturn void do_exec (const char * prog , const char * username __attribute__((unused )))
138
66
{
@@ -145,128 +73,6 @@ static _Noreturn void do_exec(const char *prog, const char *username __attribute
145
73
exit (1 );
146
74
}
147
75
148
-
149
- /* See also qrexec-agent.c:wait_for_session_maybe() */
150
- static bool wait_for_session_maybe (char * cmdline )
151
- {
152
- struct qrexec_parsed_command * cmd ;
153
- pid_t pid ;
154
- int status ;
155
- bool rc = false;
156
-
157
- cmd = parse_qubes_rpc_command (cmdline , false);
158
- if (!cmd )
159
- goto out ;
160
-
161
- if (cmd -> nogui ) {
162
- rc = true;
163
- goto out ;
164
- }
165
-
166
- if (!cmd -> service_descriptor ) {
167
- rc = true;
168
- goto out ;
169
- }
170
-
171
- if (load_service_config_v2 (cmd ) < 0 )
172
- goto out ;
173
- if (!cmd -> wait_for_session ) {
174
- rc = true;
175
- goto out ;
176
- }
177
-
178
- pid = fork ();
179
- switch (pid ) {
180
- case 0 :
181
- close (0 );
182
- exec_wait_for_session (cmd -> source_domain );
183
- PERROR ("exec" );
184
- _exit (1 );
185
- case -1 :
186
- PERROR ("fork" );
187
- goto out ;
188
- default :
189
- rc = true;
190
- }
191
-
192
- if (waitpid (local_pid , & status , 0 ) > 0 ) {
193
- if (status != 0 )
194
- LOG (ERROR , "wait-for-session exited with status %d" , status );
195
- } else
196
- PERROR ("waitpid" );
197
-
198
- out :
199
- destroy_qrexec_parsed_command (cmd );
200
- return rc ;
201
- }
202
-
203
- static int prepare_local_fds (char * cmdline , struct buffer * stdin_buffer )
204
- {
205
- if (stdin_buffer == NULL )
206
- abort ();
207
- if (!cmdline ) {
208
- local_stdin_fd = 1 ;
209
- local_stdout_fd = 0 ;
210
- return 0 ;
211
- }
212
- signal (SIGCHLD , sigchld_handler );
213
- return execute_qubes_rpc_command (cmdline , & local_pid , & local_stdin_fd , & local_stdout_fd ,
214
- NULL , false, stdin_buffer );
215
- }
216
-
217
- // See also qrexec-agent/qrexec-agent-data.c
218
- static _Noreturn void handle_failed_exec (libvchan_t * data_vchan )
219
- {
220
- int exit_code = 127 ;
221
- struct msg_header hdr = {
222
- .type = MSG_DATA_STDOUT ,
223
- .len = 0 ,
224
- };
225
-
226
- LOG (ERROR , "failed to spawn process, exiting" );
227
- /*
228
- * TODO: In case we fail to execute a *local* process (is_service false),
229
- * we should either
230
- * - exit even before connecting to remote domain, or
231
- * - send stdin EOF and keep waiting for remote exit code.
232
- *
233
- * That will require a slightly bigger refactoring. Right now it's not
234
- * important, because this function should handle QUBESRPC command failure
235
- * only (normal commands go through fork+exec), but it will be necessary
236
- * when we support sockets as a local process.
237
- */
238
- if (is_service ) {
239
- libvchan_send (data_vchan , & hdr , sizeof (hdr ));
240
- send_exit_code (data_vchan , exit_code );
241
- libvchan_close (data_vchan );
242
- }
243
- exit (exit_code );
244
- }
245
- static void select_loop (libvchan_t * vchan , int data_protocol_version , struct buffer * stdin_buf )
246
- {
247
- struct process_io_request req = { 0 };
248
- int exit_code ;
249
-
250
- req .vchan = vchan ;
251
- req .stdin_buf = stdin_buf ;
252
- req .stdin_fd = local_stdin_fd ;
253
- req .stdout_fd = local_stdout_fd ;
254
- req .stderr_fd = -1 ;
255
- req .local_pid = local_pid ;
256
- req .is_service = is_service ;
257
- req .replace_chars_stdout = replace_chars_stdout ;
258
- req .replace_chars_stderr = replace_chars_stderr ;
259
- req .data_protocol_version = data_protocol_version ;
260
- req .sigchld = & sigchld ;
261
- req .sigusr1 = NULL ;
262
- req .prefix_data .data = NULL ;
263
- req .prefix_data .len = 0 ;
264
-
265
- exit_code = process_io (& req );
266
- libvchan_close (vchan );
267
- exit (exit_with_code ? exit_code : 0 );
268
- }
269
-
270
76
static struct option longopts [] = {
271
77
{ "help" , no_argument , 0 , 'h' },
272
78
{ "socket-dir" , required_argument , 0 , 'd' + 128 },
@@ -337,12 +143,6 @@ static void parse_connect(char *str, char **request_id,
337
143
exit (1 );
338
144
}
339
145
340
- static void sigalrm_handler (int x __attribute__((__unused__ )))
341
- {
342
- LOG (ERROR , "vchan connection timeout" );
343
- exit (1 );
344
- }
345
-
346
146
static const long BILLION_NANOSECONDS = 1000000000L ;
347
147
348
148
static void wait_for_vchan_client_with_timeout (libvchan_t * conn , time_t timeout ) {
@@ -404,23 +204,6 @@ static size_t compute_service_length(const char *const remote_cmdline, const cha
404
204
return service_length ;
405
205
}
406
206
407
- static void handshake_and_go (libvchan_t * data_vchan ,
408
- struct buffer * stdin_buffer ,
409
- bool remote_send_first ,
410
- int prepare_ret )
411
- {
412
- if (data_vchan == NULL || !libvchan_is_open (data_vchan )) {
413
- LOG (ERROR , "Failed to open data vchan connection" );
414
- exit (1 );
415
- }
416
- int data_protocol_version = handle_agent_handshake (data_vchan , remote_send_first );
417
- if (data_protocol_version < 0 )
418
- exit (1 );
419
- if (prepare_ret < 0 )
420
- handle_failed_exec (data_vchan );
421
- select_loop (data_vchan , data_protocol_version , stdin_buffer );
422
- }
423
-
424
207
int main (int argc , char * * argv )
425
208
{
426
209
int opt ;
@@ -440,6 +223,9 @@ int main(int argc, char **argv)
440
223
struct service_params svc_params ;
441
224
int prepare_ret ;
442
225
bool kill = false;
226
+ bool replace_chars_stdout = false;
227
+ bool replace_chars_stderr = false;
228
+ bool exit_with_code = true;
443
229
int rc = 126 ;
444
230
445
231
if (argc < 3 ) {
@@ -461,15 +247,14 @@ int main(int argc, char **argv)
461
247
just_exec = true;
462
248
break ;
463
249
case 'E' :
464
- exit_with_code = 0 ;
250
+ exit_with_code = false ;
465
251
break ;
466
252
case 'c' :
467
253
if (request_id != NULL ) {
468
254
warnx ("ERROR: -c passed more than once" );
469
255
usage (argv [0 ]);
470
256
}
471
257
parse_connect (optarg , & request_id , & src_domain_name , & src_domain_id );
472
- is_service = true;
473
258
break ;
474
259
case 't' :
475
260
replace_chars_stdout = true;
@@ -518,39 +303,12 @@ int main(int argc, char **argv)
518
303
LOG (ERROR , "internal error: src_domain_name should not be NULL here" );
519
304
abort ();
520
305
}
521
- set_remote_domain (src_domain_name );
522
- s = connect_unix_socket_by_id (src_domain_id );
523
- if (s < 0 ) {
524
- goto cleanup ;
525
- }
526
- if (!negotiate_connection_params (s ,
527
- 0 , /* dom0 */
528
- MSG_SERVICE_CONNECT ,
529
- (void * )& svc_params ,
530
- sizeof (svc_params ),
531
- & data_domain ,
532
- & data_port )) {
533
- goto cleanup ;
534
- }
535
-
536
- struct buffer stdin_buffer ;
537
- buffer_init (& stdin_buffer );
538
- if (!wait_for_session_maybe (remote_cmdline )) {
539
- LOG (ERROR , "Cannot load service configuration, or forking process failed" );
540
- prepare_ret = -1 ;
541
- } else {
542
- prepare_ret = prepare_local_fds (remote_cmdline , & stdin_buffer );
543
- }
544
- void (* old_handler )(int );
545
-
546
- /* libvchan_client_init is blocking and does not support connection
547
- * timeout, so use alarm(2) for that... */
548
- old_handler = signal (SIGALRM , sigalrm_handler );
549
- alarm (connection_timeout );
550
- data_vchan = libvchan_client_init (data_domain , data_port );
551
- alarm (0 );
552
- signal (SIGALRM , old_handler );
553
- handshake_and_go (data_vchan , & stdin_buffer , true, prepare_ret );
306
+ rc = run_qrexec_to_dom0 (& svc_params ,
307
+ src_domain_id ,
308
+ src_domain_name ,
309
+ remote_cmdline ,
310
+ connection_timeout ,
311
+ exit_with_code );
554
312
} else {
555
313
s = connect_unix_socket (domname );
556
314
if (!negotiate_connection_params (s ,
@@ -590,15 +348,36 @@ int main(int argc, char **argv)
590
348
set_remote_domain (domname );
591
349
struct buffer stdin_buffer ;
592
350
buffer_init (& stdin_buffer );
593
- prepare_ret = prepare_local_fds (local_cmdline , & stdin_buffer );
351
+ if (local_cmdline != NULL ) {
352
+ struct qrexec_parsed_command * command =
353
+ parse_qubes_rpc_command (local_cmdline , false);
354
+ if (!command )
355
+ prepare_ret = 127 ;
356
+ else {
357
+ prepare_ret = prepare_local_fds (command , & stdin_buffer );
358
+ destroy_qrexec_parsed_command (command );
359
+ }
360
+ } else {
361
+ prepare_ret = 0 ;
362
+ }
363
+
594
364
data_vchan = libvchan_server_init (data_domain , data_port ,
595
365
VCHAN_BUFFER_SIZE , VCHAN_BUFFER_SIZE );
596
366
if (!data_vchan ) {
597
367
LOG (ERROR , "Failed to start data vchan server" );
598
368
exit (1 );
599
369
}
600
370
wait_for_vchan_client_with_timeout (data_vchan , connection_timeout );
601
- handshake_and_go (data_vchan , & stdin_buffer , false, prepare_ret );
371
+ struct handshake_params params = {
372
+ .data_vchan = data_vchan ,
373
+ .stdin_buffer = & stdin_buffer ,
374
+ .remote_send_first = false,
375
+ .prepare_ret = prepare_ret ,
376
+ .exit_with_code = exit_with_code ,
377
+ .replace_chars_stdout = replace_chars_stdout ,
378
+ .replace_chars_stderr = replace_chars_stderr ,
379
+ };
380
+ rc = handshake_and_go (& params );
602
381
}
603
382
}
604
383
0 commit comments