d013d2fd5c3971e75ee667ccf82f90946d2096c7
[platform/core/security/ode.git] / server / engine / encryption / cryptsetup-engine.cpp
1 /*
2  *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
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 "cryptsetup-engine.h"
18 #include "../../file-footer.h"
19
20 #include <cctype>
21 #include <algorithm>
22
23 #include <libcryptsetup.h>
24
25 #include <klay/exception.h>
26 #include <klay/audit/logger.h>
27
28 namespace ode {
29
30 namespace {
31
32 const char* DEFAULT_LUKS_CIPHER_NAME = "aes";
33 const char* DEFAULT_LUKS_CIPHER_MODE = "xts-plain64";
34 const char* DEFAULT_LUKS_HASH = "sha1";
35 const size_t DEFAULT_LUKS_ALIGNMENT = 0;
36
37 #ifndef NDEBUG
38 // log callback
39 void log(int level, const char *msg, void*)
40 {
41         std::string msgStr("[NULL]");
42         if (msg) {
43                 msgStr = msg;
44
45                 // trim it
46                 auto it = find_if_not(msgStr.rbegin(),
47                                                           msgStr.rend(),
48                                                           [](char c){ return isspace(c); }).base();
49                 msgStr.erase(it, msgStr.end());
50         }
51
52         switch (level)
53         {
54         case CRYPT_LOG_ERROR:
55                 ERROR("[libcryptsetup] " + msgStr);
56                 break;
57         case CRYPT_LOG_VERBOSE:
58                 INFO("[libcryptsetup] " + msgStr);
59                 break;
60         case CRYPT_LOG_DEBUG:
61         case CRYPT_LOG_NORMAL:
62                 DEBUG("[libcryptsetup] " + msgStr);
63                 break;
64         default:
65                 ERROR("[libcryptsetup] Unsupported log level. Msg: " + msgStr);
66         }
67 }
68 #endif
69
70 class Device
71 {
72 public:
73         enum class InitMethod
74         {
75                 BY_DEV_PATH, // requires device path as first ctor argument
76                 BY_MAP_NAME, // requires mapping name as first ctor argument
77         };
78
79         explicit Device(const std::string &str, InitMethod method = InitMethod::BY_DEV_PATH)
80         {
81                 int ret;
82                 if (str.empty())
83                         throw runtime::Exception("Empty argument");
84
85                 switch (method) {
86                 case InitMethod::BY_DEV_PATH:
87                         ret = crypt_init(&device, str.c_str());
88                         if (ret != 0)
89                                 throw runtime::Exception("crypt_init() failed for " + str + ": "
90                                                                                  + std::to_string(ret));
91                         break;
92                 case InitMethod::BY_MAP_NAME:
93                         ret = crypt_init_by_name(&device, str.c_str());
94                         if (ret != 0)
95                                 throw runtime::Exception("crypt_init_by_name() failed for " +
96                                                                                  str + ": " + std::to_string(ret));
97                         break;
98                 default:
99                         throw runtime::Exception("Unsupported init method " +
100                                                                          std::to_string(static_cast<int>(method)));
101                 }
102
103 #ifndef NDEBUG
104                 /*
105                  * This option causes debug logs to be printed to STDOUT. TODO execute it
106                  * once for all engines?
107                  */
108                 crypt_set_debug_level(CRYPT_DEBUG_ALL);
109                 crypt_set_log_callback(device, &log, NULL);
110 #endif
111
112                 // Needs root
113                 if (1 != crypt_memory_lock(device, 1))
114                         throw runtime::Exception("Failed to lock memory");
115         }
116
117         Device(const Device&) = delete;
118         Device& operator=(const Device&) = delete;
119
120         ~Device()
121         {
122                 if (0 != crypt_memory_lock(device, 0))
123                 ERROR("Failed to unlock memory");
124
125                 crypt_free(device);
126         }
127
128         crypt_device* get() noexcept { return device; }
129
130 private:
131         crypt_device *device;
132 };
133
134 std::string mappingPath(const std::string& name)
135 {
136         static std::string mappings_dir = std::string(crypt_get_dir()) + '/';
137         return mappings_dir + name;
138 }
139
140 } // anonymous namespace
141
142
143 CryptsetupEngine::CryptsetupEngine(const std::string &devicePath) :
144         devPath(devicePath)
145 {
146 }
147
148 CryptsetupEngine::~CryptsetupEngine()
149 {
150 }
151
152 void CryptsetupEngine::format(DeviceType type, const data &key)
153 {
154         switch (type) {
155         case DeviceType::LUKS:
156                 {
157                         Device d(devPath);
158
159                         // TODO support other formats? support other ciphers?
160                         crypt_params_luks1 params = {
161                                         .hash = DEFAULT_LUKS_HASH,
162                                         .data_alignment = DEFAULT_LUKS_ALIGNMENT,
163                                         .data_device = NULL,
164                         };
165
166                         int ret = crypt_format(d.get(),
167                                                                    CRYPT_LUKS1,
168                                                                    DEFAULT_LUKS_CIPHER_NAME,
169                                                                    DEFAULT_LUKS_CIPHER_MODE,
170                                                                    NULL,        // NULL uid
171                                                                    reinterpret_cast<const char*>(key.data()),
172                                                                    key.size(),
173                                                                    &params);
174                         if (ret != 0)
175                                 throw runtime::Exception("crypt_format() failed: " + std::to_string(ret));
176                         break;
177                 }
178         case DeviceType::PLAIN:
179                 // TODO
180         default:
181                 throw runtime::Exception("Unsupported device type " +
182                                                                  std::to_string(static_cast<int>(type)));
183         }
184 }
185
186 std::string CryptsetupEngine::open(DeviceType type, const std::string &name, const data &key)
187 {
188         // create new mapping
189         switch (type) {
190         case DeviceType::LUKS:
191         {
192                 Device d(devPath);
193                 int ret = crypt_load(d.get(), CRYPT_LUKS1, NULL);
194                 if (ret != 0)
195                         throw runtime::Exception("crypt_load() failed: " + std::to_string(ret));
196
197                 // TODO possible support for activation flags
198                 ret = crypt_activate_by_volume_key(d.get(),
199                                                                                    name.c_str(),
200                                                                                    reinterpret_cast<const char*>(key.data()),
201                                                                                    key.size(),
202                                                                                    0);
203                 if (ret != 0)
204                         throw runtime::Exception("crypt_activate_by_volume_key() failed: " +
205                                                                          std::to_string(ret));
206                 break;
207         }
208
209         case DeviceType::PLAIN:
210                 // TODO
211         default:
212                 throw runtime::Exception("Unsupported device type " +
213                                                                  std::to_string(static_cast<int>(type)));
214         }
215         return mappingPath(name);
216 }
217
218 void CryptsetupEngine::close(const std::string &name)
219 {
220         Device d(name, Device::InitMethod::BY_MAP_NAME);
221
222         int ret = crypt_deactivate(d.get(), name.c_str());
223         if (ret != 0)
224                 throw runtime::Exception("crypt_deactivate() failed for " + name +
225                                                                  ": " + std::to_string(ret));
226 }
227
228 bool CryptsetupEngine::isKeyMetaSet()
229 {
230         return FileFooter::exist(devPath);
231 }
232
233 const CryptsetupEngine::data CryptsetupEngine::getKeyMeta()
234 {
235         return FileFooter::read(devPath);
236 }
237
238 void CryptsetupEngine::setKeyMeta(const data &meta)
239 {
240         FileFooter::write(devPath, meta);
241 }
242
243 void CryptsetupEngine::clearKeyMeta()
244 {
245         FileFooter::clear(devPath);
246 }
247
248 } // namespace ode