X-Git-Url: https://git.wh0rd.org/?p=dump.git;a=blobdiff_plain;f=restore%2Fxattr.c;h=ccc01c14c21e74177ce4b4af95cdf4f227b79991;hp=6da39eb949bd8672c9fff6754ddb8cf00c18db6b;hb=15508d36add49ba67db4b0c64637dae09c2249f2;hpb=cca7148b36e60b4671518602ff9a7c2d0c22a7b2 diff --git a/restore/xattr.c b/restore/xattr.c index 6da39eb..ccc01c1 100644 --- a/restore/xattr.c +++ b/restore/xattr.c @@ -29,7 +29,7 @@ #ifndef lint static const char rcsid[] = - "$Id: xattr.c,v 1.1 2005/05/02 15:10:47 stelian Exp $"; + "$Id: xattr.c,v 1.6 2010/03/08 10:40:52 stelian Exp $"; #endif /* not lint */ #include @@ -43,6 +43,9 @@ static const char rcsid[] = #include #include #include +#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */ +# include +#endif #include "restore.h" #include "extern.h" #include "pathnames.h" @@ -195,12 +198,12 @@ struct ext2_xattr_entry { static int lsetxattr __P((const char *, const char *, void *, size_t, int)); static ssize_t lgetxattr __P((const char *, const char *, void *, size_t)); static ssize_t llistxattr __P((const char *, char *, size_t)); -static int xattr_cb_list __P((char *, char *, int, void *)); -static int xattr_cb_set __P((char *, char *, int, void *)); -static int xattr_cb_compare __P((char *, char *, int, void *)); +static int xattr_cb_list __P((char *, char *, int, int, void *)); +static int xattr_cb_set __P((char *, char *, int, int, void *)); +static int xattr_cb_compare __P((char *, char *, int, int, void *)); static int xattr_verify __P((char *)); static int xattr_count __P((char *, int *)); -static int xattr_walk __P((char *, int (*)(char *, char *, int, void *), void *)); +static int xattr_walk __P((char *, int (*)(char *, char *, int, int, void *), void *)); static int lsetxattr(const char *path, const char *name, void *value, size_t size, int flags) @@ -220,13 +223,195 @@ llistxattr(const char *path, char *list, size_t size) return SYSCALL(__NR_llistxattr, path, list, size); } +#define POSIX_ACL_XATTR_VERSION 0x0002 + +#define ACL_UNDEFINED_ID (-1) + +#define ACL_USER_OBJ (0x01) +#define ACL_USER (0x02) +#define ACL_GROUP_OBJ (0x04) +#define ACL_GROUP (0x08) +#define ACL_MASK (0x10) +#define ACL_OTHER (0x20) + +typedef struct { + u_int16_t e_tag; + u_int16_t e_perm; + u_int32_t e_id; +} posix_acl_xattr_entry; + +typedef struct { + u_int32_t a_version; + posix_acl_xattr_entry a_entries[0]; +} posix_acl_xattr_header; + +static inline size_t +posix_acl_xattr_size(int count) +{ + return (sizeof(posix_acl_xattr_header) + + (count * sizeof(posix_acl_xattr_entry))); +} + +struct posix_acl_entry { + short e_tag; + unsigned short e_perm; + unsigned int e_id; +}; + +struct posix_acl { + unsigned int a_count; + struct posix_acl_entry a_entries[0]; +}; + +#define EXT3_ACL_VERSION 0x0001 + +typedef struct { + u_int16_t e_tag; + u_int16_t e_perm; + u_int32_t e_id; +} ext3_acl_entry; + +typedef struct { + u_int16_t e_tag; + u_int16_t e_perm; +} ext3_acl_entry_short; + +typedef struct { + u_int32_t a_version; +} ext3_acl_header; + +static inline int ext3_acl_count(size_t size) +{ + ssize_t s; + size -= sizeof(ext3_acl_header); + s = size - 4 * sizeof(ext3_acl_entry_short); + if (s < 0) { + if (size % sizeof(ext3_acl_entry_short)) + return -1; + return size / sizeof(ext3_acl_entry_short); + } else { + if (s % sizeof(ext3_acl_entry)) + return -1; + return s / sizeof(ext3_acl_entry) + 4; + } +} + +int +posix_acl_to_xattr(const struct posix_acl *acl, void *buffer, size_t size) { + posix_acl_xattr_header *ext_acl = (posix_acl_xattr_header *)buffer; + posix_acl_xattr_entry *ext_entry = ext_acl->a_entries; + int real_size, n; + + real_size = posix_acl_xattr_size(acl->a_count); + if (!buffer) + return real_size; + if (real_size > size) { + fprintf(stderr, "ACL: not enough space to convert (%d %d)\n", real_size, size); + return -1; + } + + ext_acl->a_version = POSIX_ACL_XATTR_VERSION; +#if BYTE_ORDER == BIG_ENDIAN + swabst("1i", (u_char *)ext_acl); +#endif + + for (n=0; n < acl->a_count; n++, ext_entry++) { + ext_entry->e_tag = acl->a_entries[n].e_tag; + ext_entry->e_perm = acl->a_entries[n].e_perm; + ext_entry->e_id = acl->a_entries[n].e_id; +#if BYTE_ORDER == BIG_ENDIAN + swabst("2s1i", (u_char *)ext_entry); +#endif + } + return real_size; +} + +static struct posix_acl * +ext3_acl_from_disk(const void *value, size_t size) +{ + const char *end = (char *)value + size; + int n, count; + struct posix_acl *acl; + + if (!value) + return NULL; + if (size < sizeof(ext3_acl_header)) { + fprintf(stderr, "ACL size too little\n"); + return NULL; + } +#if BYTE_ORDER == BIG_ENDIAN + swabst("1i", (u_char *)value); +#endif + if (((ext3_acl_header *)value)->a_version != EXT3_ACL_VERSION) { + fprintf(stderr, "ACL version unknown\n"); + return NULL; + } + value = (char *)value + sizeof(ext3_acl_header); + count = ext3_acl_count(size); + if (count < 0) { + fprintf(stderr, "ACL bad count\n"); + return NULL; + } + if (count == 0) + return NULL; + acl = malloc(sizeof(struct posix_acl) + count * sizeof(struct posix_acl_entry)); + if (!acl) { + fprintf(stderr, "ACL malloc failed\n"); + return NULL; + } + acl->a_count = count; + + for (n=0; n < count; n++) { + ext3_acl_entry *entry = (ext3_acl_entry *)value; +#if BYTE_ORDER == BIG_ENDIAN + swabst("2s", (u_char *)entry); +#endif + if ((char *)value + sizeof(ext3_acl_entry_short) > end) + goto fail; + acl->a_entries[n].e_tag = entry->e_tag; + acl->a_entries[n].e_perm = entry->e_perm; + switch(acl->a_entries[n].e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + value = (char *)value + sizeof(ext3_acl_entry_short); + acl->a_entries[n].e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: +#if BYTE_ORDER == BIG_ENDIAN + swabst("4b1i", (u_char *)entry); +#endif + value = (char *)value + sizeof(ext3_acl_entry); + if ((char *)value > end) + goto fail; + acl->a_entries[n].e_id = entry->e_id; + break; + + default: + goto fail; + } + } + if (value != end) + goto fail; + return acl; + +fail: + fprintf(stderr, "ACL bad entry\n"); + free(acl); + return NULL; +} + /* * Dump code starts here :) */ static int -xattr_cb_list(char *name, char *value, int valuelen, void *private) +xattr_cb_list(char *name, char *value, int valuelen, int isSELinux, void *private) { + isSELinux; value[valuelen] = '\0'; printf("EA: %s:%s\n", name, value); @@ -234,37 +419,67 @@ xattr_cb_list(char *name, char *value, int valuelen, void *private) } static int -xattr_cb_set(char *name, char *value, int valuelen, void *private) +xattr_cb_set(char *name, char *value, int valuelen, int isSELinux, void *private) { char *path = (char *)private; + int err; - if (lsetxattr(path, name, value, valuelen, 0) < 0) { - warn("lsetxattr %s failed", path); + if (Nflag) + return GOOD; + + isSELinux; +#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */ + if (isSELinux) + err = lsetfilecon(path, value); + else +#endif + err = lsetxattr(path, name, value, valuelen, 0); + + if (err) { + warn("%s: EA set %s:%s failed", path, name, value); return FAIL; } + return GOOD; } static int -xattr_cb_compare(char *name, char *value, int valuelen, void *private) +xattr_cb_compare(char *name, char *value, int valuelen, int isSELinux, void *private) { char *path = (char *)private; char valuef[XATTR_MAXSIZE]; int valuesz; - - valuesz = lgetxattr(path, name, valuef, XATTR_MAXSIZE); - if (valuesz < 0) { - warn("%s: lgetxattr failed\n", path); - return FAIL; + + isSELinux; +#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */ + if (isSELinux) + { + security_context_t con = NULL; + + if (lgetfilecon(path, &con) < 0) { + warn("%s: EA compare lgetfilecon failed\n", path); + return FAIL; + } + + valuesz = strlen(con) + 1; + valuef[0] = 0; + strncat(valuef, con, sizeof valuef); + freecon(con); } - - if (valuesz != valuelen) { - fprintf(stderr, "%s: EA %s value changed\n", path, value); - return FAIL; + else { +#endif + valuesz = lgetxattr(path, name, valuef, XATTR_MAXSIZE); + if (valuesz < 0) { + warn("%s: EA compare lgetxattr failed\n", path); + return FAIL; + } +#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */ } - - if (memcmp(value, valuef, valuelen)) { - fprintf(stderr, "%s: EA %s value changed\n", path, value); +#endif + + if (valuesz != valuelen || memcmp(value, valuef, valuelen)) { + /* GAN24May06: show name and new value for user to compare */ + fprintf(stderr, "%s: EA %s:%s value changed to %s\n", path, name, value, valuef); return FAIL; } @@ -279,8 +494,9 @@ xattr_verify(char *buffer) end = buffer + XATTR_MAXSIZE; - if (Bcvt) - swabst("4i", (u_char *)buffer); +#if BYTE_ORDER == BIG_ENDIAN + swabst("4i", (u_char *)buffer); +#endif if (HDR(buffer)->h_magic != EXT2_XATTR_MAGIC && HDR(buffer)->h_magic != EXT2_XATTR_MAGIC2) { @@ -292,8 +508,9 @@ xattr_verify(char *buffer) /* check the on-disk data structure */ entry = FIRST_ENTRY(buffer); - if (Bcvt) - swabst("2b1s3i", (u_char *)entry); +#if BYTE_ORDER == BIG_ENDIAN + swabst("2b1s3i", (u_char *)entry); +#endif while (!IS_LAST_ENTRY(entry)) { struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry); @@ -302,8 +519,9 @@ xattr_verify(char *buffer) return FAIL; } entry = next; - if (Bcvt) - swabst("2b1s3i", (u_char *)entry); +#if BYTE_ORDER == BIG_ENDIAN + swabst("2b1s3i", (u_char *)entry); +#endif } return GOOD; } @@ -324,7 +542,7 @@ xattr_count(char *buffer, int *count) } static int -xattr_walk(char *buffer, int (*xattr_cb)(char *, char *, int, void *), void *private) +xattr_walk(char *buffer, int (*xattr_cb)(char *, char *, int, int, void *), void *private) { struct ext2_xattr_entry *entry; @@ -332,15 +550,22 @@ xattr_walk(char *buffer, int (*xattr_cb)(char *, char *, int, void *), void *pri for (entry = FIRST_ENTRY(buffer); !IS_LAST_ENTRY(entry); entry = EXT2_XATTR_NEXT(entry)) { char name[XATTR_MAXSIZE], value[XATTR_MAXSIZE]; + int size; int off; + int convertacl = 0; + int convertcon = 0; switch (entry->e_name_index) { case EXT2_XATTR_INDEX_USER: strcpy(name, "user."); break; case EXT2_XATTR_INDEX_POSIX_ACL_ACCESS: + strcpy(name, "system.posix_acl_access"); + convertacl = 1; + break; case EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT: - strcpy(name, "system."); + strcpy(name, "system.posix_acl_default"); + convertacl = 1; break; case EXT2_XATTR_INDEX_TRUSTED: strcpy(name, "trusted."); @@ -350,6 +575,9 @@ xattr_walk(char *buffer, int (*xattr_cb)(char *, char *, int, void *), void *pri break; case EXT2_XATTR_INDEX_SECURITY: strcpy(name, "security."); +#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */ + convertcon = transselinuxflag; +#endif break; default: fprintf(stderr, "Unknown EA index\n"); @@ -359,11 +587,51 @@ xattr_walk(char *buffer, int (*xattr_cb)(char *, char *, int, void *), void *pri off = strlen(name); memcpy(name + off, entry->e_name, entry->e_name_len); name[off + entry->e_name_len] = '\0'; + size = entry->e_value_size; + + memcpy(value, buffer + VALUE_OFFSET(buffer, entry), size); + if (convertacl) { + struct posix_acl *acl; - memcpy(value, buffer + VALUE_OFFSET(buffer, entry), entry->e_value_size); + acl = ext3_acl_from_disk(value, size); + if (!acl) + return FAIL; + size = posix_acl_to_xattr(acl, value, XATTR_MAXSIZE); + if (size < 0) + return FAIL; + free(acl); + } + +#ifdef TRANSSELINUX /*GAN6May06 SELinux MLS */ + if (convertcon && strcmp(name, "security.selinux")) + convertcon = 0; /*GAN24May06 only for selinux */ + + if (convertcon) + { + security_context_t con = NULL; + int err; + + if (!transselinuxarg) + err = security_canonicalize_context(value, &con); + else { + strncat(value, transselinuxarg, sizeof value); + err = security_canonicalize_context_raw(value, &con); + } + + if (err < 0) { + warn("%s: EA canonicalize failed\n", value); + return FAIL; + } + + size = strlen(con) + 1; + value[0] = 0; + strncat(value, con, sizeof value); + freecon(con); + } +#endif - if (xattr_cb(name, value, entry->e_value_size, private) != GOOD) + if (xattr_cb(name, value, size, convertcon, private) != GOOD) return FAIL; }