예제 #1
0
static int
mavc_cb_policyload(int seqno)
{
	security_class_t	tclass;

	pthread_rwlock_wrlock(&permissions.lock);

	tclass = string_to_security_class("kv_item");

	permissions.tclass		= tclass;
	permissions.create		= string_to_av_perm(tclass, "create");
	permissions.getattr		= string_to_av_perm(tclass, "getattr");
	permissions.setattr		= string_to_av_perm(tclass, "setattr");
	permissions.remove		= string_to_av_perm(tclass, "remove");
	permissions.read		= string_to_av_perm(tclass, "read");
	permissions.write		= string_to_av_perm(tclass, "write");
	permissions.append		= string_to_av_perm(tclass, "append");
	permissions.calculate	= string_to_av_perm(tclass, "calculate");
	permissions.relabelfrom	= string_to_av_perm(tclass, "relabelfrom")
							| string_to_av_perm(tclass, "setattr");
	permissions.relabelto	= string_to_av_perm(tclass, "relabelto");

	pthread_rwlock_unlock(&permissions.lock);

	return 0;
}
예제 #2
0
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;
}
예제 #3
0
파일: selinux.c 프로젝트: AlexHill/postgres
/*
 * sepgsql_compute_create
 *
 * It returns a default security context to be assigned on a new database
 * object. SELinux compute it based on a combination of client, upper object
 * which owns the new object and object class.
 *
 * For example, when a client (staff_u:staff_r:staff_t:s0) tries to create
 * a new table within a schema (system_u:object_r:sepgsql_schema_t:s0),
 * SELinux looks-up its security policy. If it has a special rule on the
 * combination of these security contexts and object class (db_table),
 * it returns the security context suggested by the special rule.
 * Otherwise, it returns the security context of schema, as is.
 *
 * We expect the caller already applies sanity/validation checks on the
 * given security context.
 *
 * scontext: security context of the subject (mostly, peer process).
 * tcontext: security context of the upper database object.
 * tclass: class code (SEPG_CLASS_*) of the new object in creation
 */
