yast/yast-storage-ng

View on GitHub
src/lib/y2storage/crypttab.rb

Summary

Maintainability
A
0 mins
Test Coverage
# Copyright (c) [2018-2019] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "yast"
require "storage"
require "y2storage/simple_etc_crypttab_entry"
require "y2storage/encryption_method"

module Y2Storage
  # Class to represent a crypttab file
  class Crypttab
    include Yast::Logger

    CRYPTTAB_PATH = "/etc/crypttab"
    private_constant :CRYPTTAB_PATH

    # @return [Filesystems::Base] filesystem to which the crypttab file belongs to
    attr_reader :filesystem

    # @return [Array<SimpleEtcCrypttabEntry>]
    attr_reader :entries

    # Constructor
    #
    # @param path [String] path to crypttab file
    # @param filesystem [Filesystems::Base]
    def initialize(path = CRYPTTAB_PATH, filesystem = nil)
      @path = path
      @filesystem = filesystem
      @entries = read_entries
    end

    # Saves encryption names indicated in the crypttab file
    #
    # For each entry in the crypttab file, it finds the corresponding device and updates
    # its crypttab name with the value indicated in its crypttab entry. The device is
    # not modified at all if it is not encrypted.
    #
    # @param devicegraph [Devicegraph]
    def save_encryption_names(devicegraph)
      entries.each { |e| save_encryption_name(devicegraph, e) }
    end

    private

    # @return [String] crypttab file path
    attr_reader :path

    # Reads a crypttab file and returns its entries
    #
    # @return [Array<SimpleEtcCrypttabEntry>]
    def read_entries
      entries = Storage.read_simple_etc_crypttab(path)
      entries.map { |e| SimpleEtcCrypttabEntry.new(e) }
    rescue Storage::Exception
      log.error("Not possible to read the crypttab file: #{path}")
      []
    end

    # Saves the crypttab name according to the value indicated in a crypttab entry
    #
    # Generally, when a crypttab entry points to a luks device, the encryption layer is probed. But in
    # case of plain encrypted devices, the encryption layer could not exist in the probed devicegraph.
    # Note that no headers are written into the device when using plain encryption. And, for this
    # reason, plain encryption devices are only probed for the root filesystem by parsing its crypttab.
    #
    # Due to plain encryption devices could be not probed, this method creates the plain encryption
    # layer to be able to save the encryption name indicated in the crypttab entry.
    #
    # @param devicegraph [Devicegraph]
    # @param entry [SimpleEtcCrypttabEntry]
    def save_encryption_name(devicegraph, entry)
      device = entry.find_device(devicegraph)

      return unless device

      if create_swap_encryption_for_entry?(device, entry)
        device.remove_descendants
        device.encrypt(dm_name: entry.name, method: encryption_method_for_entry(entry))
      elsif device.encrypted?
        device.encryption.crypttab_name = entry.name
      end
    end

    # Whether the encryption layer should be created for a swap device according to the given crypttab
    # entry
    #
    # @param device [BlkDevice]
    # @param entry [SimpleEtcCrypttabEntry]
    #
    # @return [Boolean]
    def create_swap_encryption_for_entry?(device, entry)
      encryption_method = encryption_method_for_entry(entry)

      return false unless detectable_by_crypttab?(encryption_method)

      encrypt_with_encryption_method?(device, encryption_method)
    end

    # Whether the given encryption method corresponds to an encryption method that might be recoginzed by
    # reading the crypttab file (it could be not probed). This could happens when using plain encryption
    # technology.
    #
    # @param encryption_method [EncryptionMethod, nil]
    #
    # @return [Boolean]
    def detectable_by_crypttab?(encryption_method)
      return false unless encryption_method

      encryption_method.only_for_swap?
    end

    # Whether the given encryption method can be used to encrypt the given device
    #
    # The encryption method can be used when the device is not encrypted yet with the given encryption
    # method and the encryption method is currently available.
    #
    # @param device [BlkDevice]
    # @param encryption_method [EncryptionMethod]
    #
    # @return [Boolean]
    def encrypt_with_encryption_method?(device, encryption_method)
      return false unless encryption_method.available?

      !device.encrypted? || !device.encryption.method.is?(encryption_method)
    end

    # Encryption method used for the given crypttab entry
    #
    # @param entry [SimpleEtcCrypttabEntry]
    # @return [EncryptionMethod, nil] nil if encryption method cannot be inferred
    def encryption_method_for_entry(entry)
      EncryptionMethod.for_crypttab(entry)
    end
  end
end