|
Packit |
3feee0 |
/*
|
|
Packit |
3feee0 |
* Property Service contexts backend for labeling Android
|
|
Packit |
3feee0 |
* property keys
|
|
Packit |
3feee0 |
*/
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
#include <stdarg.h>
|
|
Packit |
3feee0 |
#include <string.h>
|
|
Packit |
3feee0 |
#include <ctype.h>
|
|
Packit |
3feee0 |
#include <errno.h>
|
|
Packit |
3feee0 |
#include <limits.h>
|
|
Packit |
3feee0 |
#include <sys/types.h>
|
|
Packit |
3feee0 |
#include <sys/stat.h>
|
|
Packit |
3feee0 |
#include "callbacks.h"
|
|
Packit |
3feee0 |
#include "label_internal.h"
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
/* A property security context specification. */
|
|
Packit |
3feee0 |
typedef struct spec {
|
|
Packit |
3feee0 |
struct selabel_lookup_rec lr; /* holds contexts for lookup result */
|
|
Packit |
3feee0 |
char *property_key; /* property key string */
|
|
Packit |
3feee0 |
} spec_t;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
/* Our stored configuration */
|
|
Packit |
3feee0 |
struct saved_data {
|
|
Packit |
3feee0 |
/*
|
|
Packit |
3feee0 |
* The array of specifications is sorted for longest
|
|
Packit |
3feee0 |
* prefix match
|
|
Packit |
3feee0 |
*/
|
|
Packit |
3feee0 |
spec_t *spec_arr;
|
|
Packit |
3feee0 |
unsigned int nspec; /* total number of specifications */
|
|
Packit |
3feee0 |
};
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
static int cmp(const void *A, const void *B)
|
|
Packit |
3feee0 |
{
|
|
Packit |
3feee0 |
const struct spec *sp1 = A, *sp2 = B;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (strncmp(sp1->property_key, "*", 1) == 0)
|
|
Packit |
3feee0 |
return 1;
|
|
Packit |
3feee0 |
if (strncmp(sp2->property_key, "*", 1) == 0)
|
|
Packit |
3feee0 |
return -1;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
size_t L1 = strlen(sp1->property_key);
|
|
Packit |
3feee0 |
size_t L2 = strlen(sp2->property_key);
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
return (L1 < L2) - (L1 > L2);
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
/*
|
|
Packit |
3feee0 |
* Warn about duplicate specifications.
|
|
Packit |
3feee0 |
*/
|
|
Packit |
3feee0 |
static int nodups_specs(struct saved_data *data, const char *path)
|
|
Packit |
3feee0 |
{
|
|
Packit |
3feee0 |
int rc = 0;
|
|
Packit |
3feee0 |
unsigned int ii, jj;
|
|
Packit |
3feee0 |
struct spec *curr_spec, *spec_arr = data->spec_arr;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
for (ii = 0; ii < data->nspec; ii++) {
|
|
Packit |
3feee0 |
curr_spec = &spec_arr[ii];
|
|
Packit |
3feee0 |
for (jj = ii + 1; jj < data->nspec; jj++) {
|
|
Packit |
3feee0 |
if (!strcmp(spec_arr[jj].property_key,
|
|
Packit |
3feee0 |
curr_spec->property_key)) {
|
|
Packit |
3feee0 |
rc = -1;
|
|
Packit |
3feee0 |
errno = EINVAL;
|
|
Packit |
3feee0 |
if (strcmp(spec_arr[jj].lr.ctx_raw,
|
|
Packit |
3feee0 |
curr_spec->lr.ctx_raw)) {
|
|
Packit |
3feee0 |
selinux_log
|
|
Packit |
3feee0 |
(SELINUX_ERROR,
|
|
Packit |
3feee0 |
"%s: Multiple different specifications for %s (%s and %s).\n",
|
|
Packit |
3feee0 |
path, curr_spec->property_key,
|
|
Packit |
3feee0 |
spec_arr[jj].lr.ctx_raw,
|
|
Packit |
3feee0 |
curr_spec->lr.ctx_raw);
|
|
Packit |
3feee0 |
} else {
|
|
Packit |
3feee0 |
selinux_log
|
|
Packit |
3feee0 |
(SELINUX_ERROR,
|
|
Packit |
3feee0 |
"%s: Multiple same specifications for %s.\n",
|
|
Packit |
3feee0 |
path, curr_spec->property_key);
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
return rc;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
static int process_line(struct selabel_handle *rec,
|
|
Packit |
3feee0 |
const char *path, char *line_buf,
|
|
Packit |
3feee0 |
int pass, unsigned lineno)
|
|
Packit |
3feee0 |
{
|
|
Packit |
3feee0 |
int items;
|
|
Packit |
3feee0 |
char *prop = NULL, *context = NULL;
|
|
Packit |
3feee0 |
struct saved_data *data = (struct saved_data *)rec->data;
|
|
Packit |
3feee0 |
spec_t *spec_arr = data->spec_arr;
|
|
Packit |
3feee0 |
unsigned int nspec = data->nspec;
|
|
Packit |
3feee0 |
const char *errbuf = NULL;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
items = read_spec_entries(line_buf, &errbuf, 2, &prop, &context);
|
|
Packit |
3feee0 |
if (items < 0) {
|
|
Packit |
3feee0 |
items = errno;
|
|
Packit |
3feee0 |
selinux_log(SELINUX_ERROR,
|
|
Packit |
3feee0 |
"%s: line %u error due to: %s\n", path,
|
|
Packit |
3feee0 |
lineno, errbuf ?: strerror(errno));
|
|
Packit |
3feee0 |
errno = items;
|
|
Packit |
3feee0 |
return -1;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (items == 0)
|
|
Packit |
3feee0 |
return items;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (items != 2) {
|
|
Packit |
3feee0 |
selinux_log(SELINUX_ERROR,
|
|
Packit |
3feee0 |
"%s: line %u is missing fields\n", path,
|
|
Packit |
3feee0 |
lineno);
|
|
Packit |
3feee0 |
free(prop);
|
|
Packit |
3feee0 |
errno = EINVAL;
|
|
Packit |
3feee0 |
return -1;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (pass == 0) {
|
|
Packit |
3feee0 |
free(prop);
|
|
Packit |
3feee0 |
free(context);
|
|
Packit |
3feee0 |
} else if (pass == 1) {
|
|
Packit |
3feee0 |
/* On the second pass, process and store the specification in spec. */
|
|
Packit |
3feee0 |
spec_arr[nspec].property_key = prop;
|
|
Packit |
3feee0 |
spec_arr[nspec].lr.ctx_raw = context;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (rec->validating) {
|
|
Packit |
3feee0 |
if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) {
|
|
Packit |
3feee0 |
selinux_log(SELINUX_ERROR,
|
|
Packit |
3feee0 |
"%s: line %u has invalid context %s\n",
|
|
Packit |
3feee0 |
path, lineno, spec_arr[nspec].lr.ctx_raw);
|
|
Packit |
3feee0 |
errno = EINVAL;
|
|
Packit |
3feee0 |
return -1;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
data->nspec = ++nspec;
|
|
Packit |
3feee0 |
return 0;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
|
|
Packit |
3feee0 |
unsigned n)
|
|
Packit |
3feee0 |
{
|
|
Packit |
3feee0 |
struct saved_data *data = (struct saved_data *)rec->data;
|
|
Packit |
3feee0 |
const char *path = NULL;
|
|
Packit |
3feee0 |
FILE *fp;
|
|
Packit |
3feee0 |
char line_buf[BUFSIZ];
|
|
Packit |
3feee0 |
unsigned int lineno, maxnspec, pass;
|
|
Packit |
3feee0 |
int status = -1;
|
|
Packit |
3feee0 |
struct stat sb;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
/* Process arguments */
|
|
Packit |
3feee0 |
while (n--)
|
|
Packit |
3feee0 |
switch (opts[n].type) {
|
|
Packit |
3feee0 |
case SELABEL_OPT_PATH:
|
|
Packit |
3feee0 |
path = opts[n].value;
|
|
Packit |
3feee0 |
break;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (!path)
|
|
Packit |
3feee0 |
return -1;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
/* Open the specification file. */
|
|
Packit |
3feee0 |
if ((fp = fopen(path, "re")) == NULL)
|
|
Packit |
3feee0 |
return -1;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (fstat(fileno(fp), &sb) < 0)
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
errno = EINVAL;
|
|
Packit |
3feee0 |
if (!S_ISREG(sb.st_mode))
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
/*
|
|
Packit |
3feee0 |
* Two passes of the specification file. First is to get the size.
|
|
Packit |
3feee0 |
* After the first pass, the spec array is malloced to the appropriate
|
|
Packit |
3feee0 |
* size. Second pass is to populate the spec array and check for
|
|
Packit |
3feee0 |
* dups.
|
|
Packit |
3feee0 |
*/
|
|
Packit |
3feee0 |
maxnspec = UINT_MAX / sizeof(spec_t);
|
|
Packit |
3feee0 |
for (pass = 0; pass < 2; pass++) {
|
|
Packit |
3feee0 |
data->nspec = 0;
|
|
Packit |
3feee0 |
lineno = 0;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
while (fgets(line_buf, sizeof(line_buf) - 1, fp)
|
|
Packit |
3feee0 |
&& data->nspec < maxnspec) {
|
|
Packit |
3feee0 |
if (process_line(rec, path, line_buf, pass, ++lineno)
|
|
Packit |
3feee0 |
!= 0)
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (pass == 1) {
|
|
Packit |
3feee0 |
status = nodups_specs(data, path);
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (status)
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (pass == 0) {
|
|
Packit |
3feee0 |
if (data->nspec == 0) {
|
|
Packit |
3feee0 |
status = 0;
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (NULL == (data->spec_arr =
|
|
Packit |
3feee0 |
calloc(data->nspec, sizeof(spec_t))))
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
maxnspec = data->nspec;
|
|
Packit |
3feee0 |
rewind(fp);
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
|
|
Packit |
3feee0 |
if (status)
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
digest_gen_hash(rec->digest);
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
finish:
|
|
Packit |
3feee0 |
fclose(fp);
|
|
Packit |
3feee0 |
return status;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
/*
|
|
Packit |
3feee0 |
* Backend interface routines
|
|
Packit |
3feee0 |
*/
|
|
Packit |
3feee0 |
static void closef(struct selabel_handle *rec)
|
|
Packit |
3feee0 |
{
|
|
Packit |
3feee0 |
struct saved_data *data = (struct saved_data *)rec->data;
|
|
Packit |
3feee0 |
struct spec *spec;
|
|
Packit |
3feee0 |
unsigned int i;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
for (i = 0; i < data->nspec; i++) {
|
|
Packit |
3feee0 |
spec = &data->spec_arr[i];
|
|
Packit |
3feee0 |
free(spec->property_key);
|
|
Packit |
3feee0 |
free(spec->lr.ctx_raw);
|
|
Packit |
3feee0 |
free(spec->lr.ctx_trans);
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (data->spec_arr)
|
|
Packit |
3feee0 |
free(data->spec_arr);
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
free(data);
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
static struct selabel_lookup_rec *property_lookup(struct selabel_handle *rec,
|
|
Packit |
3feee0 |
const char *key,
|
|
Packit |
3feee0 |
int __attribute__((unused)) type)
|
|
Packit |
3feee0 |
{
|
|
Packit |
3feee0 |
struct saved_data *data = (struct saved_data *)rec->data;
|
|
Packit |
3feee0 |
spec_t *spec_arr = data->spec_arr;
|
|
Packit |
3feee0 |
unsigned int i;
|
|
Packit |
3feee0 |
struct selabel_lookup_rec *ret = NULL;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (!data->nspec) {
|
|
Packit |
3feee0 |
errno = ENOENT;
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
for (i = 0; i < data->nspec; i++) {
|
|
Packit |
3feee0 |
if (strncmp(spec_arr[i].property_key, key,
|
|
Packit |
3feee0 |
strlen(spec_arr[i].property_key)) == 0) {
|
|
Packit |
3feee0 |
break;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
|
|
Packit |
3feee0 |
break;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (i >= data->nspec) {
|
|
Packit |
3feee0 |
/* No matching specification. */
|
|
Packit |
3feee0 |
errno = ENOENT;
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
ret = &spec_arr[i].lr;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
finish:
|
|
Packit |
3feee0 |
return ret;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
static struct selabel_lookup_rec *service_lookup(struct selabel_handle *rec,
|
|
Packit |
3feee0 |
const char *key, int __attribute__((unused)) type)
|
|
Packit |
3feee0 |
{
|
|
Packit |
3feee0 |
struct saved_data *data = (struct saved_data *)rec->data;
|
|
Packit |
3feee0 |
spec_t *spec_arr = data->spec_arr;
|
|
Packit |
3feee0 |
unsigned int i;
|
|
Packit |
3feee0 |
struct selabel_lookup_rec *ret = NULL;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (!data->nspec) {
|
|
Packit |
3feee0 |
errno = ENOENT;
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
for (i = 0; i < data->nspec; i++) {
|
|
Packit |
3feee0 |
if (strcmp(spec_arr[i].property_key, key) == 0)
|
|
Packit |
3feee0 |
break;
|
|
Packit |
3feee0 |
if (strcmp(spec_arr[i].property_key, "*") == 0)
|
|
Packit |
3feee0 |
break;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
if (i >= data->nspec) {
|
|
Packit |
3feee0 |
/* No matching specification. */
|
|
Packit |
3feee0 |
errno = ENOENT;
|
|
Packit |
3feee0 |
goto finish;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
ret = &spec_arr[i].lr;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
finish:
|
|
Packit |
3feee0 |
return ret;
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
static void stats(struct selabel_handle __attribute__((unused)) *rec)
|
|
Packit |
3feee0 |
{
|
|
Packit |
3feee0 |
selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n");
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
int selabel_property_init(struct selabel_handle *rec,
|
|
Packit |
3feee0 |
const struct selinux_opt *opts,
|
|
Packit |
3feee0 |
unsigned nopts)
|
|
Packit |
3feee0 |
{
|
|
Packit |
3feee0 |
struct saved_data *data;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
data = (struct saved_data *)calloc(1, sizeof(*data));
|
|
Packit |
3feee0 |
if (!data)
|
|
Packit |
3feee0 |
return -1;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
rec->data = data;
|
|
Packit |
3feee0 |
rec->func_close = &closef;
|
|
Packit |
3feee0 |
rec->func_stats = &stat;;
|
|
Packit |
3feee0 |
rec->func_lookup = &property_lookup;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
return init(rec, opts, nopts);
|
|
Packit |
3feee0 |
}
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
int selabel_service_init(struct selabel_handle *rec,
|
|
Packit |
3feee0 |
const struct selinux_opt *opts, unsigned nopts)
|
|
Packit |
3feee0 |
{
|
|
Packit |
3feee0 |
struct saved_data *data;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
data = (struct saved_data *)calloc(1, sizeof(*data));
|
|
Packit |
3feee0 |
if (!data)
|
|
Packit |
3feee0 |
return -1;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
rec->data = data;
|
|
Packit |
3feee0 |
rec->func_close = &closef;
|
|
Packit |
3feee0 |
rec->func_stats = &stat;;
|
|
Packit |
3feee0 |
rec->func_lookup = &service_lookup;
|
|
Packit |
3feee0 |
|
|
Packit |
3feee0 |
return init(rec, opts, nopts);
|
|
Packit |
3feee0 |
}
|