char *
sepgsql_compute_create(const char *scontext,
					   const char *tcontext,
					   uint16 tclass,
					   const char *objname)
{
	security_context_t ncontext;
	security_class_t tclass_ex;
	const char *tclass_name;
	char	   *result;

	/* Get external code of the object class */
	Assert(tclass < SEPG_CLASS_MAX);

	tclass_name = selinux_catalog[tclass].class_name;
	tclass_ex = string_to_security_class(tclass_name);

	/*
	 * Ask SELinux what is the default context for the given object class on a
	 * pair of security contexts
	 */
	if (security_compute_create_name_raw((security_context_t) scontext,
										 (security_context_t) tcontext,
										 tclass_ex,
										 objname,
										 &ncontext) < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INTERNAL_ERROR),
				 errmsg("SELinux could not compute a new context: "
						"scontext=%s tcontext=%s tclass=%s: %m",
						scontext, tcontext, tclass_name)));

	/*
	 * libselinux returns malloc()'ed string, so we need to copy it on the
	 * palloc()'ed region.
	 */
	PG_TRY();
	{
		result = pstrdup(ncontext);
	}
	PG_CATCH();
	{
		freecon(ncontext);
		PG_RE_THROW();
	}
	PG_END_TRY();
	freecon(ncontext);

	return result;
}
예제 #4
0
int main(int argc, char **argv)
{
	struct av_decision avd;
	security_class_t tclass;
	int ret;

	if (argc != 4) {
		fprintf(stderr, "usage:  %s scontext tcontext tclass\n",
			argv[0]);
		exit(1);
	}

	tclass = string_to_security_class(argv[3]);
	if (!tclass) {
		fprintf(stderr, "%s:  invalid class '%s'\n", argv[0], argv[3]);
		exit(2);
	}

	ret = security_compute_av(argv[1], argv[2], tclass, 1, &avd);
	if (ret < 0) {
		fprintf(stderr, "%s:  security_compute_av failed\n", argv[0]);
		exit(3);
	}

	printf("allowed=");
	print_access_vector(tclass, avd.allowed);
	printf("\n");

	if (avd.decided != ~0U) {
		printf("decided=");
		print_access_vector(tclass, avd.decided);
		printf("\n");
	}

	if (avd.auditallow) {
		printf("auditallow=");
		print_access_vector(tclass, avd.auditallow);
		printf("\n");
	}

	if (avd.auditdeny != ~0U) {
		printf("auditdeny");
		print_access_vector(tclass, avd.auditdeny);
		printf("\n");
	}

	exit(0);
}
예제 #5
0
static int check_dominance(const char *pattern, const char *raw) {
	security_context_t ctx;
	context_t con;
	struct av_decision avd;
	int rc = -1;
	context_t my_tmp;
	const char *raw_range;
	security_class_t context_class = string_to_security_class("context");
	access_vector_t context_contains_perm = string_to_av_perm(context_class, "contains");

	con = context_new(raw);
	if (!con)
		return -1;
	raw_range = context_range_get(con);

	my_tmp = context_new(my_context);
	if (!my_tmp) {
		context_free(con);
		return -1;
	}

	ctx = NULL;
	if (context_range_set(my_tmp, pattern))
		goto out;
	ctx = strdup(context_str(my_tmp));
	if (!ctx)
		goto out;

	if (context_range_set(my_tmp, raw_range))
		goto out;
	raw = context_str(my_tmp);
	if (!raw)
		goto out;

	rc = security_compute_av_raw(ctx, (security_context_t)raw, context_class, context_contains_perm, &avd);
	if (rc)
		goto out;

	rc = (context_contains_perm & avd.allowed) != context_contains_perm;
out:
	free(ctx);
	context_free(my_tmp);
	context_free(con);
	return rc;
}
예제 #6
0
int within_range(security_context_t sl, security_context_t range)
{
	int rtn = 1;
	security_id_t slsid;
	security_id_t rangesid;
	struct av_decision avd;
	security_class_t tclass;
	access_vector_t av;

	if (!selinux_ready) {
		/* mls may not be enabled */
		dbg("selinux check failed");
		return 0;
	}

	/*
	 * * Get the sids for the sl and range contexts
	 */
	rtn = avc_context_to_sid(sl, &slsid);
	if (rtn != 0) {
		dbg("within_range: Unable to retrieve sid for sl context (%s)", sl);
		return 0;
	}
	rtn = avc_context_to_sid(range, &rangesid);
	if (rtn != 0) {
		dbg("within_range: Unable to retrieve sid for range context (%s)", range);
		sidput(slsid);
		return 0;
	}

	/*
	** Straight up test between sl and range
	**/
	tclass = string_to_security_class("association");
	av = string_to_av_perm(tclass, "polmatch");
	rtn = avc_has_perm(slsid, rangesid, tclass, av, NULL, &avd);
	if (rtn != 0) {
		dbg("within_range: The sl (%s) is not within range of (%s)", sl, range);
		sidput(slsid);
		sidput(rangesid);
		return 0;
	}
	dbg("within_range: The sl (%s) is within range of (%s)", sl, range);
	return 1;
}
예제 #7
0
security_class_t mode_to_security_class(mode_t m) {

	if (S_ISREG(m))
		return string_to_security_class("file");
	if (S_ISDIR(m))
		return string_to_security_class("dir");
	if (S_ISCHR(m))
		return string_to_security_class("chr_file");
	if (S_ISBLK(m))
		return string_to_security_class("blk_file");
	if (S_ISFIFO(m))
		return string_to_security_class("fifo_file");
	if (S_ISLNK(m))
		return string_to_security_class("lnk_file");
	if (S_ISSOCK(m))
		return string_to_security_class("sock_file");

	errno=EINVAL;
	return 0;
}
예제 #8
0
파일: label.c 프로젝트: terralinux/systemd
int label_get_socket_label_from_exe(const char *exe, char **label) {

        int r = 0;

#ifdef HAVE_SELINUX
        security_context_t mycon = NULL, fcon = NULL;
        security_class_t sclass;

        if (!use_selinux()) {
                *label = NULL;
                return 0;
        }

        r = getcon(&mycon);
        if (r < 0)
                goto fail;

        r = getfilecon(exe, &fcon);
        if (r < 0)
                goto fail;

        sclass = string_to_security_class("process");
        r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
        if (r == 0)
                log_debug("SELinux Socket context for %s will be set to %s", exe, *label);

fail:
        if (r < 0 && security_getenforce() == 1)
                r = -errno;

        freecon(mycon);
        freecon(fcon);
#endif

        return r;
}
예제 #9
0
파일: selinux.c 프로젝트: strk/postgres
/*
 * sepgsql_compute_avd
 *
 * It actually asks SELinux what permissions are allowed on a pair of
 * the security contexts and object class. It also returns what permissions
 * should be audited on access violation or allowed.
 * In most cases, subject's security context (scontext) is a client, and
 * target security context (tcontext) is a database object.
 *
 * The access control decision shall be set on the given av_decision.
 * The av_decision.allowed has a bitmask of SEPG_<class>__<perms>
 * to suggest a set of allowed actions in this object class.
 */
