1 /*****************************************************************************
2 * plaintext.c: Insecure 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 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
34 #include <vlc_memory.h>
35 #include <vlc_keystore.h>
36 #include <vlc_strings.h>
40 static int Open(vlc_object_t *);
41 static void Close(vlc_object_t *);
44 set_shortname(N_("plaintext keystore (insecure)"))
45 set_description(N_("secrets are stored in plaintext without any encryption"))
46 set_category(CAT_ADVANCED)
47 set_subcategory(SUBCAT_ADVANCED_MISC)
48 add_string("keystore-plaintext-file", NULL, NULL, NULL, false )
49 set_capability("keystore", 0)
50 set_callbacks(Open, Close)
55 vlc_keystore_entry *p_entries;
60 struct vlc_keystore_sys
72 unsigned int i_ref_count;
73 vlc_keystore_sys * p_sys;
75 .lock = VLC_STATIC_MUTEX,
80 static const char *const ppsz_keys[] = {
89 static_assert(sizeof(ppsz_keys)/sizeof(*ppsz_keys) == KEY_MAX, "key mismatch");
92 str2key(const char *psz_key)
94 for (unsigned int i = 0; i < KEY_MAX; ++i)
96 if (strcmp(ppsz_keys[i], psz_key) == 0)
103 list_free(struct list *p_list)
105 vlc_keystore_release_entries(p_list->p_entries, p_list->i_count);
106 p_list->p_entries = NULL;
112 values_copy(const char * ppsz_dst[KEY_MAX], const char *const ppsz_src[KEY_MAX])
114 for (unsigned int i = 0; i < KEY_MAX; ++i)
118 ppsz_dst[i] = strdup(ppsz_src[i]);
127 str_write(FILE *p_file, const char *psz_str)
129 size_t i_len = strlen(psz_str);
130 return fwrite(psz_str, sizeof(char), i_len, p_file) == i_len ? VLC_SUCCESS
135 values_write(FILE *p_file, const char *const ppsz_values[KEY_MAX])
137 for (unsigned int i = 0; i < KEY_MAX; ++i)
141 if (str_write(p_file, ppsz_keys[i]))
143 if (str_write(p_file, ":"))
145 char *psz_b64 = vlc_b64_encode(ppsz_values[i]);
146 if (!psz_b64 || str_write(p_file, psz_b64))
152 for (unsigned int j = i + 1; j < KEY_MAX; ++j)
156 if (str_write(p_file, ","))
166 static vlc_keystore_entry *
167 list_new_entry(struct list *p_list)
169 if (p_list->i_count + 1 > p_list->i_max)
172 vlc_keystore_entry *p_entries = realloc(p_list->p_entries, p_list->i_max
173 * sizeof(*p_list->p_entries));
179 p_list->p_entries = p_entries;
181 vlc_keystore_entry *p_entry = &p_list->p_entries[p_list->i_count];
182 p_entry->p_secret = NULL;
183 VLC_KEYSTORE_VALUES_INIT(p_entry->ppsz_values);
189 static vlc_keystore_entry *
190 list_get_entry(struct list *p_list, const char *const ppsz_values[KEY_MAX],
191 unsigned *p_start_index)
193 for (unsigned int i = p_start_index ? *p_start_index : 0;
194 i < p_list->i_count; ++i)
196 vlc_keystore_entry *p_entry = &p_list->p_entries[i];
197 if (!p_entry->p_secret)
201 for (unsigned int j = 0; j < KEY_MAX; ++j)
203 const char *psz_value1 = ppsz_values[j];
204 const char *psz_value2 = p_entry->ppsz_values[j];
208 if (!psz_value2 || strcmp(psz_value1, psz_value2))
214 *p_start_index = i + 1;
225 return ftruncate(i_fd, 0) == 0 ? VLC_SUCCESS : VLC_EGENERIC;
227 return _chsize(i_fd, 0) == 0 ? VLC_SUCCESS : VLC_EGENERIC;
231 /* a line is "{key1:VALUE1_B64,key2:VALUE2_B64}:PASSWORD_B64" */
233 list_save(vlc_keystore_sys *p_sys, struct list *p_list)
235 int i_ret = VLC_EGENERIC;
236 rewind(p_sys->p_file);
238 if (truncate0(p_sys->i_fd))
241 for (unsigned int i = 0; i < p_list->i_count; ++i)
243 vlc_keystore_entry *p_entry = &p_list->p_entries[i];
244 if (!p_entry->p_secret)
247 if (str_write(p_sys->p_file, "{"))
249 if (values_write(p_sys->p_file,
250 (const char *const *) p_entry->ppsz_values))
252 if (str_write(p_sys->p_file, "}:"))
254 char *psz_b64 = vlc_b64_encode_binary(p_entry->p_secret,
255 p_entry->i_secret_len);
256 if (!psz_b64 || str_write(p_sys->p_file, psz_b64))
262 if (i < p_list->i_count - 1 && str_write(p_sys->p_file, "\n"))
268 if (i_ret != VLC_SUCCESS)
270 p_sys->b_error = true;
277 list_read(vlc_keystore_sys *p_sys, struct list *p_list)
279 char *psz_line = NULL;
280 size_t i_line_len = 0;
282 bool b_valid = false;
284 while ((i_read = getline(&psz_line, &i_line_len, p_sys->p_file)) != -1)
290 vlc_keystore_entry *p_entry = list_new_entry(p_list);
295 while (*p != '\0' && !b_end)
298 char *p_key, *p_value;
302 i_len = strcspn(p, ":");
303 if (!i_len || p[i_len] == '\0')
308 i_key = str2key(p_key);
309 if (i_key == -1 || i_key >= KEY_MAX)
314 i_len = strcspn(p, ",}");
315 if (!i_len || p[i_len] == '\0')
322 p_value = vlc_b64_decode(p); /* BASE 64 */
327 p_entry->ppsz_values[i_key] = p_value;
330 if (*p == '\0' || *p != ':')
333 p_entry->i_secret_len = vlc_b64_decode_binary(&p_entry->p_secret, p + 1);
334 if (!p_entry->p_secret)
344 p_sys->b_error = true;
351 Store(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX],
352 const uint8_t *p_secret, size_t i_secret_len, const char *psz_label)
354 vlc_mutex_lock(&instance.lock);
357 vlc_keystore_sys *p_sys = p_keystore->p_sys;
358 assert(p_sys == instance.p_sys);
359 struct list *p_list = &p_sys->list;
360 vlc_keystore_entry *p_entry = list_get_entry(p_list, ppsz_values, NULL);
364 free(p_entry->p_secret);
365 p_entry->p_secret = NULL;
366 for (unsigned int i = 0; i < KEY_MAX; ++i)
368 free(p_entry->ppsz_values[i]);
369 p_entry->ppsz_values[i] = NULL;
374 p_entry = list_new_entry(p_list);
378 if (values_copy((const char **)p_entry->ppsz_values, ppsz_values))
381 if (vlc_keystore_entry_set_secret(p_entry, p_secret, i_secret_len))
384 int i_ret = list_save(p_sys, &p_sys->list);
385 vlc_mutex_unlock(&instance.lock);
389 vlc_mutex_unlock(&instance.lock);
394 Find(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX],
395 vlc_keystore_entry **pp_entries)
397 vlc_mutex_lock(&instance.lock);
399 vlc_keystore_sys *p_sys = p_keystore->p_sys;
400 assert(p_sys == instance.p_sys);
401 struct list *p_list = &p_sys->list;
402 struct list out_list = { 0 };
403 vlc_keystore_entry *p_entry;
404 unsigned i_index = 0;
406 while ((p_entry = list_get_entry(p_list, ppsz_values, &i_index)))
408 vlc_keystore_entry *p_out_entry = list_new_entry(&out_list);
410 if (!p_out_entry || values_copy((const char **)p_out_entry->ppsz_values,
411 (const char *const*)p_entry->ppsz_values))
413 list_free(&out_list);
417 if (vlc_keystore_entry_set_secret(p_out_entry, p_entry->p_secret,
418 p_entry->i_secret_len))
420 list_free(&out_list);
425 *pp_entries = out_list.p_entries;
427 vlc_mutex_unlock(&instance.lock);
428 return out_list.i_count;
432 Remove(vlc_keystore *p_keystore, const char *const ppsz_values[KEY_MAX])
434 vlc_mutex_lock(&instance.lock);
436 vlc_keystore_sys *p_sys = p_keystore->p_sys;
437 assert(p_sys == instance.p_sys);
438 struct list *p_list = &p_sys->list;
439 vlc_keystore_entry *p_entry;
440 unsigned i_index = 0, i_count = 0;
442 while ((p_entry = list_get_entry(p_list, ppsz_values, &i_index)))
444 free(p_entry->p_secret);
445 p_entry->p_secret = NULL;
449 if (list_save(p_sys, &p_sys->list) != VLC_SUCCESS)
451 vlc_mutex_unlock(&instance.lock);
455 vlc_mutex_unlock(&instance.lock);
460 CleanUp(vlc_keystore_sys *p_sys)
466 if (truncate0(p_sys->i_fd))
467 vlc_unlink(p_sys->psz_file);
471 flock(p_sys->i_fd, LOCK_UN);
473 fclose(p_sys->p_file);
476 list_free(&p_sys->list);
477 free(p_sys->psz_file);
482 Open(vlc_object_t *p_this)
484 vlc_keystore *p_keystore = (vlc_keystore *)p_this;
485 vlc_keystore_sys *p_sys;
487 vlc_mutex_lock(&instance.lock);
489 /* The p_sys context is shared and protected between all threads */
490 if (instance.i_ref_count == 0)
492 char *psz_file = var_InheritString(p_this, "keystore-plaintext-file");
496 p_sys = calloc(1, sizeof(vlc_keystore_sys));
503 p_sys->psz_file = psz_file;
504 p_sys->p_file = vlc_fopen(p_sys->psz_file, "a+");
511 p_sys->i_fd = fileno(p_sys->p_file);
512 if (p_sys->i_fd == -1)
518 /* Fail if an other LibVLC process acquired the file lock.
519 * If HAVE_FLOCK is not defined, the running OS is most likely Windows
520 * and a lock was already acquired when the file was opened. */
522 if (flock(p_sys->i_fd, LOCK_EX|LOCK_NB) != 0)
529 if (list_read(p_sys, &p_sys->list) != VLC_SUCCESS)
534 instance.p_sys = p_sys;
537 p_sys = instance.p_sys;
539 instance.i_ref_count++;
540 p_keystore->p_sys = p_sys;
542 p_keystore->pf_store = Store;
543 p_keystore->pf_find = Find;
544 p_keystore->pf_remove = Remove;
546 vlc_mutex_unlock(&instance.lock);
552 Close(vlc_object_t *p_this)
556 vlc_mutex_lock(&instance.lock);
557 assert(((vlc_keystore *)p_this)->p_sys == instance.p_sys);
558 if (--instance.i_ref_count == 0)
560 CleanUp(instance.p_sys);
561 instance.p_sys = NULL;
563 vlc_mutex_unlock(&instance.lock);