Skip to content

Commit

Permalink
libselinux: Add selabel_digest function
Browse files Browse the repository at this point in the history
selabel_digest(3) if enabled by the SELABEL_OPT_DIGEST option during
selabel_open(3) will return an SHA1 digest of the spec files, plus
a list of the specfiles used to calculate the digest. There is a
test utility supplied that will demonstrate the functionality.

The use case for selabel_digest(3) is to implement an selinux_restorecon
function based on the Android version that writes a hash of the
file_contexts files to an extended attribute to enhance performance
(see external/libselinux/src/android.c selinux_android_restorecon()).

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
  • Loading branch information
Richard Haines authored and stephensmalley committed Oct 13, 2015
1 parent c9c1f27 commit e40bbea
Show file tree
Hide file tree
Showing 14 changed files with 519 additions and 23 deletions.
21 changes: 20 additions & 1 deletion libselinux/include/selinux/label.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ struct selabel_handle;
#define SELABEL_OPT_PATH 3
/* select a subset of the search space as an optimization (file backend) */
#define SELABEL_OPT_SUBSET 4
/* require a hash calculation on spec files */
#define SELABEL_OPT_DIGEST 5
/* total number of options */
#define SELABEL_NOPT 5
#define SELABEL_NOPT 6

/*
* Label operations
Expand Down Expand Up @@ -106,6 +108,23 @@ int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con,
const char *key, const char **aliases, int type);

/**
* selabel_digest - Retrieve the SHA1 digest and the list of specfiles used to
* generate the digest. The SELABEL_OPT_DIGEST option must
* be set in selabel_open() to initiate the digest generation.
* @handle: specifies backend instance to query
* @digest: returns a pointer to the SHA1 digest.
* @digest_len: returns length of digest in bytes.
* @specfiles: a list of specfiles used in the SHA1 digest generation.
* The list is NULL terminated and will hold @num_specfiles entries.
* @num_specfiles: number of specfiles in the list.
*
* Return %0 on success, -%1 with @errno set on failure.
*/
int selabel_digest(struct selabel_handle *rec,
unsigned char **digest, size_t *digest_len,
char ***specfiles, size_t *num_specfiles);

enum selabel_cmp_result {
SELABEL_SUBSET,
SELABEL_EQUAL,
Expand Down
61 changes: 61 additions & 0 deletions libselinux/man/man3/selabel_digest.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
.TH "selabel_digest" "3" "16 Sept 2015" "" "SELinux API documentation"
.SH "NAME"
selabel_digest \- Return digest of specfiles and list of files used
.
.SH "SYNOPSIS"
.B #include <selinux/selinux.h>
.br
.B #include <selinux/label.h>
.sp
.BI "int selabel_digest(struct selabel_handle *" hnd ,
.in +\w'int selabel_digest('u
.BI "unsigned char **" digest ,
.BI "size_t *" digest_len ,
.br
.BI "char ***" specfiles,
.BI "size_t *" num_specfiles ");"
.in
.
.SH "DESCRIPTION"
.BR selabel_digest ()
performs an operation on the handle
.IR hnd ,
returning the results of the SHA1 digest pointed to by
.IR digest ,
whose length will be
.IR digest_len .
The list of specfiles used in the SHA1 digest calculation is returned in
.I specfiles
with the number of entries in
.IR num_specfiles .
.sp
To enable
.BR selabel_digest ()
to return this information the
.B SELABEL_OPT_DIGEST
option must be enable in
.BR selabel_open (3).
.sp
The result of
.BR selabel_digest ()
must not be used after
.BR selabel_close (3).
.
.SH "RETURN VALUE"
On success, zero is returned. On error, \-1 is returned and
.I errno
is set appropriately.
.
.SH "ERRORS"
.TP
.B EINVAL
No digest available (returned if
.B SELABEL_OPT_DIGEST
option not enabled).
.TP
.B ENOMEM
An attempt to allocate memory failed.
.
.SH "SEE ALSO"
.BR selabel_open (3),
.BR selinux (8)
5 changes: 5 additions & 0 deletions libselinux/man/man3/selabel_open.3
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ A non-null value for this option enables context validation. By default,
is used; a custom validation function can be provided via
.BR selinux_set_callback (3).
Note that an invalid context may not be treated as an error unless it is actually encountered during a lookup operation.
.TP
.B SELABEL_OPT_DIGEST
A non-null value for this option enables the generation of an SHA1 digest of
the spec files loaded as described in
.BR selabel_digest (3)
.
.SH "BACKENDS"
.TP
Expand Down
2 changes: 1 addition & 1 deletion libselinux/src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ $(LIBA): $(OBJS)
$(RANLIB) $@

$(LIBSO): $(LOBJS)
$(CC) $(CFLAGS) -shared -o $@ $^ -lpcre -ldl $(LDFLAGS) -L$(LIBDIR) -Wl,-soname,$(LIBSO),-z,defs,-z,relro
$(CC) $(CFLAGS) -shared -o $@ $^ -lpcre -ldl -lcrypto $(LDFLAGS) -L$(LIBDIR) -Wl,-soname,$(LIBSO),-z,defs,-z,relro
ln -sf $@ $(TARGET)

$(LIBPC): $(LIBPC).in ../VERSION
Expand Down
85 changes: 83 additions & 2 deletions libselinux/src/label.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <selinux/selinux.h>
#include "callbacks.h"
#include "label_internal.h"
Expand Down Expand Up @@ -65,15 +66,21 @@ static char *selabel_sub(struct selabel_sub *ptr, const char *src)
return NULL;
}