void
sepgsql_compute_avd(const char *scontext,
					const char *tcontext,
					uint16 tclass,
					struct av_decision *avd)
{
	const char		   *tclass_name;
	security_class_t	tclass_ex;
	struct av_decision	avd_ex;
	int					i, deny_unknown = security_deny_unknown();

	/* Get external code of the object class*/
	Assert(tclass < SEPG_CLASS_MAX);
	Assert(tclass == selinux_catalog[tclass].class_code);

	tclass_name = selinux_catalog[tclass].class_name;
	tclass_ex = string_to_security_class(tclass_name);

	if (tclass_ex == 0)
	{
		/*
		 * If the current security policy does not support permissions
		 * corresponding to database objects, we fill up them with dummy
		 * data.
		 * If security_deny_unknown() returns positive value, undefined
		 * permissions should be denied. Otherwise, allowed
		 */
		avd->allowed = (security_deny_unknown() > 0 ? 0 : ~0);
		avd->auditallow = 0U;
		avd->auditdeny =  ~0U;
		avd->flags = 0;

		return;
	}

	/*
	 * Ask SELinux what is allowed set of permissions on a pair of the
	 * security contexts and the given object class.
	 */
	if (security_compute_av_flags_raw((security_context_t)scontext,
									  (security_context_t)tcontext,
									  tclass_ex, 0, &avd_ex) < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INTERNAL_ERROR),
				 errmsg("SELinux could not compute av_decision: "
						"scontext=%s tcontext=%s tclass=%s",
						scontext, tcontext, tclass_name)));

	/*
	 * SELinux returns its access control decision as a set of permissions
	 * represented in external code which depends on run-time environment.
	 * So, we need to translate it to the internal representation before
	 * returning results for the caller.
	 */
	memset(avd, 0, sizeof(struct av_decision));

	for (i=0; selinux_catalog[tclass].av[i].av_name; i++)
	{
		access_vector_t	av_code_ex;
		const char	   *av_name = selinux_catalog[tclass].av[i].av_name;
		uint32			av_code = selinux_catalog[tclass].av[i].av_code;

		av_code_ex = string_to_av_perm(tclass_ex, av_name);
		if (av_code_ex == 0)
		{
			/* fill up undefined permissions */
			if (!deny_unknown)
				avd->allowed |= av_code;
			avd->auditdeny |= av_code;

			continue;
		}

		if (avd_ex.allowed & av_code_ex)
			avd->allowed |= av_code;
		if (avd_ex.auditallow & av_code_ex)
			avd->auditallow |= av_code;
		if (avd_ex.auditdeny & av_code_ex)
			avd->auditdeny |= av_code;
	}

	return;
}
예제 #10
0
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;
}
예제 #11
0
void service_start(struct service *svc, const char *dynamic_args)
{
    // Starting a service removes it from the disabled or reset state and
    // immediately takes it out of the restarting state if it was in there.
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
    svc->time_started = 0;

    // Running processes require no additional work --- if they're in the
    // process of exiting, we've ensured that they will immediately restart
    // on exit, unless they are ONESHOT.
    if (svc->flags & SVC_RUNNING) {
        return;
    }

    bool needs_console = (svc->flags & SVC_CONSOLE);
    if (needs_console && !have_console) {
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    struct stat sb;
    if (stat(svc->args[0], &sb) == -1) {
        ERROR("cannot find '%s' (%s), disabling '%s'\n", svc->args[0], strerror(errno), svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]);
        svc->flags |= SVC_DISABLED;
        return;
    }

    char* scon = NULL;
    if (svc->seclabel) {
        scon = strdup(svc->seclabel);
        if (!scon) {
            ERROR("Out of memory while starting '%s'\n", svc->name);
            return;
        }
    } else {
        char *mycon = NULL, *fcon = NULL;

        INFO("computing context for service '%s'\n", svc->args[0]);
        int rc = getcon(&mycon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", svc->name);
            return;
        }

        rc = getfilecon(svc->args[0], &fcon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", svc->name);
            free(mycon);
            return;
        }

        rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
        if (rc == 0 && !strcmp(scon, mycon)) {
            ERROR("Service %s does not have a SELinux domain defined.\n", svc->name);
            free(mycon);
            free(fcon);
            free(scon);
            return;
        }
        free(mycon);
        free(fcon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", svc->name);
            return;
        }
    }

    NOTICE("Starting service '%s'...\n", svc->name);

    pid_t pid = fork();
    if (pid == 0) {
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        umask(077);
        if (properties_initialized()) {
            get_property_workspace(&fd, &sz);
            snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }

        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);

        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type,
                                  si->perm, si->uid, si->gid, si->socketcon ?: scon);
            if (s >= 0) {
                publish_socket(si->name, s);
            }
        }

        free(scon);
        scon = NULL;

        if (svc->writepid_files_) {
            std::string pid_str = android::base::StringPrintf("%d", pid);
            for (auto& file : *svc->writepid_files_) {
                if (!android::base::WriteStringToFile(pid_str, file)) {
                    ERROR("couldn't write %s to %s: %s\n",
                          pid_str.c_str(), file.c_str(), strerror(errno));
                }
            }
        }

        if (svc->ioprio_class != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
            }
        }

        if (needs_console) {
            setsid();
            open_console();
        } else {
            zap_stdio();
        }

        if (false) {
            for (size_t n = 0; svc->args[n]; n++) {
                INFO("args[%zu] = '%s'\n", n, svc->args[n]);
            }
            for (size_t n = 0; ENV[n]; n++) {
                INFO("env[%zu] = '%s'\n", n, ENV[n]);
            }
        }

        setpgid(0, getpid());

        // As requested, set our gid, supplemental gids, and uid.
        if (svc->gid) {
            if (setgid(svc->gid) != 0) {
                ERROR("setgid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->nr_supp_gids) {
            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
                ERROR("setgroups failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->uid) {
            if (setuid(svc->uid) != 0) {
                ERROR("setuid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->seclabel) {
            if (setexeccon(svc->seclabel) < 0) {
                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
                _exit(127);
            }
        }

        if (!dynamic_args) {
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
        } else {
            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
            int arg_idx = svc->nargs;
            char *tmp = strdup(dynamic_args);
            char *next = tmp;
            char *bword;

            /* Copy the static arguments */
            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));

            while((bword = strsep(&next, " "))) {
                arg_ptrs[arg_idx++] = bword;
                if (arg_idx == INIT_PARSER_MAXARGS)
                    break;
            }
            arg_ptrs[arg_idx] = NULL;
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
        }
        _exit(127);
    }

    free(scon);

    if (pid < 0) {
        ERROR("failed to start '%s'\n", svc->name);
        svc->pid = 0;
        return;
    }

    svc->time_started = gettime();
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;

    if ((svc->flags & SVC_EXEC) != 0) {
        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
             svc->seclabel ? : "default");
        waiting_for_exec = true;
    }
예제 #12
0
/* Check the permission from the caller (via getpeercon) to nscd.
   Returns 0 if access is allowed, 1 if denied, and -1 on error.

   The SELinux policy, enablement, and permission bits are all dynamic and the
   caching done by glibc is not entirely correct.  This nscd support should be
   rewritten to use selinux_check_permission.  A rewrite is risky though and
   requires some refactoring.  Currently we use symbolic mappings instead of
   compile time constants (which SELinux upstream says are going away), and we
   use security_deny_unknown to determine what to do if selinux-policy* doesn't
   have a definition for the the permission or object class we are looking
   up.  */
int
nscd_request_avc_has_perm (int fd, request_type req)
{
  /* Initialize to NULL so we know what to free in case of failure.  */
  security_context_t scon = NULL;
  security_context_t tcon = NULL;
  security_id_t ssid = NULL;
  security_id_t tsid = NULL;
  int rc = -1;
  security_class_t sc_nscd;
  access_vector_t perm;
  int avc_deny_unknown;

  /* Check if SELinux denys or allows unknown object classes
     and permissions.  It is 0 if they are allowed, 1 if they
     are not allowed and -1 on error.  */
  if ((avc_deny_unknown = security_deny_unknown ()) == -1)
    dbg_log (_("Error querying policy for undefined object classes "
	       "or permissions."));

  /* Get the security class for nscd.  If this fails we will likely be
     unable to do anything unless avc_deny_unknown is 0.  */
  sc_nscd = string_to_security_class ("nscd");
  if (sc_nscd == 0 && avc_deny_unknown == 1)
    dbg_log (_("Error getting security class for nscd."));

  /* Convert permission to AVC bits.  */
  perm = string_to_av_perm (sc_nscd, perms[req]);
  if (perm == 0 && avc_deny_unknown == 1)
      dbg_log (_("Error translating permission name "
		 "\"%s\" to access vector bit."), perms[req]);

  /* If the nscd security class was not found or perms were not
     found and AVC does not deny unknown values then allow it.  */
  if ((sc_nscd == 0 || perm == 0) && avc_deny_unknown == 0)
    return 0;

  if (getpeercon (fd, &scon) < 0)
    {
      dbg_log (_("Error getting context of socket peer"));
      goto out;
    }
  if (getcon (&tcon) < 0)
    {
      dbg_log (_("Error getting context of nscd"));
      goto out;
    }
  if (avc_context_to_sid (scon, &ssid) < 0
      || avc_context_to_sid (tcon, &tsid) < 0)
    {
      dbg_log (_("Error getting sid from context"));
      goto out;
    }

  /* The SELinux API for avc_has_perm conflates access denied and error into
     the return code -1, while nscd_request_avs_has_perm has distinct error
     (-1) and denied (1) return codes. We map the avc_has_perm access denied or
     error into an access denied at the nscd interface level (we do accurately
     report error for the getpeercon, getcon, and avc_context_to_sid interfaces
     used above).  */
  rc = avc_has_perm (ssid, tsid, sc_nscd, perm, &aeref, NULL) < 0;

out:
  if (scon)
    freecon (scon);
  if (tcon)
    freecon (tcon);
  if (ssid)
    sidput (ssid);
  if (tsid)
    sidput (tsid);

  return rc;
}
예제 #13
0
int
selinux_set_mapping(struct security_class_mapping *map)
{
	size_t size = sizeof(struct selinux_mapping);
	security_class_t i, j;
	unsigned k;
	bool print_unknown_handle = false;
	bool reject = (security_reject_unknown() == 1);
	bool deny = (security_deny_unknown() == 1);

	free(current_mapping);
	current_mapping = NULL;
	current_mapping_size = 0;

	if (avc_reset() < 0)
		goto err;

	/* Find number of classes in the input mapping */
	if (!map) {
		errno = EINVAL;
		goto err;
	}
	i = 0;
	while (map[i].name)
		i++;

	/* Allocate space for the class records, plus one for class zero */
	current_mapping = (struct selinux_mapping *)calloc(++i, size);
	if (!current_mapping)
		goto err;

	/* Store the raw class and permission values */
	j = 0;
	while (map[j].name) {
		struct security_class_mapping *p_in = map + (j++);
		struct selinux_mapping *p_out = current_mapping + j;

		p_out->value = string_to_security_class(p_in->name);
		if (!p_out->value) {
			selinux_log(SELINUX_INFO,
				    "SELinux: Class %s not defined in policy.\n",
				    p_in->name);
			if (reject)
				goto err2;
			p_out->num_perms = 0;
			print_unknown_handle = true;
			continue;
		}

		k = 0;
		while (p_in->perms[k]) {
			/* An empty permission string skips ahead */
			if (!*p_in->perms[k]) {
				k++;
				continue;
			}
			p_out->perms[k] = string_to_av_perm(p_out->value,
							    p_in->perms[k]);
			if (!p_out->perms[k]) {
				selinux_log(SELINUX_INFO,
					    "SELinux:  Permission %s in class %s not defined in policy.\n",
					    p_in->perms[k], p_in->name);
				if (reject)
					goto err2;
				print_unknown_handle = true;
			}
			k++;
		}
		p_out->num_perms = k;
	}

	if (print_unknown_handle)
		selinux_log(SELINUX_INFO,
			    "SELinux: the above unknown classes and permissions will be %s\n",
			    deny ? "denied" : "allowed");

	/* Set the mapping size here so the above lookups are "raw" */
	current_mapping_size = i;
	return 0;
err2:
	free(current_mapping);
	current_mapping = NULL;
	current_mapping_size = 0;
err:
	return -1;
}
예제 #14
0
파일: selinux.c 프로젝트: radosroka/sudo
/*
 * This function attempts to relabel the tty. If this function fails, then
 * the contexts are free'd and -1 is returned. On success, 0 is returned
 * and tty_context and new_tty_context are set.
 *
 * This function will not fail if it can not relabel the tty when selinux is
 * in permissive mode.
 */
static int
relabel_tty(const char *ttyn, int ptyfd)
{
    security_context_t tty_con = NULL;
    security_context_t new_tty_con = NULL;
    struct stat sb;
    int fd;
    debug_decl(relabel_tty, SUDO_DEBUG_SELINUX)

    se_state.ttyfd = ptyfd;

    /* It is perfectly legal to have no tty. */
    if (ptyfd == -1 && ttyn == NULL)
	debug_return_int(0);

    /* If sudo is not allocating a pty for the command, open current tty. */
    if (ptyfd == -1) {
	se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY|O_NONBLOCK);
	if (se_state.ttyfd == -1 || fstat(se_state.ttyfd, &sb) == -1) {
	    sudo_warn(U_("unable to open %s, not relabeling tty"), ttyn);
	    goto bad;
	}
	if (!S_ISCHR(sb.st_mode)) {
	    sudo_warn(U_("%s is not a character device, not relabeling tty"),
		ttyn);
	    goto bad;
	}
	(void)fcntl(se_state.ttyfd, F_SETFL,
	    fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
    }

    if (fgetfilecon(se_state.ttyfd, &tty_con) < 0) {
	sudo_warn(U_("unable to get current tty context, not relabeling tty"));
	goto bad;
    }

    if (tty_con) {
	security_class_t tclass = string_to_security_class("chr_file");
	if (tclass == 0) {
	    sudo_warn(U_("unknown security class \"chr_file\", not relabeling tty"));
	    goto bad;
	}
	if (security_compute_relabel(se_state.new_context, tty_con,
	    tclass, &new_tty_con) < 0) {
	    sudo_warn(U_("unable to get new tty context, not relabeling tty"));
	    goto bad;
	}
    }

    if (new_tty_con != NULL) {
	if (fsetfilecon(se_state.ttyfd, new_tty_con) < 0) {
	    sudo_warn(U_("unable to set new tty context"));
	    goto bad;
	}
    }

    if (ptyfd != -1) {
	/* Reopen pty that was relabeled, std{in,out,err} are reset later. */
	se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY, 0);
	if (se_state.ttyfd == -1 || fstat(se_state.ttyfd, &sb) == -1) {
	    sudo_warn(U_("unable to open %s"), ttyn);
	    goto bad;
	}
	if (!S_ISCHR(sb.st_mode)) {
	    sudo_warn(U_("%s is not a character device, not relabeling tty"),
		ttyn);
	    goto bad;
	}
	if (dup2(se_state.ttyfd, ptyfd) == -1) {
	    sudo_warn("dup2");
	    goto bad;
	}
    } else {
	/* Re-open tty to get new label and reset std{in,out,err} */
	close(se_state.ttyfd);
	se_state.ttyfd = open(ttyn, O_RDWR|O_NOCTTY|O_NONBLOCK);
	if (se_state.ttyfd == -1 || fstat(se_state.ttyfd, &sb) == -1) {
	    sudo_warn(U_("unable to open %s"), ttyn);
	    goto bad;
	}
	if (!S_ISCHR(sb.st_mode)) {
	    sudo_warn(U_("%s is not a character device, not relabeling tty"),
		ttyn);
	    goto bad;
	}
	(void)fcntl(se_state.ttyfd, F_SETFL,
	    fcntl(se_state.ttyfd, F_GETFL, 0) & ~O_NONBLOCK);
	for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) {
	    if (isatty(fd) && dup2(se_state.ttyfd, fd) == -1) {
		sudo_warn("dup2");
		goto bad;
	    }
	}
    }
    /* Retain se_state.ttyfd so we can restore label when command finishes. */
    (void)fcntl(se_state.ttyfd, F_SETFD, FD_CLOEXEC);

    se_state.ttyn = ttyn;
    se_state.tty_context = tty_con;
    se_state.new_tty_context = new_tty_con;
    debug_return_int(0);

