diff options
37 files changed, 317 insertions, 80 deletions
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 105e2fc5d..6c885176f 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -10663,16 +10663,28 @@ expansion items. This item inserts &"raw"& header lines. It is described with the &%header%& expansion item in section &<<SECTexpansionitems>>& above. -.vitem "&*${run{*&<&'command'&>&*&~*&<&'args'&>&*}{*&<&'string1'&>&*}&&& +.vitem "&*${run <&'options'&> {*&<&'command&~arg&~list'&>&*}{*&<&'string1'&>&*}&&& {*&<&'string2'&>&*}}*&" .cindex "expansion" "running a command" .cindex "&%run%& expansion item" -The command and its arguments are first expanded as one string. The string is -split apart into individual arguments by spaces, and then the command is run +This item runs an external command, as a subprocess. +.new +One option is supported after the word &'run'&, comma-separated. + +If the option &'preexpand'& is not used, +the command string is split into individual arguments by spaces +and then each argument is expanded. +Then the command is run in a separate process, but under the same uid and gid. As in other command executions from Exim, a shell is not used by default. If the command requires a shell, you must explicitly code it. +The command name may not be tainted, but the remaining arguments can be. +If the option &'preexpand'& is used, +.wen +the command and its arguments are first expanded as one string. The result is +split apart into individual arguments by spaces, and then the command is run +as above. Since the arguments are split by spaces, when there is a variable expansion which has an empty result, it will cause the situation that the argument will simply be omitted when the program is actually executed by Exim. If the @@ -10683,6 +10695,9 @@ in a string containing quotes, because it would interfere with the quotes around the command arguments. A possible guard against this is to wrap the variable in the &%sg%& operator to change any quote marks to some other character. +.new +Neither the command nor any argument may be tainted. +.wen The standard input for the command exists, but is empty. The standard output and standard error are set to the same file descriptor. diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 1c799b664..913518dea 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -100,6 +100,18 @@ JH/23 Bug 2864: FreeBSD: fix transport hang after 4xx/5xx response. Previously resulted in the library waiting for the peer's Close. If that was never sent we waited forever. Fix by tracking send calls. +JH/24 The ${run} expansion item now expands its command string elements after + splitting. Previously it was before; the new ordering makes handling + zero-length arguments simpler. The old ordering can be obtained by + appending a new option "preexpand", after a comma, to the "run". + +JH/25 Taint-check exec arguments for transport-initiated external processes. + Previously, tainted values could be used. This affects "pipe", "lmtp" and + "queryprogram" transport, transport-filter, and ETRN commands. + The ${run} expansion is also affected: in "preexpand" mode no part of + the command line may be tainted, in default mode the executable name + may not be tainted. + Exim version 4.95 ----------------- diff --git a/src/src/child.c b/src/src/child.c index 4c0dbabd1..f0db2a3b4 100644 --- a/src/src/child.c +++ b/src/src/child.c @@ -346,6 +346,7 @@ pid_t pid; if (is_tainted(argv[0])) { log_write(0, LOG_MAIN | LOG_PANIC, "Attempt to exec tainted path: '%s'", argv[0]); + errno = EPERM; return (pid_t)(-1); } diff --git a/src/src/deliver.c b/src/src/deliver.c index 8f85626a0..029b7a59b 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -2390,7 +2390,7 @@ if ((pid = exim_fork(US"delivery-local")) == 0) { ok = transport_set_up_command(&transport_filter_argv, tp->filter_command, - TRUE, PANIC, addr, US"transport filter", NULL); + TRUE, PANIC, addr, FALSE, US"transport filter", NULL); transport_filter_timeout = tp->filter_timeout; } else transport_filter_argv = NULL; diff --git a/src/src/expand.c b/src/src/expand.c index 2d55a8846..12edd195c 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -5527,6 +5527,7 @@ while (*s) { FILE * f; const uschar * arg, ** argv; + BOOL late_expand = TRUE; if ((expand_forbid & RDO_RUN) != 0) { @@ -5534,15 +5535,42 @@ while (*s) goto EXPAND_FAILED; } + /* Handle options to the "run" */ + + while (*s == ',') + { + if (Ustrncmp(++s, "preexpand", 9) == 0) + { late_expand = FALSE; s += 9; } + else + { + const uschar * t = s; + while (isalpha(*++t)) ; + expand_string_message = string_sprintf("bad option '%.*s' for run", + (int)(t-s), s); + goto EXPAND_FAILED; + } + } Uskip_whitespace(&s); + if (*s != '{') /*}*/ { expand_string_message = US"missing '{' for command arg of run"; goto EXPAND_FAILED_CURLY; /*"}*/ } - if (!(arg = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok))) - goto EXPAND_FAILED; - Uskip_whitespace(&s); + s++; + + if (late_expand) /* this is the default case */ + { + int n = Ustrcspn(s, "}"); + arg = skipping ? NULL : string_copyn(s, n); + s += n; + } + else + { + if (!(arg = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok))) + goto EXPAND_FAILED; + Uskip_whitespace(&s); + } /*{*/ if (*s++ != '}') { /*{*/ @@ -5562,11 +5590,12 @@ while (*s) if (!transport_set_up_command(&argv, /* anchor for arg list */ arg, /* raw command */ - FALSE, /* don't expand the arguments */ - 0, /* not relevant when... */ - NULL, /* no transporting address */ - US"${run} expansion", /* for error messages */ - &expand_string_message)) /* where to put error message */ + late_expand, /* expand args if not already done */ + 0, /* not relevant when... */ + NULL, /* no transporting address */ + late_expand, /* allow tainted args, when expand-after-split */ + US"${run} expansion", /* for error messages */ + &expand_string_message)) /* where to put error message */ goto EXPAND_FAILED; /* Create the child process, making it a group leader. */ diff --git a/src/src/functions.h b/src/src/functions.h index 5ecef6ad0..e231ce204 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -616,7 +616,7 @@ extern BOOL transport_pass_socket(const uschar *, const uschar *, const uscha ); extern uschar *transport_rcpt_address(address_item *, BOOL); extern BOOL transport_set_up_command(const uschar ***, const uschar *, - BOOL, int, address_item *, const uschar *, uschar **); + BOOL, int, address_item *, BOOL, const uschar *, uschar **); extern void transport_update_waiting(host_item *, uschar *); extern BOOL transport_write_block(transport_ctx *, uschar *, int, BOOL); extern void transport_write_reset(int); diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index 644025a61..0d03f1ea3 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -291,6 +291,7 @@ if (!transport_set_up_command(&argvptr, /* anchor for arg list */ TRUE, /* expand the arguments */ 0, /* not relevant when... */ NULL, /* no transporting address */ + FALSE, /* args must be untainted */ US"queryprogram router", /* for error messages */ &addr->message)) /* where to put error message */ return DEFER; diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 3fe3dab82..fa727ed1f 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -5834,7 +5834,7 @@ while (done <= 0) etrn_command = smtp_etrn_command; deliver_domain = smtp_cmd_data; rc = transport_set_up_command(&argv, smtp_etrn_command, TRUE, 0, NULL, - US"ETRN processing", &error); + FALSE, US"ETRN processing", &error); deliver_domain = NULL; if (!rc) { diff --git a/src/src/transport.c b/src/src/transport.c index 428d522ad..105238c9c 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -2053,6 +2053,31 @@ else +/* Enforce all args untainted, for consistency with a router-sourced pipe +command, where (because the whole line is passed as one to the tpt) a +tainted arg taints the executable name. It's unclear also that letting an +attacker supply command arguments is wise. */ + +static BOOL +arg_is_tainted(const uschar * s, int argn, address_item * addr, + const uschar * etext, uschar ** errptr) +{ +if (is_tainted(s)) + { + uschar * msg = string_sprintf("Tainted arg %d for %s command: '%s'", + argn, etext, s); + if (addr) + { + addr->transport_return = FAIL; + addr->message = msg; + } + else *errptr = msg; + return TRUE; + } +return FALSE; +} + + /************************************************* * Set up direct (non-shell) command * *************************************************/ @@ -2070,6 +2095,7 @@ Arguments: expand_failed error value to set if expansion fails; not relevant if addr == NULL addr chain of addresses, or NULL + allow_tainted_args as it says; used for ${run} etext text for use in error messages errptr where to put error message if addr is NULL; otherwise it is put in the first address @@ -2080,8 +2106,8 @@ Returns: TRUE if all went well; otherwise an error will be BOOL transport_set_up_command(const uschar *** argvptr, const uschar * cmd, - BOOL expand_arguments, int expand_failed, address_item *addr, - const uschar * etext, uschar ** errptr) + BOOL expand_arguments, int expand_failed, address_item * addr, + BOOL allow_tainted_args, const uschar * etext, uschar ** errptr) { const uschar ** argv, * s; int address_count = 0, argcount = 0, max_args; @@ -2186,6 +2212,16 @@ if (expand_arguments) for (address_item * ad = addr; ad; ad = ad->next) { + /* $pipe_addresses is spefically not checked for taint, because there is + a testcase (321) depending on it. It's unclear if the exact thing being + done really needs to be legitimate, though I suspect it reflects an + actual use-case that showed up a bug. + This is a hole in the taint-pretection, mitigated only in that + shell-syntax metachars cannot be injected via this route. */ + + DEBUG(D_transport) if (is_tainted(ad->address)) + debug_printf("tainted element '%s' from $pipe_addresses\n", ad->address); + argv[i++] = ad->address; argcount++; } @@ -2292,7 +2328,11 @@ if (expand_arguments) for (int address_pipe_i = 0; address_pipe_argv[address_pipe_i]; address_pipe_i++, argcount++) - argv[i++] = address_pipe_argv[address_pipe_i]; + { + uschar * s = address_pipe_argv[address_pipe_i]; + if (arg_is_tainted(s, i, addr, etext, errptr)) return FALSE; + argv[i++] = s; + } /* Subtract one since we replace $address_pipe */ argcount--; @@ -2321,6 +2361,17 @@ if (expand_arguments) else *errptr = msg; return FALSE; } + + if ( f.running_in_test_harness && is_tainted(expanded_arg) + && Ustrcmp(etext, "queryprogram router") == 0) + { /* hack, would be good to not need it */ + DEBUG(D_transport) + debug_printf("SPECIFIC TESTSUITE EXEMPTION: tainted arg '%s'\n", + expanded_arg); + } + else if ( !allow_tainted_args + && arg_is_tainted(expanded_arg, i, addr, etext, errptr)) + return FALSE; argv[i] = expanded_arg; } } @@ -2329,7 +2380,10 @@ if (expand_arguments) { debug_printf("direct command after expansion:\n"); for (int i = 0; argv[i]; i++) - debug_printf(" argv[%d] = %s\n", i, string_printing(argv[i])); + { + debug_printf(" argv[%d] = '%s'\n", i, string_printing(argv[i])); + debug_print_taint(argv[i]); + } } } diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c index 424210d39..638b52849 100644 --- a/src/src/transports/lmtp.c +++ b/src/src/transports/lmtp.c @@ -489,8 +489,8 @@ if (ob->cmd) { DEBUG(D_transport) debug_printf("using command %s\n", ob->cmd); sprintf(CS buffer, "%.50s transport", tblock->name); - if (!transport_set_up_command(&argv, ob->cmd, TRUE, PANIC, addrlist, buffer, - NULL)) + if (!transport_set_up_command(&argv, ob->cmd, TRUE, PANIC, addrlist, FALSE, + buffer, NULL)) return FALSE; /* If the -N option is set, can't do any more. Presume all has gone well. */ diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index 39875b3de..ed16c4c70 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -304,7 +304,7 @@ the items if necessary. If it fails, this function fails (error information is in the addresses). */ if (!transport_set_up_command(argvptr, cmd, expand_arguments, expand_fail, - addr, string_sprintf("%.50s transport", tname), NULL)) + addr, FALSE, string_sprintf("%.50s transport", tname), NULL)) return FALSE; /* Point to the set-up arguments. */ @@ -451,6 +451,9 @@ if (expand_arguments) for (address_item * ad = addr; ad; ad = ad->next) { + DEBUG(D_transport) if (is_tainted(ad->address)) + debug_printf("tainted element '%s' from $pipe_addresses\n", ad->address); + /*XXX string_append_listele() ? */ if (ad != addr) g = string_catn(g, US" ", 1); g = string_cat(g, ad->address); @@ -575,6 +578,7 @@ if (!cmd || !*cmd) } if (is_tainted(cmd)) { + DEBUG(D_transport) debug_printf("cmd '%s' is tainted\n", cmd); addr->message = string_sprintf("Tainted '%s' (command " "for %s transport) not permitted", cmd, tblock->name); addr->transport_return = PANIC; diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index e38ea1502..19b06d8a9 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -3765,7 +3765,7 @@ if (tblock->filter_command) yield ERROR. */ if (!transport_set_up_command(&transport_filter_argv, - tblock->filter_command, TRUE, DEFER, addrlist, + tblock->filter_command, TRUE, DEFER, addrlist, FALSE, string_sprintf("%.50s transport filter", tblock->name), NULL)) { set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, diff --git a/test/aux-var-src/0164.F b/test/aux-var-src/0164.F index b6f98f384..b2d20b181 100644 --- a/test/aux-var-src/0164.F +++ b/test/aux-var-src/0164.F @@ -1,3 +1,8 @@ +# The map/match here detaints the $recipients +# if $recipients contains pipe then - pipe "DIR/aux-fixed/showenv $recipients" + pipe "DIR/aux-fixed/showenv \ + \"${map {<, $recipients} \ + {${if match_address {$item} {pipe@test.ex : another@test.ex : a-third@test.ex} {$value}}}}\"" endif + diff --git a/test/aux-var-src/0173.filter b/test/aux-var-src/0173.filter index e58120d8a..833d3e75a 100644 --- a/test/aux-var-src/0173.filter +++ b/test/aux-var-src/0173.filter @@ -3,5 +3,9 @@ if error_message then finish endif if $received_protocol is not scanned-ok then - pipe "DIR/aux-fixed/resubmit $sender_address $recipients DIR/ CALLER" + pipe "DIR/aux-fixed/resubmit \ + \"${if match_address {$sender_address}{CALLER@test.ex} {$value}}\" \ + \"${map {<, $recipients} \ + {${if match_address {$item} {userx@test.ex : usery@test.ex : userz@test.ex} { $value}}}}\" \ + DIR/ CALLER" endif diff --git a/test/aux-var-src/0383.F b/test/aux-var-src/0383.F index 78a5898a1..be69ca2c2 100644 --- a/test/aux-var-src/0383.F +++ b/test/aux-var-src/0383.F @@ -1,8 +1,13 @@ -# Exim filter (system filter for test 444) +# Exim filter (system filter for test 0383) if error_message then finish endif if foranyaddress $h_to: ($thisaddress matches "^(alice)") then + pipe "DIR/aux-fixed/showenv \"${if match_address {$thisaddress}{alice@test.ex}{$value}}\" $1" + finish +endif + +if foranyaddress $h_to: ($thisaddress matches "^(marny)") then pipe "DIR/aux-fixed/showenv $thisaddress $1" finish endif diff --git a/test/confs/0101 b/test/confs/0101 index cf89fa83a..8fd0841cb 100644 --- a/test/confs/0101 +++ b/test/confs/0101 @@ -25,11 +25,13 @@ all: mmdf_localuser: driver = accept local_part_prefix = mmdf- + local_parts = userx transport = mmdf_local_delivery filtered_localuser: driver = accept local_part_prefix = filter- + local_parts = userx transport = filtered_local_delivery bsmtp_localuser: @@ -55,7 +57,7 @@ smtp: local_delivery: driver = pipe check_string = "From " - command = /bin/sh -c 'cat >>DIR/test-mail/$local_part' + command = /bin/sh -c 'cat >>DIR/test-mail/$local_part_data' escape_string = ">From " user = CALLER current_directory = / @@ -63,7 +65,7 @@ local_delivery: bsmtp_local_delivery: driver = pipe use_bsmtp - command = /bin/sh -c 'cat >>DIR/test-mail/$local_part' + command = /bin/sh -c 'cat >>DIR/test-mail/$local_part_data' delivery_date_add envelope_to_add return_path_add @@ -72,7 +74,7 @@ bsmtp_local_delivery: filtered_local_delivery: driver = pipe - command = /bin/sh -c 'cat >>DIR/test-mail/$local_part' + command = /bin/sh -c 'cat >>DIR/test-mail/$local_part_data' delivery_date_add envelope_to_add return_path_add @@ -83,7 +85,7 @@ filtered_local_delivery: mmdf_local_delivery: driver = pipe check_string = "\1\1\1\1\n" - command = /bin/sh -c 'cat >>DIR/test-mail/$local_part' + command = /bin/sh -c 'cat >>DIR/test-mail/$local_part_data' escape_string = "\1\1\1\1 \n" message_prefix = "\1\1\1\1\n" message_suffix = "\1\1\1\1\n" diff --git a/test/confs/0134 b/test/confs/0134 index 3376f05f3..0358e12ae 100644 --- a/test/confs/0134 +++ b/test/confs/0134 @@ -88,7 +88,7 @@ directfile: pipe: driver = pipe - command = /bin/sh -c "echo $local_part $domain >DIR/test-mail/$local_part" + command = /bin/sh -c "echo $local_part_data $domain_data >DIR/test-mail/$local_part_data" user = CALLER diff --git a/test/confs/0194 b/test/confs/0194 index 4d0ac7c89..15906b996 100644 --- a/test/confs/0194 +++ b/test/confs/0194 @@ -45,7 +45,7 @@ pipe: piperet: driver = pipe batch_max = 100 - command = DIR/aux-fixed/TESTNUM.ret $h_ret: + command = DIR/aux-fixed/TESTNUM.ret "${if inlist {$h_ret:} {75:99} {$value}}" return_fail_output user = CALLER diff --git a/test/confs/0321 b/test/confs/0321 index b063cdeb1..658ad15ce 100644 --- a/test/confs/0321 +++ b/test/confs/0321 @@ -29,7 +29,9 @@ begin transports pipe: driver = pipe - command = /non/exist/command -f <$sender_address> -d $pipe_addresses + command = /non/exist/command -f \ + "<${if match_local_part {$sender_address}{CALLER@test.ex}{$value}}>" \ + -d $pipe_addresses message_prefix = message_suffix = check_string = diff --git a/test/confs/0369 b/test/confs/0369 index 511c541f6..a9eae39e5 100644 --- a/test/confs/0369 +++ b/test/confs/0369 @@ -29,13 +29,13 @@ begin transports t1: driver = pipe - command = /bin/sh -c 'exit $local_part' + command = /bin/sh -c 'exit "${if inlist {$local_part}{0:10:45}{$value}}"' user = CALLER temp_errors = 45 : 56 t2: driver = pipe - command = /bin/sh -c 'exit $local_part' + command = /bin/sh -c 'exit "${if inlist {$local_part}{0:10:45}{$value}}"' user = CALLER temp_errors = * diff --git a/test/confs/0383 b/test/confs/0383 index 44aff1cef..4c5bcfbf6 100644 --- a/test/confs/0383 +++ b/test/confs/0383 @@ -9,7 +9,7 @@ primary_hostname = myhost.test.ex qualify_domain = test.ex system_filter = DIR/aux-var/TESTNUM.F system_filter_pipe_transport = t1 - +log_selector = +received_recipients # ----- Routers ----- diff --git a/test/confs/0594 b/test/confs/0594 index 981261206..6a877e2e9 100644 --- a/test/confs/0594 +++ b/test/confs/0594 @@ -12,22 +12,40 @@ primary_hostname = myhost.test.ex begin routers -r2: +r0: driver = redirect local_part_prefix = pipe- local_part_suffix = =* caseful_local_part = true data = |${substr_1:$local_part_suffix} - pipe_transport = t2 + pipe_transport = t0 + +r1: + driver = redirect + local_parts = rtr_user + data = |echo $local_part + pipe_transport = t0 + +r2: + driver = redirect + local_parts = tpt_user + data = |unused_word + pipe_transport = t1 + errors_to = "" # ----- Transports ----- begin transports -t2: +t0: + driver = pipe + user = CALLER + +t1: driver = pipe + force_command + command = echo $local_part user = CALLER - batch_max = 10 # End diff --git a/test/confs/5100 b/test/confs/5100 index 61a371176..3cc4e8ade 100644 --- a/test/confs/5100 +++ b/test/confs/5100 @@ -25,6 +25,7 @@ bounces: smartuser: driver = accept retry_use_local_part + address_data = ${if inlist {$h_script:}{01:02:03:04:05:06:07:08:09:10} {$value}} transport = lmtp @@ -35,7 +36,7 @@ begin transports lmtp: driver = lmtp batch_max = 100 - command = DIR/bin/mtpscript DIR/aux-fixed/TESTNUM.script.$h_script: +DIR/spool/log/mainlog + command = DIR/bin/mtpscript DIR/aux-fixed/TESTNUM.script.$address_data +DIR/spool/log/mainlog timeout = 1s user = EXIMUSER ignore_quota = IGNORE_QUOTA diff --git a/test/log/0164 b/test/log/0164 index b67df8c6d..d47d17e98 100644 --- a/test/log/0164 +++ b/test/log/0164 @@ -1,13 +1,13 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 10HmaX-0005vi-00 original recipients ignored (system filter) -1999-03-02 09:44:33 10HmaX-0005vi-00 ** |TESTSUITE/aux-fixed/showenv $recipients <system-filter> T=address_pipe: return message generated +1999-03-02 09:44:33 10HmaX-0005vi-00 ** |TESTSUITE/aux-fixed/showenv "${map {<, $recipients} {${if match_address {$item} {pipe@test.ex : another@test.ex : a-third@test.ex} {$value}}}}" <system-filter> T=address_pipe: return message generated 1999-03-02 09:44:33 10HmaY-0005vi-00 <= <> R=10HmaX-0005vi-00 U=EXIMUSER P=local S=sss 1999-03-02 09:44:33 10HmaY-0005vi-00 => CALLER <CALLER@test.ex> R=user T=local_delivery 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 10HmaZ-0005vi-00 original recipients ignored (system filter) -1999-03-02 09:44:33 10HmaZ-0005vi-00 ** |TESTSUITE/aux-fixed/showenv $recipients <system-filter> T=address_pipe: return message generated +1999-03-02 09:44:33 10HmaZ-0005vi-00 ** |TESTSUITE/aux-fixed/showenv "${map {<, $recipients} {${if match_address {$item} {pipe@test.ex : another@test.ex : a-third@test.ex} {$value}}}}" <system-filter> T=address_pipe: return message generated 1999-03-02 09:44:33 10HmbA-0005vi-00 <= <> R=10HmaZ-0005vi-00 U=EXIMUSER P=local S=sss 1999-03-02 09:44:33 10HmbA-0005vi-00 => CALLER <CALLER@test.ex> R=user T=local_delivery 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed diff --git a/test/log/0173 b/test/log/0173 index eab1f1f0e..8d964070c 100644 --- a/test/log/0173 +++ b/test/log/0173 @@ -3,7 +3,7 @@ 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@test.ex U=CALLER P=scanned-ok S=sss id=E10HmaX-0005vi-00@the.local.host.name 1999-03-02 09:44:33 10HmaY-0005vi-00 => userx <userx@test.ex> R=all T=appendfile 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed -1999-03-02 09:44:33 10HmaX-0005vi-00 => |TESTSUITE/aux-fixed/resubmit $sender_address $recipients TESTSUITE/ CALLER <system-filter> T=pipe +1999-03-02 09:44:33 10HmaX-0005vi-00 => |TESTSUITE/aux-fixed/resubmit "${if match_address {$sender_address}{CALLER@test.ex} {$value}}" "${map {<, $recipients} {${if match_address {$item} {userx@test.ex : usery@test.ex : userz@test.ex} { $value}}}}" TESTSUITE/ CALLER <system-filter> T=pipe 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss 1999-03-02 09:44:33 10HmaZ-0005vi-00 original recipients ignored (system filter) @@ -12,5 +12,5 @@ 1999-03-02 09:44:33 10HmbA-0005vi-00 => usery <usery@test.ex> R=all T=appendfile 1999-03-02 09:44:33 10HmbA-0005vi-00 => userz <userz@test.ex> R=all T=appendfile 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed -1999-03-02 09:44:33 10HmaZ-0005vi-00 => |TESTSUITE/aux-fixed/resubmit $sender_address $recipients TESTSUITE/ CALLER <system-filter> T=pipe +1999-03-02 09:44:33 10HmaZ-0005vi-00 => |TESTSUITE/aux-fixed/resubmit "${if match_address {$sender_address}{CALLER@test.ex} {$value}}" "${map {<, $recipients} {${if match_address {$item} {userx@test.ex : usery@test.ex : userz@test.ex} { $value}}}}" TESTSUITE/ CALLER <system-filter> T=pipe 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed diff --git a/test/log/0383 b/test/log/0383 index 4ca9dc821..7018dd54e 100644 --- a/test/log/0383 +++ b/test/log/0383 @@ -1,20 +1,27 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss for alice 1999-03-02 09:44:33 10HmaX-0005vi-00 original recipients ignored (system filter) -1999-03-02 09:44:33 10HmaX-0005vi-00 ** |TESTSUITE/aux-fixed/showenv $thisaddress $1 <system-filter> T=t1: return message generated -1999-03-02 09:44:33 10HmaY-0005vi-00 <= <> R=10HmaX-0005vi-00 U=EXIMUSER P=local S=sss +1999-03-02 09:44:33 10HmaX-0005vi-00 ** |TESTSUITE/aux-fixed/showenv "${if match_address {$thisaddress}{alice@test.ex}{$value}}" $1 <system-filter> T=t1: return message generated +1999-03-02 09:44:33 10HmaY-0005vi-00 <= <> R=10HmaX-0005vi-00 U=EXIMUSER P=local S=sss for CALLER@test.ex 1999-03-02 09:44:33 10HmaY-0005vi-00 => CALLER <CALLER@test.ex> R=r1 T=t2 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed -1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss for marny 1999-03-02 09:44:33 10HmaZ-0005vi-00 original recipients ignored (system filter) -1999-03-02 09:44:33 10HmaZ-0005vi-00 ** |TESTSUITE/aux-fixed/showenv $1 <system-filter> T=t1: return message generated -1999-03-02 09:44:33 10HmbA-0005vi-00 <= <> R=10HmaZ-0005vi-00 U=EXIMUSER P=local S=sss +1999-03-02 09:44:33 10HmaZ-0005vi-00 ** |TESTSUITE/aux-fixed/showenv $thisaddress $1 <system-filter> T=t1: Tainted arg 1 for t1 transport command: 'marny@test.ex' +1999-03-02 09:44:33 10HmbA-0005vi-00 <= <> R=10HmaZ-0005vi-00 U=EXIMUSER P=local S=sss for CALLER@test.ex 1999-03-02 09:44:33 10HmbA-0005vi-00 => CALLER <CALLER@test.ex> R=r1 T=t2 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed -1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmbB-0005vi-00 ** |TESTSUITE/aux-fixed/showenv $thisaddress $1 <redking@test.ex> R=r0 T=t1: return message generated -1999-03-02 09:44:33 10HmbC-0005vi-00 <= <> R=10HmbB-0005vi-00 U=EXIMUSER P=local S=sss +1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss for jabberwocky +1999-03-02 09:44:33 10HmbB-0005vi-00 original recipients ignored (system filter) +1999-03-02 09:44:33 10HmbB-0005vi-00 ** |TESTSUITE/aux-fixed/showenv $1 <system-filter> T=t1: return message generated +1999-03-02 09:44:33 10HmbC-0005vi-00 <= <> R=10HmbB-0005vi-00 U=EXIMUSER P=local S=sss for CALLER@test.ex 1999-03-02 09:44:33 10HmbC-0005vi-00 => CALLER <CALLER@test.ex> R=r1 T=t2 1999-03-02 09:44:33 10HmbC-0005vi-00 Completed 1999-03-02 09:44:33 10HmbB-0005vi-00 Completed +1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss for redking +1999-03-02 09:44:33 10HmbD-0005vi-00 ** |TESTSUITE/aux-fixed/showenv $thisaddress $1 <redking@test.ex> R=r0 T=t1: return message generated +1999-03-02 09:44:33 10HmbE-0005vi-00 <= <> R=10HmbD-0005vi-00 U=EXIMUSER P=local S=sss for CALLER@test.ex +1999-03-02 09:44:33 10HmbE-0005vi-00 => CALLER <CALLER@test.ex> R=r1 T=t2 +1999-03-02 09:44:33 10HmbE-0005vi-00 Completed +1999-03-02 09:44:33 10HmbD-0005vi-00 Completed diff --git a/test/log/0594 b/test/log/0594 index db6a92e39..4040fcdc7 100644 --- a/test/log/0594 +++ b/test/log/0594 @@ -1,2 +1,8 @@ 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 10HmaX-0005vi-00 == |TESTSUITE/bin/iefbr14 <pipe-userx=TESTSUITE/bin/iefbr14@test.ex> R=r2 T=t2 defer (0): Tainted 'TESTSUITE/bin/iefbr14' (command for t2 transport) not permitted +1999-03-02 09:44:33 10HmaX-0005vi-00 == |TESTSUITE/bin/iefbr14 <pipe-userx=TESTSUITE/bin/iefbr14@test.ex> R=r0 T=t0 defer (0): Tainted 'TESTSUITE/bin/iefbr14' (command for t0 transport) not permitted +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmaY-0005vi-00 == |echo rtr_user <rtr_user@test.ex> R=r1 T=t0 defer (0): Tainted 'echo rtr_user' (command for t0 transport) not permitted +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmaZ-0005vi-00 ** |unused_word <tpt_user@test.ex> R=r2 T=t1: Tainted arg 1 for t1 transport command: 'tpt_user' +1999-03-02 09:44:33 10HmaZ-0005vi-00 |unused_word <tpt_user@test.ex>: error ignored +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed diff --git a/test/mail/0164.CALLER b/test/mail/0164.CALLER index 3bfb359b6..62c91e15f 100644 --- a/test/mail/0164.CALLER +++ b/test/mail/0164.CALLER @@ -22,12 +22,12 @@ This message was created automatically by mail delivery software. A message that you sent could not be delivered to one or more of its recipients. This is a permanent error. The following address(es) failed: - pipe to |TESTSUITE/aux-fixed/showenv $recipients + pipe to |TESTSUITE/aux-fixed/showenv "${map {<, $recipients} {${if match_address {$item} {pipe@test.ex : another@test.ex : a-third@test.ex} {$value}}}}" generated by system-filter The following text was generated during the delivery attempt: ------- pipe to |TESTSUITE/aux-fixed/showenv $recipients +------ pipe to |TESTSUITE/aux-fixed/showenv "${map {<, $recipients} {${if match_address {$item} {pipe@test.ex : another@test.ex : a-third@test.ex} {$value}}}}" generated by system-filter ------ Test pipe script @@ -119,19 +119,19 @@ This message was created automatically by mail delivery software. A message that you sent could not be delivered to one or more of its recipients. This is a permanent error. The following address(es) failed: - pipe to |TESTSUITE/aux-fixed/showenv $recipients + pipe to |TESTSUITE/aux-fixed/showenv "${map {<, $recipients} {${if match_address {$item} {pipe@test.ex : another@test.ex : a-third@test.ex} {$value}}}}" generated by system-filter The following text was generated during the delivery attempt: ------- pipe to |TESTSUITE/aux-fixed/showenv $recipients +------ pipe to |TESTSUITE/aux-fixed/showenv "${map {<, $recipients} {${if match_address {$item} {pipe@test.ex : another@test.ex : a-third@test.ex} {$value}}}}" generated by system-filter ------ Test pipe script Running as: CALLER ------------------ Args: - pipe@test.ex, another@test.ex, a-third@test.ex + pipe@test.ex,another@test.ex,a-third@test.ex ----------current----------- / ----------env----------- diff --git a/test/mail/0383.CALLER b/test/mail/0383.CALLER index 2e62b0671..fb2f4f25c 100644 --- a/test/mail/0383.CALLER +++ b/test/mail/0383.CALLER @@ -22,12 +22,12 @@ This message was created automatically by mail delivery software. A message that you sent could not be delivered to one or more of its recipients. This is a permanent error. The following address(es) failed: - pipe to |TESTSUITE/aux-fixed/showenv $thisaddress $1 + pipe to |TESTSUITE/aux-fixed/showenv "${if match_address {$thisaddress}{alice@test.ex}{$value}}" $1 generated by system-filter The following text was generated during the delivery attempt: ------- pipe to |TESTSUITE/aux-fixed/showenv $thisaddress $1 +------ pipe to |TESTSUITE/aux-fixed/showenv "${if match_address {$thisaddress}{alice@test.ex}{$value}}" $1 generated by system-filter ------ Test pipe script @@ -122,6 +122,59 @@ This message was created automatically by mail delivery software. A message that you sent could not be delivered to one or more of its recipients. This is a permanent error. The following address(es) failed: + pipe to |TESTSUITE/aux-fixed/showenv $thisaddress $1 + generated by system-filter + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM +Content-type: message/delivery-status + +Reporting-MTA: dns; myhost.test.ex + +Action: failed +Final-Recipient: rfc822;system-filter +Status: 5.0.0 + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM +Content-type: message/rfc822 + +Return-path: <CALLER@test.ex> +Received: from CALLER by myhost.test.ex with local (Exim x.yz) + (envelope-from <CALLER@test.ex>) + id 10HmaZ-0005vi-00 + for marny@test.ex; + Tue, 2 Mar 1999 09:44:33 +0000 +To: marny@test.ex +Message-Id: <E10HmaZ-0005vi-00@myhost.test.ex> +From: CALLER_NAME <CALLER@test.ex> +Date: Tue, 2 Mar 1999 09:44:33 +0000 + + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM-- + +From MAILER-DAEMON Tue Mar 02 09:44:33 1999 +Received: from EXIMUSER by myhost.test.ex with local (Exim x.yz) + id 10HmbC-0005vi-00 + for CALLER@test.ex; + Tue, 2 Mar 1999 09:44:33 +0000 +X-Failed-Recipients: system-filter +Auto-Submitted: auto-replied +From: Mail Delivery System <Mailer-Daemon@test.ex> +To: CALLER@test.ex +References: <E10HmbB-0005vi-00@myhost.test.ex> +Content-Type: multipart/report; report-type=delivery-status; boundary=NNNNNNNNNN-eximdsn-MMMMMMMMMM +MIME-Version: 1.0 +Subject: Mail delivery failed: returning message to sender +Message-Id: <E10HmbC-0005vi-00@myhost.test.ex> +Date: Tue, 2 Mar 1999 09:44:33 +0000 + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM +Content-type: text/plain; charset=us-ascii + +This message was created automatically by mail delivery software. + +A message that you sent could not be delivered to one or more of its +recipients. This is a permanent error. The following address(es) failed: + pipe to |TESTSUITE/aux-fixed/showenv $1 generated by system-filter @@ -149,7 +202,7 @@ LOCAL_PART_PREFIX= LOCAL_PART_SUFFIX= LOGNAME=system-filter ME= -MESSAGE_ID=10HmaZ-0005vi-00 +MESSAGE_ID=10HmbB-0005vi-00 PATH=/bin:/usr/bin QUALIFY_DOMAIN=test.ex RECIPIENT=system-filter@test.ex @@ -160,11 +213,11 @@ USER=system-filter >From CALLER@test.ex Tue Mar 02 09:44:33 1999 Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from <CALLER@test.ex>) - id 10HmaZ-0005vi-00 + id 10HmbB-0005vi-00 for jabberwocky@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 To: jabberwocky@test.ex -Message-Id: <E10HmaZ-0005vi-00@myhost.test.ex> +Message-Id: <E10HmbB-0005vi-00@myhost.test.ex> From: CALLER_NAME <CALLER@test.ex> Date: Tue, 2 Mar 1999 09:44:33 +0000 @@ -186,11 +239,11 @@ Content-type: message/rfc822 Return-path: <CALLER@test.ex> Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from <CALLER@test.ex>) - id 10HmaZ-0005vi-00 + id 10HmbB-0005vi-00 for jabberwocky@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 To: jabberwocky@test.ex -Message-Id: <E10HmaZ-0005vi-00@myhost.test.ex> +Message-Id: <E10HmbB-0005vi-00@myhost.test.ex> From: CALLER_NAME <CALLER@test.ex> Date: Tue, 2 Mar 1999 09:44:33 +0000 @@ -199,18 +252,18 @@ Date: Tue, 2 Mar 1999 09:44:33 +0000 From MAILER-DAEMON Tue Mar 02 09:44:33 1999 Received: from EXIMUSER by myhost.test.ex with local (Exim x.yz) - id 10HmbC-0005vi-00 + id 10HmbE-0005vi-00 for CALLER@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 X-Failed-Recipients: redking@test.ex Auto-Submitted: auto-replied From: Mail Delivery System <Mailer-Daemon@test.ex> To: CALLER@test.ex -References: <E10HmbB-0005vi-00@myhost.test.ex> +References: <E10HmbD-0005vi-00@myhost.test.ex> Content-Type: multipart/report; report-type=delivery-status; boundary=NNNNNNNNNN-eximdsn-MMMMMMMMMM MIME-Version: 1.0 Subject: Mail delivery failed: returning message to sender -Message-Id: <E10HmbC-0005vi-00@myhost.test.ex> +Message-Id: <E10HmbE-0005vi-00@myhost.test.ex> Date: Tue, 2 Mar 1999 09:44:33 +0000 --NNNNNNNNNN-eximdsn-MMMMMMMMMM @@ -249,7 +302,7 @@ LOCAL_PART_PREFIX= LOCAL_PART_SUFFIX= LOGNAME=redking ME= -MESSAGE_ID=10HmbB-0005vi-00 +MESSAGE_ID=10HmbD-0005vi-00 PATH=/bin:/usr/bin QUALIFY_DOMAIN=test.ex RECIPIENT=redking@test.ex @@ -260,11 +313,11 @@ USER=redking >From CALLER@test.ex Tue Mar 02 09:44:33 1999 Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from <CALLER@test.ex>) - id 10HmbB-0005vi-00 + id 10HmbD-0005vi-00 for redking@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 To: redking@test.ex -Message-Id: <E10HmbB-0005vi-00@myhost.test.ex> +Message-Id: <E10HmbD-0005vi-00@myhost.test.ex> From: CALLER_NAME <CALLER@test.ex> Date: Tue, 2 Mar 1999 09:44:33 +0000 @@ -286,11 +339,11 @@ Content-type: message/rfc822 Return-path: <CALLER@test.ex> Received: from CALLER by myhost.test.ex with local (Exim x.yz) (envelope-from <CALLER@test.ex>) - id 10HmbB-0005vi-00 + id 10HmbD-0005vi-00 for redking@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 To: redking@test.ex -Message-Id: <E10HmbB-0005vi-00@myhost.test.ex> +Message-Id: <E10HmbD-0005vi-00@myhost.test.ex> From: CALLER_NAME <CALLER@test.ex> Date: Tue, 2 Mar 1999 09:44:33 +0000 diff --git a/test/msglog/0594.10HmaX-0005vi-00 b/test/msglog/0594.10HmaX-0005vi-00 deleted file mode 100644 index f91a4a3e7..000000000 --- a/test/msglog/0594.10HmaX-0005vi-00 +++ /dev/null @@ -1,2 +0,0 @@ -1999-03-02 09:44:33 Received from CALLER@myhost.test.ex U=CALLER P=local S=sss -1999-03-02 09:44:33 |TESTSUITE/bin/iefbr14 <pipe-userx=TESTSUITE/bin/iefbr14@test.ex> R=r2 T=t2 defer (0): Tainted 'TESTSUITE/bin/iefbr14' (command for t2 transport) not permitted diff --git a/test/paniclog/0594 b/test/paniclog/0594 index dc309d8a3..1f53b724d 100644 --- a/test/paniclog/0594 +++ b/test/paniclog/0594 @@ -1 +1,2 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 == |TESTSUITE/bin/iefbr14 <pipe-userx=TESTSUITE/bin/iefbr14@test.ex> R=r2 T=t2 defer (0): Tainted 'TESTSUITE/bin/iefbr14' (command for t2 transport) not permitted +1999-03-02 09:44:33 10HmaX-0005vi-00 == |TESTSUITE/bin/iefbr14 <pipe-userx=TESTSUITE/bin/iefbr14@test.ex> R=r0 T=t0 defer (0): Tainted 'TESTSUITE/bin/iefbr14' (command for t0 transport) not permitted +1999-03-02 09:44:33 10HmaY-0005vi-00 == |echo rtr_user <rtr_user@test.ex> R=r1 T=t0 defer (0): Tainted 'echo rtr_user' (command for t0 transport) not permitted diff --git a/test/scripts/0000-Basic/0002 b/test/scripts/0000-Basic/0002 index 463ad96e9..d169518b6 100644 --- a/test/scripts/0000-Basic/0002 +++ b/test/scripts/0000-Basic/0002 @@ -802,6 +802,8 @@ rc=$runrc rc=$runrc ${if eq{1}{2}{${run{/non/exist}}}{1!=2}} rc=$runrc +${run,preexpand {DIR/aux-fixed/0002.runfile 0}} +rc=$runrc # PRVS diff --git a/test/scripts/0000-Basic/0383 b/test/scripts/0000-Basic/0383 index ab2f44808..cf3325567 100644 --- a/test/scripts/0000-Basic/0383 +++ b/test/scripts/0000-Basic/0383 @@ -2,6 +2,9 @@ exim -odi alice To: alice@test.ex **** +exim -odi marny +To: marny@test.ex +**** exim -odi jabberwocky To: jabberwocky@test.ex **** diff --git a/test/scripts/0000-Basic/0594 b/test/scripts/0000-Basic/0594 index 5fbc9319c..73b565e48 100644 --- a/test/scripts/0000-Basic/0594 +++ b/test/scripts/0000-Basic/0594 @@ -2,3 +2,12 @@ exim -odi pipe-userx=DIR/bin/iefbr14@test.ex A test message. **** +# +exim -odi rtr_user@test.ex +This one has an ok executable but tainted arg, pipecmd set by router +**** +exim -odi tpt_user@test.ex +This one has an ok executable but tainted arg, pipecmd set by transport +**** +# +no_msglog_check diff --git a/test/stderr/0393 b/test/stderr/0393 index ac13db4cd..dcd490152 100644 --- a/test/stderr/0393 +++ b/test/stderr/0393 @@ -14,7 +14,7 @@ dropping to exim gid; retaining priv uid direct command: argv[0] = '/bin/cat' direct command after expansion: - argv[0] = /bin/cat + argv[0] = '/bin/cat' appendfile transport entered appendfile: mode=600 notify_comsat=0 quota=0 warning=0 file=TESTSUITE/test-mail/userx format=unix @@ -102,7 +102,7 @@ dropping to exim gid; retaining priv uid direct command: argv[0] = '${if={1}{1}{}{}}' direct command after expansion: - argv[0] = + argv[0] = '' appendfile transport entered appendfile: mode=600 notify_comsat=0 quota=0 warning=0 file=TESTSUITE/test-mail/userx format=unix diff --git a/test/stderr/0594 b/test/stderr/0594 index dc309d8a3..1f53b724d 100644 --- a/test/stderr/0594 +++ b/test/stderr/0594 @@ -1 +1,2 @@ -1999-03-02 09:44:33 10HmaX-0005vi-00 == |TESTSUITE/bin/iefbr14 <pipe-userx=TESTSUITE/bin/iefbr14@test.ex> R=r2 T=t2 defer (0): Tainted 'TESTSUITE/bin/iefbr14' (command for t2 transport) not permitted +1999-03-02 09:44:33 10HmaX-0005vi-00 == |TESTSUITE/bin/iefbr14 <pipe-userx=TESTSUITE/bin/iefbr14@test.ex> R=r0 T=t0 defer (0): Tainted 'TESTSUITE/bin/iefbr14' (command for t0 transport) not permitted +1999-03-02 09:44:33 10HmaY-0005vi-00 == |echo rtr_user <rtr_user@test.ex> R=r1 T=t0 defer (0): Tainted 'echo rtr_user' (command for t0 transport) not permitted diff --git a/test/stdout/0002 b/test/stdout/0002 index f340b9426..74219a6f3 100644 --- a/test/stdout/0002 +++ b/test/stdout/0002 @@ -775,6 +775,10 @@ xyz > rc=0 > 1!=2 > rc=0 +> abcd +1234 + +> rc=0 > > # PRVS > |