@@ -282,7 +282,7 @@ static int load_service_config_raw(struct qrexec_parsed_command *cmd,
282
282
283
283
int ret = find_file (config_path , cmd -> service_descriptor ,
284
284
config_full_path , sizeof (config_full_path ), NULL );
285
- if (ret == -1 && strcmp ( cmd -> service_descriptor , cmd -> service_name ) != 0 )
285
+ if (ret == -1 )
286
286
ret = find_file (config_path , cmd -> service_name ,
287
287
config_full_path , sizeof (config_full_path ), NULL );
288
288
if (ret < 0 )
@@ -311,6 +311,21 @@ int load_service_config(struct qrexec_parsed_command *cmd,
311
311
return rc ;
312
312
}
313
313
314
+ /* Duplicates a buffer and adds a NUL terminator.
315
+ * Same as strndup(), except that it logs on failure (with PERROR())
316
+ * and always copies exactly "len" bytes, even if some of them are NUL
317
+ * bytes. This guarantees that the output buffer is of the expected
318
+ * length and saves an unneeded call to strnlen(). */
319
+ static char * memdupnul (const char * ptr , size_t len ) {
320
+ char * buf = malloc (len + 1 );
321
+ if (buf == NULL ) {
322
+ PERROR ("malloc" );
323
+ return NULL ;
324
+ }
325
+ memcpy (buf , ptr , len );
326
+ buf [len ] = '\0' ;
327
+ return buf ;
328
+ }
314
329
315
330
struct qrexec_parsed_command * parse_qubes_rpc_command (
316
331
const char * cmdline , bool strip_username ) {
@@ -332,11 +347,9 @@ struct qrexec_parsed_command *parse_qubes_rpc_command(
332
347
LOG (ERROR , "Bad command from dom0 (%s): no colon" , cmdline );
333
348
goto err ;
334
349
}
335
- cmd -> username = strndup (cmdline , (size_t )(colon - cmdline ));
336
- if (!cmd -> username ) {
337
- PERROR ("strndup" );
350
+ cmd -> username = memdupnul (cmdline , (size_t )(colon - cmdline ));
351
+ if (!cmd -> username )
338
352
goto err ;
339
- }
340
353
cmd -> command = colon + 1 ;
341
354
} else
342
355
cmd -> command = cmdline ;
@@ -361,53 +374,47 @@ struct qrexec_parsed_command *parse_qubes_rpc_command(
361
374
goto err ;
362
375
}
363
376
364
- if (end - start > MAX_SERVICE_NAME_LEN ) {
365
- LOG (ERROR , "Command too long (length %zu)" ,
366
- end - start );
377
+ if (end <= start ) {
378
+ LOG (ERROR , "Service descriptor is empty (too many spaces after QUBESRPC?)" );
367
379
goto err ;
368
380
}
369
381
370
- cmd -> service_descriptor = strndup ( start , end - start );
371
- if (! cmd -> service_descriptor ) {
372
- PERROR ( "strndup" );
382
+ size_t const descriptor_len = ( size_t )( end - start );
383
+ if (descriptor_len > MAX_SERVICE_NAME_LEN ) {
384
+ LOG ( ERROR , "Command too long (length %zu)" , descriptor_len );
373
385
goto err ;
374
386
}
375
387
376
388
/* Parse service name ("qubes.Service") */
377
389
378
- const char * plus = memchr (start , '+' , end - start );
379
- if (plus ) {
380
- if (plus - start == 0 ) {
381
- LOG (ERROR , "Service name empty" );
382
- goto err ;
383
- }
384
- if (plus - start > NAME_MAX ) {
385
- LOG (ERROR , "Service name too long to execute (length %zu)" ,
386
- plus - start );
387
- goto err ;
388
- }
389
- cmd -> service_name = strndup (start , plus - start );
390
- if (!cmd -> service_name ) {
391
- PERROR ("strndup" );
392
- goto err ;
393
- }
394
- } else {
395
- cmd -> service_name = strndup (start , end - start );
396
- if (!cmd -> service_name ) {
397
- PERROR ("strdup" );
398
- goto err ;
399
- }
390
+ const char * const plus = memchr (start , '+' , descriptor_len );
391
+ size_t const name_len = plus != NULL ? (size_t )(plus - start ) : descriptor_len ;
392
+ if (name_len > NAME_MAX ) {
393
+ LOG (ERROR , "Service name too long to execute (length %zu)" , name_len );
394
+ goto err ;
395
+ }
396
+ if (name_len < 1 ) {
397
+ LOG (ERROR , "Service name empty" );
398
+ goto err ;
400
399
}
400
+ cmd -> service_name = memdupnul (start , name_len );
401
+ if (!cmd -> service_name )
402
+ goto err ;
403
+
404
+ /* If there is no service argument, add a trailing "+" to the descriptor */
405
+ cmd -> service_descriptor = memdupnul (start , descriptor_len + (plus == NULL ));
406
+ if (!cmd -> service_descriptor )
407
+ goto err ;
408
+ if (plus == NULL )
409
+ cmd -> service_descriptor [descriptor_len ] = '+' ;
401
410
402
411
/* Parse source domain */
403
412
404
413
start = end + 1 ; /* after the space */
405
414
end = strchrnul (start , ' ' );
406
- cmd -> source_domain = strndup (start , end - start );
407
- if (!cmd -> source_domain ) {
408
- PERROR ("strndup" );
415
+ cmd -> source_domain = memdupnul (start , (size_t )(end - start ));
416
+ if (!cmd -> source_domain )
409
417
goto err ;
410
- }
411
418
}
412
419
413
420
return cmd ;
@@ -480,7 +487,7 @@ static int execute_qrexec_service(
480
487
int ret = find_file (qrexec_service_path , cmd -> service_descriptor ,
481
488
service_full_path , sizeof (service_full_path ),
482
489
& statbuf );
483
- if (ret == -1 && strcmp ( cmd -> service_descriptor , cmd -> service_name ) != 0 )
490
+ if (ret == -1 )
484
491
ret = find_file (qrexec_service_path , cmd -> service_name ,
485
492
service_full_path , sizeof (service_full_path ),
486
493
& statbuf );
0 commit comments