bad:
    if (se_state.ttyfd != -1 && se_state.ttyfd != ptyfd) {
	close(se_state.ttyfd);
	se_state.ttyfd = -1;
    }
    freecon(tty_con);
    debug_return_int(se_state.enforcing ? -1 : 0);
}
void service_start(struct service *svc, const char *dynamic_args)
{
    struct stat s;
    pid_t pid;
    int needs_console;
    int n;
#ifdef HAVE_SELINUX
    char *scon = NULL;
    int rc;
#endif
        /* starting a service removes it from the disabled or reset
         * state and immediately takes it out of the restarting
         * state if it was in there
         */
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET));
    svc->time_started = 0;

        /* running processes require no additional work -- if
         * they're in the process of exiting, we've ensured
         * that they will immediately restart on exit, unless
         * they are ONESHOT
         */
    if (svc->flags & SVC_RUNNING) {
        return;
    }

    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
    if (needs_console && (!have_console)) {
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if (stat(svc->args[0], &s) != 0) {
        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
               svc->args[0]);
        svc->flags |= SVC_DISABLED;
        return;
    }

#ifdef HAVE_SELINUX
    if (is_selinux_enabled() > 0) {
        char *mycon = NULL, *fcon = NULL;

        INFO("computing context for service '%s'\n", svc->args[0]);
        rc = getcon(&mycon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", svc->name);
            return;
        }

        rc = getfilecon(svc->args[0], &fcon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", svc->name);
            freecon(mycon);
            return;
        }

        rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
        freecon(mycon);
        freecon(fcon);
        if (rc < 0) {
            ERROR("could not get context while starting '%s'\n", svc->name);
            return;
        }
    }
