[PATCH] mtd-utils: SELinux context support for mkfs.ubifs
John Orosz
orosz at assurtech.com
Tue Feb 18 12:02:09 EST 2014
I don't expect this patch to be accepted given the requirement for SELinux
on the build machine. That said, the reasons for submitting this patch are:
1. The next person that is interested in adding SELinux context support to
UBIFS images (mkfs.ubifs) will have a reference implementation.
2. I'd like to get feedback about any shortcomings of this implementation.
This is really just a best effort hack at something that appears to never
have been tried (or at least shared).
--
This patch enables SELinux context to be added to a UBIFS image with
mkfs.ubifs. The patch was based off the following commit in mtd-utils
master branch:
commit dcea43eba91642939c82739387147da26d572758
Author: Aaron Sierra <asierra at xes-inc.com>
Date: Fri Sep 27 11:34:04 2013 -0500
The approach attempts to mirror that of Stephen Smalley with YAFFS2 in the
AOSP circa January 2012. The context file is supplied/enabled by an
optional command line argument. The following is a brief description of the
files changed:
Makefile: link to libselinux
mkfs.ubifs/key.h: added xent_key_init() from linux kernel
mkfs.ubifs/ubifs.h: added size macros from linux kernel
mkfs.ubifs/mkfs.ubifs.c: added SELinux context file support, mirrored
add_inode_with_data() in add_inode_with_xattr() to add XENT node and
xattr
inode.
This code has been tested on a Freescale p2040 processor (powerpc), linux
kernel 3.8.13+ with a 1GB Micron MT29F8G NAND flash chip.
Signed-off-by: John Orosz <orosz at assurtech.com>
diff --git a/Makefile b/Makefile
index 4ff8a49..d632cb2 100644
--- a/Makefile
+++ b/Makefile
@@ -105,7 +105,7 @@ $(call _mkdep,lib/,libmtd.a)
obj-mkfs.ubifs = crc16.o lpt.o compr.o devtable.o \
hashtable/hashtable.o hashtable/hashtable_itr.o
LDFLAGS_mkfs.ubifs = $(ZLIBLDFLAGS) $(LZOLDFLAGS) $(UUIDLDFLAGS)
-LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid
+LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid -lselinux
$(call mkdep,mkfs.ubifs/,mkfs.ubifs,,ubi-utils/libubi.a)
#
diff --git a/mkfs.ubifs/key.h b/mkfs.ubifs/key.h
index d3a02d4..07037ac 100644
--- a/mkfs.ubifs/key.h
+++ b/mkfs.ubifs/key.h
@@ -119,6 +119,25 @@ static inline void dent_key_init(const struct
ubifs_info *c,
}
/**
+ * xent_key_init - initialize extended attribute entry key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: host inode number
+ * @nm: extended attribute entry name and length
+ */
+static inline void xent_key_init(const struct ubifs_info *c,
+ union ubifs_key *key, ino_t inum,
+ const struct qstr *nm)
+{
+ uint32_t hash = c->key_hash(nm->name, nm->len);
+
+ ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+ key->u32[0] = inum;
+ key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
+}
+
+
+/**
* data_key_init - initialize data key.
* @c: UBIFS file-system description object
* @key: key to initialize
diff --git a/mkfs.ubifs/mkfs.ubifs.c b/mkfs.ubifs/mkfs.ubifs.c
index f9977bc..8a312ea 100644
--- a/mkfs.ubifs/mkfs.ubifs.c
+++ b/mkfs.ubifs/mkfs.ubifs.c
@@ -37,6 +37,14 @@
/* Default time granularity in nanoseconds */
#define DEFAULT_TIME_GRAN 1000000000
+#define XATTR_NAME_SELINUX "security.selinux"
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+static struct selabel_handle *sehnd;
+static unsigned int seprefixlen;
+static char *secontext = NULL;
+
/**
* struct idx_entry - index entry.
* @next: next index entry (NULL at end of list)
@@ -113,6 +121,10 @@ static int out_fd;
static int out_ubi;
static int squash_owner;
+static char *context;
+static int context_len;
+static struct stat context_st;
+
/* The 'head' (position) which nodes are written */
static int head_lnum;
static int head_offs;
@@ -134,7 +146,7 @@ static struct inum_mapping **hash_table;
/* Inode creation sequence number */
static unsigned long long creat_sqnum;
-static const char *optstring =
"d:r:m:o:D:yh?vVe:c:g:f:Fp:k:x:X:j:R:l:j:UQq";
+static const char *optstring =
"d:r:m:o:D:yh?vVe:c:g:f:Fp:k:x:X:j:R:l:j:UQqs:";
static const struct option longopts[] = {
{"root", 1, NULL, 'r'},
@@ -158,6 +170,7 @@ static const struct option longopts[] = {
{"log-lebs", 1, NULL, 'l'},
{"orph-lebs", 1, NULL, 'p'},
{"squash-uids" , 0, NULL, 'U'},
+ {"selinux", 1, NULL, 's'},
{NULL, 0, NULL, 0}
};
@@ -198,6 +211,7 @@ static const char *helptext =
"-V, --version display version information\n"
"-g, --debug=LEVEL display debug information (0 - none, 1 -
statistics,\n"
" 2 - files, 3 - more details)\n"
+"-s, --selinux=FILE SELinux context file\n"
"-h, --help display this help text\n\n"
"Note, SIZE is specified in bytes, but it may also be specified in
Kilobytes,\n"
"Megabytes, and Gigabytes if a KiB, MiB, or GiB suffix is used.\n\n"
@@ -623,6 +637,19 @@ static int get_options(int argc, char**argv)
case 'U':
squash_owner = 1;
break;
+ case 's':
+ context_len = strlen(optarg);
+ context = malloc(context_len + 1);
+ if (!context)
+ return err_msg("cannot allocate memory");
+
+ memcpy(context, optarg, context_len);
+
+ /* Make sure the root directory exists */
+ if (stat(context, &context_st))
+ return sys_err_msg("bad file context '%s'",
+ context);
+ break;
}
}
@@ -630,7 +657,7 @@ static int get_options(int argc, char**argv)
output = xstrdup(argv[optind]);
if (!output)
- return err_msg("not output device or file specified");
+ return err_msg("no output device or file specified");
out_ubi = !open_ubi(output);
@@ -1053,6 +1080,165 @@ static int add_inode_with_data(struct stat *st,
ino_t inum, void *data,
return add_node(&key, NULL, ino, len);
}
+static int add_dent_node(ino_t dir_inum, const char *name, ino_t inum,
+ unsigned char type);
+
+/**
+ * add_inode_with_xattr - write an inode with xattr support. much like the
+ * with_data function above but creates 3 nodes: The inode for the file/
+ * directory with xattr size set, the xent node with the same inum as
the
+ * previous inode and finally the inode with the actual context data and
+ * a new unique inum value.
+ * @st: stat information of source inode
+ * @inum: target inode number
+ * @data: inode data (for special inodes e.g. symlink path etc)
+ * @data_len: inode data length
+ * @flags: source inode flags
+ */
+static int add_inode_with_xattr(struct stat *st, ino_t inum, void *data,
+ unsigned int data_len, int flags)
+{
+ struct ubifs_ino_node *ino = node_buf;
+ struct ubifs_dent_node *xent = node_buf;
+ union ubifs_key key, xkey, nkey;
+ int len, use_flags = 0, ret;
+ char *name;
+ struct qstr nm;
+
+ if (c->default_compr != UBIFS_COMPR_NONE)
+ use_flags |= UBIFS_COMPR_FL;
+ if (flags & FS_COMPR_FL)
+ use_flags |= UBIFS_COMPR_FL;
+ if (flags & FS_SYNC_FL)
+ use_flags |= UBIFS_SYNC_FL;
+ if (flags & FS_IMMUTABLE_FL)
+ use_flags |= UBIFS_IMMUTABLE_FL;
+ if (flags & FS_APPEND_FL)
+ use_flags |= UBIFS_APPEND_FL;
+ if (flags & FS_DIRSYNC_FL && S_ISDIR(st->st_mode))
+ use_flags |= UBIFS_DIRSYNC_FL;
+
+ // file inode
+ memset(ino, 0, UBIFS_INO_NODE_SZ);
+
+ ino_key_init(&key, inum);
+ ino->ch.node_type = UBIFS_INO_NODE;
+ key_write(&key, &ino->key);
+ ino->creat_sqnum = cpu_to_le64(creat_sqnum);
+ ino->size = cpu_to_le64(st->st_size);
+ ino->nlink = cpu_to_le32(st->st_nlink);
+ /*
+ * The time fields are updated assuming the default time granularity
+ * of 1 second. To support finer granularities, utime() would be
needed.
+ */
+ ino->atime_sec = cpu_to_le64(st->st_atime);
+ ino->ctime_sec = cpu_to_le64(st->st_ctime);
+ ino->mtime_sec = cpu_to_le64(st->st_mtime);
+ ino->atime_nsec = 0;
+ ino->ctime_nsec = 0;
+ ino->mtime_nsec = 0;
+ ino->uid = cpu_to_le32(st->st_uid);
+ ino->gid = cpu_to_le32(st->st_gid);
+ ino->mode = cpu_to_le32(st->st_mode);
+ ino->flags = cpu_to_le32(use_flags);
+ ino->data_len = 0;
+ ino->compr_type = cpu_to_le16(c->default_compr);
+
+ name = strdup(XATTR_NAME_SELINUX);
+ if ( name == NULL ) printf("alloc failed\n");
+ nm.name = (void *)name;
+ nm.len = strlen(name);
+
+ ino->xattr_cnt = 1;
+ ino->xattr_size += CALC_DENT_SIZE(strlen(name));
+ ino->xattr_size += CALC_XATTR_BYTES(data_len);
+ ino->xattr_names += strlen(name);
+
+ len = UBIFS_INO_NODE_SZ;
+
+ ret = add_node(&key, name, ino, len) ;
+ if (ret) {
+ printf("host inode add failed\n");
+ return ret;
+ }
+
+ /* do xattr node */
+ memset(xent, 0, UBIFS_XENT_NODE_SZ);
+
+ xent_key_init(c, &xkey, inum, &nm);
+ xent->ch.node_type = UBIFS_XENT_NODE;
+ key_write(&xkey, &xent->key);
+
+ len = UBIFS_XENT_NODE_SZ + strlen(name) + 1;
+
+ xent->ch.len = len;
+ xent->padding1 = 0;
+ xent->type = UBIFS_ITYPE_DIR;
+ xent->nlen = cpu_to_le16(strlen(name));
+
+ memcpy(xent->name, name, strlen(name) + 1);
+
+ /* unique inum for inode with xattr */
+ inum = ++c->highest_inum;
+ creat_sqnum = ++c->max_sqnum;
+
+ xent->inum = cpu_to_le64(inum);
+
+ ret = add_node(&xkey, name, xent, len);
+ if (ret) {
+ printf("xent inode add failed\n");
+ return ret;
+ }
+
+ /* add inode with xattr data */
+ memset(ino, 0, UBIFS_INO_NODE_SZ);
+
+ ino_key_init(&nkey, inum);
+ ino->ch.node_type = UBIFS_INO_NODE;
+ key_write(&nkey, &ino->key);
+ ino->creat_sqnum = cpu_to_le64(creat_sqnum);
+ ino->size = cpu_to_le64(data_len);
+ /*
+ * The time fields are updated assuming the default time granularity
+ * of 1 second. To support finer granularities, utime() would be
needed.
+ */
+ ino->atime_sec = cpu_to_le64(st->st_atime);
+ ino->ctime_sec = cpu_to_le64(st->st_ctime);
+ ino->mtime_sec = cpu_to_le64(st->st_mtime);
+ ino->atime_nsec = 0;
+ ino->ctime_nsec = 0;
+ ino->mtime_nsec = 0;
+ ino->uid = cpu_to_le32(st->st_uid);
+ ino->gid = cpu_to_le32(st->st_gid);
+ ino->mode = cpu_to_le32((st->st_mode & (~S_IFDIR)) |
(S_IFREG));
+ ino->flags = cpu_to_le32(UBIFS_XATTR_FL);
+ ino->data_len = cpu_to_le32(data_len);
+ ino->compr_type = cpu_to_le16(c->default_compr);
+
+ name = strdup(XATTR_NAME_SELINUX);
+ if ( name == NULL ) printf("alloc failed\n");
+ nm.name = (void *)name;
+ nm.len = strlen(name);
+
+ ino->xattr_cnt = 1;
+ ino->xattr_size += CALC_DENT_SIZE(strlen(name));
+ ino->xattr_size += CALC_XATTR_BYTES(data_len);
+ ino->xattr_names += strlen(name);
+
+ if (data_len)
+ memcpy(&ino->data, data, data_len);
+
+ len = UBIFS_INO_NODE_SZ + data_len;
+
+ ret = add_node(&nkey, name, ino, len) ;
+ if (ret) {
+ printf("new inode add failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
/**
* add_inode - write an inode.
* @st: stat information of source inode
@@ -1078,9 +1264,9 @@ static int add_inode(struct stat *st, ino_t inum, int
flags)
* the device table.
*/
static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int
nlink,
- struct stat *st)
+ struct stat *st, const char *path_name)
{
- int fd, flags = 0;
+ int fd, flags = 0, con_size;
st->st_size = size;
st->st_nlink = nlink;
@@ -1093,7 +1279,37 @@ static int add_dir_inode(DIR *dir, ino_t inum, loff_t
size, unsigned int nlink,
flags = 0;
}
- return add_inode(st, inum, flags);
+ // special case for root inode, no xattr
+ if( inum == UBIFS_ROOT_INO)
+ return add_inode(st, inum, flags);
+
+ if ( context ) {
+ char *sepath = NULL;
+ if (path_name[0] == '/')
+ sepath = strdup(&path_name[strlen(root)]);
+ else if (asprintf(&sepath, "/%s", &path_name[strlen(root)]) < 0)
+ sepath = NULL;
+
+ if (!sepath) {
+ return sys_err_msg("malloc");
+ }
+
+ if (selabel_lookup(sehnd, &secontext,
+ sepath,
+ st->st_mode) < 0) {
+
+ /* failed to lookup context, assume undefined */
+ secontext = strdup("system_u:object_r:undefined_t");
+ printf("missing context: %s\t%s\t%d\n", secontext, sepath,
st->st_mode);
+ }
+ free(sepath);
+
+ con_size = strlen(secontext)+1;
+ } else {
+ secontext = NULL;
+ con_size = 0;
+ }
+ return add_inode_with_xattr(st, inum, secontext, con_size, flags);
}
/**
@@ -1238,7 +1454,7 @@ static int add_file(const char *path_name, struct stat
*st, ino_t inum,
loff_t file_size = 0;
ssize_t ret, bytes_read;
union ubifs_key key;
- int fd, dn_len, err, compr_type, use_compr;
+ int fd, dn_len, err, compr_type, use_compr, con_size;
unsigned int block_no = 0;
size_t out_len;
@@ -1295,7 +1511,34 @@ static int add_file(const char *path_name, struct
stat *st, ino_t inum,
if (file_size != st->st_size)
return err_msg("file size changed during writing file '%s'",
path_name);
- return add_inode(st, inum, flags);
+
+ if ( context ) {
+ char *sepath = NULL;
+ if (path_name[0] == '/')
+ sepath = strdup(&path_name[strlen(root)]);
+ else if (asprintf(&sepath, "/%s", &path_name[strlen(root)]) < 0)
+ sepath = NULL;
+
+ if (!sepath) {
+ return sys_err_msg("malloc");
+ }
+
+ if (selabel_lookup(sehnd, &secontext,
+ sepath,
+ st->st_mode) < 0) {
+
+ /* failed to lookup context, assume undefined */
+ secontext = strdup("system_u:object_r:undefined_t");
+ printf("missing context: %s\t%s\t%d\n", secontext, sepath,
st->st_mode);
+ }
+ free(sepath);
+
+ con_size = strlen(secontext)+1;
+ } else {
+ secontext = NULL;
+ con_size = 0;
+ }
+ return add_inode_with_xattr(st, inum, secontext, con_size, flags);
}
/**
@@ -1307,7 +1550,7 @@ static int add_file(const char *path_name, struct stat
*st, ino_t inum,
* @st: struct stat object containing inode attributes which should be use
when
* creating the UBIFS inode
*/
-static int add_non_dir(const char *path_name, ino_t *inum, unsigned int
nlink,
+static int add_non_dir(const char *path_name, ino_t inum, unsigned int
nlink,
unsigned char *type, struct stat *st)
{
int fd, flags = 0;
@@ -1353,7 +1596,7 @@ static int add_non_dir(const char *path_name, ino_t
*inum, unsigned int nlink,
return err_msg("out of memory");
if (im->use_nlink == 0) {
/* New entry */
- im->use_inum = *inum;
+ im->use_inum = inum;
im->use_nlink = 1;
im->path_name = malloc(strlen(path_name) + 1);
if (!im->path_name)
@@ -1361,10 +1604,8 @@ static int add_non_dir(const char *path_name, ino_t
*inum, unsigned int nlink,
strcpy(im->path_name, path_name);
} else {
/* Existing entry */
- *inum = im->use_inum;
+ inum = im->use_inum;
im->use_nlink += 1;
- /* Return unused inode number */
- c->highest_inum -= 1;
}
memcpy(&im->st, st, sizeof(struct stat));
@@ -1375,17 +1616,17 @@ static int add_non_dir(const char *path_name, ino_t
*inum, unsigned int nlink,
creat_sqnum = ++c->max_sqnum;
if (S_ISREG(st->st_mode))
- return add_file(path_name, st, *inum, flags);
+ return add_file(path_name, st, inum, flags);
if (S_ISCHR(st->st_mode))
- return add_dev_inode(st, *inum, flags);
+ return add_dev_inode(st, inum, flags);
if (S_ISBLK(st->st_mode))
- return add_dev_inode(st, *inum, flags);
+ return add_dev_inode(st, inum, flags);
if (S_ISLNK(st->st_mode))
- return add_symlink_inode(path_name, st, *inum, flags);
+ return add_symlink_inode(path_name, st, inum, flags);
if (S_ISSOCK(st->st_mode))
- return add_inode(st, *inum, flags);
+ return add_inode(st, inum, flags);
if (S_ISFIFO(st->st_mode))
- return add_inode(st, *inum, flags);
+ return add_inode(st, inum, flags);
return err_msg("file '%s' has unknown inode type", path_name);
}
@@ -1498,7 +1739,7 @@ static int add_directory(const char *dir_name, ino_t
dir_inum, struct stat *st,
nlink += 1;
type = UBIFS_ITYPE_DIR;
} else {
- err = add_non_dir(name, &inum, 0, &type, &dent_st);
+ err = add_non_dir(name, inum, 0, &type, &dent_st);
if (err)
goto out_free;
}
@@ -1550,7 +1791,7 @@ static int add_directory(const char *dir_name, ino_t
dir_inum, struct stat *st,
nlink += 1;
type = UBIFS_ITYPE_DIR;
} else {
- err = add_non_dir(name, &inum, 0, &type, &fake_st);
+ err = add_non_dir(name, inum, 0, &type, &fake_st);
if (err)
goto out_free;
}
@@ -1565,7 +1806,7 @@ static int add_directory(const char *dir_name, ino_t
dir_inum, struct stat *st,
creat_sqnum = dir_creat_sqnum;
- err = add_dir_inode(dir, dir_inum, size, nlink, st);
+ err = add_dir_inode(dir, dir_inum, size, nlink, st, dir_name);
if (err)
goto out_free;
@@ -1595,7 +1836,7 @@ static int add_multi_linked_files(void)
for (im = hash_table[i]; im; im = im->next) {
dbg_msg(2, "%s", im->path_name);
- err = add_non_dir(im->path_name, &im->use_inum,
+ err = add_non_dir(im->path_name, im->use_inum,
im->use_nlink, &type, &im->st);
if (err)
return err;
@@ -2204,6 +2445,21 @@ static int init(void)
if (err)
return err;
+ if( context ) {
+ seprefixlen = strlen(root);
+ struct selinux_opt seopts[] = {
+ { SELABEL_OPT_PATH, context }
+ };
+
+ sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1);
+
+ if (!sehnd)
+ {
+ return err_msg("could not open context");
+ }
+
+ }
+
return 0;
}
@@ -2228,6 +2484,9 @@ static void destroy_hash_table(void)
*/
static void deinit(void)
{
+ if ( sehnd )
+ selabel_close(sehnd);
+
free(c->lpt);
free(c->ltab);
free(leb_buf);
diff --git a/mkfs.ubifs/ubifs.h b/mkfs.ubifs/ubifs.h
index 434b651..2f080a8 100644
--- a/mkfs.ubifs/ubifs.h
+++ b/mkfs.ubifs/ubifs.h
@@ -42,6 +42,15 @@
*/
#define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT
+/*
+ * How much a directory entry/extended attribute entry adds to the
parent/host
+ * inode.
+ */
+#define CALC_DENT_SIZE(name_len) ALIGN(UBIFS_DENT_NODE_SZ + (name_len) + 1,
8)
+
+/* How much an extended attribute adds to the host inode */
+#define CALC_XATTR_BYTES(data_len) ALIGN(UBIFS_INO_NODE_SZ + (data_len) +
1, 8)
+
/* The below union makes it easier to deal with keys */
union ubifs_key
{
More information about the linux-mtd
mailing list