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