#endif

    NOTICE("starting '%s'\n", svc->name);

    pid = fork();

    if (pid == 0) {
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        umask(077);
#ifdef __arm__
        /*
         * b/7188322 - Temporarily revert to the compat memory layout
         * to avoid breaking third party apps.
         *
         * THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE.
         *
         * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466
         * changes the kernel mapping from bottom up to top-down.
         * This breaks some programs which improperly embed
         * an out of date copy of Android's linker.
         */
        int current = personality(0xffffFFFF);
        personality(current | ADDR_COMPAT_LAYOUT);
#endif
        if (properties_inited()) {
            get_property_workspace(&fd, &sz);
            sprintf(tmp, "%d,%d", dup(fd), sz);
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }

        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);

#ifdef HAVE_SELINUX
        setsockcreatecon(scon);
#endif

        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type,
                                  si->perm, si->uid, si->gid);
            if (s >= 0) {
                publish_socket(si->name, s);
            }
        }

#ifdef HAVE_SELINUX
        freecon(scon);
        scon = NULL;
        setsockcreatecon(NULL);
#endif

        if (svc->ioprio_class != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
            }
        }

        if (needs_console) {
            setsid();
            open_console();
        } else {
            zap_stdio();
        }

#if 0
        for (n = 0; svc->args[n]; n++) {
            INFO("args[%d] = '%s'\n", n, svc->args[n]);
        }
        for (n = 0; ENV[n]; n++) {
            INFO("env[%d] = '%s'\n", n, ENV[n]);
        }
