Update translations from 2.2.x branch
[vlc.git] / modules / keystore / file.c
blobca91a01bf24b858293543706a5ed37e9e652f729
1 /*****************************************************************************
2 * file.c: File and crypt keystore
3 *****************************************************************************
4 * Copyright © 2015-2016 VLC authors, VideoLAN and VideoLabs
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <stdio.h>
26 #include <sys/stat.h>
27 #ifdef HAVE_FLOCK
28 #include <sys/file.h>
29 #endif
30 #ifdef HAVE_FCNTL
31 #include <fcntl.h>
32 #endif
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_fs.h>
37 #include <vlc_memory.h>
38 #include <vlc_keystore.h>
39 #include <vlc_strings.h>
41 #include <assert.h>
43 #include "file_crypt.h"
44 #include "list_util.h"
46 static int Open(vlc_object_t *);
47 static void Close(vlc_object_t *);
48 #ifdef CRYPTFILE
49 static int OpenCrypt(vlc_object_t *);
50 static void CloseCrypt(vlc_object_t *);
51 #endif
53 vlc_module_begin()
54 set_shortname(N_("file keystore (plaintext)"))
55 set_description(N_("secrets are stored on a file without any encryption"))
56 set_category(CAT_ADVANCED)
57 set_subcategory(SUBCAT_ADVANCED_MISC)
58 set_callbacks(Open, Close)
59 add_savefile("keystore-file", NULL, NULL, NULL, true)
60 change_private()
61 set_capability("keystore", 0)
62 add_shortcut("file_plaintext")
63 #ifdef CRYPTFILE
64 add_submodule()
65 set_shortname(N_("crypt keystore"))
66 set_description(N_("secrets are stored encrypted on a file"))
67 set_category(CAT_ADVANCED)
68 set_subcategory(SUBCAT_ADVANCED_MISC)
69 set_callbacks(OpenCrypt, CloseCrypt)
70 set_capability("keystore", 1)
71 add_shortcut("file_crypt")
72 #endif
73 vlc_module_end ()
75 struct vlc_keystore_sys
77 char * psz_file;
78 #ifdef CRYPTFILE
79 bool b_crypted;
80 struct crypt crypt;
81 #endif
84 static const char *const ppsz_keys[] = {
85 "protocol",
86 "user",
87 "server",
88 "path",
89 "port",
90 "realm",
91 "authtype",
93 static_assert(sizeof(ppsz_keys)/sizeof(*ppsz_keys) == KEY_MAX, "key mismatch");
95 static int
96 str2key(const char *psz_key)
98 for (unsigned int i = 0; i < KEY_MAX; ++i)
100 if (strcmp(ppsz_keys[i], psz_key) == 0)
101 return i;
103 return -1;
106 static int
107 values_write(FILE *p_file, const char *const ppsz_values[KEY_MAX])
109 for (unsigned int i = 0; i < KEY_MAX; ++i)
111 if (!ppsz_values[i])
112 continue;
113 char *psz_b64 = vlc_b64_encode(ppsz_values[i]);
114 if (!psz_b64)
115 return VLC_EGENERIC;
116 const char *psz_end_sep = "";
117 for (unsigned int j = i + 1; j < KEY_MAX; ++j)
119 if (ppsz_values[j])
121 psz_end_sep = ",";
122 break;
125 if (fprintf(p_file, "%s:%s%s", ppsz_keys[i], psz_b64, psz_end_sep) < 0)
127 free(psz_b64);
128 return VLC_EGENERIC;
130 free(psz_b64);
133 return VLC_SUCCESS;
136 static int
137 truncate0(int i_fd)
139 #ifndef _WIN32
140 return ftruncate(i_fd, 0) == 0 ? VLC_SUCCESS : VLC_EGENERIC;
141 #else
142 return _chsize(i_fd, 0) == 0 ? VLC_SUCCESS : VLC_EGENERIC;
143 #endif
146 /* a line is "{key1:VALUE1_B64,key2:VALUE2_B64}:PASSWORD_B64" */
147 static int
148 file_save(vlc_keystore *p_keystore, FILE *p_file, int i_fd, struct ks_list *p_list)
150 vlc_keystore_sys *p_sys = p_keystore->p_sys;
151 int i_ret = VLC_EGENERIC;
153 rewind(p_file);
154 if (truncate0(i_fd))
156 vlc_unlink(p_sys->psz_file);
157 return i_ret;
160 for (unsigned int i = 0; i < p_list->i_count; ++i)
162 vlc_keystore_entry *p_entry = &p_list->p_entries[i];
163 if (!p_entry->p_secret)
164 continue;
166 if (fprintf(p_file, "{") < 0)
167 goto end;
168 if (values_write(p_file, (const char *const *) p_entry->ppsz_values))
169 goto end;
170 char *psz_b64 = vlc_b64_encode_binary(p_entry->p_secret,
171 p_entry->i_secret_len);
172 if (!psz_b64)
173 goto end;
174 if (fprintf(p_file, "}:%s\n", psz_b64) < 0)
176 free(psz_b64);
177 goto end;
179 free(psz_b64);
181 i_ret = VLC_SUCCESS;
182 end:
184 if (i_ret != VLC_SUCCESS)
186 if (truncate0(i_fd))
187 vlc_unlink(p_sys->psz_file);
189 return i_ret;
192 static int
193 file_read(vlc_keystore *p_keystore, FILE *p_file, int i_fd, struct ks_list *p_list)
195 vlc_keystore_sys *p_sys = p_keystore->p_sys;
196 char *psz_line = NULL;
197 size_t i_line_len = 0;
198 ssize_t i_read;
199 bool b_valid = false;
201 while ((i_read = getline(&psz_line, &i_line_len, p_file)) != -1)
203 char *p = psz_line;
204 if (*(p++) != '{')
206 getchar();
207 goto end;
210 vlc_keystore_entry *p_entry = ks_list_new_entry(p_list);
211 if (!p_entry)
212 goto end;
214 bool b_end = false;
215 while (*p != '\0' && !b_end)
217 int i_key;
218 char *p_key, *p_value;
219 size_t i_len;
221 /* read key */
222 i_len = strcspn(p, ":");
223 if (!i_len || p[i_len] == '\0')
224 goto end;
226 p[i_len] = '\0';
227 p_key = p;
228 i_key = str2key(p_key);
229 if (i_key == -1 || i_key >= KEY_MAX)
230 goto end;
231 p += i_len + 1;
233 /* read value */
234 i_len = strcspn(p, ",}");
235 if (!i_len || p[i_len] == '\0')
236 goto end;
238 if (p[i_len] == '}')
239 b_end = true;
241 p[i_len] = '\0';
242 p_value = vlc_b64_decode(p); /* BASE 64 */
243 if (!p_value)
244 goto end;
245 p += i_len + 1;
247 p_entry->ppsz_values[i_key] = p_value;
249 /* read passwd */
250 if (*p == '\0' || *p != ':')
251 goto end;
253 p_entry->i_secret_len = vlc_b64_decode_binary(&p_entry->p_secret, p + 1);
254 if (!p_entry->p_secret)
255 goto end;
258 b_valid = true;
260 end:
261 free(psz_line);
262 if (!b_valid)
264 if (truncate0(i_fd))
265 vlc_unlink(p_sys->psz_file);
267 return VLC_SUCCESS;
270 #if (!defined(HAVE_FLOCK) && defined (HAVE_FCNTL) && defined (F_SETLKW))
271 static int
272 posix_lock_fd(int fd)
274 struct flock lock;
275 int flags;
277 if (fd == -1)
278 return -1;
280 flags = fcntl(fd, F_GETFL);
282 lock.l_start = 0;
283 lock.l_len = 0;
284 lock.l_whence = SEEK_SET;
285 lock.l_type = (flags & O_ACCMODE) == O_RDONLY ? F_RDLCK : F_WRLCK;
287 return fcntl(fd, F_SETLKW, &lock);
289 #endif
291 static int
292 file_open(const char *psz_file, const char *psz_mode, FILE **pp_file)
294 FILE *p_file = vlc_fopen(psz_file, psz_mode);
295 if (p_file == NULL)
296 return -1;
298 int i_fd = fileno(p_file);
299 if (i_fd == -1)
301 fclose(p_file);
302 return -1;
305 #ifdef HAVE_FLOCK
306 if (flock(i_fd, LOCK_EX) != 0)
308 fclose(p_file);
309 return -1;
311 #elif defined (HAVE_FCNTL) && defined (F_SETLKW)
312 if (posix_lock_fd(i_fd) != 0)
314 fclose(p_file);
315 return -1;
317 #endif
318 *pp_file = p_file;
319 return i_fd;
322 static void
323 file_close(FILE *p_file)
325 fclose(p_file);
328 static int
329 Store(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX],
330 const uint8_t *p_secret, size_t i_secret_len, const char *psz_label)
332 (void) psz_label;
333 vlc_keystore_sys *p_sys = p_keystore->p_sys;
334 int i_ret = VLC_EGENERIC;
335 struct ks_list list = { 0 };
336 FILE *p_file;
337 int i_fd = file_open(p_sys->psz_file, "r+", &p_file);
338 if (i_fd == -1)
339 return i_ret;
341 if (file_read(p_keystore, p_file, i_fd, &list) != VLC_SUCCESS)
342 goto end;
344 vlc_keystore_entry *p_entry = ks_list_find_entry(&list, ppsz_values, NULL);
346 if (p_entry)
347 vlc_keystore_release_entry(p_entry);
348 else
350 p_entry = ks_list_new_entry(&list);
351 if (!p_entry)
352 goto end;
354 if (ks_values_copy((const char **)p_entry->ppsz_values, ppsz_values))
355 goto end;
357 #ifdef CRYPTFILE
358 if (p_sys->b_crypted)
360 struct crypt *p_crypt = &p_sys->crypt;
361 uint8_t *p_enc_secret;
362 size_t i_enc_secret_len =
363 p_crypt->pf_encrypt(p_keystore, p_crypt->p_ctx, p_secret,
364 i_secret_len, &p_enc_secret);
365 if (i_enc_secret_len == 0)
366 goto end;
368 if (vlc_keystore_entry_set_secret(p_entry, p_enc_secret,
369 i_enc_secret_len))
370 goto end;
371 free(p_enc_secret);
373 else
374 #endif
376 if (vlc_keystore_entry_set_secret(p_entry, p_secret, i_secret_len))
377 goto end;
381 i_ret = file_save(p_keystore, p_file, i_fd, &list);
383 end:
384 file_close(p_file);
385 ks_list_free(&list);
386 return i_ret;
389 static unsigned int
390 Find(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX],
391 vlc_keystore_entry **pp_entries)
393 vlc_keystore_sys *p_sys = p_keystore->p_sys;
394 struct ks_list list = { 0 };
395 struct ks_list out_list = { 0 };
396 FILE *p_file;
397 int i_fd = file_open(p_sys->psz_file, "r", &p_file);
398 if (i_fd == -1)
399 return 0;
401 if (file_read(p_keystore, p_file, i_fd, &list) != VLC_SUCCESS)
402 goto end;
404 vlc_keystore_entry *p_entry;
405 unsigned i_index = 0;
406 while ((p_entry = ks_list_find_entry(&list, ppsz_values, &i_index)))
408 vlc_keystore_entry *p_out_entry = ks_list_new_entry(&out_list);
410 if (!p_out_entry
411 || ks_values_copy((const char **)p_out_entry->ppsz_values,
412 (const char *const*)p_entry->ppsz_values))
414 ks_list_free(&out_list);
415 goto end;
418 #ifdef CRYPTFILE
419 if (p_sys->b_crypted)
421 struct crypt *p_crypt = &p_sys->crypt;
422 uint8_t *p_dec_secret;
423 size_t i_dec_secret_len =
424 p_crypt->pf_decrypt(p_keystore, p_crypt->p_ctx, p_entry->p_secret,
425 p_entry->i_secret_len, &p_dec_secret);
426 if (i_dec_secret_len == 0)
428 ks_list_free(&out_list);
429 goto end;
432 free(p_entry->p_secret);
433 p_entry->p_secret = p_dec_secret;
434 p_entry->i_secret_len = i_dec_secret_len;
436 #endif
438 if (vlc_keystore_entry_set_secret(p_out_entry, p_entry->p_secret,
439 p_entry->i_secret_len))
441 ks_list_free(&out_list);
442 goto end;
446 *pp_entries = out_list.p_entries;
447 end:
448 file_close(p_file);
449 ks_list_free(&list);
450 return out_list.i_count;
453 static unsigned int
454 Remove(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX])
456 vlc_keystore_sys *p_sys = p_keystore->p_sys;
457 unsigned int i_count = 0;
458 struct ks_list list = { 0 };
459 FILE *p_file;
460 int i_fd = file_open(p_sys->psz_file, "r+", &p_file);
461 if (i_fd == -1)
462 return 0;
464 if (file_read(p_keystore, p_file, i_fd, &list) != VLC_SUCCESS)
465 goto end;
467 vlc_keystore_entry *p_entry;
468 unsigned i_index = 0;
469 while ((p_entry = ks_list_find_entry(&list, ppsz_values, &i_index)))
471 vlc_keystore_release_entry(p_entry);
472 i_count++;
475 if (i_count > 0
476 && file_save(p_keystore, p_file, i_fd, &list) != VLC_SUCCESS)
477 i_count = 0;
479 end:
480 file_close(p_file);
481 ks_list_free(&list);
482 return i_count;
485 static void
486 Close(vlc_object_t *p_this)
488 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
489 vlc_keystore_sys *p_sys = p_keystore->p_sys;
491 free(p_sys->psz_file);
492 free(p_sys);
495 static int
496 Open(vlc_object_t *p_this)
498 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
500 vlc_keystore_sys *p_sys = calloc(1, sizeof(vlc_keystore_sys));
501 if (!p_sys)
502 return VLC_EGENERIC;
504 char *psz_file = var_InheritString(p_this, "keystore-file");
505 if (!psz_file)
507 free(p_sys);
508 return VLC_EGENERIC;
511 struct stat stat;
512 bool b_file_exists = false;
513 if (vlc_stat(psz_file, &stat) != 0)
515 FILE *p_file = vlc_fopen(psz_file, "a+");
516 if (p_file != NULL)
518 b_file_exists= true;
519 fclose(p_file);
522 else
523 b_file_exists = true;
525 if (!b_file_exists)
527 free(p_sys);
528 free(psz_file);
529 return VLC_EGENERIC;
532 p_sys->psz_file = psz_file;
533 p_keystore->p_sys = p_sys;
534 p_keystore->pf_store = Store;
535 p_keystore->pf_find = Find;
536 p_keystore->pf_remove = Remove;
538 return VLC_SUCCESS;
541 #ifdef CRYPTFILE
542 static void
543 CloseCrypt(vlc_object_t *p_this)
545 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
546 struct crypt *p_crypt = &p_keystore->p_sys->crypt;
548 if (p_crypt->pf_clean != NULL)
549 p_crypt->pf_clean(p_keystore, p_crypt->p_ctx);
551 Close(p_this);
554 static int
555 OpenCrypt(vlc_object_t *p_this)
557 int i_ret = Open(p_this);
559 if (i_ret != VLC_SUCCESS)
560 return i_ret;
562 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
563 vlc_keystore_sys *p_sys = p_keystore->p_sys;
565 if (CryptInit(p_keystore, &p_sys->crypt) != VLC_SUCCESS)
567 Close(p_this);
568 return VLC_EGENERIC;
570 assert(p_sys->crypt.pf_encrypt != NULL && p_sys->crypt.pf_decrypt != NULL);
571 p_sys->b_crypted = true;
573 return VLC_SUCCESS;
575 #endif /* CRYPTFILE */