1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "nvram/core/nvram_manager.h"
18
19extern "C" {
20#include <inttypes.h>
21#include <string.h>
22}  // extern "C"
23
24#include <nvram/core/logger.h>
25
26#include "crypto.h"
27
28using namespace nvram::storage;
29
30namespace nvram {
31
32namespace {
33
34// Maximum size of a single space's contents.
35constexpr size_t kMaxSpaceSize = 1024;
36
37// Maximum authorization blob size;
38constexpr size_t kMaxAuthSize = 32;
39
40// The bitmask of all supported control flags.
41constexpr uint32_t kSupportedControlsMask =
42    (1 << NV_CONTROL_PERSISTENT_WRITE_LOCK) |
43    (1 << NV_CONTROL_BOOT_WRITE_LOCK) |
44    (1 << NV_CONTROL_BOOT_READ_LOCK) |
45    (1 << NV_CONTROL_WRITE_AUTHORIZATION) |
46    (1 << NV_CONTROL_READ_AUTHORIZATION) |
47    (1 << NV_CONTROL_WRITE_EXTEND);
48
49// Convert the |space.controls| bitmask to vector representation.
50nvram_result_t GetControlsVector(const NvramSpace& space,
51                                 Vector<nvram_control_t>* controls) {
52  for (size_t control = 0; control < sizeof(uint32_t) * 8; ++control) {
53    if (space.HasControl(control)) {
54      if (!controls->Resize(controls->size() + 1)) {
55        NVRAM_LOG_ERR("Allocation failure.");
56        return NV_RESULT_INTERNAL_ERROR;
57      }
58      (*controls)[controls->size() - 1] = static_cast<nvram_control_t>(control);
59    }
60  }
61  return NV_RESULT_SUCCESS;
62}
63
64// Constant time memory block comparison.
65bool ConstantTimeEquals(const Blob& a, const Blob& b) {
66  if (a.size() != b.size())
67    return false;
68
69  // The volatile qualifiers prevent the compiler from making assumptions that
70  // allow shortcuts:
71  //  * The entire array data must be read from memory.
72  //  * Marking |result| volatile ensures the subsequent loop iterations must
73  //    still store to |result|, thus avoiding the loop to exit early.
74  // This achieves the desired constant-time behavior.
75  volatile const uint8_t* data_a = a.data();
76  volatile const uint8_t* data_b = b.data();
77  volatile uint8_t result = 0;
78  for (size_t i = 0; i < a.size(); ++i) {
79    result |= data_a[i] ^ data_b[i];
80  }
81
82  return result == 0;
83}
84
85// A standard minimum function.
86template <typename Type>
87const Type& min(const Type& a, const Type& b) {
88  return (a < b) ? a : b;
89}
90
91// Filter status codes from the storage layer to only include known values.
92// Anything outside the range will be mapped to the generic |kStorageError|.
93storage::Status SanitizeStorageStatus(storage::Status status) {
94  switch (status) {
95    case storage::Status::kSuccess:
96      return storage::Status::kSuccess;
97    case storage::Status::kNotFound:
98      return storage::Status::kNotFound;
99    case storage::Status::kStorageError:
100      return storage::Status::kStorageError;
101  }
102  NVRAM_LOG_ERR("Unknown status code %u!", status);
103  return storage::Status::kStorageError;
104}
105
106}  // namespace
107
108// Looks at |request| to determine the command to execute, then invokes
109// the appropriate handler.
110void NvramManager::Dispatch(const nvram::Request& request,
111                            nvram::Response* response) {
112  nvram_result_t result = NV_RESULT_INVALID_PARAMETER;
113  const nvram::RequestUnion& input = request.payload;
114  nvram::ResponseUnion* output = &response->payload;
115
116  switch (input.which()) {
117    case nvram::COMMAND_GET_INFO:
118      result = GetInfo(*input.get<COMMAND_GET_INFO>(),
119                       &output->Activate<COMMAND_GET_INFO>());
120      break;
121    case nvram::COMMAND_CREATE_SPACE:
122      result = CreateSpace(*input.get<COMMAND_CREATE_SPACE>(),
123                           &output->Activate<COMMAND_CREATE_SPACE>());
124      break;
125    case nvram::COMMAND_GET_SPACE_INFO:
126      result = GetSpaceInfo(*input.get<COMMAND_GET_SPACE_INFO>(),
127                            &output->Activate<COMMAND_GET_SPACE_INFO>());
128      break;
129    case nvram::COMMAND_DELETE_SPACE:
130      result = DeleteSpace(*input.get<COMMAND_DELETE_SPACE>(),
131                           &output->Activate<COMMAND_DELETE_SPACE>());
132      break;
133    case nvram::COMMAND_DISABLE_CREATE:
134      result = DisableCreate(*input.get<COMMAND_DISABLE_CREATE>(),
135                             &output->Activate<COMMAND_DISABLE_CREATE>());
136      break;
137    case nvram::COMMAND_WRITE_SPACE:
138      result = WriteSpace(*input.get<COMMAND_WRITE_SPACE>(),
139                          &output->Activate<COMMAND_WRITE_SPACE>());
140      break;
141    case nvram::COMMAND_READ_SPACE:
142      result = ReadSpace(*input.get<COMMAND_READ_SPACE>(),
143                         &output->Activate<COMMAND_READ_SPACE>());
144      break;
145    case nvram::COMMAND_LOCK_SPACE_WRITE:
146      result = LockSpaceWrite(*input.get<COMMAND_LOCK_SPACE_WRITE>(),
147                              &output->Activate<COMMAND_LOCK_SPACE_WRITE>());
148      break;
149    case nvram::COMMAND_LOCK_SPACE_READ:
150      result = LockSpaceRead(*input.get<COMMAND_LOCK_SPACE_READ>(),
151                             &output->Activate<COMMAND_LOCK_SPACE_READ>());
152      break;
153    case nvram::COMMAND_WIPE_STORAGE:
154      result = WipeStorage(*input.get<COMMAND_WIPE_STORAGE>(),
155                           &output->Activate<COMMAND_WIPE_STORAGE>());
156      break;
157    case nvram::COMMAND_DISABLE_WIPE:
158      result = DisableWipe(*input.get<COMMAND_DISABLE_WIPE>(),
159                           &output->Activate<COMMAND_DISABLE_WIPE>());
160      break;
161  }
162
163  response->result = result;
164}
165
166nvram_result_t NvramManager::GetInfo(const GetInfoRequest& /* request */,
167                                     GetInfoResponse* response) {
168  NVRAM_LOG_INFO("GetInfo");
169
170  if (!Initialize())
171    return NV_RESULT_INTERNAL_ERROR;
172
173  // TODO: Get better values for total and available size from the storage
174  // layer.
175  response->total_size = kMaxSpaceSize * kMaxSpaces;
176  response->available_size = kMaxSpaceSize * (kMaxSpaces - num_spaces_);
177  response->max_space_size = kMaxSpaceSize;
178  response->max_spaces = kMaxSpaces;
179  Vector<uint32_t>& space_list = response->space_list;
180  if (!space_list.Resize(num_spaces_)) {
181    NVRAM_LOG_ERR("Allocation failure.");
182    return NV_RESULT_INTERNAL_ERROR;
183  }
184  for (size_t i = 0; i < num_spaces_; ++i) {
185    space_list[i] = spaces_[i].index;
186  }
187  response->wipe_disabled = disable_wipe_;
188
189  return NV_RESULT_SUCCESS;
190}
191
192nvram_result_t NvramManager::CreateSpace(const CreateSpaceRequest& request,
193                                         CreateSpaceResponse* /* response */) {
194  const uint32_t index = request.index;
195  NVRAM_LOG_INFO("CreateSpace Ox%" PRIx32, index);
196
197  if (!Initialize())
198    return NV_RESULT_INTERNAL_ERROR;
199
200  if (disable_create_) {
201    NVRAM_LOG_INFO("Creation of further spaces is disabled.");
202    return NV_RESULT_OPERATION_DISABLED;
203  }
204
205  if (FindSpace(index) != kMaxSpaces) {
206    NVRAM_LOG_INFO("Space 0x%" PRIx32 " already exists.", index);
207    return NV_RESULT_SPACE_ALREADY_EXISTS;
208  }
209
210  if (num_spaces_ + 1 > kMaxSpaces) {
211    NVRAM_LOG_INFO("Too many spaces.");
212    return NV_RESULT_INVALID_PARAMETER;
213  }
214
215  if (request.size > kMaxSpaceSize) {
216    NVRAM_LOG_INFO("Create request exceeds max space size.");
217    return NV_RESULT_INVALID_PARAMETER;
218  }
219
220  if (request.authorization_value.size() > kMaxAuthSize) {
221    NVRAM_LOG_INFO("Authorization blob too large.");
222    return NV_RESULT_INVALID_PARAMETER;
223  }
224
225  uint32_t controls = 0;
226  for (uint32_t control : request.controls) {
227    controls |= (1 << control);
228  }
229  if ((controls & ~kSupportedControlsMask) != 0) {
230    NVRAM_LOG_INFO("Bad controls.");
231    return NV_RESULT_INVALID_PARAMETER;
232  }
233  if ((controls & (1 << NV_CONTROL_PERSISTENT_WRITE_LOCK)) != 0 &&
234      (controls & (1 << NV_CONTROL_BOOT_WRITE_LOCK)) != 0) {
235    NVRAM_LOG_INFO("Write lock controls are exclusive.");
236    return NV_RESULT_INVALID_PARAMETER;
237  }
238  if ((controls & (1 << NV_CONTROL_WRITE_EXTEND)) != 0 &&
239      request.size != crypto::kSHA256DigestSize) {
240    NVRAM_LOG_INFO("Write-extended space size must be %zu.",
241                   crypto::kSHA256DigestSize);
242    return NV_RESULT_INVALID_PARAMETER;
243  }
244
245  // Mark the index as allocated.
246  spaces_[num_spaces_].index = index;
247  spaces_[num_spaces_].write_locked = false;
248  spaces_[num_spaces_].read_locked = false;
249  ++num_spaces_;
250
251  // Create a space record.
252  NvramSpace space;
253  space.flags = 0;
254  space.controls = controls;
255
256  // Copy the auth blob.
257  if (space.HasControl(NV_CONTROL_WRITE_AUTHORIZATION) ||
258      space.HasControl(NV_CONTROL_READ_AUTHORIZATION)) {
259    if (!space.authorization_value.Assign(request.authorization_value.data(),
260                                          request.authorization_value.size())) {
261      NVRAM_LOG_ERR("Allocation failure.");
262      return NV_RESULT_INTERNAL_ERROR;
263    }
264  }
265
266  // Initialize the space content.
267  if (!space.contents.Resize(request.size)) {
268    NVRAM_LOG_ERR("Allocation failure.");
269    return NV_RESULT_INTERNAL_ERROR;
270  }
271  memset(space.contents.data(), 0, request.size);
272
273  // Write the header before the space data. This ensures that all space
274  // definitions present in storage are also recorded in the header. Thus, the
275  // set of spaces present in the header is always a superset of the set of
276  // spaces that have state in storage. If there's a crash after writing the
277  // header but before writing the space information, the space data will be
278  // missing in storage. The initialization code handles this by checking the
279  // for the space data corresponding to the index marked as provisional in the
280  // header.
281  nvram_result_t result;
282  if ((result = WriteHeader(Optional<uint32_t>(index))) != NV_RESULT_SUCCESS ||
283      (result = WriteSpace(index, space)) != NV_RESULT_SUCCESS) {
284    --num_spaces_;
285  }
286  return result;
287}
288
289nvram_result_t NvramManager::GetSpaceInfo(const GetSpaceInfoRequest& request,
290                                          GetSpaceInfoResponse* response) {
291  const uint32_t index = request.index;
292  NVRAM_LOG_INFO("GetSpaceInfo Ox%" PRIx32, index);
293
294  if (!Initialize())
295    return NV_RESULT_INTERNAL_ERROR;
296
297  SpaceRecord space_record;
298  nvram_result_t result;
299  if (!LoadSpaceRecord(index, &space_record, &result)) {
300    return result;
301  }
302
303  response->size = space_record.persistent.contents.size();
304
305  result = GetControlsVector(space_record.persistent, &response->controls);
306  if (result != NV_RESULT_SUCCESS) {
307    return NV_RESULT_INTERNAL_ERROR;
308  }
309
310  if (space_record.persistent.HasControl(NV_CONTROL_BOOT_READ_LOCK)) {
311    response->read_locked = space_record.transient->read_locked;
312  }
313
314  if (space_record.persistent.HasControl(NV_CONTROL_PERSISTENT_WRITE_LOCK)) {
315    response->write_locked =
316        space_record.persistent.HasFlag(NvramSpace::kFlagWriteLocked);
317  } else if (space_record.persistent.HasControl(NV_CONTROL_BOOT_WRITE_LOCK)) {
318    response->write_locked = space_record.transient->write_locked;
319  }
320
321  return NV_RESULT_SUCCESS;
322}
323
324nvram_result_t NvramManager::DeleteSpace(const DeleteSpaceRequest& request,
325                                         DeleteSpaceResponse* /* response */) {
326  const uint32_t index = request.index;
327  NVRAM_LOG_INFO("DeleteSpace Ox%" PRIx32, index);
328
329  if (!Initialize())
330    return NV_RESULT_INTERNAL_ERROR;
331
332  SpaceRecord space_record;
333  nvram_result_t result;
334  if (!LoadSpaceRecord(index, &space_record, &result)) {
335    return result;
336  }
337
338  result = space_record.CheckWriteAccess(request.authorization_value);
339  if (result != NV_RESULT_SUCCESS) {
340    return result;
341  }
342
343  // Delete the space. First mark the space as provisionally removed in the
344  // header. Then, delete the space data from storage. This allows orphaned
345  // space data be cleaned up after a crash.
346  SpaceListEntry tmp = spaces_[space_record.array_index];
347  spaces_[space_record.array_index] = spaces_[num_spaces_ - 1];
348  --num_spaces_;
349  result = WriteHeader(Optional<uint32_t>(index));
350  if (result == NV_RESULT_SUCCESS) {
351    switch (SanitizeStorageStatus(persistence::DeleteSpace(index))) {
352      case storage::Status::kStorageError:
353        NVRAM_LOG_ERR("Failed to delete space 0x%" PRIx32 " data.", index);
354        result = NV_RESULT_INTERNAL_ERROR;
355        break;
356      case storage::Status::kNotFound:
357        // The space was missing even if it shouldn't have been. Log an error,
358        // but return success as we're in the desired state.
359        NVRAM_LOG_ERR("Space 0x%" PRIx32 " data missing on deletion.", index);
360        return NV_RESULT_SUCCESS;
361      case storage::Status::kSuccess:
362        return NV_RESULT_SUCCESS;
363    }
364  }
365
366  // Failed to delete, re-add the transient state to |spaces_|.
367  spaces_[num_spaces_] = tmp;
368  ++num_spaces_;
369  return result;
370}
371
372nvram_result_t NvramManager::DisableCreate(
373    const DisableCreateRequest& /* request */,
374    DisableCreateResponse* /* response */) {
375  NVRAM_LOG_INFO("DisableCreate");
376
377  if (!Initialize())
378    return NV_RESULT_INTERNAL_ERROR;
379
380  // Set the |disable_create_| flag and call |WriteHeader| to persist the flag
381  // such that it remains effective after a reboot. Make sure to restore the
382  // current value of |disable_create_| if the write call fails, as we return an
383  // error in that case and client code would not expect state changes.
384  bool disable_create_previous = disable_create_;
385  disable_create_ = true;
386  nvram_result_t result = WriteHeader(Optional<uint32_t>());
387  if (result != NV_RESULT_SUCCESS) {
388    disable_create_ = disable_create_previous;
389  }
390  return result;
391}
392
393nvram_result_t NvramManager::WriteSpace(const WriteSpaceRequest& request,
394                                        WriteSpaceResponse* /* response */) {
395  const uint32_t index = request.index;
396  NVRAM_LOG_INFO("WriteSpace Ox%" PRIx32, index);
397
398  if (!Initialize())
399    return NV_RESULT_INTERNAL_ERROR;
400
401  SpaceRecord space_record;
402  nvram_result_t result;
403  if (!LoadSpaceRecord(index, &space_record, &result)) {
404    return result;
405  }
406
407  result = space_record.CheckWriteAccess(request.authorization_value);
408  if (result != NV_RESULT_SUCCESS) {
409    return result;
410  }
411
412  Blob& contents = space_record.persistent.contents;
413  if (space_record.persistent.HasControl(NV_CONTROL_WRITE_EXTEND)) {
414    // Concatenate the current space |contents| with the input data.
415    Blob sha256_input;
416    if (!sha256_input.Resize(contents.size() + request.buffer.size())) {
417      return NV_RESULT_INTERNAL_ERROR;
418    }
419    memcpy(sha256_input.data(), contents.data(), contents.size());
420    memcpy(sha256_input.data() + contents.size(), request.buffer.data(),
421           request.buffer.size());
422
423    // Compute the SHA-256 digest and write it back to |contents|.
424    crypto::SHA256(sha256_input.data(), sha256_input.size(), contents.data(),
425                   contents.size());
426  } else {
427    if (contents.size() < request.buffer.size()) {
428      return NV_RESULT_INVALID_PARAMETER;
429    }
430
431    memcpy(contents.data(), request.buffer.data(), request.buffer.size());
432    memset(contents.data() + request.buffer.size(), 0x0,
433           contents.size() - request.buffer.size());
434  }
435
436  return WriteSpace(index, space_record.persistent);
437}
438
439nvram_result_t NvramManager::ReadSpace(const ReadSpaceRequest& request,
440                                       ReadSpaceResponse* response) {
441  const uint32_t index = request.index;
442  NVRAM_LOG_INFO("ReadSpace Ox%" PRIx32, index);
443
444  if (!Initialize())
445    return NV_RESULT_INTERNAL_ERROR;
446
447  SpaceRecord space_record;
448  nvram_result_t result;
449  if (!LoadSpaceRecord(index, &space_record, &result)) {
450    return result;
451  }
452
453  result = space_record.CheckReadAccess(request.authorization_value);
454  if (result != NV_RESULT_SUCCESS) {
455    return result;
456  }
457
458  if (!response->buffer.Assign(space_record.persistent.contents.data(),
459                               space_record.persistent.contents.size())) {
460    NVRAM_LOG_ERR("Allocation failure.");
461    return NV_RESULT_INTERNAL_ERROR;
462  }
463
464  return NV_RESULT_SUCCESS;
465}
466
467nvram_result_t NvramManager::LockSpaceWrite(
468    const LockSpaceWriteRequest& request,
469    LockSpaceWriteResponse* /* response */) {
470  const uint32_t index = request.index;
471  NVRAM_LOG_INFO("LockSpaceWrite Ox%" PRIx32, index);
472
473  if (!Initialize())
474    return NV_RESULT_INTERNAL_ERROR;
475
476  SpaceRecord space_record;
477  nvram_result_t result;
478  if (!LoadSpaceRecord(index, &space_record, &result)) {
479    return result;
480  }
481
482  result = space_record.CheckWriteAccess(request.authorization_value);
483  if (result != NV_RESULT_SUCCESS) {
484    return result;
485  }
486
487  if (space_record.persistent.HasControl(NV_CONTROL_PERSISTENT_WRITE_LOCK)) {
488    space_record.persistent.SetFlag(NvramSpace::kFlagWriteLocked);
489    return WriteSpace(index, space_record.persistent);
490  } else if (space_record.persistent.HasControl(NV_CONTROL_BOOT_WRITE_LOCK)) {
491    space_record.transient->write_locked = true;
492    return NV_RESULT_SUCCESS;
493  }
494
495  NVRAM_LOG_ERR("Space not configured for write locking.");
496  return NV_RESULT_INVALID_PARAMETER;
497}
498
499nvram_result_t NvramManager::LockSpaceRead(
500    const LockSpaceReadRequest& request,
501    LockSpaceReadResponse* /* response */) {
502  const uint32_t index = request.index;
503  NVRAM_LOG_INFO("LockSpaceRead Ox%" PRIx32, index);
504
505  if (!Initialize())
506    return NV_RESULT_INTERNAL_ERROR;
507
508  SpaceRecord space_record;
509  nvram_result_t result;
510  if (!LoadSpaceRecord(index, &space_record, &result)) {
511    return result;
512  }
513
514  result = space_record.CheckReadAccess(request.authorization_value);
515  if (result != NV_RESULT_SUCCESS) {
516    return result;
517  }
518
519  if (space_record.persistent.HasControl(NV_CONTROL_BOOT_READ_LOCK)) {
520    space_record.transient->read_locked = true;
521    return NV_RESULT_SUCCESS;
522  }
523
524  NVRAM_LOG_ERR("Space not configured for read locking.");
525  return NV_RESULT_INVALID_PARAMETER;
526}
527
528nvram_result_t NvramManager::WipeStorage(
529    const WipeStorageRequest& /* request */,
530    WipeStorageResponse* /* response */) {
531  if (!Initialize())
532    return NV_RESULT_INTERNAL_ERROR;
533
534#ifdef NVRAM_WIPE_STORAGE_SUPPORT
535  if (disable_wipe_) {
536    return NV_RESULT_OPERATION_DISABLED;
537  }
538
539  // Go through all spaces and wipe the corresponding data. Note that the header
540  // is only updated once all space data is gone. This will "break" all spaces
541  // that are left declared but don't have data. This situation can be observed
542  // if we crash somewhere during the wiping process before clearing the header.
543  //
544  // Note that we deliberately choose this wiping sequence so we can never end
545  // up in a state where the header appears clean but existing space data
546  // remains.
547  //
548  // As a final note, the ideal solution would be to atomically clear the header
549  // and delete all space data. While more desirable from an operational point
550  // of view, this would drastically complicate storage layer requirements to
551  // support cross-object atomicity instead of per-object atomicity.
552  for (size_t i = 0; i < num_spaces_; ++i) {
553    const uint32_t index = spaces_[i].index;
554    switch (SanitizeStorageStatus(persistence::DeleteSpace(index))) {
555      case storage::Status::kStorageError:
556        NVRAM_LOG_ERR("Failed to wipe space 0x%" PRIx32 " data.", index);
557        return NV_RESULT_INTERNAL_ERROR;
558      case storage::Status::kNotFound:
559        // The space was missing even if it shouldn't have been. This may occur
560        // if a previous wiping attempt was aborted half-way. Log an error, but
561        // return success as we're in the desired state.
562        NVRAM_LOG_WARN("Space 0x%" PRIx32 " data missing on wipe.", index);
563        break;
564      case storage::Status::kSuccess:
565        break;
566    }
567  }
568
569  // All spaces are gone, clear the header.
570  num_spaces_ = 0;
571  return WriteHeader(Optional<uint32_t>());
572#else  // NVRAM_WIPE_STORAGE_SUPPORT
573  // We're not accessing the flag member, so prevent a compiler warning. The
574  // alternative of conditionally including the member in the class declaration
575  // looks cleaner at first sight, but comes with the risk of
576  // NVRAM_WIPE_STORAGE_SUPPORT polarity mismatches between compilation units,
577  // which is more subtly dangerous, so we rather keep the member even for the
578  // case in which it is not used.
579  (void)disable_wipe_;
580  return NV_RESULT_OPERATION_DISABLED;
581#endif  // NVRAM_WIPE_STORAGE_SUPPORT
582}
583
584nvram_result_t NvramManager::DisableWipe(
585    const DisableWipeRequest& /* request */,
586    DisableWipeResponse* /* response */) {
587  if (!Initialize())
588    return NV_RESULT_INTERNAL_ERROR;
589
590#ifdef NVRAM_WIPE_STORAGE_SUPPORT
591  disable_wipe_ = true;
592  return NV_RESULT_SUCCESS;
593#else  // NVRAM_WIPE_STORAGE_SUPPORT
594  return NV_RESULT_OPERATION_DISABLED;
595#endif  // NVRAM_WIPE_STORAGE_SUPPORT
596}
597
598nvram_result_t NvramManager::SpaceRecord::CheckWriteAccess(
599    const Blob& authorization_value) {
600  if (persistent.HasControl(NV_CONTROL_PERSISTENT_WRITE_LOCK)) {
601    if (persistent.HasFlag(NvramSpace::kFlagWriteLocked)) {
602      NVRAM_LOG_INFO("Attempt to write persistently locked space 0x%" PRIx32
603                     ".",
604                     transient->index);
605      return NV_RESULT_OPERATION_DISABLED;
606    }
607  } else if (persistent.HasControl(NV_CONTROL_BOOT_WRITE_LOCK)) {
608    if (transient->write_locked) {
609      NVRAM_LOG_INFO("Attempt to write per-boot locked space 0x%" PRIx32 ".",
610                     transient->index);
611      return NV_RESULT_OPERATION_DISABLED;
612    }
613  }
614
615  if (persistent.HasControl(NV_CONTROL_WRITE_AUTHORIZATION) &&
616      !ConstantTimeEquals(persistent.authorization_value,
617                          authorization_value)) {
618    NVRAM_LOG_INFO(
619        "Authorization value mismatch for write access to space 0x%" PRIx32 ".",
620        transient->index);
621    return NV_RESULT_ACCESS_DENIED;
622  }
623
624  // All checks passed, allow the write.
625  return NV_RESULT_SUCCESS;
626}
627
628nvram_result_t NvramManager::SpaceRecord::CheckReadAccess(
629    const Blob& authorization_value) {
630  if (persistent.HasControl(NV_CONTROL_BOOT_READ_LOCK)) {
631    if (transient->read_locked) {
632      NVRAM_LOG_INFO("Attempt to read per-boot locked space 0x%" PRIx32 ".",
633                     transient->index);
634      return NV_RESULT_OPERATION_DISABLED;
635    }
636  }
637
638  if (persistent.HasControl(NV_CONTROL_READ_AUTHORIZATION) &&
639      !ConstantTimeEquals(persistent.authorization_value,
640                          authorization_value)) {
641    NVRAM_LOG_INFO(
642        "Authorization value mismatch for read access to space 0x%" PRIx32 ".",
643        transient->index);
644    return NV_RESULT_ACCESS_DENIED;
645  }
646
647  // All checks passed, allow the read.
648  return NV_RESULT_SUCCESS;
649}
650
651bool NvramManager::Initialize() {
652  if (initialized_)
653    return true;
654
655  NvramHeader header;
656  switch (SanitizeStorageStatus(persistence::LoadHeader(&header))) {
657    case storage::Status::kStorageError:
658      NVRAM_LOG_ERR("Init failed to load header.");
659      return false;
660    case storage::Status::kNotFound:
661      // No header in storage. This happens the very first time we initialize
662      // on a fresh device where the header isn't present yet. The first write
663      // will flush the fresh header to storage.
664      initialized_ = true;
665      return true;
666    case storage::Status::kSuccess:
667      if (header.version > NvramHeader::kVersion) {
668        NVRAM_LOG_ERR("Storage format %" PRIu32 " is more recent than %" PRIu32
669                      ", aborting.",
670                      header.version, NvramHeader::kVersion);
671        return false;
672      }
673      break;
674  }
675
676  // Check the state of the provisional space if applicable.
677  const Optional<uint32_t>& provisional_index = header.provisional_index;
678  bool provisional_space_in_storage = false;
679  if (provisional_index.valid()) {
680    NvramSpace space;
681    switch (SanitizeStorageStatus(
682        persistence::LoadSpace(provisional_index.value(), &space))) {
683      case storage::Status::kStorageError:
684        // Log an error but leave the space marked as allocated. This will allow
685        // initialization to complete, so other spaces can be accessed.
686        // Operations on the bad space will fail however. The choice of keeping
687        // the bad space around (as opposed to dropping it) is intentional:
688        //  * Failing noisily reduces the chances of bugs going undetected.
689        //  * Keeping the index allocated prevents it from being accidentally
690        //    clobbered due to appearing absent after transient storage errors.
691        NVRAM_LOG_ERR("Failed to load provisional space 0x%" PRIx32 ".",
692                      provisional_index.value());
693        provisional_space_in_storage = true;
694        break;
695      case storage::Status::kNotFound:
696        break;
697      case storage::Status::kSuccess:
698        provisional_space_in_storage = true;
699        break;
700    }
701  }
702
703  // If there are more spaces allocated than this build supports, fail
704  // initialization. This may seem a bit drastic, but the alternatives aren't
705  // acceptable:
706  //  * If we continued with just a subset of the spaces, that may lead to wrong
707  //    conclusions about the system state in consumers. Furthermore, consumers
708  //    might delete a space to make room and then create a space that appears
709  //    free but is present in storage. This would clobber the existing space
710  //    data and potentially violate its access control rules.
711  //  * We could just try to allocate more memory to hold the larger number of
712  //    spaces. That'd render the memory footprint of the NVRAM implementation
713  //    unpredictable. One variation that may work is to allow a maximum number
714  //    of existing spaces larger than kMaxSpaces, but still within sane limits.
715  if (header.allocated_indices.size() > kMaxSpaces) {
716    NVRAM_LOG_ERR("Excess spaces %zu in header.",
717                  header.allocated_indices.size());
718    return false;
719  }
720
721  // Initialize the transient space bookkeeping data.
722  bool delete_provisional_space = provisional_index.valid();
723  for (uint32_t index : header.allocated_indices) {
724    if (provisional_index.valid() && provisional_index.value() == index) {
725      // The provisional space index refers to a created space. If it isn't
726      // valid, pretend it was never created.
727      if (!provisional_space_in_storage) {
728        continue;
729      }
730
731      // The provisional space index corresponds to a created space that is
732      // present in storage. Retain the space.
733      delete_provisional_space = false;
734    }
735
736    spaces_[num_spaces_].index = index;
737    spaces_[num_spaces_].write_locked = false;
738    spaces_[num_spaces_].read_locked = false;
739    ++num_spaces_;
740  }
741
742  // If the provisional space data is present in storage, but the index wasn't
743  // in |header.allocated_indices|, it refers to half-deleted space. Destroy the
744  // space in that case.
745  if (delete_provisional_space) {
746    switch (SanitizeStorageStatus(
747        persistence::DeleteSpace(provisional_index.value()))) {
748      case storage::Status::kStorageError:
749        NVRAM_LOG_ERR("Failed to delete provisional space 0x%" PRIx32 " data.",
750                      provisional_index.value());
751        return false;
752      case storage::Status::kNotFound:
753        // The space isn't present in storage. This may happen if the space
754        // deletion succeeded, but the header wasn't written subsequently.
755        break;
756      case storage::Status::kSuccess:
757        break;
758    }
759  }
760
761  disable_create_ = header.HasFlag(NvramHeader::kFlagDisableCreate);
762  initialized_ = true;
763
764  // Write the header to clear the provisional index if necessary. It's actually
765  // not a problem if this fails, because the state is consistent regardless. We
766  // still do this opportunistically in order to avoid loading the provisional
767  // space data for each reboot after a crash.
768  if (provisional_index.valid()) {
769    WriteHeader(Optional<uint32_t>());
770  }
771
772  return true;
773}
774
775size_t NvramManager::FindSpace(uint32_t space_index) {
776  for (size_t i = 0; i < num_spaces_; ++i) {
777    if (spaces_[i].index == space_index) {
778      return i;
779    }
780  }
781
782  return kMaxSpaces;
783}
784
785bool NvramManager::LoadSpaceRecord(uint32_t index,
786                                   SpaceRecord* space_record,
787                                   nvram_result_t* result) {
788  space_record->array_index = FindSpace(index);
789  if (space_record->array_index == kMaxSpaces) {
790    *result = NV_RESULT_SPACE_DOES_NOT_EXIST;
791    return false;
792  }
793
794  space_record->transient = &spaces_[space_record->array_index];
795
796  switch (SanitizeStorageStatus(
797      persistence::LoadSpace(index, &space_record->persistent))) {
798    case storage::Status::kStorageError:
799      NVRAM_LOG_ERR("Failed to load space 0x%" PRIx32 " data.", index);
800      *result = NV_RESULT_INTERNAL_ERROR;
801      return false;
802    case storage::Status::kNotFound:
803      // This should never happen if the header contains the index.
804      NVRAM_LOG_ERR("Space index 0x%" PRIx32
805                    " present in header, but data missing.",
806                    index);
807      *result = NV_RESULT_INTERNAL_ERROR;
808      return false;
809    case storage::Status::kSuccess:
810      *result = NV_RESULT_SUCCESS;
811      return true;
812  }
813
814  *result = NV_RESULT_INTERNAL_ERROR;
815  return false;
816}
817
818nvram_result_t NvramManager::WriteHeader(Optional<uint32_t> provisional_index) {
819  NvramHeader header;
820  header.version = NvramHeader::kVersion;
821  if (disable_create_) {
822    header.SetFlag(NvramHeader::kFlagDisableCreate);
823  }
824
825  if (!header.allocated_indices.Resize(num_spaces_)) {
826    NVRAM_LOG_ERR("Allocation failure.");
827    return NV_RESULT_INTERNAL_ERROR;
828  }
829  for (size_t i = 0; i < num_spaces_; ++i) {
830    header.allocated_indices[i] = spaces_[i].index;
831  }
832
833  header.provisional_index = provisional_index;
834
835  if (SanitizeStorageStatus(persistence::StoreHeader(header)) !=
836      storage::Status::kSuccess) {
837    NVRAM_LOG_ERR("Failed to store header.");
838    return NV_RESULT_INTERNAL_ERROR;
839  }
840
841  return NV_RESULT_SUCCESS;
842}
843
844nvram_result_t NvramManager::WriteSpace(uint32_t index,
845                                        const NvramSpace& space) {
846  if (SanitizeStorageStatus(persistence::StoreSpace(index, space)) !=
847      storage::Status::kSuccess) {
848    NVRAM_LOG_ERR("Failed to store space 0x%" PRIx32 ".", index);
849    return NV_RESULT_INTERNAL_ERROR;
850  }
851
852  return NV_RESULT_SUCCESS;
853}
854
855}  // namespace nvram
856