#endif

        setpgid(0, getpid());

    /* as requested, set our gid, supplemental gids, and uid */
        if (svc->gid) {
            if (setgid(svc->gid) != 0) {
                ERROR("setgid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->nr_supp_gids) {
            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
                ERROR("setgroups failed: %s\n", strerror(errno));
                _exit(127);
            }
        }
        if (svc->uid) {
            if (setuid(svc->uid) != 0) {
                ERROR("setuid failed: %s\n", strerror(errno));
                _exit(127);
            }
        }

#ifdef HAVE_SELINUX
        if (svc->seclabel) {
            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
                _exit(127);
            }
        }
#endif

        if (!dynamic_args) {
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
        } else {
            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
            int arg_idx = svc->nargs;
            char *tmp = strdup(dynamic_args);
            char *next = tmp;
            char *bword;

            /* Copy the static arguments */
            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));

            while((bword = strsep(&next, " "))) {
                arg_ptrs[arg_idx++] = bword;
                if (arg_idx == INIT_PARSER_MAXARGS)
                    break;
            }
            arg_ptrs[arg_idx] = '\0';
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
        }
        _exit(127);
    }

#ifdef HAVE_SELINUX
    freecon(scon);
#endif

    if (pid < 0) {
        ERROR("failed to start '%s'\n", svc->name);
        svc->pid = 0;
        return;
    }

    svc->time_started = gettime();
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;

    if (properties_inited())
        notify_service_state(svc->name, "running");
}
예제 #16
0
파일: mapping.c 프로젝트: Chainfire/selinux
int
selinux_set_mapping(struct security_class_mapping *map)
{
	size_t size = sizeof(struct selinux_mapping);
	security_class_t i, j;
	unsigned k;

	free(current_mapping);
	current_mapping = NULL;
	current_mapping_size = 0;

	if (avc_reset() < 0)
		goto err;

	/* Find number of classes in the input mapping */
	if (!map) {
		errno = EINVAL;
		goto err;
	}
	i = 0;
	while (map[i].name)
		i++;

	/* Allocate space for the class records, plus one for class zero */
	current_mapping = (struct selinux_mapping *)calloc(++i, size);
	if (!current_mapping)
		goto err;

	/* Store the raw class and permission values */
	j = 0;
	while (map[j].name) {
		struct security_class_mapping *p_in = map + (j++);
		struct selinux_mapping *p_out = current_mapping + j;

		p_out->value = string_to_security_class(p_in->name);
		if (!p_out->value)
			goto err2;

		k = 0;
		while (p_in->perms[k]) {
			/* An empty permission string skips ahead */
			if (!*p_in->perms[k]) {
				k++;
				continue;
			}
			p_out->perms[k] = string_to_av_perm(p_out->value,
							    p_in->perms[k]);
			if (!p_out->perms[k])
				goto err2;
			k++;
		}
		p_out->num_perms = k;
	}

	/* Set the mapping size here so the above lookups are "raw" */
	current_mapping_size = i;
	return 0;
err2:
	free(current_mapping);
	current_mapping = NULL;
	current_mapping_size = 0;
err:
	return -1;
}
예제 #17
0
void service_start(struct service *svc, const char *dynamic_args)
{
    struct stat s;
    pid_t pid;
    int needs_console;
    int n;
    char *scon = NULL;
    int rc;

        /* starting a service removes it from the disabled or reset
         * state and immediately takes it out of the restarting
         * state if it was in there
         */
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
    svc->time_started = 0;

        /* running processes require no additional work -- if
         * they're in the process of exiting, we've ensured
         * that they will immediately restart on exit, unless
         * they are ONESHOT
         */
    if (svc->flags & SVC_RUNNING) {
#ifdef MTK_INIT
		ERROR("service '%s' still running, return directly\n", svc->name);
#endif
        return;
    }

    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
    if (needs_console && (!have_console)) {
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if (stat(svc->args[0], &s) != 0) {
        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
               svc->args[0]);
        svc->flags |= SVC_DISABLED;
        return;
    }

    if (is_selinux_enabled() > 0) {
        if (svc->seclabel) {
            scon = strdup(svc->seclabel);
            if (!scon) {
                ERROR("Out of memory while starting '%s'\n", svc->name);
                return;
            }
        } else {
            char *mycon = NULL, *fcon = NULL;

            INFO("computing context for service '%s'\n", svc->args[0]);
            rc = getcon(&mycon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
            }

            rc = getfilecon(svc->args[0], &fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                freecon(mycon);
                return;
            }

            rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
            if (rc == 0 && !strcmp(scon, mycon)) {
                ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);
            }
            freecon(mycon);
            freecon(fcon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
            }
        }
    }

    NOTICE("starting '%s'\n", svc->name);

    pid = fork();

    if (pid == 0) {
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        umask(077);
        if (properties_inited()) {
            get_property_workspace(&fd, &sz);
            sprintf(tmp, "%d,%d", dup(fd), sz);
            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
        }

        for (ei = svc->envvars; ei; ei = ei->next)
            add_environment(ei->name, ei->value);

        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type,
                                  si->perm, si->uid, si->gid, si->socketcon ?: scon);
            if (s >= 0) {
                publish_socket(si->name, s);
            }
        }

        freecon(scon);
        scon = NULL;

        if (svc->ioprio_class != IoSchedClass_NONE) {
            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
            }
        }

        if (needs_console) {
            setsid();
            open_console();
        } else {
            zap_stdio();
        }

