From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: taylanbayirli@gmail.com (Taylan Ulrich =?UTF-8?Q?Bay=C4=B1rl=C4=B1/Kammer?=) Newsgroups: gmane.emacs.bugs Subject: bug#21702: shell-quote-argument semantics and safety Date: Sun, 18 Oct 2015 14:36:03 +0200 Message-ID: <871tcstkuk.fsf@T420.taylan> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: ger.gmane.org 1445171843 26352 80.91.229.3 (18 Oct 2015 12:37:23 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sun, 18 Oct 2015 12:37:23 +0000 (UTC) To: 21702@debbugs.gnu.org Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Sun Oct 18 14:37:15 2015 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZnnD3-0007FO-RH for geb-bug-gnu-emacs@m.gmane.org; Sun, 18 Oct 2015 14:37:10 +0200 Original-Received: from localhost ([::1]:33592 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZnnD3-0005Vm-4a for geb-bug-gnu-emacs@m.gmane.org; Sun, 18 Oct 2015 08:37:09 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:48942) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZnnCy-0005Vh-8S for bug-gnu-emacs@gnu.org; Sun, 18 Oct 2015 08:37:05 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZnnCw-0000Lg-Me for bug-gnu-emacs@gnu.org; Sun, 18 Oct 2015 08:37:04 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:35985) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZnnCw-0000LH-Ix for bug-gnu-emacs@gnu.org; Sun, 18 Oct 2015 08:37:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.80) (envelope-from ) id 1ZnnCw-00038P-CU for bug-gnu-emacs@gnu.org; Sun, 18 Oct 2015 08:37:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: taylanbayirli@gmail.com (Taylan Ulrich =?UTF-8?Q?Bay=C4=B1rl=C4=B1/Kammer?=) Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 18 Oct 2015 12:37:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 21702 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: X-Debbugs-Original-To: bug-gnu-emacs@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.144517177511995 (code B ref -1); Sun, 18 Oct 2015 12:37:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 18 Oct 2015 12:36:15 +0000 Original-Received: from localhost ([127.0.0.1]:54926 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1ZnnCB-00037O-2z for submit@debbugs.gnu.org; Sun, 18 Oct 2015 08:36:15 -0400 Original-Received: from eggs.gnu.org ([208.118.235.92]:38859) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1ZnnC7-00037F-D7 for submit@debbugs.gnu.org; Sun, 18 Oct 2015 08:36:12 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZnnC5-0008V1-LB for submit@debbugs.gnu.org; Sun, 18 Oct 2015 08:36:11 -0400 Original-Received: from lists.gnu.org ([2001:4830:134:3::11]:52927) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZnnC5-0008Ux-IP for submit@debbugs.gnu.org; Sun, 18 Oct 2015 08:36:09 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:48865) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZnnC4-0005Uc-8c for bug-gnu-emacs@gnu.org; Sun, 18 Oct 2015 08:36:09 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZnnC3-0008Uj-2K for bug-gnu-emacs@gnu.org; Sun, 18 Oct 2015 08:36:08 -0400 Original-Received: from mail-wi0-x22d.google.com ([2a00:1450:400c:c05::22d]:33258) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZnnC2-0008Uf-Oq for bug-gnu-emacs@gnu.org; Sun, 18 Oct 2015 08:36:06 -0400 Original-Received: by wijp11 with SMTP id p11so64647142wij.0 for ; Sun, 18 Oct 2015 05:36:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:subject:date:message-id:user-agent:mime-version :content-type; bh=0QZYKI6l0EXkHkDo2goYU6n+ut86W9e/soUiukJjXZM=; b=GOIUamkthQelE+6YY54+LdgqswQwrIqPUcjpwpc96zbprhdrTjAIXeQdsYq1mevQbP sJZaK+oTKY+hy6s4JiS1UuzLWXwGoStbbGHA9hCAwdt4fVk0sdTbPoQzYH6nCDGM86da o8FHLcfp5pRXfLhiXLCh3BIkR6HVdxmZDarP2aVCg7h0R2kQDe7b5F4UdpgqdU48Mf02 Bl4IXTWM8WNgJAUnXv3HW2Uw1o0Q7ce/OH7N6t4IeO7cBMDW7TrOvwcPCMneOmLzftnM jiIVJaunx6usVjvStb2mXswg/EkabSGnBDSi8wFz0SHMq5DLy85rspnJiV1rvMbj1zlx kddQ== X-Received: by 10.194.171.3 with SMTP id aq3mr27733519wjc.54.1445171765842; Sun, 18 Oct 2015 05:36:05 -0700 (PDT) Original-Received: from T420.taylan ([2a02:908:c32:4740:221:ccff:fe66:68f0]) by smtp.gmail.com with ESMTPSA id uq5sm33840863wjc.3.2015.10.18.05.36.04 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 18 Oct 2015 05:36:04 -0700 (PDT) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux) X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 208.118.235.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.bugs:107708 Archived-At: --=-=-= Content-Type: text/plain The documentation of shell-quote-argument only says Quote ARGUMENT for passing as argument to an inferior shell. It's unclear for which shells this is supposed to work. In a recent thread in emacs-devel, it has been demonstrated that if the result is passed to csh, it can allow an attacker to execute an arbitrary shell command, although without arguments: (let ((argument (read untrusted-source))) (assert (stringp argument)) (call-process "csh" nil t nil "-c" (concat "echo " (shell-quote-argument argument)))) ;; If untrusted-source gives us "\nevil-command\n", we get: ;; evil-command: Command not found. The function should clearly document 1) for which shells will the quoting work absolutely, i.e. lead to the given string to appear *verbatim* in an element of the ARGV of the called command, 2) optionally, for which shells will the quoting at least prevent code injection, 3) optionally, for which shells and character sets for ARGUMENT will the quoting work absolutely, 4) optionally, for which shells and character sets for ARGUMENT will the quoting at least prevent code injection, 5) optionally, for which shells will the quoting work at all even if it provides no clear semantics, such that one can at least use it with data coming from trusted sources (e.g. other parts of Emacs's source code, or the user sitting in front of Emacs), where it's the user's/programmer's responsibility to stick to values for ARGUMENT that are intuitively known to be unproblematic even if the character set isn't well-defined. Currently #5 seems to be implied for all shells, for lack of further documentation. Possibly, the function was never meant to be used with untrusted data, but there's no warning against doing so either. I stress-tested the strategy it uses for POSIX shells with the following horrible hack; the results are positive, i.e. the strategy seems to meet the criteria #1 above for POSIX shells. for i in {0..999} do dd if=/dev/urandom of=/dev/stdout bs=1K count=1 2>/dev/null | tr -d '\000' > randomfile # NULL bytes in ARGV are impossible emacs -q --batch --eval \ "(with-temp-buffer (insert-file-contents-literally \"randomfile\") (let ((data (replace-regexp-in-string \"\\n\" \"'\\n'\" (replace-regexp-in-string \"[^-0-9a-zA-Z_./\\n]\" \"\\\\\\\\\\\\&\" (buffer-substring (point-min) (point-max)))))) (erase-buffer) (insert \"printf %s \") (insert data) (write-region (point-min) (point-max) \"commandfile\")))" sh - < commandfile > output # tested with bash, dash, and ksh diff randomfile output || exit done There's also wording in POSIX which seems to guarantee the safety of the strategy: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01 "A that is not quoted shall preserve the literal value of the following character, with the exception of a . [...]" For now, here's a trivial patch improving the docstring. If anyone is confident in the safety of the function for shells other than those conforming to POSIX sh, feel free to change the docstring accordingly. --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=0001-lisp-subr.el-shell-quote-argument-Improve-documentat.patch >From dedcb603da981dcab8f576dea2f36d58fd2ddcfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taylan=20Ulrich=20Bay=C4=B1rl=C4=B1/Kammer?= Date: Sun, 18 Oct 2015 14:23:35 +0200 Subject: [PATCH] * lisp/subr.el (shell-quote-argument): Improve documentation. --- lisp/subr.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lisp/subr.el b/lisp/subr.el index e176907..940ebe6 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -2711,7 +2711,11 @@ Note: :data and :device are currently not supported on Windows." (declare-function w32-shell-dos-semantics "w32-fns" nil) (defun shell-quote-argument (argument) - "Quote ARGUMENT for passing as argument to an inferior shell." + "Quote ARGUMENT for passing as argument to an inferior shell. + +This is safe for shells conforming to POSIX sh. No guarantees +regarding code injection are made for other shells, but csh, +MS-DOS and Windows NT are supported for simple cases as well." (cond ((eq system-type 'ms-dos) ;; Quote using double quotes, but escape any existing quotes in -- 2.5.0 --=-=-=--