Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0ffad60 authored by Tom Cherry's avatar Tom Cherry Committed by Gerrit Code Review
Browse files

Merge "Introduce property types"

parents 765516ec 927c5d5f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ cc_library_static {
        "persistent_properties.cpp",
        "persistent_properties.proto",
        "property_service.cpp",
        "property_type.cpp",
        "security.cpp",
        "selinux.cpp",
        "service.cpp",
@@ -178,6 +179,7 @@ cc_test {
        "init_test.cpp",
        "persistent_properties_test.cpp",
        "property_service_test.cpp",
        "property_type_test.cpp",
        "result_test.cpp",
        "rlimit_parser_test.cpp",
        "service_test.cpp",
+57 −59
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@

#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
#include "util.h"

using android::base::ReadFileToString;
@@ -95,14 +96,9 @@ void property_init() {
        LOG(FATAL) << "Failed to load serialized property info file";
    }
}
static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
    if (!sctx) {
      return false;
    }

    const char* target_context = nullptr;
    property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
    if (target_context == nullptr) {
static bool CheckMacPerms(const std::string& name, const char* target_context,
                          const char* source_context, struct ucred* cr) {
    if (!target_context || !source_context) {
        return false;
    }

@@ -111,29 +107,12 @@ static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* c
    audit_data.name = name.c_str();
    audit_data.cr = cr;

    bool has_access =
        (selinux_check_access(sctx, target_context, "property_service", "set", &audit_data) == 0);
    bool has_access = (selinux_check_access(source_context, target_context, "property_service",
                                            "set", &audit_data) == 0);

    return has_access;
}

static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
{
    /*
     *  Create a name prefix out of ctl.<service name>
     *  The new prefix allows the use of the existing
     *  property service backend labeling while avoiding
     *  mislabels based on true property prefixes.
     */
    char ctl_name[PROP_VALUE_MAX+4];
    int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name);

    if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
        return 0;

    return check_mac_perms(ctl_name, sctx, cr);
}

bool is_legal_property_name(const std::string& name) {
    size_t namelen = name.size();

@@ -422,25 +401,51 @@ static void handle_property_set(SocketConnection& socket,
  struct ucred cr = socket.cred();
  char* source_ctx = nullptr;
  getpeercon(socket.socket(), &source_ctx);
  std::string source_context = source_ctx;
  freecon(source_ctx);

  if (StartsWith(name, "ctl.")) {
    if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
      // ctl. properties have their name ctl.<action> and their value is the name of the service to
      // apply that action to.  Permissions for these actions are based on the service, so we must
      // create a fake name of ctl.<service> to check permissions.
      auto control_string = "ctl." + value;
      const char* target_context = nullptr;
      const char* type = nullptr;
      property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
      if (!CheckMacPerms(control_string, target_context, source_context.c_str(), &cr)) {
          LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
                     << " service ctl [" << value << "]"
                     << " uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid;
          if (!legacy_protocol) {
              socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
          }
          return;
      }

      handle_control_message(name.c_str() + 4, value.c_str());
      if (!legacy_protocol) {
          socket.SendUint32(PROP_SUCCESS);
      }
  } else {
      LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
                 << " service ctl [" << value << "]"
                 << " uid:" << cr.uid
                 << " gid:" << cr.gid
                 << " pid:" << cr.pid;
      const char* target_context = nullptr;
      const char* type = nullptr;
      property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
      if (!CheckMacPerms(name, target_context, source_context.c_str(), &cr)) {
          LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid
                     << " name:" << name;
          if (!legacy_protocol) {
        socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
              socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
          }
          return;
      }
      if (type == nullptr || !CheckType(type, value)) {
          LOG(ERROR) << "sys_prop(" << cmd_name << "): type check failed, type: '"
                     << (type ?: "(null)") << "' value: '" << value << "'";
          if (!legacy_protocol) {
              socket.SendUint32(PROP_ERROR_INVALID_VALUE);
          }
          return;
      }
  } else {
    if (check_mac_perms(name, source_ctx, &cr)) {
      // sys.powerctl is a special property that is used to make the device reboot.  We want to log
      // any process that sets this property to be able to accurately blame the cause of a shutdown.
      if (name == "sys.powerctl") {
@@ -459,17 +464,9 @@ static void handle_property_set(SocketConnection& socket,
      if (!legacy_protocol) {
          socket.SendUint32(result);
      }
    } else {
      LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
      if (!legacy_protocol) {
        socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
      }
  }
}

  freecon(source_ctx);
}

static void handle_property_set_fd() {
    static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */

@@ -764,9 +761,10 @@ void CreateSerializedPropertyInfo() {
        }
        LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
    }

    auto serialized_contexts = std::string();
    auto error = std::string();
    if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "\\s*", &serialized_contexts,
    if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
                   &error)) {
        LOG(ERROR) << "Unable to serialize property contexts: " << error;
        return;

init/property_type.cpp

0 → 100644
+81 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2017 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "property_type.h"

#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>

using android::base::ParseDouble;
using android::base::ParseInt;
using android::base::ParseUint;
using android::base::Split;

namespace android {
namespace init {

bool CheckType(const std::string& type_string, const std::string& value) {
    auto type_strings = Split(type_string, " ");
    if (type_strings.empty()) {
        return false;
    }
    auto type = type_strings[0];

    if (type == "string") {
        return true;
    }
    if (type == "bool") {
        return value == "true" || value == "false" || value == "1" || value == "0";
    }
    if (type == "int") {
        int64_t parsed;
        return ParseInt(value, &parsed);
    }
    if (type == "uint") {
        uint64_t parsed;
        if (value.empty() || value.front() == '-') {
            return false;
        }
        return ParseUint(value, &parsed);
    }
    if (type == "double") {
        double parsed;
        return ParseDouble(value.c_str(), &parsed);
    }
    if (type == "size") {
        auto it = value.begin();
        while (it != value.end() && isdigit(*it)) {
            it++;
        }
        if (it == value.begin() || it == value.end() || (*it != 'g' && *it != 'k' && *it != 'm')) {
            return false;
        }
        it++;
        return it == value.end();
    }
    if (type == "enum") {
        for (auto it = std::next(type_strings.begin()); it != type_strings.end(); ++it) {
            if (*it == value) {
                return true;
            }
        }
    }
    return false;
}

}  // namespace init
}  // namespace android

init/property_type.h

0 → 100644
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _INIT_PROPERTY_TYPE_H
#define _INIT_PROPERTY_TYPE_H

#include <string>

namespace android {
namespace init {

bool CheckType(const std::string& type_string, const std::string& value);

}  // namespace init
}  // namespace android

#endif
+95 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "property_type.h"

#include <gtest/gtest.h>

namespace android {
namespace init {

TEST(property_type, CheckType_string) {
    EXPECT_TRUE(CheckType("string", ""));
    EXPECT_TRUE(CheckType("string", "-234"));
    EXPECT_TRUE(CheckType("string", "234"));
    EXPECT_TRUE(CheckType("string", "true"));
    EXPECT_TRUE(CheckType("string", "false"));
    EXPECT_TRUE(CheckType("string", "45645634563456345634563456"));
    EXPECT_TRUE(CheckType("string", "some other string"));
}

TEST(property_type, CheckType_int) {
    EXPECT_FALSE(CheckType("int", ""));
    EXPECT_FALSE(CheckType("int", "abc"));
    EXPECT_FALSE(CheckType("int", "-abc"));
    EXPECT_TRUE(CheckType("int", "0"));
    EXPECT_TRUE(CheckType("int", std::to_string(std::numeric_limits<int64_t>::min())));
    EXPECT_TRUE(CheckType("int", std::to_string(std::numeric_limits<int64_t>::max())));
    EXPECT_TRUE(CheckType("int", "123"));
    EXPECT_TRUE(CheckType("int", "-123"));
}

TEST(property_type, CheckType_uint) {
    EXPECT_FALSE(CheckType("uint", ""));
    EXPECT_FALSE(CheckType("uint", "abc"));
    EXPECT_FALSE(CheckType("uint", "-abc"));
    EXPECT_TRUE(CheckType("uint", "0"));
    EXPECT_TRUE(CheckType("uint", std::to_string(std::numeric_limits<uint64_t>::max())));
    EXPECT_TRUE(CheckType("uint", "123"));
    EXPECT_FALSE(CheckType("uint", "-123"));
}

TEST(property_type, CheckType_double) {
    EXPECT_FALSE(CheckType("double", ""));
    EXPECT_FALSE(CheckType("double", "abc"));
    EXPECT_FALSE(CheckType("double", "-abc"));
    EXPECT_TRUE(CheckType("double", "0.0"));
    EXPECT_TRUE(CheckType("double", std::to_string(std::numeric_limits<double>::min())));
    EXPECT_TRUE(CheckType("double", std::to_string(std::numeric_limits<double>::max())));
    EXPECT_TRUE(CheckType("double", "123.1"));
    EXPECT_TRUE(CheckType("double", "-123.1"));
}

TEST(property_type, CheckType_size) {
    EXPECT_FALSE(CheckType("size", ""));
    EXPECT_FALSE(CheckType("size", "ab"));
    EXPECT_FALSE(CheckType("size", "abcd"));
    EXPECT_FALSE(CheckType("size", "0"));

    EXPECT_TRUE(CheckType("size", "512g"));
    EXPECT_TRUE(CheckType("size", "512k"));
    EXPECT_TRUE(CheckType("size", "512m"));

    EXPECT_FALSE(CheckType("size", "512gggg"));
    EXPECT_FALSE(CheckType("size", "512mgk"));
    EXPECT_FALSE(CheckType("size", "g"));
    EXPECT_FALSE(CheckType("size", "m"));
}

TEST(property_type, CheckType_enum) {
    EXPECT_FALSE(CheckType("enum abc", ""));
    EXPECT_FALSE(CheckType("enum abc", "ab"));
    EXPECT_FALSE(CheckType("enum abc", "abcd"));
    EXPECT_FALSE(CheckType("enum 123 456 789", "0"));

    EXPECT_TRUE(CheckType("enum abc", "abc"));
    EXPECT_TRUE(CheckType("enum 123 456 789", "123"));
    EXPECT_TRUE(CheckType("enum 123 456 789", "456"));
    EXPECT_TRUE(CheckType("enum 123 456 789", "789"));
}

}  // namespace init
}  // namespace android
Loading