restore(8): Handle extended attribute names correctly

UFS2 extended attribute names are not NUL-terminated.  Handle
appropriately.

Correct the EXTATTR_BASE_LENGTH() macro, which handled ea_namelength ==
one (mod eight) extended attributes incorrectly.

PR:		216127
Reported by:	dewayne at heuristicsystems.com.au
Reviewed by:	kib@
Sponsored by:	Dell EMC Isilon
Differential Revision:	https://reviews.freebsd.org/D9208
This commit is contained in:
Conrad Meyer 2017-01-18 18:16:57 +00:00
parent 4a42ab7dda
commit c9bf814804
5 changed files with 72 additions and 145 deletions

View File

@ -645,7 +645,7 @@ setdirmodes(int flags)
if (!Nflag) { if (!Nflag) {
if (node.extsize > 0) { if (node.extsize > 0) {
if (bufsize >= node.extsize) { if (bufsize >= node.extsize) {
set_extattr_file(cp, buf, node.extsize); set_extattr(-1, cp, buf, node.extsize, SXA_FILE);
} else { } else {
fprintf(stderr, "Cannot restore %s%s\n", fprintf(stderr, "Cannot restore %s%s\n",
"extended attributes for ", cp); "extended attributes for ", cp);

View File

@ -87,7 +87,12 @@ struct direct *rst_readdir(RST_DIR *);
void rst_closedir(void *); void rst_closedir(void *);
void runcmdshell(void); void runcmdshell(void);
char *savename(char *); char *savename(char *);
void set_extattr_file(char *, void *, int); enum set_extattr_mode {
SXA_FILE,
SXA_LINK,
SXA_FD,
};
void set_extattr(int, char *, void *, int, enum set_extattr_mode);
void setdirmodes(int); void setdirmodes(int);
void setinput(char *, int); void setinput(char *, int);
void setup(void); void setup(void);

View File

@ -105,8 +105,6 @@ static void findinode(struct s_spcl *);
static void findtapeblksize(void); static void findtapeblksize(void);
static char *setupextattr(int); static char *setupextattr(int);
static void xtrattr(char *, size_t); static void xtrattr(char *, size_t);
static void set_extattr_link(char *, void *, int);
static void set_extattr_fd(int, char *, void *, int);
static void skiphole(void (*)(char *, size_t), size_t *); static void skiphole(void (*)(char *, size_t), size_t *);
static int gethead(struct s_spcl *); static int gethead(struct s_spcl *);
static void readtape(char *); static void readtape(char *);
@ -627,7 +625,7 @@ extractfile(char *name)
} }
if (linkit(lnkbuf, name, SYMLINK) == GOOD) { if (linkit(lnkbuf, name, SYMLINK) == GOOD) {
if (extsize > 0) if (extsize > 0)
set_extattr_link(name, buf, extsize); set_extattr(-1, name, buf, extsize, SXA_LINK);
(void) lchown(name, uid, gid); (void) lchown(name, uid, gid);
(void) lchmod(name, mode); (void) lchmod(name, mode);
(void) utimensat(AT_FDCWD, name, ctimep, (void) utimensat(AT_FDCWD, name, ctimep,
@ -658,7 +656,7 @@ extractfile(char *name)
} else { } else {
buf = setupextattr(extsize); buf = setupextattr(extsize);
getfile(xtrnull, xtrattr, xtrnull); getfile(xtrnull, xtrattr, xtrnull);
set_extattr_file(name, buf, extsize); set_extattr(-1, name, buf, extsize, SXA_FILE);
} }
(void) chown(name, uid, gid); (void) chown(name, uid, gid);
(void) chmod(name, mode); (void) chmod(name, mode);
@ -688,7 +686,7 @@ extractfile(char *name)
} else { } else {
buf = setupextattr(extsize); buf = setupextattr(extsize);
getfile(xtrnull, xtrattr, xtrnull); getfile(xtrnull, xtrattr, xtrnull);
set_extattr_file(name, buf, extsize); set_extattr(-1, name, buf, extsize, SXA_FILE);
} }
(void) chown(name, uid, gid); (void) chown(name, uid, gid);
(void) chmod(name, mode); (void) chmod(name, mode);
@ -715,7 +713,7 @@ extractfile(char *name)
buf = setupextattr(extsize); buf = setupextattr(extsize);
getfile(xtrfile, xtrattr, xtrskip); getfile(xtrfile, xtrattr, xtrskip);
if (extsize > 0) if (extsize > 0)
set_extattr_fd(ofile, name, buf, extsize); set_extattr(ofile, name, buf, extsize, SXA_FD);
(void) fchown(ofile, uid, gid); (void) fchown(ofile, uid, gid);
(void) fchmod(ofile, mode); (void) fchmod(ofile, mode);
(void) futimens(ofile, ctimep); (void) futimens(ofile, ctimep);
@ -728,12 +726,16 @@ extractfile(char *name)
} }
/* /*
* Set attributes for a file. * Set attributes on a file descriptor, link, or file.
*/ */
void void
set_extattr_file(char *name, void *buf, int size) set_extattr(int fd, char *name, void *buf, int size, enum set_extattr_mode mode)
{ {
struct extattr *eap, *eaend; struct extattr *eap, *eaend;
const char *method;
ssize_t res;
int error;
char eaname[EXTATTR_MAXNAMELEN + 1];
vprintf(stdout, "Set attributes for %s:", name); vprintf(stdout, "Set attributes for %s:", name);
eaend = buf + size; eaend = buf + size;
@ -748,17 +750,34 @@ set_extattr_file(char *name, void *buf, int size)
} }
if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY) if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY)
continue; continue;
vprintf(stdout, "\n\t%s, (%d bytes), %*s", snprintf(eaname, sizeof(eaname), "%.*s",
(int)eap->ea_namelength, eap->ea_name);
vprintf(stdout, "\n\t%s, (%d bytes), %s",
namespace_names[eap->ea_namespace], eap->ea_length, namespace_names[eap->ea_namespace], eap->ea_length,
eap->ea_namelength, eap->ea_name); eaname);
/* /*
* First we try the general attribute setting interface. * First we try the general attribute setting interface.
* However, some attributes can only be set by root or * However, some attributes can only be set by root or
* by using special interfaces (for example, ACLs). * by using special interfaces (for example, ACLs).
*/ */
if (extattr_set_file(name, eap->ea_namespace, eap->ea_name, if (mode == SXA_FD) {
EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) { res = extattr_set_fd(fd, eap->ea_namespace,
dprintf(stdout, " (set using extattr_set_file)"); eaname, EXTATTR_CONTENT(eap),
EXTATTR_CONTENT_SIZE(eap));
method = "extattr_set_fd";
} else if (mode == SXA_LINK) {
res = extattr_set_link(name, eap->ea_namespace,
eaname, EXTATTR_CONTENT(eap),
EXTATTR_CONTENT_SIZE(eap));
method = "extattr_set_link";
} else if (mode == SXA_FILE) {
res = extattr_set_file(name, eap->ea_namespace,
eaname, EXTATTR_CONTENT(eap),
EXTATTR_CONTENT_SIZE(eap));
method = "extattr_set_file";
}
if (res != -1) {
dprintf(stdout, " (set using %s)", method);
continue; continue;
} }
/* /*
@ -767,137 +786,37 @@ set_extattr_file(char *name, void *buf, int size)
* know about. * know about.
*/ */
if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
!strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) { strcmp(eaname, POSIX1E_ACL_ACCESS_EXTATTR_NAME) == 0) {
if (acl_set_file(name, ACL_TYPE_ACCESS, if (mode == SXA_FD) {
EXTATTR_CONTENT(eap)) != -1) { error = acl_set_fd(fd, EXTATTR_CONTENT(eap));
dprintf(stdout, " (set using acl_set_file)"); method = "acl_set_fd";
} else if (mode == SXA_LINK) {
error = acl_set_link_np(name, ACL_TYPE_ACCESS,
EXTATTR_CONTENT(eap));
method = "acl_set_link_np";
} else if (mode == SXA_FILE) {
error = acl_set_file(name, ACL_TYPE_ACCESS,
EXTATTR_CONTENT(eap));
method = "acl_set_file";
}
if (error != -1) {
dprintf(stdout, " (set using %s)", method);
continue; continue;
} }
} }
if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
!strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) { strcmp(eaname, POSIX1E_ACL_DEFAULT_EXTATTR_NAME) == 0) {
if (acl_set_file(name, ACL_TYPE_DEFAULT, if (mode == SXA_LINK) {
EXTATTR_CONTENT(eap)) != -1) { error = acl_set_link_np(name, ACL_TYPE_DEFAULT,
dprintf(stdout, " (set using acl_set_file)"); EXTATTR_CONTENT(eap));
continue; method = "acl_set_link_np";
} else {
error = acl_set_file(name, ACL_TYPE_DEFAULT,
EXTATTR_CONTENT(eap));
method = "acl_set_file";
} }
} if (error != -1) {
vprintf(stdout, " (unable to set)"); dprintf(stdout, " (set using %s)", method);
}
vprintf(stdout, "\n");
}
/*
* Set attributes for a symbolic link.
*/
static void
set_extattr_link(char *name, void *buf, int size)
{
struct extattr *eap, *eaend;
vprintf(stdout, "Set attributes for %s:", name);
eaend = buf + size;
for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) {
/*
* Make sure this entry is complete.
*/
if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) {
dprintf(stdout, "\n\t%scorrupted",
eap == buf ? "" : "remainder ");
break;
}
if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY)
continue;
vprintf(stdout, "\n\t%s, (%d bytes), %*s",
namespace_names[eap->ea_namespace], eap->ea_length,
eap->ea_namelength, eap->ea_name);
/*
* First we try the general attribute setting interface.
* However, some attributes can only be set by root or
* by using special interfaces (for example, ACLs).
*/
if (extattr_set_link(name, eap->ea_namespace, eap->ea_name,
EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) {
dprintf(stdout, " (set using extattr_set_link)");
continue;
}
/*
* If the general interface refuses to set the attribute,
* then we try all the specialized interfaces that we
* know about.
*/
if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
!strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) {
if (acl_set_link_np(name, ACL_TYPE_ACCESS,
EXTATTR_CONTENT(eap)) != -1) {
dprintf(stdout, " (set using acl_set_link_np)");
continue;
}
}
if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
!strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) {
if (acl_set_link_np(name, ACL_TYPE_DEFAULT,
EXTATTR_CONTENT(eap)) != -1) {
dprintf(stdout, " (set using acl_set_link_np)");
continue;
}
}
vprintf(stdout, " (unable to set)");
}
vprintf(stdout, "\n");
}
/*
* Set attributes on a file descriptor.
*/
static void
set_extattr_fd(int fd, char *name, void *buf, int size)
{
struct extattr *eap, *eaend;
vprintf(stdout, "Set attributes for %s:", name);
eaend = buf + size;
for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) {
/*
* Make sure this entry is complete.
*/
if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) {
dprintf(stdout, "\n\t%scorrupted",
eap == buf ? "" : "remainder ");
break;
}
if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY)
continue;
vprintf(stdout, "\n\t%s, (%d bytes), %*s",
namespace_names[eap->ea_namespace], eap->ea_length,
eap->ea_namelength, eap->ea_name);
/*
* First we try the general attribute setting interface.
* However, some attributes can only be set by root or
* by using special interfaces (for example, ACLs).
*/
if (extattr_set_fd(fd, eap->ea_namespace, eap->ea_name,
EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)) != -1) {
dprintf(stdout, " (set using extattr_set_fd)");
continue;
}
/*
* If the general interface refuses to set the attribute,
* then we try all the specialized interfaces that we
* know about.
*/
if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
!strcmp(eap->ea_name, POSIX1E_ACL_ACCESS_EXTATTR_NAME)) {
if (acl_set_fd(fd, EXTATTR_CONTENT(eap)) != -1) {
dprintf(stdout, " (set using acl_set_fd)");
continue;
}
}
if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM &&
!strcmp(eap->ea_name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME)) {
if (acl_set_file(name, ACL_TYPE_DEFAULT,
EXTATTR_CONTENT(eap)) != -1) {
dprintf(stdout, " (set using acl_set_file)");
continue; continue;
} }
} }

