| 1 | #! /bin/sh |
| 2 | ### |
| 3 | ### Fetch an archive, and unpack it into a directory in a safe manner. |
| 4 | |
| 5 | set -e |
| 6 | |
| 7 | ## Parse the command line. |
| 8 | case $# in |
| 9 | 3) ;; |
| 10 | *) echo >&2 "usage: $0 DIR LABEL URL"; exit 1 ;; |
| 11 | esac |
| 12 | dir=$1 label=$2 url=$3 |
| 13 | cd "$dir" |
| 14 | |
| 15 | ## Fetch the archive. |
| 16 | rm -rf tmp; mkdir tmp |
| 17 | curl -s -o tmp/"$label.tar.gz" "$url" |
| 18 | |
| 19 | ## Check the archive for unpleasantness. The GNU and FreeBSD versions of |
| 20 | ## tar(1) do something vaguely sensible with `..' components in the pathnames |
| 21 | ## of archive members. (Specifically, FreeBSD simply ignores the affected |
| 22 | ## members; GNU strips leading components in a bizarre way.) But OpenBSD |
| 23 | ## gets a special security award for cheerily following the `..' components. |
| 24 | ## So we have to do this complicated laundering thing. |
| 25 | ## |
| 26 | ## The archive ought to unpack everything into a single directory and not |
| 27 | ## contain anythig weird. So check. Actually, this won't catch newlines in |
| 28 | ## member names, so we'll have to be careful about those. The regular |
| 29 | ## expression insists that everything be in a single directory identified by |
| 30 | ## the LABEL, and that the rest of the name contains no two adjacent dots. |
| 31 | ## We use the LABEL as part of an ERE, so it ought not contain bad things. |
| 32 | if |
| 33 | tar tzf tmp/"$label.tar.gz" | |
| 34 | grep -Ev "^$label/([^.]+|\.[^.])*$" >&2 |
| 35 | then |
| 36 | echo >&2 "$0: archive has bad member pathnames" |
| 37 | exit 1 |
| 38 | fi |
| 39 | |
| 40 | ## Unpack the archive now that we know it's safe. |
| 41 | (cd tmp; tar xzf "$label.tar.gz") |
| 42 | |
| 43 | ## Replace any existing tree with the new one. |
| 44 | rm -rf "$label" |
| 45 | mv tmp/"$label" . |
| 46 | rm -rf tmp |