summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rw-r--r--bin/pax/ar_subs.c8
-rw-r--r--bin/pax/extern.h7
-rw-r--r--bin/pax/file_subs.c36
-rw-r--r--bin/pax/pat_rep.c21
-rw-r--r--bin/pax/pax.c3
-rw-r--r--bin/pax/tables.c327
-rw-r--r--bin/pax/tables.h3
7 files changed, 394 insertions, 11 deletions
diff --git a/bin/pax/ar_subs.c b/bin/pax/ar_subs.c
index 48493bd17ab..6c2a9c8abca 100644
--- a/bin/pax/ar_subs.c
+++ b/bin/pax/ar_subs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ar_subs.c,v 1.39 2014/05/23 19:47:49 guenther Exp $ */
+/* $OpenBSD: ar_subs.c,v 1.40 2015/02/12 23:44:57 guenther Exp $ */
/* $NetBSD: ar_subs.c,v 1.5 1995/03/21 09:07:06 cgd Exp $ */
/*-
@@ -165,6 +165,8 @@ extract(void)
int fd;
time_t now;
+ sltab_start();
+
arcn = &archd;
/*
* figure out archive type; pass any format specific options to the
@@ -360,6 +362,7 @@ popd:
(void)(*frmt->end_rd)();
(void)sigprocmask(SIG_BLOCK, &s_mask, NULL);
ar_close(0);
+ sltab_process(0);
proc_dir(0);
pat_chk();
}
@@ -758,6 +761,8 @@ copy(void)
ARCHD archd;
char dirbuf[PAXPATHLEN+1];
+ sltab_start();
+
arcn = &archd;
/*
* set up the destination dir path and make sure it is a directory. We
@@ -969,6 +974,7 @@ copy(void)
*/
(void)sigprocmask(SIG_BLOCK, &s_mask, NULL);
ar_close(0);
+ sltab_process(0);
proc_dir(0);
ftree_chk();
}
diff --git a/bin/pax/extern.h b/bin/pax/extern.h
index 00ead23398f..22912087c10 100644
--- a/bin/pax/extern.h
+++ b/bin/pax/extern.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: extern.h,v 1.46 2015/02/12 01:30:47 guenther Exp $ */
+/* $OpenBSD: extern.h,v 1.47 2015/02/12 23:44:57 guenther Exp $ */
/* $NetBSD: extern.h,v 1.5 1996/03/26 23:54:16 mrg Exp $ */
/*-
@@ -202,6 +202,7 @@ int pat_sel(ARCHD *);
int pat_match(ARCHD *);
int mod_name(ARCHD *);
int set_dest(ARCHD *, char *, int);
+int has_dotdot(const char *);
/*
* pax.c
@@ -263,6 +264,10 @@ void purg_lnk(ARCHD *);
void lnk_end(void);
int ftime_start(void);
int chk_ftime(ARCHD *);
+int sltab_start(void);
+int sltab_add_sym(const char *_path, const char *_value, mode_t _mode);
+int sltab_add_link(const char *, const struct stat *);
+void sltab_process(int _in_sig);
int name_start(void);
int add_name(char *, int, char *);
void sub_name(char *, int *, size_t);
diff --git a/bin/pax/file_subs.c b/bin/pax/file_subs.c
index ca546c080a6..2c5475b963c 100644
--- a/bin/pax/file_subs.c
+++ b/bin/pax/file_subs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: file_subs.c,v 1.41 2015/02/11 23:14:46 guenther Exp $ */
+/* $OpenBSD: file_subs.c,v 1.42 2015/02/12 23:44:57 guenther Exp $ */
/* $NetBSD: file_subs.c,v 1.4 1995/03/21 09:07:18 cgd Exp $ */
/*-
@@ -166,6 +166,7 @@ int
lnk_creat(ARCHD *arcn)
{
struct stat sb;
+ int res;
/*
* we may be running as root, so we have to be sure that link target
@@ -183,7 +184,18 @@ lnk_creat(ARCHD *arcn)
return(-1);
}
- return(mk_link(arcn->ln_name, &sb, arcn->name, 0));
+ res = mk_link(arcn->ln_name, &sb, arcn->name, 0);
+ if (res == 0) {
+ /* check for a hardlink to a placeholder symlink */
+ res = sltab_add_link(arcn->name, &sb);
+
+ if (res < 0) {
+ /* arrgh, it failed, clean up */
+ unlink(arcn->name);
+ }
+ }
+
+ return (res);
}
/*
@@ -340,7 +352,7 @@ node_creat(ARCHD *arcn)
struct stat sb;
char target[PATH_MAX];
char *nm = arcn->name;
- int len;
+ int len, defer_pmode = 0;
/*
* create node based on type, if that fails try to unlink the node and
@@ -400,7 +412,21 @@ badlink:
nm);
return(-1);
case PAX_SLK:
- res = symlink(arcn->ln_name, nm);
+ if (arcn->ln_name[0] != '/' &&
+ !has_dotdot(arcn->ln_name))
+ res = symlink(arcn->ln_name, nm);
+ else {
+ /*
+ * absolute symlinks and symlinks with ".."
+ * have to be deferred to prevent the archive
+ * from bootstrapping itself to outside the
+ * working directory.
+ */
+ res = sltab_add_sym(nm, arcn->ln_name,
+ arcn->sb.st_mode);
+ if (res == 0)
+ defer_pmode = 1;
+ }
break;
case PAX_CTG:
case PAX_HLK:
@@ -454,7 +480,7 @@ badlink:
*/
if (!pmode || res)
arcn->sb.st_mode &= ~(SETBITS);
- if (pmode)
+ if (pmode && !defer_pmode)
set_pmode(nm, arcn->sb.st_mode);
if (arcn->type == PAX_DIR && strcmp(NM_CPIO, argv0) != 0) {
diff --git a/bin/pax/pat_rep.c b/bin/pax/pat_rep.c
index 6f3eb1bf98b..8094ee3a3e8 100644
--- a/bin/pax/pat_rep.c
+++ b/bin/pax/pat_rep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pat_rep.c,v 1.35 2015/02/12 23:01:58 guenther Exp $ */
+/* $OpenBSD: pat_rep.c,v 1.36 2015/02/12 23:44:57 guenther Exp $ */
/* $NetBSD: pat_rep.c,v 1.4 1995/03/21 09:07:33 cgd Exp $ */
/*-
@@ -583,6 +583,25 @@ range_match(char *pattern, int test)
}
/*
+ * has_dotdot()
+ * Returns true iff the supplied path contains a ".." component.
+ */
+
+int
+has_dotdot(const char *path)
+{
+ const char *p = path;
+
+ while ((p = strstr(p, "..")) != NULL) {
+ if ((p == path || p[-1] == '/') &&
+ (p[2] == '/' || p[2] == '\0'))
+ return (1);
+ p += 2;
+ }
+ return (0);
+}
+
+/*
* mod_name()
* modify a selected file name. first attempt to apply replacement string
* expressions, then apply interactive file rename. We apply replacement
diff --git a/bin/pax/pax.c b/bin/pax/pax.c
index 49508254ef1..67a1c47bbd8 100644
--- a/bin/pax/pax.c
+++ b/bin/pax/pax.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pax.c,v 1.38 2014/11/23 05:28:12 guenther Exp $ */
+/* $OpenBSD: pax.c,v 1.39 2015/02/12 23:44:57 guenther Exp $ */
/* $NetBSD: pax.c,v 1.5 1996/03/26 23:54:20 mrg Exp $ */
/*-
@@ -311,6 +311,7 @@ sig_cleanup(int which_sig)
(void) write(STDERR_FILENO, errbuf, strlen(errbuf));
ar_close(1);
+ sltab_process(1);
proc_dir(1);
if (tflag)
atdir_end();
diff --git a/bin/pax/tables.c b/bin/pax/tables.c
index f61aeb165bc..b99dbbe4534 100644
--- a/bin/pax/tables.c
+++ b/bin/pax/tables.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tables.c,v 1.41 2015/02/12 01:30:47 guenther Exp $ */
+/* $OpenBSD: tables.c,v 1.42 2015/02/12 23:44:57 guenther Exp $ */
/* $NetBSD: tables.c,v 1.4 1995/03/21 09:07:45 cgd Exp $ */
/*-
@@ -37,6 +37,7 @@
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
@@ -461,6 +462,330 @@ chk_ftime(ARCHD *arcn)
}
/*
+ * escaping (absolute or w/"..") symlink table routines
+ *
+ * By default, an archive shouldn't be able extract to outside of the
+ * current directory. What should we do if the archive contains a symlink
+ * whose value is either absolute or contains ".." components? What we'll
+ * do is initially create the path as an empty file (to block attempts to
+ * reference _through_ it) and instead record its path and desired
+ * final value and mode. Then once all the other archive
+ * members are created (but before the pass to set timestamps on
+ * directories) we'll process those records, replacing the placeholder with
+ * the correct symlink and setting them to the correct mode, owner, group,
+ * and timestamps.
+ *
+ * Note: we also need to handle hardlinks to symlinks (barf) as well as
+ * hardlinks whose target is replaced by a later entry in the archive (barf^2).
+ *
+ * So we track things by dev+ino of the placeholder file, associating with
+ * that the value and mode of the final symlink and a list of paths that
+ * should all be hardlinks of that. We'll 'store' the symlink's desired
+ * timestamps, owner, and group by setting them on the placeholder file.
+ *
+ * The operations are:
+ * a) create an escaping symlink: create the placeholder file and add an entry
+ * for the new link
+ * b) create a hardlink: do the link. If the target turns out to be a
+ * zero-length file whose dev+ino are in the symlink table, then add this
+ * path to the list of names for that link
+ * c) perform deferred processing: for each entry, check each associated path:
+ * if it's a zero-length file with the correct dev+ino then recreate it as
+ * the specified symlink or hardlink to the first such
+ */
+
+struct slpath {
+ char *sp_path;
+ struct slpath *sp_next;
+};
+struct slinode {
+ ino_t sli_ino;
+ char *sli_value;
+ struct slpath sli_paths;
+ struct slinode *sli_fow; /* hash table chain */
+ dev_t sli_dev;
+ mode_t sli_mode;
+};
+
+static struct slinode **slitab = NULL;
+
+/*
+ * sltab_start()
+ * create the hash table
+ * Return:
+ * 0 if the table and file was created ok, -1 otherwise
+ */
+
+int
+sltab_start(void)
+{
+
+ if ((slitab = calloc(SL_TAB_SZ, sizeof *slitab)) == NULL) {
+ syswarn(1, errno, "symlink table");
+ return(-1);
+ }
+
+ return(0);
+}
+
+/*
+ * sltab_add_sym()
+ * Create the placeholder and tracking info for an escaping symlink.
+ * Return:
+ * 0 on success, -1 otherwise
+ */
+
+int
+sltab_add_sym(const char *path0, const char *value0, mode_t mode)
+{
+ struct stat sb;
+ struct slinode *s;
+ struct slpath *p;
+ char *path, *value;
+ u_int indx;
+ int fd;
+
+ /* create the placeholder */
+ fd = open(path0, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
+ if (fd == -1)
+ return (-1);
+ if (fstat(fd, &sb) == -1) {
+ unlink(path0);
+ close(fd);
+ return (-1);
+ }
+ close(fd);
+
+ if ((path = strdup(path0)) == NULL) {
+ syswarn(1, errno, "defered symlink path");
+ unlink(path0);
+ return (-1);
+ }
+ if ((value = strdup(value0)) == NULL) {
+ syswarn(1, errno, "defered symlink value");
+ unlink(path);
+ free(path);
+ return (-1);
+ }
+
+ /* now check the hash table for conflicting entry */
+ indx = (sb.st_ino ^ sb.st_dev) % SL_TAB_SZ;
+ for (s = slitab[indx]; s != NULL; s = s->sli_fow) {
+ if (s->sli_ino != sb.st_ino || s->sli_dev != sb.st_dev)
+ continue;
+
+ /*
+ * One of our placeholders got removed behind our back and
+ * we've reused the inode. Weird, but clean up the mess.
+ */
+ free(s->sli_value);
+ free(s->sli_paths.sp_path);
+ p = s->sli_paths.sp_next;
+ while (p != NULL) {
+ struct slpath *next_p = p->sp_next;
+
+ free(p->sp_path);
+ free(p);
+ p = next_p;
+ }
+ goto set_value;
+ }
+
+ /* Normal case: create a new node */
+ if ((s = malloc(sizeof *s)) == NULL) {
+ syswarn(1, errno, "defered symlink");
+ unlink(path);
+ free(path);
+ free(value);
+ return (-1);
+ }
+ s->sli_ino = sb.st_ino;
+ s->sli_dev = sb.st_dev;
+ s->sli_fow = slitab[indx];
+ slitab[indx] = s;
+
+set_value:
+ s->sli_paths.sp_path = path;
+ s->sli_paths.sp_next = NULL;
+ s->sli_value = value;
+ s->sli_mode = mode;
+ return (0);
+}
+
+/*
+ * sltab_add_link()
+ * A hardlink was created; if it looks like a placeholder, handle the
+ * tracking.
+ * Return:
+ * 0 if things are ok, -1 if something went wrong
+ */
+
+int
+sltab_add_link(const char *path, const struct stat *sb)
+{
+ struct slinode *s;
+ struct slpath *p;
+ u_int indx;
+
+ if (!S_ISREG(sb->st_mode) || sb->st_size != 0)
+ return (1);
+
+ /* find the hash table entry for this hardlink */
+ indx = (sb->st_ino ^ sb->st_dev) % SL_TAB_SZ;
+ for (s = slitab[indx]; s != NULL; s = s->sli_fow) {
+ if (s->sli_ino != sb->st_ino || s->sli_dev != sb->st_dev)
+ continue;
+
+ if ((p = malloc(sizeof *p)) == NULL) {
+ syswarn(1, errno, "deferred symlink hardlink");
+ return (-1);
+ }
+ if ((p->sp_path = strdup(path)) == NULL) {
+ syswarn(1, errno, "defered symlink hardlink path");
+ free(p);
+ return (-1);
+ }
+
+ /* link it in */
+ p->sp_next = s->sli_paths.sp_next;
+ s->sli_paths.sp_next = p;
+ return (0);
+ }
+
+ /* not found */
+ return (1);
+}
+
+
+static int
+sltab_process_one(struct slinode *s, struct slpath *p, const char *first,
+ int in_sig)
+{
+ struct stat sb;
+ char *path = p->sp_path;
+ mode_t mode;
+ int err;
+
+ /*
+ * is it the expected placeholder? This can fail legimately
+ * if the archive overwrote the link with another, later entry,
+ * so don't warn.
+ */
+ if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode) || sb.st_size != 0 ||
+ sb.st_ino != s->sli_ino || sb.st_dev != s->sli_dev)
+ return (0);
+
+ if (unlink(path) && errno != ENOENT) {
+ if (!in_sig)
+ syswarn(1, errno, "deferred symlink removal");
+ return (0);
+ }
+
+ err = 0;
+ if (first != NULL) {
+ /* add another hardlink to the existing symlink */
+ if (linkat(AT_FDCWD, first, AT_FDCWD, path, 0) == 0)
+ return (0);
+
+ /*
+ * Couldn't hardlink the symlink for some reason, so we'll
+ * try creating it as its own symlink, but save the error
+ * for reporting if that fails.
+ */
+ err = errno;
+ }
+
+ if (symlink(s->sli_value, path)) {
+ if (!in_sig) {
+ const char *qualifier = "";
+ if (err)
+ qualifier = " hardlink";
+ else
+ err = errno;
+
+ syswarn(1, err, "deferred symlink%s: %s",
+ qualifier, path);
+ }
+ return (0);
+ }
+
+ /* success, so set the id, mode, and times */
+ mode = s->sli_mode;
+ if (pids) {
+ /* if can't set the ids, force the set[ug]id bits off */
+ if (set_ids(path, sb.st_uid, sb.st_gid))
+ mode &= ~(SETBITS);
+ }
+
+ if (pmode)
+ set_pmode(path, mode);
+
+ if (patime || pmtime)
+ set_ftime(path, sb.st_mtime, sb.st_atime, 0);
+
+ /*
+ * If we tried to link to first but failed, then this new symlink
+ * might be a better one to try in the future. Guess from the errno.
+ */
+ if (err == 0 || err == ENOENT || err == EMLINK || err == EOPNOTSUPP)
+ return (1);
+ return (0);
+}
+
+/*
+ * sltab_process()
+ * Do all the delayed process for escape symlinks
+ */
+
+void
+sltab_process(int in_sig)
+{
+ struct slinode *s;
+ struct slpath *p;
+ char *first;
+ u_int indx;
+
+ if (slitab == NULL)
+ return;
+
+ /* walk across the entire hash table */
+ for (indx = 0; indx < SL_TAB_SZ; indx++) {
+ while ((s = slitab[indx]) != NULL) {
+ /* pop this entry */
+ slitab[indx] = s->sli_fow;
+
+ first = NULL;
+ p = &s->sli_paths;
+ while (1) {
+ struct slpath *next_p;
+
+ if (sltab_process_one(s, p, first, in_sig)) {
+ if (!in_sig)
+ free(first);
+ first = p->sp_path;
+ } else if (!in_sig)
+ free(p->sp_path);
+
+ if ((next_p = p->sp_next) == NULL)
+ break;
+ *p = *next_p;
+ if (!in_sig)
+ free(next_p);
+ }
+ if (!in_sig) {
+ free(first);
+ free(s->sli_value);
+ free(s);
+ }
+ }
+ }
+ if (!in_sig)
+ free(slitab);
+ slitab = NULL;
+}
+
+
+/*
* Interactive rename table routines
*
* The interactive rename table keeps track of the new names that the user
diff --git a/bin/pax/tables.h b/bin/pax/tables.h
index f4ac151f911..45dc40aed60 100644
--- a/bin/pax/tables.h
+++ b/bin/pax/tables.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tables.h,v 1.12 2015/02/11 23:14:46 guenther Exp $ */
+/* $OpenBSD: tables.h,v 1.13 2015/02/12 23:44:57 guenther Exp $ */
/* $NetBSD: tables.h,v 1.3 1995/03/21 09:07:47 cgd Exp $ */
/*-
@@ -50,6 +50,7 @@
#define N_TAB_SZ 541 /* interactive rename hash table */
#define D_TAB_SZ 317 /* unique device mapping table */
#define A_TAB_SZ 317 /* ftree dir access time reset table */
+#define SL_TAB_SZ 317 /* escape symlink tables */
#define MAXKEYLEN 64 /* max number of chars for hash */
#define DIRP_SIZE 64 /* initial size of created dir table */