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,119 +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 void wait_for_session_maybe (char * cmdline )
151
- {
152
- int wait_for_session = 0 ;
153
- struct qrexec_parsed_command * cmd ;
154
- pid_t pid ;
155
- int status ;
156
-
157
- cmd = parse_qubes_rpc_command (cmdline , false);
158
- if (!cmd )
159
- goto out ;
160
-
161
- if (cmd -> nogui )
162
- goto out ;
163
-
164
- if (!cmd -> service_descriptor )
165
- goto out ;
166
-
167
- char * user = NULL ;
168
- load_service_config (cmd , & wait_for_session , & user );
169
- if (!wait_for_session )
170
- goto out ;
171
-
172
- pid = fork ();
173
- switch (pid ) {
174
- case 0 :
175
- close (0 );
176
- exec_wait_for_session (cmd -> source_domain );
177
- PERROR ("exec" );
178
- exit (1 );
179
- case -1 :
180
- PERROR ("fork" );
181
- goto out ;
182
- }
183
-
184
- if (waitpid (local_pid , & status , 0 ) > 0 ) {
185
- if (status != 0 )
186
- LOG (ERROR , "wait-for-session exited with status %d" , status );
187
- } else
188
- PERROR ("waitpid" );
189
-
190
- out :
191
- destroy_qrexec_parsed_command (cmd );
192
- }
193
-
194
- static int prepare_local_fds (char * cmdline , struct buffer * stdin_buffer )
195
- {
196
- if (stdin_buffer == NULL )
197
- abort ();
198
- if (!cmdline ) {
199
- local_stdin_fd = 1 ;
200
- local_stdout_fd = 0 ;
201
- return 0 ;
202
- }
203
- signal (SIGCHLD , sigchld_handler );
204
- return execute_qubes_rpc_command (cmdline , & local_pid , & local_stdin_fd , & local_stdout_fd ,
205
- NULL , false, stdin_buffer );
206
- }
207
-
208
- // See also qrexec-agent/qrexec-agent-data.c
209
- static _Noreturn void handle_failed_exec (libvchan_t * data_vchan )
210
- {
211
- int exit_code = 127 ;
212
- struct msg_header hdr = {
213
- .type = MSG_DATA_STDOUT ,
214
- .len = 0 ,
215
- };
216
-
217
- LOG (ERROR , "failed to spawn process, exiting" );
218
- /*
219
- * TODO: In case we fail to execute a *local* process (is_service false),
220
- * we should either
221
- * - exit even before connecting to remote domain, or
222
- * - send stdin EOF and keep waiting for remote exit code.
223
- *
224
- * That will require a slightly bigger refactoring. Right now it's not
225
- * important, because this function should handle QUBESRPC command failure
226
- * only (normal commands go through fork+exec), but it will be necessary
227
- * when we support sockets as a local process.
228
- */
229
- if (is_service ) {
230
- libvchan_send (data_vchan , & hdr , sizeof (hdr ));
231
- send_exit_code (data_vchan , exit_code );
232
- libvchan_close (data_vchan );
233
- }
234
- exit (exit_code );
235
- }
236
- static void select_loop (libvchan_t * vchan , int data_protocol_version , struct buffer * stdin_buf )
237
- {
238
- struct process_io_request req = { 0 };
239
- int exit_code ;
240
-
241
- req .vchan = vchan ;
242
- req .stdin_buf = stdin_buf ;
243
- req .stdin_fd = local_stdin_fd ;
244
- req .stdout_fd = local_stdout_fd ;
245
- req .stderr_fd = -1 ;
246
- req .local_pid = local_pid ;
247
- req .is_service = is_service ;
248
- req .replace_chars_stdout = replace_chars_stdout ;
249
- req .replace_chars_stderr = replace_chars_stderr ;
250
- req .data_protocol_version = data_protocol_version ;
251
- req .sigchld = & sigchld ;
252
- req .sigusr1 = NULL ;
253
- req .prefix_data .data = NULL ;
254
- req .prefix_data .len = 0 ;
255
-
256
- exit_code = process_io (& req );
257
- libvchan_close (vchan );
258
- exit (exit_with_code ? exit_code : 0 );
259
- }
260
-
261
76
static struct option longopts [] = {
262
77
{ "help" , no_argument , 0 , 'h' },
263
78
{ "socket-dir" , required_argument , 0 , 'd' + 128 },
@@ -328,12 +143,6 @@ static void parse_connect(char *str, char **request_id,
328
143
exit (1 );
329
144
}
330
145
331
- static void sigalrm_handler (int x __attribute__((__unused__ )))
332
- {
333
- LOG (ERROR , "vchan connection timeout" );
334
- exit (1 );
335
- }
336
-
337
146
static const long BILLION_NANOSECONDS = 1000000000L ;
338
147
339
148
static void wait_for_vchan_client_with_timeout (libvchan_t * conn , time_t timeout ) {
@@ -395,23 +204,6 @@ static size_t compute_service_length(const char *const remote_cmdline, const cha
395
204
return service_length ;
396
205
}
397
206
398
- static void handshake_and_go (libvchan_t * data_vchan ,
399
- struct buffer * stdin_buffer ,
400
- bool remote_send_first ,
401
- int prepare_ret )
402
- {
403
- if (data_vchan == NULL || !libvchan_is_open (data_vchan )) {
404
- LOG (ERROR , "Failed to open data vchan connection" );
405
- exit (1 );
406
- }
407
- int data_protocol_version = handle_agent_handshake (data_vchan , remote_send_first );
408
- if (data_protocol_version < 0 )
409
- exit (1 );
410
- if (prepare_ret < 0 )
411
- handle_failed_exec (data_vchan );
412
- select_loop (data_vchan , data_protocol_version , stdin_buffer );
413
- }
414
-
415
207
int main (int argc , char * * argv )
416
208
{
417
209
int opt ;
@@ -431,6 +223,9 @@ int main(int argc, char **argv)
431
223
struct service_params svc_params ;
432
224
int prepare_ret ;
433
225
bool kill = false;
226
+ bool replace_chars_stdout = false;
227
+ bool replace_chars_stderr = false;
228
+ bool exit_with_code = true;
434
229
int rc = 126 ;
435
230
436
231
setup_logging ("qrexec-client" );
@@ -447,11 +242,14 @@ int main(int argc, char **argv)
447
242
just_exec = true;
448
243
break ;
449
244
case 'E' :
450
- exit_with_code = 0 ;
245
+ exit_with_code = false ;
451
246
break ;
452
247
case 'c' :
248
+ if (request_id != NULL ) {
249
+ warnx ("ERROR: -c passed more than once" );
250
+ usage (argv [0 ]);
251
+ }
453
252
parse_connect (optarg , & request_id , & src_domain_name , & src_domain_id );
454
- is_service = true;
455
253
break ;
456
254
case 't' :
457
255
replace_chars_stdout = true;
@@ -500,35 +298,12 @@ int main(int argc, char **argv)
500
298
LOG (ERROR , "internal error: src_domain_name should not be NULL here" );
501
299
abort ();
502
300
}
503
- set_remote_domain (src_domain_name );
504
- s = connect_unix_socket_by_id (src_domain_id );
505
- if (s < 0 ) {
506
- goto cleanup ;
507
- }
508
- if (!negotiate_connection_params (s ,
509
- 0 , /* dom0 */
510
- MSG_SERVICE_CONNECT ,
511
- (void * )& svc_params ,
512
- sizeof (svc_params ),
513
- & data_domain ,
514
- & data_port )) {
515
- goto cleanup ;
516
- }
517
-
518
- struct buffer stdin_buffer ;
519
- buffer_init (& stdin_buffer );
520
- wait_for_session_maybe (remote_cmdline );
521
- prepare_ret = prepare_local_fds (remote_cmdline , & stdin_buffer );
522
- void (* old_handler )(int );
523
-
524
- /* libvchan_client_init is blocking and does not support connection
525
- * timeout, so use alarm(2) for that... */
526
- old_handler = signal (SIGALRM , sigalrm_handler );
527
- alarm (connection_timeout );
528
- data_vchan = libvchan_client_init (data_domain , data_port );
529
- alarm (0 );
530
- signal (SIGALRM , old_handler );
531
- handshake_and_go (data_vchan , & stdin_buffer , true, prepare_ret );
301
+ rc = run_qrexec_to_dom0 (& svc_params ,
302
+ src_domain_id ,
303
+ src_domain_name ,
304
+ remote_cmdline ,
305
+ connection_timeout ,
306
+ exit_with_code );
532
307
} else {
533
308
s = connect_unix_socket (domname );
534
309
if (!negotiate_connection_params (s ,
@@ -576,7 +351,16 @@ int main(int argc, char **argv)
576
351
exit (1 );
577
352
}
578
353
wait_for_vchan_client_with_timeout (data_vchan , connection_timeout );
579
- handshake_and_go (data_vchan , & stdin_buffer , false, prepare_ret );
354
+ struct handshake_params params = {
355
+ .data_vchan = data_vchan ,
356
+ .stdin_buffer = & stdin_buffer ,
357
+ .remote_send_first = false,
358
+ .prepare_ret = prepare_ret ,
359
+ .exit_with_code = exit_with_code ,
360
+ .replace_chars_stdout = replace_chars_stdout ,
361
+ .replace_chars_stderr = replace_chars_stderr ,
362
+ };
363
+ rc = handshake_and_go (& params );
580
364
}
581
365
}
582
366
0 commit comments