[vlc-devel] [RFC PATCH 2/8] keystore: add plaintext module
Rémi Denis-Courmont
remi at remlab.net
Thu Dec 31 17:09:57 CET 2015
Le 2015-12-30 20:36, Thomas Guillem a écrit :
> Deactivated by default since secret is not encrypted.
>
> To use it (for test purpose only):
> ./vlc --keystore=plaintext --keystore-plaintext-file=<my_file> <url>
> ---
> modules/Makefile.am | 1 +
> modules/keystore/Makefile.am | 4 +
> modules/keystore/plaintext.c | 483
> +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 488 insertions(+)
> create mode 100644 modules/keystore/Makefile.am
> create mode 100644 modules/keystore/plaintext.c
>
> diff --git a/modules/Makefile.am b/modules/Makefile.am
> index e6de1d0..a4a9977 100644
> --- a/modules/Makefile.am
> +++ b/modules/Makefile.am
> @@ -32,6 +32,7 @@ include control/Makefile.am
> include demux/Makefile.am
> include gui/Makefile.am
> include hw/vdpau/Makefile.am
> +include keystore/Makefile.am
> include logger/Makefile.am
> include lua/Makefile.am
> include meta_engine/Makefile.am
> diff --git a/modules/keystore/Makefile.am
> b/modules/keystore/Makefile.am
> new file mode 100644
> index 0000000..34cbf00
> --- /dev/null
> +++ b/modules/keystore/Makefile.am
> @@ -0,0 +1,4 @@
> +keystoredir = $(pluginsdir)/keystore
> +
> +libplaintext_keystore_plugin_la_SOURCES = keystore/plaintext.c
> +keystore_LTLIBRARIES = libplaintext_keystore_plugin.la
> diff --git a/modules/keystore/plaintext.c
> b/modules/keystore/plaintext.c
> new file mode 100644
> index 0000000..c5eeb40
> --- /dev/null
> +++ b/modules/keystore/plaintext.c
> @@ -0,0 +1,483 @@
>
> +/*****************************************************************************
> + * plaintext.c: Insecure keystore
> +
>
> *****************************************************************************
> + * Copyright © 2015 VLC authors, VideoLAN and VideoLabs
> + *
> + * This program is free software; you can redistribute it and/or
> modify
> + * it under the terms of the GNU Lesser General Public License as
> published by
> + * the Free Software Foundation; either version 2.1 of the License,
> or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> License
> + * along with this program; if not, write to the Free Software
> Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
> +
>
> *****************************************************************************/
> +
> +#ifdef HAVE_CONFIG_H
> +# include "config.h"
> +#endif
> +
> +#include <stdarg.h>
> +#include <stdio.h>
> +
> +#include <vlc_common.h>
> +#include <vlc_plugin.h>
> +#include <vlc_fs.h>
> +#include <vlc_memory.h>
> +#include <vlc_keystore.h>
> +#include <vlc_strings.h>
> +
> +#include <assert.h>
> +
> +static int Open(vlc_object_t *);
> +static void Close(vlc_object_t *);
> +
> +vlc_module_begin()
> + set_shortname(N_("plaintext keystore (insecure)"))
> + set_description(N_("secrets are stored in plaintext without any
> encryption"))
> + set_category(CAT_ADVANCED)
> + set_subcategory(SUBCAT_ADVANCED_MISC)
> + add_string("keystore-plaintext-file", NULL, NULL, NULL, false )
> + set_capability("keystore", 0)
> + set_callbacks(Open, Close)
> +vlc_module_end ()
> +
> +struct list
> +{
> + vlc_keystore_entry *p_entries;
> + unsigned i_count;
> + unsigned i_max;
> +};
> +
> +struct vlc_keystore_sys
> +{
> + char * psz_file;
> + struct list list;
> +};
> +
> +static vlc_mutex_t lock = VLC_STATIC_MUTEX;
> +static unsigned int i_ref_count = 0;
> +static vlc_keystore_sys instance = { 0 };
> +
> +static const char *const ppsz_keys[] = {
> + "protocol",
> + "user",
> + "server",
> + "path",
> + "port",
> + "realm",
> + "authtype",
> +};
> +static_assert(sizeof(ppsz_keys)/sizeof(*ppsz_keys) == KEY_MAX, "key
> mismatch");
> +
> +static int
> +str2key(const char *psz_key)
> +{
> + for (unsigned int i = 0; i < KEY_MAX; ++i)
> + {
> + if (strcmp(ppsz_keys[i], psz_key) == 0)
> + return i;
> + }
> + return -1;
> +}
> +
> +static void
> +list_free(struct list *p_list)
> +{
> + vlc_keystore_release_entries(p_list->p_entries,
> p_list->i_count);
> + p_list->p_entries = NULL;
> + p_list->i_count = 0;
> + p_list->i_max = 0;
> +}
> +
> +static int
> +values_copy(const char * ppsz_dst[KEY_MAX], const char *const
> ppsz_src[KEY_MAX])
> +{
> + for (unsigned int i = 0; i < KEY_MAX; ++i)
> + {
> + if (ppsz_src[i])
> + {
> + ppsz_dst[i] = strdup(ppsz_src[i]);
> + if (!ppsz_dst[i])
> + return VLC_EGENERIC;
> + }
> + }
> + return VLC_SUCCESS;
> +}
> +
> +static inline int
> +str_write(FILE *p_file, const char *psz_str)
> +{
> + size_t i_len = strlen(psz_str);
> + return fwrite(psz_str, sizeof(char), i_len, p_file) == i_len ?
> VLC_SUCCESS
> + :
> VLC_EGENERIC;
> +}
> +#define WRITE_STR(str) do {\
> + if (str_write(p_file, str)) \
> + goto end; \
> +} while(0)
I have seen far too many VLC bugs caused by flow-controlling macros.
No more.
> +
> +static int
> +values_write(FILE *p_file, const char *const ppsz_values[KEY_MAX])
> +{
> + char *psz_b64 = NULL;
> + for (unsigned int i = 0; i < KEY_MAX; ++i)
> + {
> + if (!ppsz_values[i])
> + continue;
> + WRITE_STR(ppsz_keys[i]);
> + WRITE_STR(":");
> + psz_b64 = vlc_b64_encode(ppsz_values[i]);
> + if (!psz_b64)
> + goto end;
> + WRITE_STR(psz_b64);
> + free(psz_b64);
> + psz_b64 = NULL;
> + for (unsigned int j = i + 1; j < KEY_MAX; ++j)
> + {
> + if (ppsz_values[j])
> + {
> + WRITE_STR(",");
> + break;
> + }
> + }
> + }
> +
> + return VLC_SUCCESS;
> +end:
> + free(psz_b64);
> + return VLC_EGENERIC;
> +}
> +
> +static vlc_keystore_entry *
> +list_new_entry(struct list *p_list)
> +{
> + if (p_list->i_count + 1 > p_list->i_max)
> + {
> + p_list->i_max += 10;
> + vlc_keystore_entry *p_entries = realloc(p_list->p_entries,
> p_list->i_max
> + *
> sizeof(*p_list->p_entries));
> + if (!p_entries)
> + {
> + list_free(p_list);
> + return NULL;
> + }
> + p_list->p_entries = p_entries;
> + }
> + vlc_keystore_entry *p_entry =
> &p_list->p_entries[p_list->i_count];
> + p_entry->p_secret = NULL;
> + VLC_KEYSTORE_VALUES_INIT(p_entry->ppsz_values);
> + p_list->i_count++;
> +
> + return p_entry;
> +}
> +
> +static vlc_keystore_entry *
> +list_get_entry(struct list *p_list, const char *const
> ppsz_values[KEY_MAX],
> + unsigned *p_start_index)
> +{
> + for (unsigned int i = p_start_index ? *p_start_index : 0;
> + i < p_list->i_count; ++i)
> + {
> + vlc_keystore_entry *p_entry = &p_list->p_entries[i];
> + if (!p_entry->p_secret)
> + continue;
> +
> + bool b_match = true;
> + for (unsigned int j = 0; j < KEY_MAX; ++j)
> + {
> + const char *psz_value1 = ppsz_values[j];
> + const char *psz_value2 = p_entry->ppsz_values[j];
> +
> + if (!psz_value1)
> + continue;
> + if (!psz_value2 || strcmp(psz_value1, psz_value2))
> + b_match = false;
> + }
> + if (b_match)
> + {
> + if (p_start_index)
> + *p_start_index = i + 1;
> + return p_entry;
> + }
> + }
> + return NULL;
> +}
> +
> +/* a line is "{key1:VALUE1_B64,key2:VALUE2_B64}:PASSWORD_B64" */
> +static int
> +list_save(struct list *p_list, const char *psz_file)
> +{
> + int i_ret = VLC_EGENERIC;
> + FILE *p_file = vlc_fopen(psz_file, "w");
This won't work with concurrent accesses. Consider using "a" mode.
> + if (!p_file)
> + return VLC_EGENERIC;
> +
> + for (unsigned int i = 0; i < p_list->i_count; ++i)
> + {
> + vlc_keystore_entry *p_entry = &p_list->p_entries[i];
> + if (!p_entry->p_secret)
> + continue;
> +
> + WRITE_STR("{");
> + if (values_write(p_file, (const char *const *)
> p_entry->ppsz_values))
> + goto end;
> + WRITE_STR("}:");
> + char *psz_b64 = vlc_b64_encode_binary(p_entry->p_secret,
> +
> p_entry->i_secret_len);
> + if (!psz_b64)
> + goto end;
> + WRITE_STR(psz_b64);
> + free(psz_b64);
> + if (i < p_list->i_count - 1)
> + WRITE_STR("\n");
> + }
> + i_ret = VLC_SUCCESS;
> +end:
> +
> + fclose(p_file);
> + if (i_ret != VLC_SUCCESS)
> + {
> + vlc_unlink(psz_file);
> + list_free(p_list);
> + }
> + return i_ret;
> +}
> +#undef WRITE_STR
> +
> +static int
> +list_read(struct list *p_list, const char *psz_file)
> +{
> + /* "a+" to create the file if it doesn't exist */
> + FILE *p_file = vlc_fopen(psz_file, "a+");
> + if (!p_file)
> + return VLC_EGENERIC;
> +
> + char *psz_line = NULL;
> + size_t i_line_len = 0;
> + ssize_t i_read;
> + bool b_valid = false;
> +
> + while ((i_read = getline(&psz_line, &i_line_len, p_file)) != -1)
> + {
> + char *p = psz_line;
> + if (*(p++) != '{')
> + goto end;
> +
> + vlc_keystore_entry *p_entry = list_new_entry(p_list);
> + if (!p_entry)
> + goto end;
> +
> + bool b_end = false;
> + while (*p != '\0' && !b_end)
> + {
> + int i_key;
> + char *p_key, *p_value;
> + size_t i_len;
> +
> + /* read key */
> + i_len = strcspn(p, ":");
> + if (!i_len || p[i_len] == '\0')
> + goto end;
> +
> + p[i_len] = '\0';
> + p_key = p;
> + i_key = str2key(p_key);
> + if (i_key == -1 || i_key >= KEY_MAX)
> + goto end;
> + p += i_len + 1;
> +
> + /* read value */
> + i_len = strcspn(p, ",}");
> + if (!i_len || p[i_len] == '\0')
> + goto end;
> +
> + if (p[i_len] == '}')
> + b_end = true;
> +
> + p[i_len] = '\0';
> + p_value = vlc_b64_decode(p); /* BASE 64 */
> + if (!p_value)
> + goto end;
> + p += i_len + 1;
> +
> + p_entry->ppsz_values[i_key] = p_value;
> + }
> + /* read passwd */
> + if (*p == '\0' || *p != ':')
> + goto end;
> +
> + p_entry->i_secret_len =
> vlc_b64_decode_binary(&p_entry->p_secret, p + 1);
> + if (!p_entry->p_secret)
> + goto end;
> + }
> +
> + b_valid = true;
> +
> +end:
> + free(psz_line);
> + fclose(p_file);
> + if (!b_valid)
> + {
> + vlc_unlink(psz_file);
> + list_free(p_list);
> + }
> + return VLC_SUCCESS;
> +}
> +
> +static int
> +Store(vlc_keystore *p_keystore, const char *const
> ppsz_values[KEY_MAX],
> + const uint8_t *p_secret, size_t i_secret_len, const char
> *psz_label)
> +{
> + (void) psz_label;
> + vlc_mutex_lock(&lock);
> +
> + (void) p_keystore;
> + vlc_keystore_sys *p_sys = &instance;
> + struct list *p_list = &p_sys->list;
> + vlc_keystore_entry *p_entry = list_get_entry(p_list,
> ppsz_values, NULL);
> +
> + if (p_entry)
> + {
> + free(p_entry->p_secret);
> + p_entry->p_secret = NULL;
> + for (unsigned int i = 0; i < KEY_MAX; ++i)
> + {
> + free(p_entry->ppsz_values[i]);
> + p_entry->ppsz_values[i] = NULL;
> + }
> + }
> + else
> + {
> + p_entry = list_new_entry(p_list);
> + if (!p_entry)
> + goto error;
> + }
> + if (values_copy((const char **)p_entry->ppsz_values,
> ppsz_values))
> + goto error;
> +
> + if (vlc_keystore_entry_set_secret(p_entry, p_secret,
> i_secret_len))
> + goto error;
> +
> + int i_ret = list_save(&p_sys->list, p_sys->psz_file);
> + vlc_mutex_unlock(&lock);
> + return i_ret;
> +
> +error:
> + vlc_mutex_unlock(&lock);
> + return VLC_EGENERIC;
> +}
> +
> +static unsigned int
> +Find(vlc_keystore *p_keystore, const char *const
> ppsz_values[KEY_MAX],
> + vlc_keystore_entry **pp_entries)
> +{
> + vlc_mutex_lock(&lock);
> +
> + (void) p_keystore;
> + vlc_keystore_sys *p_sys = &instance;
> + struct list *p_list = &p_sys->list;
> + struct list out_list = { 0 };
> + vlc_keystore_entry *p_entry;
> + unsigned i_index = 0;
> +
> + while ((p_entry = list_get_entry(p_list, ppsz_values,
> &i_index)))
> + {
> + vlc_keystore_entry *p_out_entry = list_new_entry(&out_list);
> +
> + if (!p_out_entry || values_copy((const char
> **)p_out_entry->ppsz_values,
> + (const char
> *const*)p_entry->ppsz_values))
> + {
> + list_free(&out_list);
> + break;
> + }
> +
> + if (vlc_keystore_entry_set_secret(p_out_entry,
> p_entry->p_secret,
> + p_entry->i_secret_len))
> + {
> + list_free(&out_list);
> + break;
> + }
> + }
> +
> + *pp_entries = out_list.p_entries;
> +
> + vlc_mutex_unlock(&lock);
> + return out_list.i_count;
> +}
> +
> +static unsigned int
> +Remove(vlc_keystore *p_keystore, const char *const
> ppsz_values[KEY_MAX])
> +{
> + vlc_mutex_lock(&lock);
> +
> + (void) p_keystore;
> + vlc_keystore_sys *p_sys = &instance;
> + struct list *p_list = &p_sys->list;
> + vlc_keystore_entry *p_entry;
> + unsigned i_index = 0, i_count = 0;
> +
> + while ((p_entry = list_get_entry(p_list, ppsz_values,
> &i_index)))
> + {
> + free(p_entry->p_secret);
> + p_entry->p_secret = NULL;
> + i_count++;
> + }
> +
> + vlc_mutex_unlock(&lock);
> + return i_count;
> +}
> +
> +static int
> +Open(vlc_object_t *p_this)
> +{
> + vlc_mutex_lock(&lock);
> + if (i_ref_count == 0)
> + {
> + vlc_keystore_sys *p_sys = &instance;
> +
> + p_sys->psz_file = var_InheritString(p_this,
> "keystore-plaintext-file");
> + if (!p_sys->psz_file)
> + {
> + vlc_mutex_unlock(&lock);
> + return VLC_EGENERIC;
> + }
> +
> + if (list_read(&p_sys->list, p_sys->psz_file) != VLC_SUCCESS)
> + {
> + free(p_sys->psz_file);
> + vlc_mutex_unlock(&lock);
> + return VLC_EGENERIC;
> + }
> + }
> + i_ref_count++;
> + vlc_mutex_unlock(&lock);
> +
> + vlc_keystore *p_keystore = (vlc_keystore *)p_this;
> + p_keystore->pf_store = Store;
> + p_keystore->pf_find = Find;
> + p_keystore->pf_remove = Remove;
> +
> + return VLC_SUCCESS;
> +}
> +
> +static void
> +Close(vlc_object_t *p_this)
> +{
> + (void) p_this;
> + vlc_mutex_lock(&lock);
> +
> + if (--i_ref_count == 0)
> + {
> + vlc_keystore_sys *p_sys = &instance;
> +
> + list_free(&p_sys->list);
> + free(p_sys->psz_file);
> + }
> + vlc_mutex_unlock(&lock);
> +}
--
Rémi Denis-Courmont
http://www.remlab.net/
More information about the vlc-devel
mailing list