1 /* 2 * Property Service contexts backend for labeling Android 3 * property keys 4 */ 5 6 #include <stdarg.h> 7 #include <string.h> 8 #include <ctype.h> 9 #include <errno.h> 10 #include <limits.h> 11 #include <sys/types.h> 12 #include <sys/stat.h> 13 #include "callbacks.h" 14 #include "label_internal.h" 15 16 /* A property security context specification. */ 17 typedef struct spec { 18 struct selabel_lookup_rec lr; /* holds contexts for lookup result */ 19 char *property_key; /* property key string */ 20 } spec_t; 21 22 /* Our stored configuration */ 23 struct saved_data { 24 /* 25 * The array of specifications is sorted for longest 26 * prefix match 27 */ 28 spec_t *spec_arr; 29 unsigned int nspec; /* total number of specifications */ 30 }; 31 32 static int cmp(const void *A, const void *B) 33 { 34 const struct spec *sp1 = A, *sp2 = B; 35 36 if (strncmp(sp1->property_key,"*",1) == 0) 37 return 1; 38 if (strncmp(sp2->property_key,"*",1) == 0) 39 return -1; 40 41 size_t L1 = strlen(sp1->property_key); 42 size_t L2 = strlen(sp2->property_key); 43 44 return (L1 < L2) - (L1 > L2); 45 } 46 47 /* 48 * Warn about duplicate specifications. 49 */ 50 static int nodups_specs(struct saved_data *data, const char *path) 51 { 52 int rc = 0; 53 unsigned int ii, jj; 54 struct spec *curr_spec, *spec_arr = data->spec_arr; 55 56 for (ii = 0; ii < data->nspec; ii++) { 57 curr_spec = &spec_arr[ii]; 58 for (jj = ii + 1; jj < data->nspec; jj++) { 59 if ((!strcmp(spec_arr[jj].property_key, curr_spec->property_key))) { 60 rc = -1; 61 errno = EINVAL; 62 if (strcmp 63 (spec_arr[jj].lr.ctx_raw, 64 curr_spec->lr.ctx_raw)) { 65 selinux_log 66 (SELINUX_ERROR, 67 "%s: Multiple different specifications for %s (%s and %s).\n", 68 path, curr_spec->property_key, 69 spec_arr[jj].lr.ctx_raw, 70 curr_spec->lr.ctx_raw); 71 } else { 72 selinux_log 73 (SELINUX_ERROR, 74 "%s: Multiple same specifications for %s.\n", 75 path, curr_spec->property_key); 76 } 77 } 78 } 79 } 80 return rc; 81 } 82 83 static int process_line(struct selabel_handle *rec, 84 const char *path, char *line_buf, 85 int pass, unsigned lineno) 86 { 87 int items, len; 88 char buf1[BUFSIZ], buf2[BUFSIZ]; 89 char *buf_p, *prop = buf1, *context = buf2; 90 struct saved_data *data = (struct saved_data *)rec->data; 91 spec_t *spec_arr = data->spec_arr; 92 unsigned int nspec = data->nspec; 93 94 len = strlen(line_buf); 95 if (line_buf[len - 1] == '\n') 96 line_buf[len - 1] = 0; 97 buf_p = line_buf; 98 while (isspace(*buf_p)) 99 buf_p++; 100 /* Skip comment lines and empty lines. */ 101 if (*buf_p == '#' || *buf_p == 0) 102 return 0; 103 items = sscanf(line_buf, "%255s %255s", prop, context); 104 if (items != 2) { 105 selinux_log(SELINUX_WARNING, 106 "%s: line %d is missing fields, skipping\n", path, 107 lineno); 108 return 0; 109 } 110 111 if (pass == 1) { 112 /* On the second pass, process and store the specification in spec. */ 113 spec_arr[nspec].property_key = strdup(prop); 114 if (!spec_arr[nspec].property_key) { 115 selinux_log(SELINUX_WARNING, 116 "%s: out of memory at line %d on prop %s\n", 117 path, lineno, prop); 118 return -1; 119 120 } 121 122 spec_arr[nspec].lr.ctx_raw = strdup(context); 123 if (!spec_arr[nspec].lr.ctx_raw) { 124 selinux_log(SELINUX_WARNING, 125 "%s: out of memory at line %d on context %s\n", 126 path, lineno, context); 127 return -1; 128 } 129 130 if (rec->validating) { 131 if (selabel_validate(rec, &spec_arr[nspec].lr) < 0) { 132 selinux_log(SELINUX_WARNING, 133 "%s: line %d has invalid context %s\n", 134 path, lineno, spec_arr[nspec].lr.ctx_raw); 135 } 136 } 137 } 138 139 data->nspec = ++nspec; 140 return 0; 141 } 142 143 static int init(struct selabel_handle *rec, const struct selinux_opt *opts, 144 unsigned n) 145 { 146 struct saved_data *data = (struct saved_data *)rec->data; 147 const char *path = NULL; 148 FILE *fp; 149 char line_buf[BUFSIZ]; 150 unsigned int lineno = 0, maxnspec, pass; 151 int status = -1; 152 struct stat sb; 153 154 /* Process arguments */ 155 while (n--) 156 switch (opts[n].type) { 157 case SELABEL_OPT_PATH: 158 path = opts[n].value; 159 break; 160 } 161 162 /* Open the specification file. */ 163 if ((fp = fopen(path, "r")) == NULL) 164 return -1; 165 166 if (fstat(fileno(fp), &sb) < 0) 167 return -1; 168 if (!S_ISREG(sb.st_mode)) { 169 errno = EINVAL; 170 return -1; 171 } 172 173 /* 174 * Two passes of the specification file. First is to get the size. 175 * After the first pass, the spec array is malloced to the appropriate 176 * size. Second pass is to populate the spec array and check for 177 * dups. 178 */ 179 maxnspec = UINT_MAX / sizeof(spec_t); 180 for (pass = 0; pass < 2; pass++) { 181 data->nspec = 0; 182 183 while (fgets(line_buf, sizeof line_buf - 1, fp) 184 && data->nspec < maxnspec) { 185 if (process_line(rec, path, line_buf, pass, ++lineno) != 0) { 186 goto finish; 187 } 188 } 189 190 if (pass == 1) { 191 status = nodups_specs(data, path); 192 193 if (status) 194 goto finish; 195 } 196 197 if (pass == 0) { 198 199 if (data->nspec == 0) { 200 status = 0; 201 goto finish; 202 } 203 204 if (NULL == (data->spec_arr = 205 malloc(sizeof(spec_t) * data->nspec))) 206 goto finish; 207 208 memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec); 209 maxnspec = data->nspec; 210 rewind(fp); 211 } 212 } 213 214 qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp); 215 216 status = 0; 217 finish: 218 fclose(fp); 219 return status; 220 } 221 222 /* 223 * Backend interface routines 224 */ 225 static void closef(struct selabel_handle *rec) 226 { 227 struct saved_data *data = (struct saved_data *)rec->data; 228 struct spec *spec; 229 unsigned int i; 230 231 for (i = 0; i < data->nspec; i++) { 232 spec = &data->spec_arr[i]; 233 free(spec->property_key); 234 free(spec->lr.ctx_raw); 235 free(spec->lr.ctx_trans); 236 } 237 238 if (data->spec_arr) 239 free(data->spec_arr); 240 241 free(data); 242 } 243 244 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, 245 const char *key, 246 int __attribute__((unused)) type) 247 { 248 struct saved_data *data = (struct saved_data *)rec->data; 249 spec_t *spec_arr = data->spec_arr; 250 unsigned int i; 251 struct selabel_lookup_rec *ret = NULL; 252 253 if (!data->nspec) { 254 errno = ENOENT; 255 goto finish; 256 } 257 258 for (i = 0; i < data->nspec; i++) { 259 if (strncmp(spec_arr[i].property_key, key, 260 strlen(spec_arr[i].property_key)) == 0) { 261 break; 262 } 263 if (strncmp(spec_arr[i].property_key, "*", 1) == 0) 264 break; 265 } 266 267 if (i >= data->nspec) { 268 /* No matching specification. */ 269 errno = ENOENT; 270 goto finish; 271 } 272 273 ret = &spec_arr[i].lr; 274 275 finish: 276 return ret; 277 } 278 279 static void stats(struct selabel_handle __attribute__((unused)) *rec) 280 { 281 selinux_log(SELINUX_WARNING, "'stats' functionality not implemented.\n"); 282 } 283 284 int selabel_property_init(struct selabel_handle *rec, 285 const struct selinux_opt *opts, 286 unsigned nopts) 287 { 288 struct saved_data *data; 289 290 data = (struct saved_data *)malloc(sizeof(*data)); 291 if (!data) 292 return -1; 293 memset(data, 0, sizeof(*data)); 294 295 rec->data = data; 296 rec->func_close = &closef; 297 rec->func_stats = &stats; 298 rec->func_lookup = &lookup; 299 300 return init(rec, opts, nopts); 301 } 302