1 /* 2 * Media contexts backend for DB objects 3 * 4 * Author: KaiGai Kohei <kaigai (at) ak.jp.nec.com> 5 */ 6 7 #include <sys/stat.h> 8 #include <string.h> 9 #include <stdio.h> 10 #include <stdio_ext.h> 11 #include <ctype.h> 12 #include <errno.h> 13 #include <limits.h> 14 #include <fnmatch.h> 15 #include "callbacks.h" 16 #include "label_internal.h" 17 18 /* 19 * Regular database object's security context interface 20 * 21 * It provides applications a regular security context for the given 22 * database objects. The pair of object's name and a security context 23 * are described in the specfile. In the default, it shall be stored 24 * in the /etc/selinux/$POLICYTYPE/contexts/sepgsql_contexts . 25 * (It assumes SE-PostgreSQL in the default. For other RDBMS, use the 26 * SELABEL_OPT_PATH option to specify different specfile.) 27 * 28 * Each line has the following format: 29 * <object class> <object name/identifier> <security context> 30 * 31 * For example: 32 * ---------------------------------------- 33 * # 34 * # It is an example specfile for database obejcts 35 * # 36 * db_database template1 system_u:object_r:sepgsql_db_t:s0 37 * 38 * db_schema *.pg_catalog system_u:object_r:sepgsql_sys_schema_t:s0 39 * 40 * db_table *.pg_catalog.* system_u:object_r:sepgsql_sysobj_t:s0 41 * db_column *.pg_catalog.*.* system_u:object_r:sepgsql_sysobj_t:s0 42 * ---------------------------------------- 43 * 44 * All the characters after the '#' are dealt as comments. 45 * 46 * The first token is object class. SELABEL_DB_* declared in label.h are 47 * corresponding to a certain database object. 48 * 49 * The object name/identifier is compared to the given key. 50 * A database object can have its own namespace hierarchy. 51 * In the case of SE-PgSQL, database is the top level object, and schema 52 * is deployed just under a database. A schema can contains various kind 53 * of objects, such as tables, procedures and so on. 54 * Thus, when we lookup an expected security context for a table of 55 * "pg_class", it is necessary to assume selabel_lookup() is called with 56 * "postgres.pg_catalog.pg_class", not just a "pg_class". 57 * 58 * Wildcards ('*' or '?') are available on the patterns, so if you want 59 * to match a table within any schema, you should set '*' on the upper 60 * namespaces of the table. 61 * 62 * The structure of namespace depends on RDBMS. 63 * For example, Trusted-RUBIX has an idea of "catalog" which performs 64 * as a namespace between a database and individual schemas. In this 65 * case, a table has upper three layers. 66 */ 67 68 /* 69 * spec_t : It holds a pair of a key and an expected security context 70 */ 71 typedef struct spec { 72 struct selabel_lookup_rec lr; 73 char *key; 74 int type; 75 int matches; 76 } spec_t; 77 78 /* 79 * catalog_t : An array of spec_t 80 */ 81 typedef struct catalog { 82 unsigned int nspec; /* number of specs in use */ 83 unsigned int limit; /* physical limitation of specs[] */ 84 spec_t specs[0]; 85 } catalog_t; 86 87 /* 88 * Helper function to parse a line read from the specfile 89 */ 90 static int 91 process_line(const char *path, char *line_buf, unsigned int line_num, 92 catalog_t *catalog) 93 { 94 spec_t *spec = &catalog->specs[catalog->nspec]; 95 char *type, *key, *context, *temp; 96 int items; 97 98 /* Cut off comments */ 99 temp = strchr(line_buf, '#'); 100 if (temp) 101 *temp = '\0'; 102 103 /* 104 * Every entry must have the following format 105 * <object class> <object name> <security context> 106 */ 107 type = key = context = temp = NULL; 108 items = sscanf(line_buf, "%ms %ms %ms %ms", 109 &type, &key, &context, &temp); 110 if (items != 3) { 111 if (items > 0) 112 selinux_log(SELINUX_WARNING, 113 "%s: line %u has invalid format, skipped", 114 path, line_num); 115 goto skip; 116 } 117 118 /* 119 * Set up individual spec entry 120 */ 121 memset(spec, 0, sizeof(spec_t)); 122 123 if (!strcmp(type, "db_database")) 124 spec->type = SELABEL_DB_DATABASE; 125 else if (!strcmp(type, "db_schema")) 126 spec->type = SELABEL_DB_SCHEMA; 127 else if (!strcmp(type, "db_table")) 128 spec->type = SELABEL_DB_TABLE; 129 else if (!strcmp(type, "db_column")) 130 spec->type = SELABEL_DB_COLUMN; 131 else if (!strcmp(type, "db_sequence")) 132 spec->type = SELABEL_DB_SEQUENCE; 133 else if (!strcmp(type, "db_view")) 134 spec->type = SELABEL_DB_VIEW; 135 else if (!strcmp(type, "db_procedure")) 136 spec->type = SELABEL_DB_PROCEDURE; 137 else if (!strcmp(type, "db_blob")) 138 spec->type = SELABEL_DB_BLOB; 139 else if (!strcmp(type, "db_tuple")) 140 spec->type = SELABEL_DB_TUPLE; 141 else if (!strcmp(type, "db_language")) 142 spec->type = SELABEL_DB_LANGUAGE; 143 else if (!strcmp(type, "db_exception")) 144 spec->type = SELABEL_DB_EXCEPTION; 145 else if (!strcmp(type, "db_datatype")) 146 spec->type = SELABEL_DB_DATATYPE; 147 else { 148 selinux_log(SELINUX_WARNING, 149 "%s: line %u has invalid object type %s\n", 150 path, line_num, type); 151 goto skip; 152 } 153 154 free(type); 155 spec->key = key; 156 spec->lr.ctx_raw = context; 157 158 catalog->nspec++; 159 160 return 0; 161 162 skip: 163 free(type); 164 free(key); 165 free(context); 166 free(temp); 167 168 return 0; 169 } 170 171 /* 172 * selabel_close() handler 173 */ 174 static void 175 db_close(struct selabel_handle *rec) 176 { 177 catalog_t *catalog = (catalog_t *)rec->data; 178 spec_t *spec; 179 unsigned int i; 180 181 for (i = 0; i < catalog->nspec; i++) { 182 spec = &catalog->specs[i]; 183 free(spec->key); 184 free(spec->lr.ctx_raw); 185 free(spec->lr.ctx_trans); 186 } 187 free(catalog); 188 } 189 190 /* 191 * selabel_lookup() handler 192 */ 193 static struct selabel_lookup_rec * 194 db_lookup(struct selabel_handle *rec, const char *key, int type) 195 { 196 catalog_t *catalog = (catalog_t *)rec->data; 197 spec_t *spec; 198 unsigned int i; 199 200 for (i = 0; i < catalog->nspec; i++) { 201 spec = &catalog->specs[i]; 202 203 if (spec->type != type) 204 continue; 205 if (!fnmatch(spec->key, key, 0)) { 206 spec->matches++; 207 208 return &spec->lr; 209 } 210 } 211 212 /* No found */ 213 errno = ENOENT; 214 return NULL; 215 } 216 217 /* 218 * selabel_stats() handler 219 */ 220 static void 221 db_stats(struct selabel_handle *rec) 222 { 223 catalog_t *catalog = (catalog_t *)rec->data; 224 unsigned int i, total = 0; 225 226 for (i = 0; i < catalog->nspec; i++) 227 total += catalog->specs[i].matches; 228 229 selinux_log(SELINUX_INFO, "%u entries, %u matches made\n", 230 catalog->nspec, total); 231 } 232 233 /* 234 * selabel_open() handler 235 */ 236 static catalog_t * 237 db_init(const struct selinux_opt *opts, unsigned nopts, 238 struct selabel_handle *rec) 239 { 240 catalog_t *catalog; 241 FILE *filp; 242 const char *path = NULL; 243 char *line_buf = NULL; 244 size_t line_len = 0; 245 unsigned int line_num = 0; 246 unsigned int i; 247 struct stat sb; 248 249 /* 250 * Initialize catalog data structure 251 */ 252 catalog = malloc(sizeof(catalog_t) + 32 * sizeof(spec_t)); 253 if (!catalog) 254 return NULL; 255 catalog->limit = 32; 256 catalog->nspec = 0; 257 258 /* 259 * Process arguments 260 * 261 * SELABEL_OPT_PATH: 262 * It allows to specify an alternative specification file instead of 263 * the default one. If RDBMS is not SE-PostgreSQL, it may need to 264 * specify an explicit specfile for database objects. 265 */ 266 while (nopts--) { 267 switch (opts[nopts].type) { 268 case SELABEL_OPT_PATH: 269 path = opts[nopts].value; 270 break; 271 } 272 } 273 274 /* 275 * Open the specification file 276 */ 277 if (!path) 278 path = selinux_sepgsql_context_path(); 279 280 if ((filp = fopen(path, "rb")) == NULL) { 281 free(catalog); 282 return NULL; 283 } 284 if (fstat(fileno(filp), &sb) < 0) 285 return NULL; 286 if (!S_ISREG(sb.st_mode)) { 287 errno = EINVAL; 288 return NULL; 289 } 290 rec->spec_file = strdup(path); 291 292 /* 293 * Parse for each lines 294 */ 295 while (getline(&line_buf, &line_len, filp) > 0) { 296 /* 297 * Expand catalog array, if necessary 298 */ 299 if (catalog->limit == catalog->nspec) { 300 size_t length; 301 unsigned int new_limit = 2 * catalog->limit; 302 catalog_t *new_catalog; 303 304 length = sizeof(catalog_t) 305 + new_limit * sizeof(spec_t); 306 new_catalog = realloc(catalog, length); 307 if (!new_catalog) 308 goto out_error; 309 310 catalog = new_catalog; 311 catalog->limit = new_limit; 312 } 313 314 /* 315 * Parse a line 316 */ 317 if (process_line(path, line_buf, ++line_num, catalog) < 0) 318 goto out_error; 319 } 320 free(line_buf); 321 322 if (digest_add_specfile(rec->digest, filp, NULL, sb.st_size, path) < 0) 323 goto out_error; 324 325 digest_gen_hash(rec->digest); 326 327 fclose(filp); 328 329 return catalog; 330 331 out_error: 332 for (i = 0; i < catalog->nspec; i++) { 333 spec_t *spec = &catalog->specs[i]; 334 335 free(spec->key); 336 free(spec->lr.ctx_raw); 337 free(spec->lr.ctx_trans); 338 } 339 free(catalog); 340 341 return NULL; 342 } 343 344 /* 345 * Initialize selabel_handle and load the entries of specfile 346 */ 347 int selabel_db_init(struct selabel_handle *rec, 348 const struct selinux_opt *opts, unsigned nopts) 349 { 350 rec->func_close = &db_close; 351 rec->func_lookup = &db_lookup; 352 rec->func_stats = &db_stats; 353 rec->data = db_init(opts, nopts, rec); 354 355 return !rec->data ? -1 : 0; 356 } 357