View File

@ -57,10 +57,11 @@
EXTATTR_NAMESPACE_USER_STRING, \ EXTATTR_NAMESPACE_USER_STRING, \
EXTATTR_NAMESPACE_SYSTEM_STRING } EXTATTR_NAMESPACE_SYSTEM_STRING }
#define EXTATTR_MAXNAMELEN NAME_MAX
#ifdef _KERNEL #ifdef _KERNEL
#include <sys/types.h> #include <sys/types.h>
#define EXTATTR_MAXNAMELEN NAME_MAX
struct thread; struct thread;
struct ucred; struct ucred;
struct vnode; struct vnode;

View File

@ -93,12 +93,14 @@ struct extattr {
* content referenced by eap. * content referenced by eap.
*/ */
#define EXTATTR_NEXT(eap) \ #define EXTATTR_NEXT(eap) \
((struct extattr *)(((void *)(eap)) + (eap)->ea_length)) ((struct extattr *)(((u_char *)(eap)) + (eap)->ea_length))
#define EXTATTR_CONTENT(eap) (((void *)(eap)) + EXTATTR_BASE_LENGTH(eap)) #define EXTATTR_CONTENT(eap) \
(void *)(((u_char *)(eap)) + EXTATTR_BASE_LENGTH(eap))
#define EXTATTR_CONTENT_SIZE(eap) \ #define EXTATTR_CONTENT_SIZE(eap) \
((eap)->ea_length - EXTATTR_BASE_LENGTH(eap) - (eap)->ea_contentpadlen) ((eap)->ea_length - EXTATTR_BASE_LENGTH(eap) - (eap)->ea_contentpadlen)
/* -1 below compensates for ea_name[1] */
#define EXTATTR_BASE_LENGTH(eap) \ #define EXTATTR_BASE_LENGTH(eap) \
((sizeof(struct extattr) + (eap)->ea_namelength + 7) & ~7) roundup2((sizeof(struct extattr) - 1 + (eap)->ea_namelength), 8)
#ifdef _KERNEL #ifdef _KERNEL