1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/chaps_util/chaps_slot_session.h"
#include <dlfcn.h>
#include <pkcs11.h>
#include <pkcs11t.h>
#include <memory>
#include <optional>
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/threading/scoped_blocking_call.h"
namespace chromeos {
namespace {
const char kChapsPath[] = "libchaps.so";
// Error codes used for logging. Should not be reordered or reused.
enum class ErrorCode {
kDlopenFailed = 0,
kFunctionListNotFound = 1,
kFunctionListFailed = 2,
kRequiredFunctionMissing = 3,
kOpenSessionFailed = 4,
kCloseSessionFailed = 5,
};
// Central logging functions that output error codes to avoid binary size bloat.
void LogError(ErrorCode error_code) {
LOG(ERROR) << "ChapsSlotSession error=" << (int)error_code;
}
void LogError(ErrorCode error_code, CK_RV pkcs11_error) {
LOG(ERROR) << "ChapsSlotSession error=" << (int)error_code
<< ", pkcs11_error=" << pkcs11_error;
}
struct ChapsFunctions {
static std::optional<ChapsFunctions> GetFunctions(
CK_FUNCTION_LIST_PTR function_list) {
ChapsFunctions functions;
functions.open_session = function_list->C_OpenSession;
functions.close_session = function_list->C_CloseSession;
functions.create_object = function_list->C_CreateObject;
functions.generate_key_pair = function_list->C_GenerateKeyPair;
functions.get_attribute_value = function_list->C_GetAttributeValue;
functions.set_attribute_value = function_list->C_SetAttributeValue;
if (functions.open_session && functions.close_session &&
functions.create_object && functions.generate_key_pair &&
functions.get_attribute_value && functions.set_attribute_value) {
return functions;
}
return std::nullopt;
}
CK_C_OpenSession open_session = nullptr;
CK_C_CloseSession close_session = nullptr;
CK_C_CreateObject create_object = nullptr;
CK_C_GenerateKeyPair generate_key_pair = nullptr;
CK_C_GetAttributeValue get_attribute_value = nullptr;
CK_C_SetAttributeValue set_attribute_value = nullptr;
};
// Default implementation of a ChapsSlotSession using the libchaps.so library.
// This implementation expects that C_Initialize has already been called for
// chaps in this process.
class ChapsSlotSessionImpl : public ChapsSlotSession {
public:
static std::unique_ptr<ChapsSlotSessionImpl> Create(CK_SLOT_ID slot_id) {
void* chaps_handle =
dlopen(kChapsPath, RTLD_LOCAL | RTLD_NOW | RTLD_DEEPBIND);
if (!chaps_handle) {
LogError(ErrorCode::kDlopenFailed);
return nullptr;
}
CK_C_GetFunctionList get_function_list =
(CK_C_GetFunctionList)dlsym(chaps_handle, "C_GetFunctionList");
if (!get_function_list) {
LogError(ErrorCode::kFunctionListNotFound);
return nullptr;
}
CK_FUNCTION_LIST_PTR function_list = nullptr;
{
CK_RV get_function_list_result = get_function_list(&function_list);
if (CKR_OK != get_function_list_result || !function_list) {
LogError(ErrorCode::kFunctionListFailed, get_function_list_result);
return nullptr;
}
}
auto functions = ChapsFunctions::GetFunctions(function_list);
if (!functions) {
LogError(ErrorCode::kRequiredFunctionMissing);
return nullptr;
}
CK_SESSION_HANDLE session_handle = CK_INVALID_HANDLE;
// Start a new PKCS#11 session for |slot_id_|.
CK_RV open_session_result;
{
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::WILL_BLOCK);
open_session_result = functions->open_session(
slot_id, kOpenSessionFlags, /*pApplication=*/nullptr,
/*Notify=*/nullptr, &session_handle);
}
if (CKR_OK != open_session_result) {
LogError(ErrorCode::kOpenSessionFailed, open_session_result);
return nullptr;
}
return base::WrapUnique(new ChapsSlotSessionImpl(chaps_handle, *functions,
slot_id, session_handle));
}
~ChapsSlotSessionImpl() override {
if (session_handle_ != CK_INVALID_HANDLE) {
CK_RV close_session_result;
{
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::WILL_BLOCK);
close_session_result = functions_.close_session(session_handle_);
}
if (close_session_result != CKR_OK) {
LogError(ErrorCode::kCloseSessionFailed, close_session_result);
}
}
if (chaps_handle_) {
dlclose(chaps_handle_);
}
}
bool ReopenSession() override {
CK_RV close_session_result;
{
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::WILL_BLOCK);
close_session_result = functions_.close_session(session_handle_);
}
if (close_session_result != CKR_SESSION_HANDLE_INVALID &&
close_session_result != CKR_OK) {
LogError(ErrorCode::kCloseSessionFailed, close_session_result);
}
session_handle_ = CK_INVALID_HANDLE;
CK_RV open_session_result;
{
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::WILL_BLOCK);
open_session_result = functions_.open_session(
slot_id_, kOpenSessionFlags, /*pApplication=*/nullptr,
/*Notify=*/nullptr, &session_handle_);
}
if (CKR_OK != open_session_result) {
LogError(ErrorCode::kOpenSessionFailed, open_session_result);
return false;
}
return true;
}
CK_RV CreateObject(CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount,
CK_OBJECT_HANDLE_PTR phObject) override {
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::WILL_BLOCK);
return functions_.create_object(session_handle_, pTemplate, ulCount,
phObject);
}
CK_RV GenerateKeyPair(CK_MECHANISM_PTR pMechanism,
CK_ATTRIBUTE_PTR pPublicKeyTemplate,
CK_ULONG ulPublicKeyAttributeCount,
CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
CK_ULONG ulPrivateKeyAttributeCount,
CK_OBJECT_HANDLE_PTR phPublicKey,
CK_OBJECT_HANDLE_PTR phPrivateKey) override {
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::WILL_BLOCK);
return functions_.generate_key_pair(
session_handle_, pMechanism, pPublicKeyTemplate,
ulPublicKeyAttributeCount, pPrivateKeyTemplate,
ulPrivateKeyAttributeCount, phPublicKey, phPrivateKey);
}
CK_RV GetAttributeValue(CK_OBJECT_HANDLE hObject,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount) override {
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::WILL_BLOCK);
return functions_.get_attribute_value(session_handle_, hObject, pTemplate,
ulCount);
}
CK_RV SetAttributeValue(CK_OBJECT_HANDLE hObject,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount) override {
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::WILL_BLOCK);
return functions_.set_attribute_value(session_handle_, hObject, pTemplate,
ulCount);
}
private:
ChapsSlotSessionImpl(void* chaps_handle,
const ChapsFunctions& functions,
const CK_SLOT_ID slot_id,
CK_SESSION_HANDLE session_handle)
: chaps_handle_(chaps_handle),
functions_(functions),
slot_id_(slot_id),
session_handle_(session_handle) {}
// Pass CKF_RW_SESSION in case the intention is to generate keys.
// CKF_SERIAL_SESSION should always be set according to
// http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html#_Toc416959688
// and chaps verifies that.
static constexpr CK_FLAGS kOpenSessionFlags =
CKF_RW_SESSION | CKF_SERIAL_SESSION;
raw_ptr<void> chaps_handle_ = nullptr;
ChapsFunctions functions_;
const CK_SLOT_ID slot_id_;
CK_SESSION_HANDLE session_handle_ = CK_INVALID_HANDLE;
};
} // namespace
std::unique_ptr<ChapsSlotSession>
ChapsSlotSessionFactoryImpl::CreateChapsSlotSession(CK_SLOT_ID slot_id) {
return ChapsSlotSessionImpl::Create(slot_id);
}
} // namespace chromeos
|