// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/win/sid.h"

#include <windows.h>

#include <sddl.h>
#include <stdlib.h>

#include <iterator>

#include "base/check.h"
#include "base/notreached.h"
#include "base/rand_util.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_localalloc.h"
#include "base/win/windows_version.h"

namespace base {
namespace win {

namespace {

absl::optional<DWORD> WellKnownCapabilityToRid(WellKnownCapability capability) {
  switch (capability) {
    case WellKnownCapability::kInternetClient:
      return SECURITY_CAPABILITY_INTERNET_CLIENT;
    case WellKnownCapability::kInternetClientServer:
      return SECURITY_CAPABILITY_INTERNET_CLIENT_SERVER;
    case WellKnownCapability::kPrivateNetworkClientServer:
      return SECURITY_CAPABILITY_PRIVATE_NETWORK_CLIENT_SERVER;
    case WellKnownCapability::kPicturesLibrary:
      return SECURITY_CAPABILITY_PICTURES_LIBRARY;
    case WellKnownCapability::kVideosLibrary:
      return SECURITY_CAPABILITY_VIDEOS_LIBRARY;
    case WellKnownCapability::kMusicLibrary:
      return SECURITY_CAPABILITY_MUSIC_LIBRARY;
    case WellKnownCapability::kDocumentsLibrary:
      return SECURITY_CAPABILITY_DOCUMENTS_LIBRARY;
    case WellKnownCapability::kEnterpriseAuthentication:
      return SECURITY_CAPABILITY_ENTERPRISE_AUTHENTICATION;
    case WellKnownCapability::kSharedUserCertificates:
      return SECURITY_CAPABILITY_SHARED_USER_CERTIFICATES;
    case WellKnownCapability::kRemovableStorage:
      return SECURITY_CAPABILITY_REMOVABLE_STORAGE;
    case WellKnownCapability::kAppointments:
      return SECURITY_CAPABILITY_APPOINTMENTS;
    case WellKnownCapability::kContacts:
      return SECURITY_CAPABILITY_CONTACTS;
  }
  return absl::nullopt;
}

absl::optional<WELL_KNOWN_SID_TYPE> WellKnownSidToEnum(WellKnownSid sid) {
  switch (sid) {
    case WellKnownSid::kNull:
      return WinNullSid;
    case WellKnownSid::kWorld:
      return WinWorldSid;
    case WellKnownSid::kCreatorOwner:
      return WinCreatorOwnerSid;
    case WellKnownSid::kNetwork:
      return WinNetworkSid;
    case WellKnownSid::kBatch:
      return WinBatchSid;
    case WellKnownSid::kInteractive:
      return WinInteractiveSid;
    case WellKnownSid::kService:
      return WinServiceSid;
    case WellKnownSid::kAnonymous:
      return WinAnonymousSid;
    case WellKnownSid::kSelf:
      return WinSelfSid;
    case WellKnownSid::kAuthenticatedUser:
      return WinAuthenticatedUserSid;
    case WellKnownSid::kRestricted:
      return WinRestrictedCodeSid;
    case WellKnownSid::kLocalSystem:
      return WinLocalSystemSid;
    case WellKnownSid::kLocalService:
      return WinLocalServiceSid;
    case WellKnownSid::kNetworkService:
      return WinNetworkServiceSid;
    case WellKnownSid::kBuiltinAdministrators:
      return WinBuiltinAdministratorsSid;
    case WellKnownSid::kBuiltinUsers:
      return WinBuiltinUsersSid;
    case WellKnownSid::kBuiltinGuests:
      return WinBuiltinGuestsSid;
    case WellKnownSid::kUntrustedLabel:
      return WinUntrustedLabelSid;
    case WellKnownSid::kLowLabel:
      return WinLowLabelSid;
    case WellKnownSid::kMediumLabel:
      return WinMediumLabelSid;
    case WellKnownSid::kHighLabel:
      return WinHighLabelSid;
    case WellKnownSid::kSystemLabel:
      return WinSystemLabelSid;
    case WellKnownSid::kWriteRestricted:
      return WinWriteRestrictedCodeSid;
    case WellKnownSid::kCreatorOwnerRights:
      return WinCreatorOwnerRightsSid;
    case WellKnownSid::kAllApplicationPackages:
      return WinBuiltinAnyPackageSid;
    case WellKnownSid::kAllRestrictedApplicationPackages:
      // This should be handled by FromKnownSid.
      NOTREACHED();
      break;
  }
  return absl::nullopt;
}

absl::optional<Sid> FromSubAuthorities(
    PSID_IDENTIFIER_AUTHORITY identifier_authority,
    BYTE sub_authority_count,
    PDWORD sub_authorities) {
  BYTE sid[SECURITY_MAX_SID_SIZE];
  if (!::InitializeSid(sid, identifier_authority, sub_authority_count))
    return absl::nullopt;

  for (DWORD index = 0; index < sub_authority_count; ++index) {
    PDWORD sub_authority = ::GetSidSubAuthority(sid, index);
    *sub_authority = sub_authorities[index];
  }
  return Sid::FromPSID(sid);
}

template <typename T>
absl::optional<std::vector<Sid>> FromVector(
    const std::vector<T>& values,
    absl::optional<Sid> (*create_sid)(T)) {
  std::vector<Sid> converted_sids;
  converted_sids.reserve(values.size());
  for (T value : values) {
    auto sid = create_sid(value);
    if (!sid)
      return absl::nullopt;
    converted_sids.push_back(std::move(*sid));
  }
  return converted_sids;
}

}  // namespace

Sid::Sid(const void* sid, size_t length)
    : sid_(static_cast<const char*>(sid),
           static_cast<const char*>(sid) + length) {
  DCHECK(::IsValidSid(GetPSID()));
}

absl::optional<Sid> Sid::FromKnownCapability(WellKnownCapability capability) {
  absl::optional<DWORD> capability_rid = WellKnownCapabilityToRid(capability);
  if (!capability_rid)
    return absl::nullopt;
  SID_IDENTIFIER_AUTHORITY capability_authority = {
      SECURITY_APP_PACKAGE_AUTHORITY};
  DWORD sub_authorities[] = {SECURITY_CAPABILITY_BASE_RID, *capability_rid};
  return FromSubAuthorities(&capability_authority, std::size(sub_authorities),
                            sub_authorities);
}

absl::optional<Sid> Sid::FromNamedCapability(const wchar_t* capability_name) {
  DCHECK_GE(GetVersion(), Version::WIN10);

  if (!capability_name || !*capability_name)
    return absl::nullopt;

  typedef decltype(
      ::DeriveCapabilitySidsFromName)* DeriveCapabilitySidsFromNameFunc;
  static const DeriveCapabilitySidsFromNameFunc derive_capability_sids =
      []() -> DeriveCapabilitySidsFromNameFunc {
    HMODULE module = GetModuleHandle(L"api-ms-win-security-base-l1-2-2.dll");
    if (!module)
      return nullptr;

    return reinterpret_cast<DeriveCapabilitySidsFromNameFunc>(
        ::GetProcAddress(module, "DeriveCapabilitySidsFromName"));
  }();
  if (!derive_capability_sids)
    return absl::nullopt;

  // Pre-reserve some space for SID deleters.
  std::vector<ScopedLocalAlloc> deleter_list;
  deleter_list.reserve(16);

  PSID* capability_groups = nullptr;
  DWORD capability_group_count = 0;
  PSID* capability_sids = nullptr;
  DWORD capability_sid_count = 0;

  if (!derive_capability_sids(capability_name, &capability_groups,
                              &capability_group_count, &capability_sids,
                              &capability_sid_count)) {
    return absl::nullopt;
  }

  deleter_list.emplace_back(capability_groups);
  deleter_list.emplace_back(capability_sids);

  for (DWORD i = 0; i < capability_group_count; ++i) {
    deleter_list.emplace_back(capability_groups[i]);
  }
  for (DWORD i = 0; i < capability_sid_count; ++i) {
    deleter_list.emplace_back(capability_sids[i]);
  }

  if (capability_sid_count < 1)
    return absl::nullopt;

  return FromPSID(capability_sids[0]);
}

absl::optional<Sid> Sid::FromKnownSid(WellKnownSid type) {
  if (type == WellKnownSid::kAllRestrictedApplicationPackages) {
    SID_IDENTIFIER_AUTHORITY package_authority = {
        SECURITY_APP_PACKAGE_AUTHORITY};
    DWORD sub_authorities[] = {SECURITY_APP_PACKAGE_BASE_RID,
                               SECURITY_BUILTIN_PACKAGE_ANY_RESTRICTED_PACKAGE};
    return FromSubAuthorities(&package_authority, 2, sub_authorities);
  }

  BYTE sid[SECURITY_MAX_SID_SIZE];
  DWORD size_sid = SECURITY_MAX_SID_SIZE;
  absl::optional<WELL_KNOWN_SID_TYPE> known_sid = WellKnownSidToEnum(type);
  if (!known_sid)
    return absl::nullopt;
  if (!::CreateWellKnownSid(*known_sid, nullptr, sid, &size_sid))
    return absl::nullopt;

  return Sid(sid, size_sid);
}

absl::optional<Sid> Sid::FromSddlString(const wchar_t* sddl_sid) {
  PSID psid = nullptr;
  if (!::ConvertStringSidToSid(sddl_sid, &psid))
    return absl::nullopt;
  return FromPSID(TakeLocalAlloc(psid).get());
}

absl::optional<Sid> Sid::FromPSID(PSID sid) {
  DCHECK(sid);
  if (!sid || !::IsValidSid(sid))
    return absl::nullopt;
  return Sid(sid, ::GetLengthSid(sid));
}

absl::optional<Sid> Sid::GenerateRandomSid() {
  SID_IDENTIFIER_AUTHORITY package_authority = {SECURITY_NULL_SID_AUTHORITY};
  DWORD sub_authorities[4] = {};
  RandBytes(&sub_authorities, sizeof(sub_authorities));
  return FromSubAuthorities(&package_authority, _countof(sub_authorities),
                            sub_authorities);
}

absl::optional<Sid> Sid::FromIntegrityLevel(DWORD integrity_level) {
  SID_IDENTIFIER_AUTHORITY package_authority = {
      SECURITY_MANDATORY_LABEL_AUTHORITY};
  return FromSubAuthorities(&package_authority, 1, &integrity_level);
}

absl::optional<std::vector<Sid>> Sid::FromSddlStringVector(
    const std::vector<const wchar_t*>& sddl_sids) {
  return FromVector(sddl_sids, Sid::FromSddlString);
}

absl::optional<std::vector<Sid>> Sid::FromNamedCapabilityVector(
    const std::vector<const wchar_t*>& capability_names) {
  return FromVector(capability_names, Sid::FromNamedCapability);
}

absl::optional<std::vector<Sid>> Sid::FromKnownCapabilityVector(
    const std::vector<WellKnownCapability>& capabilities) {
  return FromVector(capabilities, Sid::FromKnownCapability);
}

absl::optional<std::vector<Sid>> Sid::FromKnownSidVector(
    const std::vector<WellKnownSid>& sids) {
  return FromVector(sids, Sid::FromKnownSid);
}

Sid::Sid(Sid&& sid) = default;
Sid& Sid::operator=(Sid&&) = default;
Sid::~Sid() = default;

PSID Sid::GetPSID() const {
  DCHECK(!sid_.empty());
  return const_cast<char*>(sid_.data());
}

// Converts the SID to an SDDL format string.
absl::optional<std::wstring> Sid::ToSddlString() const {
  LPWSTR sid = nullptr;
  if (!::ConvertSidToStringSid(GetPSID(), &sid))
    return absl::nullopt;
  return TakeLocalAlloc(sid).get();
}

Sid Sid::Clone() const {
  return Sid(sid_.data(), sid_.size());
}

bool Sid::Equal(PSID sid) const {
  return !!::EqualSid(GetPSID(), sid);
}

bool Sid::operator==(const Sid& sid) const {
  return Equal(sid.GetPSID());
}

bool Sid::operator!=(const Sid& sid) const {
  return !(operator==(sid));
}

}  // namespace win
}  // namespace base
