/* * Copyright (C) 2019 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static constexpr int kMaxCertSize = 4096; static std::vector SplitBySpace(const std::string& s) { std::istringstream iss(s); return std::vector{std::istream_iterator{iss}, std::istream_iterator{}}; } // Find the keyring id. Because request_key(2) syscall is not available or the key is // kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other // information in the descritption section depending on the key type, only the first word in the // keyring description is used for searching. static key_serial_t GetKeyringIdOrDie(const std::string& keyring_desc) { // If the keyring id is already a hex number, directly convert it to keyring id key_serial_t keyring_id; if (android::base::ParseInt(keyring_desc.c_str(), &keyring_id)) { return keyring_id; } // Only keys allowed by SELinux rules will be shown here. std::ifstream proc_keys_file("/proc/keys"); if (!proc_keys_file.is_open()) { error(1, errno, "Failed to open /proc/keys"); return -1; } std::string line; while (getline(proc_keys_file, line)) { std::vector tokens = SplitBySpace(line); if (tokens.size() < 9) { continue; } std::string key_id = "0x" + tokens[0]; std::string key_type = tokens[7]; // The key description may contain space. std::string key_desc_prefix = tokens[8]; // The prefix has a ":" at the end std::string key_desc_pattern = keyring_desc + ":"; if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) { continue; } if (!android::base::ParseInt(key_id.c_str(), &keyring_id)) { error(1, 0, "Unexpected key format in /proc/keys: %s", key_id.c_str()); return -1; } return keyring_id; } return -1; } int Unlink(key_serial_t key, const std::string& keyring) { key_serial_t keyring_id = GetKeyringIdOrDie(keyring); if (keyctl_unlink(key, keyring_id) < 0) { error(1, errno, "Failed to unlink key %x from keyring %s", key, keyring.c_str()); return 1; } return 0; } int Add(const std::string& type, const std::string& desc, const std::string& data, const std::string& keyring) { if (data.size() > kMaxCertSize) { error(1, 0, "Certificate too large"); return 1; } key_serial_t keyring_id = GetKeyringIdOrDie(keyring); key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id); if (key < 0) { error(1, errno, "Failed to add key"); return 1; } std::cout << key << std::endl; return 0; } int Padd(const std::string& type, const std::string& desc, const std::string& keyring) { key_serial_t keyring_id = GetKeyringIdOrDie(keyring); // read from stdin to get the certificates std::istreambuf_iterator begin(std::cin), end; std::string data(begin, end); if (data.size() > kMaxCertSize) { error(1, 0, "Certificate too large"); return 1; } key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id); if (key < 0) { error(1, errno, "Failed to add key"); return 1; } std::cout << key << std::endl; return 0; } int RestrictKeyring(const std::string& keyring) { key_serial_t keyring_id = GetKeyringIdOrDie(keyring); if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) { error(1, errno, "Cannot restrict keyring '%s'", keyring.c_str()); return 1; } return 0; } std::string RetrieveSecurityContext(key_serial_t key) { // Simply assume this size is enough in practice. const int kMaxSupportedSize = 256; std::string context; context.resize(kMaxSupportedSize); long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize); if (retval < 0) { error(1, errno, "Cannot get security context of key %x", key); return std::string(); } if (retval > kMaxSupportedSize) { error(1, 0, "The key has unexpectedly long security context than %d", kMaxSupportedSize); return std::string(); } context.resize(retval); return context; }