File: chaps_slot_session.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (243 lines) | stat: -rw-r--r-- 8,684 bytes parent folder | download | duplicates (6)
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