blob: fc556e1e38672dadf34944b9b7b0f34c207953e1 [file] [log] [blame]
Lorenz Brunf95909d2019-09-11 19:48:26 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package storage
18
19import (
20 "encoding/binary"
21 "encoding/hex"
22 "fmt"
23 "os"
Lorenz Brunf95909d2019-09-11 19:48:26 +020024 "syscall"
25
Hendrik Hofstadt8efe51e2020-02-28 12:53:41 +010026 "git.monogon.dev/source/nexantic.git/core/pkg/devicemapper"
27
Lorenz Brunf95909d2019-09-11 19:48:26 +020028 "golang.org/x/sys/unix"
29)
30
31func readDataSectors(path string) (uint64, error) {
32 integrityPartition, err := os.Open(path)
33 if err != nil {
34 return 0, err
35 }
36 defer integrityPartition.Close()
37 // Based on structure defined in
38 // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/md/dm-integrity.c#n59
39 if _, err := integrityPartition.Seek(16, 0); err != nil {
40 return 0, err
41 }
42 var providedDataSectors uint64
43 if err := binary.Read(integrityPartition, binary.LittleEndian, &providedDataSectors); err != nil {
44 return 0, err
45 }
46 return providedDataSectors, nil
47}
48
49// MapEncryptedBlockDevice maps an encrypted device (node) at baseName to a
50// decrypted device at /dev/$name using the given encryptionKey
51func MapEncryptedBlockDevice(name string, baseName string, encryptionKey []byte) error {
52 integritySectors, err := readDataSectors(baseName)
53 if err != nil {
54 return fmt.Errorf("failed to read the number of usable sectors on the integrity device: %w", err)
55 }
56
57 integrityDevName := fmt.Sprintf("/dev/%v-integrity", name)
58 integrityDMName := fmt.Sprintf("%v-integrity", name)
59 integrityDev, err := devicemapper.CreateActiveDevice(integrityDMName, []devicemapper.Target{
60 devicemapper.Target{
61 Length: integritySectors,
62 Type: "integrity",
63 Parameters: fmt.Sprintf("%v 0 28 J 1 journal_sectors:1024", baseName),
64 },
65 })
66 if err != nil {
67 return fmt.Errorf("failed to create Integrity device: %w", err)
68 }
69 if err := unix.Mknod(integrityDevName, 0600|unix.S_IFBLK, int(integrityDev)); err != nil {
70 unix.Unlink(integrityDevName)
71 devicemapper.RemoveDevice(integrityDMName)
72 return fmt.Errorf("failed to create integrity device node: %w", err)
73 }
74
75 cryptDevName := fmt.Sprintf("/dev/%v", name)
76 cryptDev, err := devicemapper.CreateActiveDevice(name, []devicemapper.Target{
77 devicemapper.Target{
78 Length: integritySectors,
79 Type: "crypt",
80 Parameters: fmt.Sprintf("capi:gcm(aes)-random %v 0 %v 0 1 integrity:28:aead", hex.EncodeToString(encryptionKey), integrityDevName),
81 },
82 })
83 if err != nil {
84 unix.Unlink(integrityDevName)
85 devicemapper.RemoveDevice(integrityDMName)
86 return fmt.Errorf("failed to create crypt device: %w", err)
87 }
88 if err := unix.Mknod(cryptDevName, 0600|unix.S_IFBLK, int(cryptDev)); err != nil {
89 unix.Unlink(cryptDevName)
90 devicemapper.RemoveDevice(name)
91
92 unix.Unlink(integrityDevName)
93 devicemapper.RemoveDevice(integrityDMName)
94 return fmt.Errorf("failed to create crypt device node: %w", err)
95 }
96 return nil
97}
98
99// InitializeEncryptedBlockDevice initializes a new encrypted block device. This can take a long
100// time since all bytes on the mapped block device need to be zeroed.
101func InitializeEncryptedBlockDevice(name, baseName string, encryptionKey []byte) error {
102 integrityPartition, err := os.OpenFile(baseName, os.O_WRONLY, 0)
103 if err != nil {
104 return err
105 }
106 defer integrityPartition.Close()
Lorenz Brun52f7f292020-06-24 16:42:02 +0200107 zeroed512BBuf := make([]byte, 4096*128)
Lorenz Brunf95909d2019-09-11 19:48:26 +0200108 if _, err := integrityPartition.Write(zeroed512BBuf); err != nil {
109 return fmt.Errorf("failed to wipe header: %w", err)
110 }
111 integrityPartition.Close()
112
113 integrityDMName := fmt.Sprintf("%v-integrity", name)
114 _, err = devicemapper.CreateActiveDevice(integrityDMName, []devicemapper.Target{
115 devicemapper.Target{
116 Length: 1,
117 Type: "integrity",
118 Parameters: fmt.Sprintf("%v 0 28 J 1 journal_sectors:1024", baseName),
119 },
120 })
121 if err != nil {
122 return fmt.Errorf("failed to create discovery integrity device: %w", err)
123 }
124 if err := devicemapper.RemoveDevice(integrityDMName); err != nil {
125 return fmt.Errorf("failed to remove discovery integrity device: %w", err)
126 }
127
128 if err := MapEncryptedBlockDevice(name, baseName, encryptionKey); err != nil {
129 return err
130 }
131
132 blkdev, err := os.OpenFile(fmt.Sprintf("/dev/%v", name), unix.O_DIRECT|os.O_WRONLY, 0000)
133 if err != nil {
134 return fmt.Errorf("failed to open new encrypted device for zeroing: %w", err)
135 }
136 defer blkdev.Close()
137 blockSize, err := unix.IoctlGetUint32(int(blkdev.Fd()), unix.BLKSSZGET)
138 zeroedBuf := make([]byte, blockSize*100) // Make it faster
139 for {
140 _, err := blkdev.Write(zeroedBuf)
141 if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOSPC {
142 break
143 }
144 if err != nil {
145 return fmt.Errorf("failed to zero-initalize new encrypted device: %w", err)
146 }
147 }
148 return nil
149}