16cd6a6acSopenharmony_ci/* 26cd6a6acSopenharmony_ci * Media contexts backend for DB objects 36cd6a6acSopenharmony_ci * 46cd6a6acSopenharmony_ci * Author: KaiGai Kohei <kaigai@ak.jp.nec.com> 56cd6a6acSopenharmony_ci */ 66cd6a6acSopenharmony_ci 76cd6a6acSopenharmony_ci#include <sys/stat.h> 86cd6a6acSopenharmony_ci#include <string.h> 96cd6a6acSopenharmony_ci#include <stdio.h> 106cd6a6acSopenharmony_ci#include <stdio_ext.h> 116cd6a6acSopenharmony_ci#include <ctype.h> 126cd6a6acSopenharmony_ci#include <errno.h> 136cd6a6acSopenharmony_ci#include <limits.h> 146cd6a6acSopenharmony_ci#include <fnmatch.h> 156cd6a6acSopenharmony_ci#include "callbacks.h" 166cd6a6acSopenharmony_ci#include "label_internal.h" 176cd6a6acSopenharmony_ci 186cd6a6acSopenharmony_ci/* 196cd6a6acSopenharmony_ci * Regular database object's security context interface 206cd6a6acSopenharmony_ci * 216cd6a6acSopenharmony_ci * It provides applications a regular security context for the given 226cd6a6acSopenharmony_ci * database objects. The pair of object's name and a security context 236cd6a6acSopenharmony_ci * are described in the specfile. In the default, it shall be stored 246cd6a6acSopenharmony_ci * in the /etc/selinux/$POLICYTYPE/contexts/sepgsql_contexts . 256cd6a6acSopenharmony_ci * (It assumes SE-PostgreSQL in the default. For other RDBMS, use the 266cd6a6acSopenharmony_ci * SELABEL_OPT_PATH option to specify different specfile.) 276cd6a6acSopenharmony_ci * 286cd6a6acSopenharmony_ci * Each line has the following format: 296cd6a6acSopenharmony_ci * <object class> <object name/identifier> <security context> 306cd6a6acSopenharmony_ci * 316cd6a6acSopenharmony_ci * For example: 326cd6a6acSopenharmony_ci * ---------------------------------------- 336cd6a6acSopenharmony_ci * # 346cd6a6acSopenharmony_ci * # It is an example specfile for database objects 356cd6a6acSopenharmony_ci * # 366cd6a6acSopenharmony_ci * db_database template1 system_u:object_r:sepgsql_db_t:s0 376cd6a6acSopenharmony_ci * 386cd6a6acSopenharmony_ci * db_schema *.pg_catalog system_u:object_r:sepgsql_sys_schema_t:s0 396cd6a6acSopenharmony_ci * 406cd6a6acSopenharmony_ci * db_table *.pg_catalog.* system_u:object_r:sepgsql_sysobj_t:s0 416cd6a6acSopenharmony_ci * db_column *.pg_catalog.*.* system_u:object_r:sepgsql_sysobj_t:s0 426cd6a6acSopenharmony_ci * ---------------------------------------- 436cd6a6acSopenharmony_ci * 446cd6a6acSopenharmony_ci * All the characters after the '#' are dealt as comments. 456cd6a6acSopenharmony_ci * 466cd6a6acSopenharmony_ci * The first token is object class. SELABEL_DB_* declared in label.h are 476cd6a6acSopenharmony_ci * corresponding to a certain database object. 486cd6a6acSopenharmony_ci * 496cd6a6acSopenharmony_ci * The object name/identifier is compared to the given key. 506cd6a6acSopenharmony_ci * A database object can have its own namespace hierarchy. 516cd6a6acSopenharmony_ci * In the case of SE-PgSQL, database is the top level object, and schema 526cd6a6acSopenharmony_ci * is deployed just under a database. A schema can contains various kind 536cd6a6acSopenharmony_ci * of objects, such as tables, procedures and so on. 546cd6a6acSopenharmony_ci * Thus, when we lookup an expected security context for a table of 556cd6a6acSopenharmony_ci * "pg_class", it is necessary to assume selabel_lookup() is called with 566cd6a6acSopenharmony_ci * "postgres.pg_catalog.pg_class", not just a "pg_class". 576cd6a6acSopenharmony_ci * 586cd6a6acSopenharmony_ci * Wildcards ('*' or '?') are available on the patterns, so if you want 596cd6a6acSopenharmony_ci * to match a table within any schema, you should set '*' on the upper 606cd6a6acSopenharmony_ci * namespaces of the table. 616cd6a6acSopenharmony_ci * 626cd6a6acSopenharmony_ci * The structure of namespace depends on RDBMS. 636cd6a6acSopenharmony_ci * For example, Trusted-RUBIX has an idea of "catalog" which performs 646cd6a6acSopenharmony_ci * as a namespace between a database and individual schemas. In this 656cd6a6acSopenharmony_ci * case, a table has upper three layers. 666cd6a6acSopenharmony_ci */ 676cd6a6acSopenharmony_ci 686cd6a6acSopenharmony_ci/* 696cd6a6acSopenharmony_ci * spec_t : It holds a pair of a key and an expected security context 706cd6a6acSopenharmony_ci */ 716cd6a6acSopenharmony_citypedef struct spec { 726cd6a6acSopenharmony_ci struct selabel_lookup_rec lr; 736cd6a6acSopenharmony_ci char *key; 746cd6a6acSopenharmony_ci int type; 756cd6a6acSopenharmony_ci int matches; 766cd6a6acSopenharmony_ci} spec_t; 776cd6a6acSopenharmony_ci 786cd6a6acSopenharmony_ci/* 796cd6a6acSopenharmony_ci * catalog_t : An array of spec_t 806cd6a6acSopenharmony_ci */ 816cd6a6acSopenharmony_citypedef struct catalog { 826cd6a6acSopenharmony_ci unsigned int nspec; /* number of specs in use */ 836cd6a6acSopenharmony_ci unsigned int limit; /* physical limitation of specs[] */ 846cd6a6acSopenharmony_ci spec_t specs[0]; 856cd6a6acSopenharmony_ci} catalog_t; 866cd6a6acSopenharmony_ci 876cd6a6acSopenharmony_ci/* 886cd6a6acSopenharmony_ci * Helper function to parse a line read from the specfile 896cd6a6acSopenharmony_ci */ 906cd6a6acSopenharmony_cistatic int 916cd6a6acSopenharmony_ciprocess_line(const char *path, char *line_buf, unsigned int line_num, 926cd6a6acSopenharmony_ci catalog_t *catalog) 936cd6a6acSopenharmony_ci{ 946cd6a6acSopenharmony_ci spec_t *spec = &catalog->specs[catalog->nspec]; 956cd6a6acSopenharmony_ci char *type, *key, *context, *temp; 966cd6a6acSopenharmony_ci int items; 976cd6a6acSopenharmony_ci 986cd6a6acSopenharmony_ci /* Cut off comments */ 996cd6a6acSopenharmony_ci temp = strchr(line_buf, '#'); 1006cd6a6acSopenharmony_ci if (temp) 1016cd6a6acSopenharmony_ci *temp = '\0'; 1026cd6a6acSopenharmony_ci 1036cd6a6acSopenharmony_ci /* 1046cd6a6acSopenharmony_ci * Every entry must have the following format 1056cd6a6acSopenharmony_ci * <object class> <object name> <security context> 1066cd6a6acSopenharmony_ci */ 1076cd6a6acSopenharmony_ci type = key = context = temp = NULL; 1086cd6a6acSopenharmony_ci items = sscanf(line_buf, "%ms %ms %ms %ms", 1096cd6a6acSopenharmony_ci &type, &key, &context, &temp); 1106cd6a6acSopenharmony_ci if (items != 3) { 1116cd6a6acSopenharmony_ci if (items > 0) 1126cd6a6acSopenharmony_ci selinux_log(SELINUX_WARNING, 1136cd6a6acSopenharmony_ci "%s: line %u has invalid format, skipped", 1146cd6a6acSopenharmony_ci path, line_num); 1156cd6a6acSopenharmony_ci goto skip; 1166cd6a6acSopenharmony_ci } 1176cd6a6acSopenharmony_ci 1186cd6a6acSopenharmony_ci /* 1196cd6a6acSopenharmony_ci * Set up individual spec entry 1206cd6a6acSopenharmony_ci */ 1216cd6a6acSopenharmony_ci memset(spec, 0, sizeof(spec_t)); 1226cd6a6acSopenharmony_ci 1236cd6a6acSopenharmony_ci if (!strcmp(type, "db_database")) 1246cd6a6acSopenharmony_ci spec->type = SELABEL_DB_DATABASE; 1256cd6a6acSopenharmony_ci else if (!strcmp(type, "db_schema")) 1266cd6a6acSopenharmony_ci spec->type = SELABEL_DB_SCHEMA; 1276cd6a6acSopenharmony_ci else if (!strcmp(type, "db_table")) 1286cd6a6acSopenharmony_ci spec->type = SELABEL_DB_TABLE; 1296cd6a6acSopenharmony_ci else if (!strcmp(type, "db_column")) 1306cd6a6acSopenharmony_ci spec->type = SELABEL_DB_COLUMN; 1316cd6a6acSopenharmony_ci else if (!strcmp(type, "db_sequence")) 1326cd6a6acSopenharmony_ci spec->type = SELABEL_DB_SEQUENCE; 1336cd6a6acSopenharmony_ci else if (!strcmp(type, "db_view")) 1346cd6a6acSopenharmony_ci spec->type = SELABEL_DB_VIEW; 1356cd6a6acSopenharmony_ci else if (!strcmp(type, "db_procedure")) 1366cd6a6acSopenharmony_ci spec->type = SELABEL_DB_PROCEDURE; 1376cd6a6acSopenharmony_ci else if (!strcmp(type, "db_blob")) 1386cd6a6acSopenharmony_ci spec->type = SELABEL_DB_BLOB; 1396cd6a6acSopenharmony_ci else if (!strcmp(type, "db_tuple")) 1406cd6a6acSopenharmony_ci spec->type = SELABEL_DB_TUPLE; 1416cd6a6acSopenharmony_ci else if (!strcmp(type, "db_language")) 1426cd6a6acSopenharmony_ci spec->type = SELABEL_DB_LANGUAGE; 1436cd6a6acSopenharmony_ci else if (!strcmp(type, "db_exception")) 1446cd6a6acSopenharmony_ci spec->type = SELABEL_DB_EXCEPTION; 1456cd6a6acSopenharmony_ci else if (!strcmp(type, "db_datatype")) 1466cd6a6acSopenharmony_ci spec->type = SELABEL_DB_DATATYPE; 1476cd6a6acSopenharmony_ci else { 1486cd6a6acSopenharmony_ci selinux_log(SELINUX_WARNING, 1496cd6a6acSopenharmony_ci "%s: line %u has invalid object type %s\n", 1506cd6a6acSopenharmony_ci path, line_num, type); 1516cd6a6acSopenharmony_ci goto skip; 1526cd6a6acSopenharmony_ci } 1536cd6a6acSopenharmony_ci 1546cd6a6acSopenharmony_ci free(type); 1556cd6a6acSopenharmony_ci spec->key = key; 1566cd6a6acSopenharmony_ci spec->lr.ctx_raw = context; 1576cd6a6acSopenharmony_ci 1586cd6a6acSopenharmony_ci catalog->nspec++; 1596cd6a6acSopenharmony_ci 1606cd6a6acSopenharmony_ci return 0; 1616cd6a6acSopenharmony_ci 1626cd6a6acSopenharmony_ciskip: 1636cd6a6acSopenharmony_ci free(type); 1646cd6a6acSopenharmony_ci free(key); 1656cd6a6acSopenharmony_ci free(context); 1666cd6a6acSopenharmony_ci free(temp); 1676cd6a6acSopenharmony_ci 1686cd6a6acSopenharmony_ci return 0; 1696cd6a6acSopenharmony_ci} 1706cd6a6acSopenharmony_ci 1716cd6a6acSopenharmony_ci/* 1726cd6a6acSopenharmony_ci * selabel_close() handler 1736cd6a6acSopenharmony_ci */ 1746cd6a6acSopenharmony_cistatic void 1756cd6a6acSopenharmony_cidb_close(struct selabel_handle *rec) 1766cd6a6acSopenharmony_ci{ 1776cd6a6acSopenharmony_ci catalog_t *catalog = (catalog_t *)rec->data; 1786cd6a6acSopenharmony_ci spec_t *spec; 1796cd6a6acSopenharmony_ci unsigned int i; 1806cd6a6acSopenharmony_ci 1816cd6a6acSopenharmony_ci for (i = 0; i < catalog->nspec; i++) { 1826cd6a6acSopenharmony_ci spec = &catalog->specs[i]; 1836cd6a6acSopenharmony_ci free(spec->key); 1846cd6a6acSopenharmony_ci free(spec->lr.ctx_raw); 1856cd6a6acSopenharmony_ci free(spec->lr.ctx_trans); 1866cd6a6acSopenharmony_ci } 1876cd6a6acSopenharmony_ci free(catalog); 1886cd6a6acSopenharmony_ci} 1896cd6a6acSopenharmony_ci 1906cd6a6acSopenharmony_ci/* 1916cd6a6acSopenharmony_ci * selabel_lookup() handler 1926cd6a6acSopenharmony_ci */ 1936cd6a6acSopenharmony_cistatic struct selabel_lookup_rec * 1946cd6a6acSopenharmony_cidb_lookup(struct selabel_handle *rec, const char *key, int type) 1956cd6a6acSopenharmony_ci{ 1966cd6a6acSopenharmony_ci catalog_t *catalog = (catalog_t *)rec->data; 1976cd6a6acSopenharmony_ci spec_t *spec; 1986cd6a6acSopenharmony_ci unsigned int i; 1996cd6a6acSopenharmony_ci 2006cd6a6acSopenharmony_ci for (i = 0; i < catalog->nspec; i++) { 2016cd6a6acSopenharmony_ci spec = &catalog->specs[i]; 2026cd6a6acSopenharmony_ci 2036cd6a6acSopenharmony_ci if (spec->type != type) 2046cd6a6acSopenharmony_ci continue; 2056cd6a6acSopenharmony_ci if (!fnmatch(spec->key, key, 0)) { 2066cd6a6acSopenharmony_ci spec->matches++; 2076cd6a6acSopenharmony_ci 2086cd6a6acSopenharmony_ci return &spec->lr; 2096cd6a6acSopenharmony_ci } 2106cd6a6acSopenharmony_ci } 2116cd6a6acSopenharmony_ci 2126cd6a6acSopenharmony_ci /* No found */ 2136cd6a6acSopenharmony_ci errno = ENOENT; 2146cd6a6acSopenharmony_ci return NULL; 2156cd6a6acSopenharmony_ci} 2166cd6a6acSopenharmony_ci 2176cd6a6acSopenharmony_ci/* 2186cd6a6acSopenharmony_ci * selabel_stats() handler 2196cd6a6acSopenharmony_ci */ 2206cd6a6acSopenharmony_cistatic void 2216cd6a6acSopenharmony_cidb_stats(struct selabel_handle *rec) 2226cd6a6acSopenharmony_ci{ 2236cd6a6acSopenharmony_ci catalog_t *catalog = (catalog_t *)rec->data; 2246cd6a6acSopenharmony_ci unsigned int i, total = 0; 2256cd6a6acSopenharmony_ci 2266cd6a6acSopenharmony_ci for (i = 0; i < catalog->nspec; i++) 2276cd6a6acSopenharmony_ci total += catalog->specs[i].matches; 2286cd6a6acSopenharmony_ci 2296cd6a6acSopenharmony_ci selinux_log(SELINUX_INFO, "%u entries, %u matches made\n", 2306cd6a6acSopenharmony_ci catalog->nspec, total); 2316cd6a6acSopenharmony_ci} 2326cd6a6acSopenharmony_ci 2336cd6a6acSopenharmony_ci/* 2346cd6a6acSopenharmony_ci * selabel_open() handler 2356cd6a6acSopenharmony_ci */ 2366cd6a6acSopenharmony_cistatic catalog_t * 2376cd6a6acSopenharmony_cidb_init(const struct selinux_opt *opts, unsigned nopts, 2386cd6a6acSopenharmony_ci struct selabel_handle *rec) 2396cd6a6acSopenharmony_ci{ 2406cd6a6acSopenharmony_ci catalog_t *catalog; 2416cd6a6acSopenharmony_ci FILE *filp; 2426cd6a6acSopenharmony_ci const char *path = NULL; 2436cd6a6acSopenharmony_ci char *line_buf = NULL; 2446cd6a6acSopenharmony_ci size_t line_len = 0; 2456cd6a6acSopenharmony_ci unsigned int line_num = 0; 2466cd6a6acSopenharmony_ci unsigned int i; 2476cd6a6acSopenharmony_ci struct stat sb; 2486cd6a6acSopenharmony_ci 2496cd6a6acSopenharmony_ci /* 2506cd6a6acSopenharmony_ci * Initialize catalog data structure 2516cd6a6acSopenharmony_ci */ 2526cd6a6acSopenharmony_ci catalog = malloc(sizeof(catalog_t) + 32 * sizeof(spec_t)); 2536cd6a6acSopenharmony_ci if (!catalog) 2546cd6a6acSopenharmony_ci return NULL; 2556cd6a6acSopenharmony_ci catalog->limit = 32; 2566cd6a6acSopenharmony_ci catalog->nspec = 0; 2576cd6a6acSopenharmony_ci 2586cd6a6acSopenharmony_ci /* 2596cd6a6acSopenharmony_ci * Process arguments 2606cd6a6acSopenharmony_ci * 2616cd6a6acSopenharmony_ci * SELABEL_OPT_PATH: 2626cd6a6acSopenharmony_ci * It allows to specify an alternative specification file instead of 2636cd6a6acSopenharmony_ci * the default one. If RDBMS is not SE-PostgreSQL, it may need to 2646cd6a6acSopenharmony_ci * specify an explicit specfile for database objects. 2656cd6a6acSopenharmony_ci */ 2666cd6a6acSopenharmony_ci while (nopts--) { 2676cd6a6acSopenharmony_ci switch (opts[nopts].type) { 2686cd6a6acSopenharmony_ci case SELABEL_OPT_PATH: 2696cd6a6acSopenharmony_ci path = opts[nopts].value; 2706cd6a6acSopenharmony_ci break; 2716cd6a6acSopenharmony_ci } 2726cd6a6acSopenharmony_ci } 2736cd6a6acSopenharmony_ci 2746cd6a6acSopenharmony_ci /* 2756cd6a6acSopenharmony_ci * Open the specification file 2766cd6a6acSopenharmony_ci */ 2776cd6a6acSopenharmony_ci if (!path) 2786cd6a6acSopenharmony_ci path = selinux_sepgsql_context_path(); 2796cd6a6acSopenharmony_ci 2806cd6a6acSopenharmony_ci if ((filp = fopen(path, "re")) == NULL) { 2816cd6a6acSopenharmony_ci free(catalog); 2826cd6a6acSopenharmony_ci return NULL; 2836cd6a6acSopenharmony_ci } 2846cd6a6acSopenharmony_ci if (fstat(fileno(filp), &sb) < 0) { 2856cd6a6acSopenharmony_ci free(catalog); 2866cd6a6acSopenharmony_ci fclose(filp); 2876cd6a6acSopenharmony_ci return NULL; 2886cd6a6acSopenharmony_ci } 2896cd6a6acSopenharmony_ci if (!S_ISREG(sb.st_mode)) { 2906cd6a6acSopenharmony_ci free(catalog); 2916cd6a6acSopenharmony_ci fclose(filp); 2926cd6a6acSopenharmony_ci errno = EINVAL; 2936cd6a6acSopenharmony_ci return NULL; 2946cd6a6acSopenharmony_ci } 2956cd6a6acSopenharmony_ci rec->spec_file = strdup(path); 2966cd6a6acSopenharmony_ci if (!rec->spec_file) { 2976cd6a6acSopenharmony_ci free(catalog); 2986cd6a6acSopenharmony_ci fclose(filp); 2996cd6a6acSopenharmony_ci return NULL; 3006cd6a6acSopenharmony_ci } 3016cd6a6acSopenharmony_ci 3026cd6a6acSopenharmony_ci /* 3036cd6a6acSopenharmony_ci * Parse for each lines 3046cd6a6acSopenharmony_ci */ 3056cd6a6acSopenharmony_ci while (getline(&line_buf, &line_len, filp) > 0) { 3066cd6a6acSopenharmony_ci /* 3076cd6a6acSopenharmony_ci * Expand catalog array, if necessary 3086cd6a6acSopenharmony_ci */ 3096cd6a6acSopenharmony_ci if (catalog->limit == catalog->nspec) { 3106cd6a6acSopenharmony_ci size_t length; 3116cd6a6acSopenharmony_ci unsigned int new_limit = 2 * catalog->limit; 3126cd6a6acSopenharmony_ci catalog_t *new_catalog; 3136cd6a6acSopenharmony_ci 3146cd6a6acSopenharmony_ci length = sizeof(catalog_t) 3156cd6a6acSopenharmony_ci + new_limit * sizeof(spec_t); 3166cd6a6acSopenharmony_ci new_catalog = realloc(catalog, length); 3176cd6a6acSopenharmony_ci if (!new_catalog) 3186cd6a6acSopenharmony_ci goto out_error; 3196cd6a6acSopenharmony_ci 3206cd6a6acSopenharmony_ci catalog = new_catalog; 3216cd6a6acSopenharmony_ci catalog->limit = new_limit; 3226cd6a6acSopenharmony_ci } 3236cd6a6acSopenharmony_ci 3246cd6a6acSopenharmony_ci /* 3256cd6a6acSopenharmony_ci * Parse a line 3266cd6a6acSopenharmony_ci */ 3276cd6a6acSopenharmony_ci if (process_line(path, line_buf, ++line_num, catalog) < 0) 3286cd6a6acSopenharmony_ci goto out_error; 3296cd6a6acSopenharmony_ci } 3306cd6a6acSopenharmony_ci 3316cd6a6acSopenharmony_ci if (digest_add_specfile(rec->digest, filp, NULL, sb.st_size, path) < 0) 3326cd6a6acSopenharmony_ci goto out_error; 3336cd6a6acSopenharmony_ci 3346cd6a6acSopenharmony_ci digest_gen_hash(rec->digest); 3356cd6a6acSopenharmony_ci 3366cd6a6acSopenharmony_ci free(line_buf); 3376cd6a6acSopenharmony_ci fclose(filp); 3386cd6a6acSopenharmony_ci 3396cd6a6acSopenharmony_ci return catalog; 3406cd6a6acSopenharmony_ci 3416cd6a6acSopenharmony_ciout_error: 3426cd6a6acSopenharmony_ci free(line_buf); 3436cd6a6acSopenharmony_ci for (i = 0; i < catalog->nspec; i++) { 3446cd6a6acSopenharmony_ci spec_t *spec = &catalog->specs[i]; 3456cd6a6acSopenharmony_ci 3466cd6a6acSopenharmony_ci free(spec->key); 3476cd6a6acSopenharmony_ci free(spec->lr.ctx_raw); 3486cd6a6acSopenharmony_ci free(spec->lr.ctx_trans); 3496cd6a6acSopenharmony_ci } 3506cd6a6acSopenharmony_ci free(catalog); 3516cd6a6acSopenharmony_ci fclose(filp); 3526cd6a6acSopenharmony_ci 3536cd6a6acSopenharmony_ci return NULL; 3546cd6a6acSopenharmony_ci} 3556cd6a6acSopenharmony_ci 3566cd6a6acSopenharmony_ci/* 3576cd6a6acSopenharmony_ci * Initialize selabel_handle and load the entries of specfile 3586cd6a6acSopenharmony_ci */ 3596cd6a6acSopenharmony_ciint selabel_db_init(struct selabel_handle *rec, 3606cd6a6acSopenharmony_ci const struct selinux_opt *opts, unsigned nopts) 3616cd6a6acSopenharmony_ci{ 3626cd6a6acSopenharmony_ci rec->func_close = &db_close; 3636cd6a6acSopenharmony_ci rec->func_lookup = &db_lookup; 3646cd6a6acSopenharmony_ci rec->func_stats = &db_stats; 3656cd6a6acSopenharmony_ci rec->data = db_init(opts, nopts, rec); 3666cd6a6acSopenharmony_ci 3676cd6a6acSopenharmony_ci return !rec->data ? -1 : 0; 3686cd6a6acSopenharmony_ci} 369