static int selinux_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { security_context_t context; char *domain; int rc; if (!server_domain) return OK; if (getcon_raw(&context) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "SELinux: getcon_raw() failed"); return DONE; } domain = apr_pstrdup(ptemp, server_domain); rc = do_set_domain(context, domain, s); if (rc < 0) { freecon(context); return DONE; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "SELinux: %s: %s/%s", (rc == 0 ? "replace server domain" : "no need to change server domain"), context, server_domain); freecon(context); return OK; }
int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { int r = -EOPNOTSUPP; #ifdef HAVE_SELINUX _cleanup_freecon_ char *mycon = NULL, *fcon = NULL; security_class_t sclass; assert(exe); assert(label); if (!mac_selinux_have()) return -EOPNOTSUPP; r = getcon_raw(&mycon); if (r < 0) return -errno; r = getfilecon_raw(exe, &fcon); if (r < 0) return -errno; sclass = string_to_security_class("process"); r = security_compute_create_raw(mycon, fcon, sclass, label); if (r < 0) return -errno; #endif return r; }
int mac_selinux_get_our_label(char **label) { int r = -EOPNOTSUPP; assert(label); #ifdef HAVE_SELINUX if (!mac_selinux_have()) return -EOPNOTSUPP; r = getcon_raw(label); if (r < 0) return -errno; #endif return r; }
static security_context_t get_scon(void) { static char dummy_NIL[1] = ""; security_context_t con = NULL; int ret = -1; int raw = TRUE; switch (opts->from_type) { case OPTS_FROM_ARG: if (!(con = strdup(opts->f.arg))) err(EXIT_FAILURE, " Couldn't allocate security context"); raw = !opts->disp_raw; /* always do conversion */ break; case OPTS_FROM_STDIN: { char buf[4096] = ""; char *ptr = buf; while (!*ptr) { if (!(ptr = fgets(buf, sizeof(buf), stdin))) err(EXIT_FAILURE, " Couldn't read security context"); ptr += strspn(ptr, " \n\t"); ptr[strcspn(ptr, " \n\t")] = 0; } if (!(con = strdup(ptr))) err(EXIT_FAILURE, " Couldn't allocate security context"); raw = !opts->disp_raw; /* always do conversion */ break; } case OPTS_FROM_CUR: ret = getcon_raw(&con); if (ret == -1) err(EXIT_FAILURE, " Couldn't get current security context"); break; case OPTS_FROM_CUREXE: ret = getexeccon_raw(&con); if (ret == -1) err(EXIT_FAILURE, " Couldn't get current exec security context"); if (!con) con = strdup(dummy_NIL); break; case OPTS_FROM_CURFS: ret = getfscreatecon_raw(&con); if (ret == -1) err(EXIT_FAILURE, " Couldn't get current fs security context"); if (!con) con = strdup(dummy_NIL); break; case OPTS_FROM_CURKEY: ret = getkeycreatecon_raw(&con); if (ret == -1) err(EXIT_FAILURE, " Couldn't get current key security context"); if (!con) con = strdup(dummy_NIL); break; case OPTS_FROM_PROC: ret = getpidcon_raw(opts->f.pid, &con); if (ret == -1) err(EXIT_FAILURE, " Couldn't get security context for pid %lu", (unsigned long)opts->f.pid); break; case OPTS_FROM_PROCEXE: ret = my_getpidexeccon_raw(opts->f.pid, &con); if (ret == -1) err(EXIT_FAILURE, " Couldn't get security context for pid %lu", (unsigned long)opts->f.pid); if (!con) con = strdup(dummy_NIL); break; case OPTS_FROM_PROCFS: ret = my_getpidfscreatecon_raw(opts->f.pid, &con); if (ret == -1) err(EXIT_FAILURE, " Couldn't get security context for pid %lu", (unsigned long)opts->f.pid); if (!con) con = strdup(dummy_NIL); /* disabled -- override with normal context ... { opts->from_type = OPTS_FROM_PROC; return (get_scon()); } */ break; case OPTS_FROM_PROCKEY: ret = my_getpidkeycreatecon_raw(opts->f.pid, &con); if (ret == -1) err(EXIT_FAILURE, " Couldn't get security context for pid %lu", (unsigned long)opts->f.pid); if (!con) con = strdup(dummy_NIL); break; case OPTS_FROM_FILE: ret = getfilecon_raw(opts->f.file, &con); if (ret == -1) err(EXIT_FAILURE, " Couldn't get security context for file %s", opts->f.file); break; case OPTS_FROM_LINK: ret = lgetfilecon_raw(opts->f.link, &con); if (ret == -1) err(EXIT_FAILURE, " Couldn't get security context for symlink %s", opts->f.link); break; default: assert(FALSE); } if (opts->disp_raw != raw) { security_context_t ncon = NULL; if (opts->disp_raw) selinux_trans_to_raw_context(con, &ncon); else selinux_raw_to_trans_context(con, &ncon); freecon(con); con = ncon; } return (con); }
int mac_selinux_setup(bool *loaded_policy) { #ifdef HAVE_SELINUX int enforce = 0; usec_t before_load, after_load; security_context_t con; int r; union selinux_callback cb; bool initialized = false; assert(loaded_policy); /* Turn off all of SELinux' own logging, we want to do that */ cb.func_log = null_log; selinux_set_callback(SELINUX_CB_LOG, cb); /* Don't load policy in the initrd if we don't appear to have * it. For the real root, we check below if we've already * loaded policy, and return gracefully. */ if (in_initrd() && access(selinux_path(), F_OK) < 0) return 0; /* Already initialized by somebody else? */ r = getcon_raw(&con); if (r == 0) { initialized = !streq(con, "kernel"); freecon(con); } /* Make sure we have no fds open while loading the policy and * transitioning */ log_close(); /* Now load the policy */ before_load = now(CLOCK_MONOTONIC); r = selinux_init_load_policy(&enforce); if (r == 0) { _cleanup_(mac_selinux_freep) char *label = NULL; char timespan[FORMAT_TIMESPAN_MAX]; mac_selinux_retest(); /* Transition to the new context */ r = mac_selinux_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label); if (r < 0 || !label) { log_open(); log_error("Failed to compute init label, ignoring."); } else { r = setcon_raw(label); log_open(); if (r < 0) log_error("Failed to transition into init label '%s', ignoring.", label); } after_load = now(CLOCK_MONOTONIC); log_info("Successfully loaded SELinux policy in %s.", format_timespan(timespan, sizeof(timespan), after_load - before_load, 0)); *loaded_policy = true; } else {
/* This function communicates with the kernel to check whether or not it should allow the access. If the machine is in permissive mode it will return ok. Audit messages will still be generated if the access would be denied in enforcing mode. */ int mac_selinux_generic_access_check( sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; const char *tclass = NULL, *scon = NULL; struct audit_info audit_info = {}; _cleanup_free_ char *cl = NULL; char *fcon = NULL; char **cmdline = NULL; int r = 0; assert(message); assert(permission); assert(error); r = access_init(error); if (r <= 0) return r; r = sd_bus_query_sender_creds( message, SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID| SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID| SD_BUS_CREDS_SELINUX_CONTEXT| SD_BUS_CREDS_AUGMENT /* get more bits from /proc */, &creds); if (r < 0) goto finish; /* The SELinux context is something we really should have * gotten directly from the message or sender, and not be an * augmented field. If it was augmented we cannot use it for * authorization, since this is racy and vulnerable. Let's add * an extra check, just in case, even though this really * shouldn't be possible. */ assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_SELINUX_CONTEXT) == 0, -EPERM); r = sd_bus_creds_get_selinux_context(creds, &scon); if (r < 0) goto finish; if (path) { /* Get the file context of the unit file */ r = getfilecon_raw(path, &fcon); if (r < 0) { r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path); goto finish; } tclass = "service"; } else { r = getcon_raw(&fcon); if (r < 0) { r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context."); goto finish; } tclass = "system"; } sd_bus_creds_get_cmdline(creds, &cmdline); cl = strv_join(cmdline, " "); audit_info.creds = creds; audit_info.path = path; audit_info.cmdline = cl; r = selinux_check_access(scon, fcon, tclass, permission, &audit_info); if (r < 0) r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access."); log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, cl, r); finish: freecon(fcon); if (r < 0 && security_getenforce() != 1) { sd_bus_error_free(error); r = 0; } return r; }
int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) { int r = -EOPNOTSUPP; #ifdef HAVE_SELINUX _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL; _cleanup_context_free_ context_t pcon = NULL, bcon = NULL; security_class_t sclass; const char *range = NULL; assert(socket_fd >= 0); assert(exe); assert(label); if (!mac_selinux_have()) return -EOPNOTSUPP; r = getcon_raw(&mycon); if (r < 0) return -errno; r = getpeercon_raw(socket_fd, &peercon); if (r < 0) return -errno; if (!exec_label) { /* If there is no context set for next exec let's use context of target executable */ r = getfilecon_raw(exe, &fcon); if (r < 0) return -errno; } bcon = context_new(mycon); if (!bcon) return -ENOMEM; pcon = context_new(peercon); if (!pcon) return -ENOMEM; range = context_range_get(pcon); if (!range) return -errno; r = context_range_set(bcon, range); if (r) return -errno; freecon(mycon); mycon = strdup(context_str(bcon)); if (!mycon) return -ENOMEM; sclass = string_to_security_class("process"); r = security_compute_create_raw(mycon, fcon, sclass, label); if (r < 0) return -errno; #endif return r; }
/* * selinux_set_domain * * It assigns an appropriate security context on the current * working thread based on attributes of the given request. */ static int selinux_set_domain(request_rec *r) { security_context_t context; selinux_config *sconf; selinux_list *entry; const char *envval; char *domain; int rc = 0; sconf = ap_get_module_config(r->per_dir_config, &selinux_module); if (!sconf || !sconf->list) return 0; for (entry = sconf->list; !rc && entry; entry = entry->next) { switch (entry->method) { case SELINUX_DOMAIN_MAP: rc = selinux_lookup_mapfile(r, entry->value, &domain); break; case SELINUX_DOMAIN_ENV: envval = apr_table_get(r->subprocess_env, entry->value); if (envval) { domain = apr_pstrdup(r->pool, envval); rc = 1; } break; default: /* SELINUX_DOMAIN_VAL */ domain = apr_pstrdup(r->pool, entry->value); rc = 1; break; } if (rc < 0) return -1; } /* no matched entry */ if (rc == 0) return 0; if (getcon_raw(&context) < 0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "SELinux: getcon_raw() failed"); return -1; } rc = do_set_domain(context, domain, r->server); if (rc < 0) { freecon(context); return -1; } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "SELinux: %s: %s/%s (uri=%s dir=%s user=%s remote=%s)", (rc == 0 ? "replace handler domain" : "no need to change handler domain"), context, domain, r->uri, sconf->dirname, r->user, r->connection->remote_ip); freecon(context); return 0; }
/* This function communicates with the kernel to check whether or not it should allow the access. If the machine is in permissive mode it will return ok. Audit messages will still be generated if the access would be denied in enforcing mode. */ int mac_selinux_generic_access_check( sd_bus_message *message, bool system, const char *path, const char *permission, sd_bus_error *error) { #ifdef HAVE_SELINUX _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; const char *tclass = NULL, *scon = NULL; struct audit_info audit_info = {}; _cleanup_free_ char *cl = NULL; security_context_t fcon = NULL; char **cmdline = NULL; int r = 0; assert(message); assert(permission); assert(error); if (!mac_selinux_use()) return 0; r = mac_selinux_access_init(error); if (r < 0) return r; r = sd_bus_query_sender_creds( message, SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID| SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID| SD_BUS_CREDS_SELINUX_CONTEXT| SD_BUS_CREDS_AUGMENT /* get more bits from /proc */, &creds); if (r < 0) goto finish; r = sd_bus_creds_get_selinux_context(creds, &scon); if (r < 0) goto finish; tclass = "service"; if (path && !system) { /* Get the file context of the unit file */ r = getfilecon_raw(path, &fcon); if (r < 0) { r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path); goto finish; } } else { r = getcon_raw(&fcon); if (r < 0) { r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context."); goto finish; } if (system) tclass = "system"; } sd_bus_creds_get_cmdline(creds, &cmdline); cl = strv_join(cmdline, " "); audit_info.creds = creds; audit_info.path = path; audit_info.cmdline = cl; r = selinux_check_access(scon, fcon, tclass, permission, &audit_info); if (r < 0) r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access."); log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, cl, r); finish: freecon(fcon); if (r < 0 && security_getenforce() != 1) { sd_bus_error_free(error); r = 0; } return r; #else return 0; #endif }
uint32_t mselinux_check_alloc(selinux_engine_t *se, const void *cookie, const void *key, size_t keylen) { static security_id_t tsid = NULL; mcache_t *mcache; security_id_t ssid; security_id_t nsid; uint32_t secid; if (!se->config.selinux) return 0; if (!tsid) { security_context_t context; if (getcon_raw(&context) < 0) return 0; if (avc_context_to_sid_raw(context, &tsid) < 0) { freecon(context); return 0; } freecon(context); } /* * It the new allocation tries to replace an existing key, * we copy security id from the older chunk. */ mcache = mcache_get(se, key, keylen); if (mcache) { secid = mcache->mchunk->item.secid; if (secid > 0) { mchunk_t *lchunk = mlabel_lookup_secid(se, secid); if (!lchunk) secid = 0; else lchunk->label.refcount++; } mcache_put(se, mcache); return secid; } ssid = se->server.core->get_engine_specific(cookie); pthread_rwlock_rdlock(&permissions.lock); avc_compute_create(ssid, tsid, permissions.tclass, &nsid); pthread_rwlock_unlock(&permissions.lock); secid = mlabel_get(se, nsid->ctx); if (se->config.debug) fprintf(stderr, "%s: ssid=%s tsid=%s nsid=%s (secid=%u)\n", __FUNCTION__, ssid->ctx, tsid->ctx, nsid->ctx, secid); return secid; }
int getcon(security_context_t *context) { return getcon_raw(context); }
int selinux_setup(bool *loaded_policy) { #ifdef HAVE_SELINUX int enforce = 0; usec_t before_load, after_load; security_context_t con; int r; union selinux_callback cb; assert(loaded_policy); /* Turn off all of SELinux' own logging, we want to do that */ cb.func_log = null_log; selinux_set_callback(SELINUX_CB_LOG, cb); /* Already initialized by somebody else? */ r = getcon_raw(&con); if (r == 0) { bool initialized; initialized = !streq(con, "kernel"); freecon(con); if (initialized) return 0; } /* Make sure we have no fds open while loading the policy and * transitioning */ log_close(); /* Now load the policy */ before_load = now(CLOCK_MONOTONIC); r = selinux_init_load_policy(&enforce); if (r == 0) { char timespan[FORMAT_TIMESPAN_MAX]; char *label; retest_selinux(); /* Transition to the new context */ r = label_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label); if (r < 0 || label == NULL) { log_open(); log_error("Failed to compute init label, ignoring."); } else { r = setcon(label); log_open(); if (r < 0) log_error("Failed to transition into init label '%s', ignoring.", label); label_free(label); } after_load = now(CLOCK_MONOTONIC); log_info("Successfully loaded SELinux policy in %s.", format_timespan(timespan, sizeof(timespan), after_load - before_load)); *loaded_policy = true; } else { log_open(); if (enforce > 0) { log_error("Failed to load SELinux policy. Freezing."); return -EIO; } else log_debug("Unable to load SELinux policy. Ignoring."); } #endif return 0; }