#if 0
        for (n = 0; svc->args[n]; n++) {
            INFO("args[%d] = '%s'\n", n, svc->args[n]);
        }
        for (n = 0; ENV[n]; n++) {
            INFO("env[%d] = '%s'\n", n, ENV[n]);
        }
#endif

        setpgid(0, getpid());

    /* as requested, set our gid, supplemental gids, and uid */
        if (svc->gid) {
            if (setgid(svc->gid) != 0) {
                ERROR("setgid failed: %s service name %s\n", strerror(errno),svc->name);
                _exit(127);
            }
        }
        if (svc->nr_supp_gids) {
            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
                ERROR("setgroups failed: %s service name %s\n", strerror(errno),svc->name);
                _exit(127);
            }
        }
        if (svc->uid) {
            if (setuid(svc->uid) != 0) {
                ERROR("setuid failed: %s service name %s\n", strerror(errno),svc->name);
                _exit(127);
            }
        }
        if (svc->seclabel) {
            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
                _exit(127);
            }
        }

        if (!dynamic_args) {
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
        } else {
            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
            int arg_idx = svc->nargs;
            char *tmp = strdup(dynamic_args);
            char *next = tmp;
            char *bword;

            /* Copy the static arguments */
            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));

            while((bword = strsep(&next, " "))) {
                arg_ptrs[arg_idx++] = bword;
                if (arg_idx == INIT_PARSER_MAXARGS)
                    break;
            }
            arg_ptrs[arg_idx] = '\0';
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
        }
        _exit(127);
    }

    freecon(scon);

    if (pid < 0) {
        ERROR("failed to start '%s'\n", svc->name);
        svc->pid = 0;
        return;
    }

    svc->time_started = gettime();
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;

#ifdef MTK_INIT
    //record the usage of serivce
    svc->flags |= SVC_MT_GOTLIFE;
#endif
    if (properties_inited())
        notify_service_state(svc->name, "running");
}