[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