1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2011-2012 Planets Communications B.V.
5 Copyright (C) 2013-2020 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * crypto_cache.c Encryption key caching functions
24 *
25 * Marco van Wieringen, April 2012
26 */
27
28 #include "include/bareos.h"
29 #include "crypto_cache.h"
30 #include "lib/berrno.h"
31 #include "lib/dlist.h"
32
33 static pthread_mutex_t crypto_cache_lock = PTHREAD_MUTEX_INITIALIZER;
34 static dlist* cached_crypto_keys = NULL;
35
36 static s_crypto_cache_hdr crypto_cache_hdr = {"BAREOS Crypto Cache\n", 1, 0};
37
38 /*
39 * Read the content of the crypto cache from the filesystem.
40 */
ReadCryptoCache(const char * cache_file)41 void ReadCryptoCache(const char* cache_file)
42 {
43 int fd, cnt;
44 ssize_t status;
45 bool ok = false;
46 s_crypto_cache_hdr hdr;
47 int hdr_size = sizeof(hdr);
48 crypto_cache_entry_t* cce = NULL;
49
50 if ((fd = open(cache_file, O_RDONLY | O_BINARY)) < 0) {
51 BErrNo be;
52
53 Dmsg2(010, "Could not open crypto cache file. %s ERR=%s\n", cache_file,
54 be.bstrerror());
55 goto bail_out;
56 }
57
58 if ((status = read(fd, &hdr, hdr_size)) != hdr_size) {
59 BErrNo be;
60
61 Dmsg4(010,
62 "Could not read crypto cache file. fd=%d status=%d size=%d: ERR=%s\n",
63 fd, (int)status, hdr_size, be.bstrerror());
64 goto bail_out;
65 }
66
67 if (hdr.version != crypto_cache_hdr.version) {
68 Dmsg2(010, "Crypto cache bad hdr version. Wanted %d got %d\n",
69 crypto_cache_hdr.version, hdr.version);
70 goto bail_out;
71 }
72
73 hdr.id[20] = 0;
74 if (!bstrcmp(hdr.id, crypto_cache_hdr.id)) {
75 Dmsg0(000, "Crypto cache file header id invalid.\n");
76 goto bail_out;
77 }
78
79 if (!cached_crypto_keys) { cached_crypto_keys = new dlist(cce, &cce->link); }
80
81 /*
82 * Read as many crypto cache entries as available.
83 */
84 cnt = 0;
85 cce = (crypto_cache_entry_t*)malloc(sizeof(crypto_cache_entry_t));
86 while (read(fd, cce, sizeof(crypto_cache_entry_t))
87 == sizeof(crypto_cache_entry_t)) {
88 cnt++;
89 cached_crypto_keys->append(cce);
90 cce = (crypto_cache_entry_t*)malloc(sizeof(crypto_cache_entry_t));
91 }
92
93 /*
94 * We always allocate a dangling crypto_cache_entry_t structure in
95 * the way that we malloc before the loop and in the loop. So drop
96 * the last unused entry.
97 */
98 free(cce);
99
100 /*
101 * Check if we read the number of entries the header said are in the file.
102 */
103 if (cnt == hdr.nr_entries) {
104 ok = true;
105 Dmsg2(010, "Crypto cache read %d entries in file %s\n", cnt, cache_file);
106 } else {
107 Dmsg3(
108 000,
109 "Crypto cache read %d entries while %d entries should be in file %s\n",
110 cnt, hdr.nr_entries, cache_file);
111 }
112
113 bail_out:
114 if (fd >= 0) { close(fd); }
115
116 if (!ok) {
117 SecureErase(NULL, cache_file);
118 if (cached_crypto_keys) {
119 cached_crypto_keys->destroy();
120 delete cached_crypto_keys;
121 cached_crypto_keys = NULL;
122 }
123 }
124 }
125
ReadCryptoCache(const char * dir,const char * progname,int port)126 void ReadCryptoCache(const char* dir, const char* progname, int port)
127 {
128 POOLMEM* fname = GetPoolMemory(PM_FNAME);
129
130 Mmsg(fname, "%s/%s.%d.cryptoc", dir, progname, port);
131 ReadCryptoCache(fname);
132 FreePoolMemory(fname);
133 }
134
135 /*
136 * Write the content of the crypto cache to the filesystem.
137 */
WriteCryptoCache(const char * cache_file)138 void WriteCryptoCache(const char* cache_file)
139 {
140 int fd;
141 bool ok = false;
142 crypto_cache_entry_t* cce;
143
144 if (!cached_crypto_keys) { return; }
145
146 /*
147 * Lock the cache.
148 */
149 P(crypto_cache_lock);
150
151 SecureErase(NULL, cache_file);
152 if ((fd = open(cache_file, O_CREAT | O_WRONLY | O_BINARY, 0640)) < 0) {
153 BErrNo be;
154
155 Emsg2(M_ERROR, 0, _("Could not create crypto cache file. %s ERR=%s\n"),
156 cache_file, be.bstrerror());
157 goto bail_out;
158 }
159
160 crypto_cache_hdr.nr_entries = cached_crypto_keys->size();
161 if (write(fd, &crypto_cache_hdr, sizeof(crypto_cache_hdr))
162 != sizeof(crypto_cache_hdr)) {
163 BErrNo be;
164
165 Dmsg1(000, "Write hdr error: ERR=%s\n", be.bstrerror());
166 goto bail_out;
167 }
168
169 foreach_dlist (cce, cached_crypto_keys) {
170 if (write(fd, cce, sizeof(crypto_cache_entry_t))
171 != sizeof(crypto_cache_entry_t)) {
172 BErrNo be;
173
174 Dmsg1(000, "Write record error: ERR=%s\n", be.bstrerror());
175 goto bail_out;
176 }
177 }
178
179 ok = true;
180
181 bail_out:
182 if (fd >= 0) { close(fd); }
183
184 if (!ok) { SecureErase(NULL, cache_file); }
185
186 V(crypto_cache_lock);
187 }
188
WriteCryptoCache(const char * dir,const char * progname,int port)189 void WriteCryptoCache(const char* dir, const char* progname, int port)
190 {
191 POOLMEM* fname = GetPoolMemory(PM_FNAME);
192
193 Mmsg(fname, "%s/%s.%d.cryptoc", dir, progname, port);
194 WriteCryptoCache(fname);
195 FreePoolMemory(fname);
196 }
197
198 /*
199 * Update the internal cache with new data. When the cache gets
200 * modified by a new entry or by expiring old data the return
201 * value gives an indication of that.
202 * Returns: true - cache was updated with new data.
203 * false - cache was not updated with new data.
204 */
UpdateCryptoCache(const char * VolumeName,const char * EncryptionKey)205 bool UpdateCryptoCache(const char* VolumeName, const char* EncryptionKey)
206 {
207 time_t now;
208 bool found;
209 bool retval = false;
210 crypto_cache_entry_t* cce = NULL;
211 crypto_cache_entry_t* next_cce;
212
213 /*
214 * Lock the cache.
215 */
216 P(crypto_cache_lock);
217
218 /*
219 * See if there are any cached encryption keys.
220 */
221 if (!cached_crypto_keys) {
222 cached_crypto_keys = new dlist(cce, &cce->link);
223
224 cce = (crypto_cache_entry_t*)malloc(sizeof(crypto_cache_entry_t));
225 bstrncpy(cce->VolumeName, VolumeName, sizeof(cce->VolumeName));
226 bstrncpy(cce->EncryptionKey, EncryptionKey, sizeof(cce->EncryptionKey));
227 cce->added = time(NULL);
228 cached_crypto_keys->append(cce);
229 retval = true;
230 } else {
231 found = false;
232 now = time(NULL);
233 cce = (crypto_cache_entry_t*)cached_crypto_keys->first();
234 while (cce) {
235 next_cce = (crypto_cache_entry_t*)cached_crypto_keys->next(cce);
236 if (bstrcmp(cce->VolumeName, VolumeName)) {
237 found = true;
238
239 /*
240 * If the key changed update the cached entry.
241 */
242 if (!bstrcmp(cce->EncryptionKey, EncryptionKey)) {
243 bstrncpy(cce->EncryptionKey, EncryptionKey,
244 sizeof(cce->EncryptionKey));
245 retval = true;
246 }
247
248 cce->added = time(NULL);
249 cce = next_cce;
250 continue;
251 }
252
253 /*
254 * Validate the entry.
255 * Any entry older the CRYPTO_CACHE_MAX_AGE seconds is removed.
256 */
257 if ((cce->added + CRYPTO_CACHE_MAX_AGE) < now) {
258 cached_crypto_keys->remove(cce);
259 retval = true;
260 }
261 cce = next_cce;
262 }
263
264 /*
265 * New entry.
266 */
267 if (!found) {
268 cce = (crypto_cache_entry_t*)malloc(sizeof(crypto_cache_entry_t));
269 bstrncpy(cce->VolumeName, VolumeName, sizeof(cce->VolumeName));
270 bstrncpy(cce->EncryptionKey, EncryptionKey, sizeof(cce->EncryptionKey));
271 cce->added = time(NULL);
272 cached_crypto_keys->append(cce);
273 retval = true;
274 }
275 }
276
277 V(crypto_cache_lock);
278 return retval;
279 }
280
281 /*
282 * Lookup a cache entry for the given VolumeName.
283 * Returns: string dupped encryption key must be freed by caller.
284 */
lookup_crypto_cache_entry(const char * VolumeName)285 char* lookup_crypto_cache_entry(const char* VolumeName)
286 {
287 crypto_cache_entry_t* cce;
288
289 if (!cached_crypto_keys) { return NULL; }
290
291 /*
292 * Lock the cache.
293 */
294 P(crypto_cache_lock);
295
296 foreach_dlist (cce, cached_crypto_keys) {
297 if (bstrcmp(cce->VolumeName, VolumeName)) {
298 V(crypto_cache_lock);
299 return strdup(cce->EncryptionKey);
300 }
301 }
302
303 V(crypto_cache_lock);
304 return NULL;
305 }
306
307 /*
308 * Dump the content of the crypto cache to a filedescriptor.
309 */
DumpCryptoCache(int fd)310 void DumpCryptoCache(int fd)
311 {
312 int len;
313 PoolMem msg(PM_MESSAGE);
314 crypto_cache_entry_t* cce;
315 char dt1[MAX_TIME_LENGTH], dt2[MAX_TIME_LENGTH];
316 unsigned int max_vol_length, max_key_length;
317
318 if (!cached_crypto_keys) { return; }
319
320 /*
321 * Lock the cache.
322 */
323 P(crypto_cache_lock);
324
325 /*
326 * See how long the biggest volumename and key are.
327 */
328 max_vol_length = strlen(_("Volumename"));
329 max_key_length = strlen(_("EncryptionKey"));
330 foreach_dlist (cce, cached_crypto_keys) {
331 if (strlen(cce->VolumeName) > max_vol_length) {
332 max_vol_length = strlen(cce->VolumeName);
333 }
334
335 if (strlen(cce->EncryptionKey) > max_key_length) {
336 max_key_length = strlen(cce->EncryptionKey);
337 }
338 }
339
340 len = Mmsg(msg, "%-*s %-*s %-20s %-20s\n", max_vol_length, _("Volumename"),
341 max_key_length, _("EncryptionKey"), _("Added"), _("Expires"));
342
343 write(fd, msg.c_str(), len);
344
345 foreach_dlist (cce, cached_crypto_keys) {
346 bstrutime(dt1, sizeof(dt1), cce->added);
347 bstrutime(dt2, sizeof(dt2), cce->added + CRYPTO_CACHE_MAX_AGE);
348 len = Mmsg(msg, "%-*s %-*s %-20s %-20s\n", max_vol_length, cce->VolumeName,
349 max_key_length, cce->EncryptionKey, dt1, dt2);
350
351 write(fd, msg.c_str(), len);
352 }
353
354 V(crypto_cache_lock);
355 }
356
357 /*
358 * Reset all entries in the cache to the current time.
359 */
ResetCryptoCache(void)360 void ResetCryptoCache(void)
361 {
362 time_t now;
363 crypto_cache_entry_t* cce;
364
365 if (!cached_crypto_keys) { return; }
366
367 now = time(NULL);
368
369 /*
370 * Lock the cache.
371 */
372 P(crypto_cache_lock);
373
374 foreach_dlist (cce, cached_crypto_keys) {
375 cce->added = now;
376 }
377
378 V(crypto_cache_lock);
379 }
380
381 /*
382 * Flush the date from the internal cache.
383 */
FlushCryptoCache(void)384 void FlushCryptoCache(void)
385 {
386 if (!cached_crypto_keys) { return; }
387
388 /*
389 * Lock the cache.
390 */
391 P(crypto_cache_lock);
392
393 cached_crypto_keys->destroy();
394 delete cached_crypto_keys;
395 cached_crypto_keys = NULL;
396
397 V(crypto_cache_lock);
398 }
399