struct selabel_sub *selabel_subs_init(const char *path, struct selabel_sub *list)
struct selabel_sub *selabel_subs_init(const char *path,
struct selabel_sub *list,
struct selabel_digest *digest)
{
char buf[1024];
FILE *cfg = fopen(path, "r");
struct selabel_sub *sub;
struct selabel_sub *sub = NULL;
struct stat sb;

if (!cfg)
return list;

if (fstat(fileno(cfg), &sb) < 0)
return list;

while (fgets_unlocked(buf, sizeof(buf) - 1, cfg)) {
char *ptr = NULL;
char *src = buf;
Expand Down Expand Up @@ -115,6 +122,10 @@ struct selabel_sub *selabel_subs_init(const char *path, struct selabel_sub *list
sub->next = list;
list = sub;
}

if (digest_add_specfile(digest, cfg, NULL, sb.st_size, path) < 0)
goto err;

out:
fclose(cfg);
return list;
Expand All @@ -125,6 +136,57 @@ struct selabel_sub *selabel_subs_init(const char *path, struct selabel_sub *list
goto out;
}

static inline struct selabel_digest *selabel_is_digest_set
(const struct selinux_opt *opts,
unsigned n,
struct selabel_digest *entry)
{
struct selabel_digest *digest = NULL;

while (n--) {
if (opts[n].type == SELABEL_OPT_DIGEST &&
opts[n].value == (char *)1) {
digest = calloc(1, sizeof(*digest));
if (!digest)
goto err;

digest->digest = calloc(1, DIGEST_SPECFILE_SIZE + 1);
if (!digest->digest)
goto err;

digest->specfile_list = calloc(DIGEST_FILES_MAX,
sizeof(char *));
if (!digest->specfile_list)
goto err;

entry = digest;
return entry;
}
}
return NULL;

err:
free(digest->digest);
free(digest->specfile_list);
free(digest);
return NULL;
}

static void selabel_digest_fini(struct selabel_digest *ptr)
{
int i;

free(ptr->digest);
free(ptr->hashbuf);

if (ptr->specfile_list) {
for (i = 0; ptr->specfile_list[i]; i++)
free(ptr->specfile_list[i]);
free(ptr->specfile_list);
}
free(ptr);
}

/*
* Validation functions
*/
Expand Down Expand Up @@ -273,6 +335,7 @@ struct selabel_handle *selabel_open(unsigned int backend,

rec->subs = NULL;
rec->dist_subs = NULL;
rec->digest = selabel_is_digest_set(opts, nopts, rec->digest);

if ((*initfuncs[backend])(rec, opts, nopts)) {
free(rec);
Expand Down Expand Up @@ -378,10 +441,28 @@ enum selabel_cmp_result selabel_cmp(struct selabel_handle *h1,
return h1->func_cmp(h1, h2);
}

int selabel_digest(struct selabel_handle *rec,
unsigned char **digest, size_t *digest_len,
char ***specfiles, size_t *num_specfiles)
{
if (!rec->digest) {
errno = EINVAL;
return -1;
}

*digest = rec->digest->digest;
*digest_len = DIGEST_SPECFILE_SIZE;
*specfiles = rec->digest->specfile_list;
*num_specfiles = rec->digest->specfile_cnt;
return 0;
}

void selabel_close(struct selabel_handle *rec)
{
selabel_subs_fini(rec->subs);
selabel_subs_fini(rec->dist_subs);
if (rec->digest)
selabel_digest_fini(rec->digest);
rec->func_close(rec);
free(rec->spec_file);
free(rec);
Expand Down
7 changes: 6 additions & 1 deletion libselinux/src/label_android_property.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,12 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,

qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);

status = 0;
status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
if (status)
goto finish;

status = digest_gen_hash(rec->digest);

finish:
fclose(fp);
return status;
Expand Down
13 changes: 13 additions & 0 deletions libselinux/src/label_db.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ db_init(const struct selinux_opt *opts, unsigned nopts,
size_t line_len = 0;
unsigned int line_num = 0;
unsigned int i;
struct stat sb;

/*
* Initialize catalog data structure
Expand Down Expand Up @@ -280,6 +281,12 @@ db_init(const struct selinux_opt *opts, unsigned nopts,
free(catalog);
return NULL;
}
if (fstat(fileno(filp), &sb) < 0)
return NULL;
if (!S_ISREG(sb.st_mode)) {
errno = EINVAL;
return NULL;
}
rec->spec_file = strdup(path);

/*
Expand Down Expand Up @@ -312,6 +319,12 @@ db_init(const struct selinux_opt *opts, unsigned nopts,
}
free(line_buf);

if (digest_add_specfile(rec->digest, filp, NULL, sb.st_size, path) < 0)
goto out_error;

if (digest_gen_hash(rec->digest) < 0)
goto out_error;

fclose(filp);

return catalog;
Expand Down

0 comments on commit e40bbea

Please sign in to comment.