macOS vout: fix forgotten ';'
[vlc.git] / modules / keystore / file.c
blobae375754221af7071003b48a49a0176d593d0a59
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_keystore.h>
38 #include <vlc_strings.h>
40 #include <assert.h>
42 #include "file_crypt.h"
43 #include "list_util.h"
45 static int Open(vlc_object_t *);
46 static void Close(vlc_object_t *);
47 #ifdef CRYPTFILE
48 static int OpenCrypt(vlc_object_t *);
49 static void CloseCrypt(vlc_object_t *);
50 #endif
52 vlc_module_begin()
53 set_shortname(N_("File keystore (plaintext)"))
54 set_description(N_("Secrets are stored on a file without any encryption"))
55 set_category(CAT_ADVANCED)
56 set_subcategory(SUBCAT_ADVANCED_MISC)
57 set_callbacks(Open, Close)
58 add_savefile("keystore-file", NULL, NULL, NULL)
59 change_private()
60 set_capability("keystore", 0)
61 add_shortcut("file_plaintext")
62 #ifdef CRYPTFILE
63 add_submodule()
64 set_shortname(N_("Crypt keystore"))
65 set_description(N_("Secrets are stored encrypted on a file"))
66 set_category(CAT_ADVANCED)
67 set_subcategory(SUBCAT_ADVANCED_MISC)
68 set_callbacks(OpenCrypt, CloseCrypt)
69 set_capability("keystore", 1)
70 add_shortcut("file_crypt")
71 #endif
72 vlc_module_end ()
74 struct vlc_keystore_sys
76 char * psz_file;
77 #ifdef CRYPTFILE
78 bool b_crypted;
79 struct crypt crypt;
80 #endif
83 static const char *const ppsz_keys[] = {
84 "protocol",
85 "user",
86 "server",
87 "path",
88 "port",
89 "realm",
90 "authtype",
92 static_assert(sizeof(ppsz_keys)/sizeof(*ppsz_keys) == KEY_MAX, "key mismatch");
94 static int
95 str2key(const char *psz_key)
97 for (unsigned int i = 0; i < KEY_MAX; ++i)
99 if (strcmp(ppsz_keys[i], psz_key) == 0)
100 return i;
102 return -1;
105 static int
106 values_write(FILE *p_file, const char *const ppsz_values[KEY_MAX])
108 for (unsigned int i = 0; i < KEY_MAX; ++i)
110 if (!ppsz_values[i])
111 continue;
112 char *psz_b64 = vlc_b64_encode(ppsz_values[i]);
113 if (!psz_b64)
114 return VLC_EGENERIC;
115 const char *psz_end_sep = "";
116 for (unsigned int j = i + 1; j < KEY_MAX; ++j)
118 if (ppsz_values[j])
120 psz_end_sep = ",";
121 break;
124 if (fprintf(p_file, "%s:%s%s", ppsz_keys[i], psz_b64, psz_end_sep) < 0)
126 free(psz_b64);
127 return VLC_EGENERIC;
129 free(psz_b64);
132 return VLC_SUCCESS;
135 static int
136 truncate0(int i_fd)
138 #ifndef _WIN32
139 return ftruncate(i_fd, 0) == 0 ? VLC_SUCCESS : VLC_EGENERIC;
140 #else
141 return _chsize(i_fd, 0) == 0 ? VLC_SUCCESS : VLC_EGENERIC;
142 #endif
145 /* a line is "{key1:VALUE1_B64,key2:VALUE2_B64}:PASSWORD_B64" */
146 static int
147 file_save(vlc_keystore *p_keystore, FILE *p_file, int i_fd, struct ks_list *p_list)
149 vlc_keystore_sys *p_sys = p_keystore->p_sys;
150 int i_ret = VLC_EGENERIC;
152 rewind(p_file);
153 if (truncate0(i_fd))
155 vlc_unlink(p_sys->psz_file);
156 return i_ret;
159 for (unsigned int i = 0; i < p_list->i_count; ++i)
161 vlc_keystore_entry *p_entry = &p_list->p_entries[i];
162 if (!p_entry->p_secret)
163 continue;
165 if (fprintf(p_file, "{") < 0)
166 goto end;
167 if (values_write(p_file, (const char *const *) p_entry->ppsz_values))
168 goto end;
169 char *psz_b64 = vlc_b64_encode_binary(p_entry->p_secret,
170 p_entry->i_secret_len);
171 if (!psz_b64)
172 goto end;
173 if (fprintf(p_file, "}:%s\n", psz_b64) < 0)
175 free(psz_b64);
176 goto end;
178 free(psz_b64);
180 i_ret = VLC_SUCCESS;
181 end:
183 if (i_ret != VLC_SUCCESS)
185 if (truncate0(i_fd))
186 vlc_unlink(p_sys->psz_file);
188 return i_ret;
191 static int
192 file_read(vlc_keystore *p_keystore, FILE *p_file, int i_fd, struct ks_list *p_list)
194 vlc_keystore_sys *p_sys = p_keystore->p_sys;
195 char *psz_line = NULL;
196 size_t i_line_len = 0;
197 ssize_t i_read;
198 bool b_valid = false;
200 while ((i_read = getline(&psz_line, &i_line_len, p_file)) != -1)
202 char *p = psz_line;
203 if (*(p++) != '{')
205 getchar();
206 goto end;
209 vlc_keystore_entry *p_entry = ks_list_new_entry(p_list);
210 if (!p_entry)
211 goto end;
213 bool b_end = false;
214 while (*p != '\0' && !b_end)
216 int i_key;
217 char *p_key, *p_value;
218 size_t i_len;
220 /* read key */
221 i_len = strcspn(p, ":");
222 if (!i_len || p[i_len] == '\0')
223 goto end;
225 p[i_len] = '\0';
226 p_key = p;
227 i_key = str2key(p_key);
228 if (i_key == -1 || i_key >= KEY_MAX)
229 goto end;
230 p += i_len + 1;
232 /* read value */
233 i_len = strcspn(p, ",}");
234 if (!i_len || p[i_len] == '\0')
235 goto end;
237 if (p[i_len] == '}')
238 b_end = true;
240 p[i_len] = '\0';
241 p_value = vlc_b64_decode(p); /* BASE 64 */
242 if (!p_value)
243 goto end;
244 p += i_len + 1;
246 p_entry->ppsz_values[i_key] = p_value;
248 /* read passwd */
249 if (*p == '\0' || *p != ':')
250 goto end;
252 p_entry->i_secret_len = vlc_b64_decode_binary(&p_entry->p_secret, p + 1);
253 if (!p_entry->p_secret)
254 goto end;
257 b_valid = true;
259 end:
260 free(psz_line);
261 if (!b_valid)
263 if (truncate0(i_fd))
264 vlc_unlink(p_sys->psz_file);
266 return VLC_SUCCESS;
269 #if (!defined(HAVE_FLOCK) && defined (HAVE_FCNTL) && defined (F_SETLKW))
270 static int
271 posix_lock_fd(int fd)
273 struct flock lock;
274 int flags;
276 if (fd == -1)
277 return -1;
279 flags = fcntl(fd, F_GETFL);
281 lock.l_start = 0;
282 lock.l_len = 0;
283 lock.l_whence = SEEK_SET;
284 lock.l_type = (flags & O_ACCMODE) == O_RDONLY ? F_RDLCK : F_WRLCK;
286 return fcntl(fd, F_SETLKW, &lock);
288 #endif
290 static int
291 file_open(const char *psz_file, const char *psz_mode, FILE **pp_file)
293 FILE *p_file = vlc_fopen(psz_file, psz_mode);
294 if (p_file == NULL)
295 return -1;
297 int i_fd = fileno(p_file);
298 if (i_fd == -1)
300 fclose(p_file);
301 return -1;
304 #ifdef HAVE_FLOCK
305 if (flock(i_fd, LOCK_EX) != 0)
307 fclose(p_file);
308 return -1;
310 #elif defined (HAVE_FCNTL) && defined (F_SETLKW)
311 if (posix_lock_fd(i_fd) != 0)
313 fclose(p_file);
314 return -1;
316 #endif
317 *pp_file = p_file;
318 return i_fd;
321 static void
322 file_close(FILE *p_file)
324 fclose(p_file);
327 static int
328 Store(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX],
329 const uint8_t *p_secret, size_t i_secret_len, const char *psz_label)
331 (void) psz_label;
332 vlc_keystore_sys *p_sys = p_keystore->p_sys;
333 int i_ret = VLC_EGENERIC;
334 struct ks_list list = { 0 };
335 FILE *p_file;
336 int i_fd = file_open(p_sys->psz_file, "r+", &p_file);
337 if (i_fd == -1)
338 return i_ret;
340 if (file_read(p_keystore, p_file, i_fd, &list) != VLC_SUCCESS)
341 goto end;
343 vlc_keystore_entry *p_entry = ks_list_find_entry(&list, ppsz_values, NULL);
345 if (p_entry)
346 vlc_keystore_release_entry(p_entry);
347 else
349 p_entry = ks_list_new_entry(&list);
350 if (!p_entry)
351 goto end;
353 if (ks_values_copy((const char **)p_entry->ppsz_values, ppsz_values))
354 goto end;
356 #ifdef CRYPTFILE
357 if (p_sys->b_crypted)
359 struct crypt *p_crypt = &p_sys->crypt;
360 uint8_t *p_enc_secret;
361 size_t i_enc_secret_len =
362 p_crypt->pf_encrypt(p_keystore, p_crypt->p_ctx, p_secret,
363 i_secret_len, &p_enc_secret);
364 if (i_enc_secret_len == 0)
365 goto end;
367 if (vlc_keystore_entry_set_secret(p_entry, p_enc_secret,
368 i_enc_secret_len))
369 goto end;
370 free(p_enc_secret);
372 else
373 #endif
375 if (vlc_keystore_entry_set_secret(p_entry, p_secret, i_secret_len))
376 goto end;
380 i_ret = file_save(p_keystore, p_file, i_fd, &list);
382 end:
383 file_close(p_file);
384 ks_list_free(&list);
385 return i_ret;
388 static unsigned int
389 Find(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX],
390 vlc_keystore_entry **pp_entries)
392 vlc_keystore_sys *p_sys = p_keystore->p_sys;
393 struct ks_list list = { 0 };
394 struct ks_list out_list = { 0 };
395 FILE *p_file;
396 int i_fd = file_open(p_sys->psz_file, "r", &p_file);
397 if (i_fd == -1)
398 return 0;
400 if (file_read(p_keystore, p_file, i_fd, &list) != VLC_SUCCESS)
401 goto end;
403 vlc_keystore_entry *p_entry;
404 unsigned i_index = 0;
405 while ((p_entry = ks_list_find_entry(&list, ppsz_values, &i_index)))
407 vlc_keystore_entry *p_out_entry = ks_list_new_entry(&out_list);
409 if (!p_out_entry
410 || ks_values_copy((const char **)p_out_entry->ppsz_values,
411 (const char *const*)p_entry->ppsz_values))
413 ks_list_free(&out_list);
414 goto end;
417 #ifdef CRYPTFILE
418 if (p_sys->b_crypted)
420 struct crypt *p_crypt = &p_sys->crypt;
421 uint8_t *p_dec_secret;
422 size_t i_dec_secret_len =
423 p_crypt->pf_decrypt(p_keystore, p_crypt->p_ctx, p_entry->p_secret,
424 p_entry->i_secret_len, &p_dec_secret);
425 if (i_dec_secret_len == 0)
427 ks_list_free(&out_list);
428 goto end;
431 free(p_entry->p_secret);
432 p_entry->p_secret = p_dec_secret;
433 p_entry->i_secret_len = i_dec_secret_len;
435 #endif
437 if (vlc_keystore_entry_set_secret(p_out_entry, p_entry->p_secret,
438 p_entry->i_secret_len))
440 ks_list_free(&out_list);
441 goto end;
445 *pp_entries = out_list.p_entries;
446 end:
447 file_close(p_file);
448 ks_list_free(&list);
449 return out_list.i_count;
452 static unsigned int
453 Remove(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX])
455 vlc_keystore_sys *p_sys = p_keystore->p_sys;
456 unsigned int i_count = 0;
457 struct ks_list list = { 0 };
458 FILE *p_file;
459 int i_fd = file_open(p_sys->psz_file, "r+", &p_file);
460 if (i_fd == -1)
461 return 0;
463 if (file_read(p_keystore, p_file, i_fd, &list) != VLC_SUCCESS)
464 goto end;
466 vlc_keystore_entry *p_entry;
467 unsigned i_index = 0;
468 while ((p_entry = ks_list_find_entry(&list, ppsz_values, &i_index)))
470 vlc_keystore_release_entry(p_entry);
471 i_count++;
474 if (i_count > 0
475 && file_save(p_keystore, p_file, i_fd, &list) != VLC_SUCCESS)
476 i_count = 0;
478 end:
479 file_close(p_file);
480 ks_list_free(&list);
481 return i_count;
484 static void
485 Close(vlc_object_t *p_this)
487 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
488 vlc_keystore_sys *p_sys = p_keystore->p_sys;
490 free(p_sys->psz_file);
491 free(p_sys);
494 static int
495 Open(vlc_object_t *p_this)
497 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
499 vlc_keystore_sys *p_sys = calloc(1, sizeof(vlc_keystore_sys));
500 if (!p_sys)
501 return VLC_EGENERIC;
503 char *psz_file = var_InheritString(p_this, "keystore-file");
504 if (!psz_file)
506 free(p_sys);
507 return VLC_EGENERIC;
510 struct stat stat;
511 bool b_file_exists = false;
512 if (vlc_stat(psz_file, &stat) != 0)
514 FILE *p_file = vlc_fopen(psz_file, "a+");
515 if (p_file != NULL)
517 b_file_exists= true;
518 fclose(p_file);
521 else
522 b_file_exists = true;
524 if (!b_file_exists)
526 free(p_sys);
527 free(psz_file);
528 return VLC_EGENERIC;
531 p_sys->psz_file = psz_file;
532 p_keystore->p_sys = p_sys;
533 p_keystore->pf_store = Store;
534 p_keystore->pf_find = Find;
535 p_keystore->pf_remove = Remove;
537 return VLC_SUCCESS;
540 #ifdef CRYPTFILE
541 static void
542 CloseCrypt(vlc_object_t *p_this)
544 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
545 struct crypt *p_crypt = &p_keystore->p_sys->crypt;
547 if (p_crypt->pf_clean != NULL)
548 p_crypt->pf_clean(p_keystore, p_crypt->p_ctx);
550 Close(p_this);
553 static int
554 OpenCrypt(vlc_object_t *p_this)
556 int i_ret = Open(p_this);
558 if (i_ret != VLC_SUCCESS)
559 return i_ret;
561 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
562 vlc_keystore_sys *p_sys = p_keystore->p_sys;
564 if (CryptInit(p_keystore, &p_sys->crypt) != VLC_SUCCESS)
566 Close(p_this);
567 return VLC_EGENERIC;
569 assert(p_sys->crypt.pf_encrypt != NULL && p_sys->crypt.pf_decrypt != NULL);
570 p_sys->b_crypted = true;
572 return VLC_SUCCESS;
574 #endif /* CRYPTFILE */