<commit_before>/*
 * virsh-secret.c: Commands to manage secret
 *
 * Copyright (C) 2005, 2007-2016 Red Hat, Inc.
 *
 * This library 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 library 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 library.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <config.h>
#include "virsh-secret.h"
#include "virsh-util.h"

#include "internal.h"
#include "virbuffer.h"
#include "viralloc.h"
#include "virfile.h"
#include "virutil.h"
#include "virsecret.h"
#include "virtime.h"
#include "vsh-table.h"
#include "virenum.h"

static virSecretPtr
virshCommandOptSecret(vshControl *ctl, const vshCmd *cmd, const char **name)
{
    virSecretPtr secret = NULL;
    const char *n = NULL;
    const char *optname = "secret";
    virshControlPtr priv = ctl->privData;

    if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
        return NULL;

    vshDebug(ctl, VSH_ERR_DEBUG,
             "%s: found option <%s>: %s\n", cmd->def->name, optname, n);

    if (name != NULL)
        *name = n;

    secret = virSecretLookupByUUIDString(priv->conn, n);

    if (secret == NULL)
        vshError(ctl, _("failed to get secret '%s'"), n);

    return secret;
}

/*
 * "secret-define" command
 */
static const vshCmdInfo info_secret_define[] = {
    {.name = "help",
     .data = N_("define or modify a secret from an XML file")
    },
    {.name = "desc",
     .data = N_("Define or modify a secret.")
    },
    {.name = NULL}
};

static const vshCmdOptDef opts_secret_define[] = {
    VIRSH_COMMON_OPT_FILE(N_("file containing secret attributes in XML")),
    {.name = NULL}
};

static bool
cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
{
    const char *from = NULL;
    char *buffer;
    virSecretPtr res;
    char uuid[VIR_UUID_STRING_BUFLEN];
    bool ret = false;
    virshControlPtr priv = ctl->privData;

    if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
        return false;

    if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
        return false;

    if (!(res = virSecretDefineXML(priv->conn, buffer, 0))) {
        vshError(ctl, _("Failed to set attributes from %s"), from);
        goto cleanup;
    }

    if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
        vshError(ctl, "%s", _("Failed to get UUID of created secret"));
        goto cleanup;
    }

    vshPrintExtra(ctl, _("Secret %s created\n"), uuid);
    ret = true;

 cleanup:
    VIR_FREE(buffer);
    virshSecretFree(res);
    return ret;
}

/*
 * "secret-dumpxml" command
 */
static const vshCmdInfo info_secret_dumpxml[] = {
    {.name = "help",
     .data = N_("secret attributes in XML")
    },
    {.name = "desc",
     .data = N_("Output attributes of a secret as an XML dump to stdout.")
    },
    {.name = NULL}
};

static const vshCmdOptDef opts_secret_dumpxml[] = {
    {.name = "secret",
     .type = VSH_OT_DATA,
     .flags = VSH_OFLAG_REQ,
     .help = N_("secret UUID"),
     .completer = virshSecretUUIDCompleter,
    },
    {.name = NULL}
};

static bool
cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
    bool ret = false;
    char *xml;

    secret = virshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
        return false;

    xml = virSecretGetXMLDesc(secret, 0);
    if (xml == NULL)
        goto cleanup;
    vshPrint(ctl, "%s", xml);
    VIR_FREE(xml);
    ret = true;

 cleanup:
    virshSecretFree(secret);
    return ret;
}

/*
 * "secret-set-value" command
 */
static const vshCmdInfo info_secret_set_value[] = {
    {.name = "help",
     .data = N_("set a secret value")
    },
    {.name = "desc",
     .data = N_("Set a secret value.")
    },
    {.name = NULL}
};

static const vshCmdOptDef opts_secret_set_value[] = {
    {.name = "secret",
     .type = VSH_OT_DATA,
     .flags = VSH_OFLAG_REQ,
     .help = N_("secret UUID"),
     .completer = virshSecretUUIDCompleter,
    },
    {.name = "file",
     .type = VSH_OT_STRING,
     .flags = VSH_OFLAG_REQ_OPT,
     .help = N_("read secret from file"),
    },
    {.name = "plain",
     .type = VSH_OT_BOOL,
     .help = N_("read the secret from file without converting from base64")
    },
    {.name = "interactive",
     .type = VSH_OT_BOOL,
     .help = N_("read the secret from the terminal")
    },
    {.name = "base64",
     .type = VSH_OT_STRING,
     .help = N_("base64-encoded secret value")
    },
    {.name = NULL}
};

static bool
cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
{
    g_autoptr(virshSecret) secret = NULL;
    const char *base64 = NULL;
    const char *filename = NULL;
    char *file_buf = NULL;
    size_t file_len = 0;
    unsigned char *value;
    size_t value_size;
    bool plain = vshCommandOptBool(cmd, "plain");
    bool interactive = vshCommandOptBool(cmd, "interactive");
    int res;

    VSH_EXCLUSIVE_OPTIONS("file", "base64");
    VSH_EXCLUSIVE_OPTIONS("plain", "base64");
    VSH_EXCLUSIVE_OPTIONS("interactive", "base64");
    VSH_EXCLUSIVE_OPTIONS("interactive", "plain");
    VSH_EXCLUSIVE_OPTIONS("interactive", "file");

    if (!(secret = virshCommandOptSecret(ctl, cmd, NULL)))
        return false;

    if (vshCommandOptStringReq(ctl, cmd, "base64", &base64) < 0)
        return false;

    if (vshCommandOptStringReq(ctl, cmd, "file", &filename) < 0)
        return false;

    if (base64) {
        /* warn users that the --base64 option passed from command line is wrong */
        vshError(ctl, _("Passing secret value as command-line argument is insecure!"));
    } else if (filename) {
        ssize_t read_ret;
        if ((read_ret = virFileReadAll(filename, 1024, &file_buf)) < 0) {
            vshSaveLibvirtError();
            return false;
        }

        file_len = read_ret;
        base64 = file_buf;
    } else if (interactive) {
        vshPrint(ctl, "%s", _("Enter new value for secret:"));
        fflush(stdout);

        if (!(file_buf = virGetPassword())) {
            vshError(ctl, "%s", _("Failed to read secret"));
            return false;
        }
        file_len = strlen(file_buf);
        plain = true;
    } else {
        vshError(ctl, _("Input secret value is missing"));
        return false;
    }

    if (plain) {
        value = g_steal_pointer(&file_buf);
        value_size = file_len;
        file_len = 0;
    } else {
        value = g_base64_decode(base64, &value_size);
    }

    res = virSecretSetValue(secret, value, value_size, 0);
    VIR_DISPOSE_N(value, value_size);
    VIR_DISPOSE_N(file_buf, file_len);

    if (res != 0) {
        vshError(ctl, "%s", _("Failed to set secret value"));
        return false;
    }
    vshPrintExtra(ctl, "%s", _("Secret value set\n"));
    return true;
}

/*
 * "secret-get-value" command
 */
static const vshCmdInfo info_secret_get_value[] = {
    {.name = "help",
     .data = N_("Output a secret value")
    },
    {.name = "desc",
     .data = N_("Output a secret value to stdout.")
    },
    {.name = NULL}
};

static const vshCmdOptDef opts_secret_get_value[] = {
    {.name = "secret",
     .type = VSH_OT_DATA,<commit_msg>virsh: cmdSecretSetValue: Rework handling of the secret value<commit_after>/*
 * virsh-secret.c: Commands to manage secret
 *
 * Copyright (C) 2005, 2007-2016 Red Hat, Inc.
 *
 * This library 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 library 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 library.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <config.h>
#include "virsh-secret.h"
#include "virsh-util.h"

#include "internal.h"
#include "virbuffer.h"
#include "viralloc.h"
#include "virfile.h"
#include "virutil.h"
#include "virsecret.h"
#include "virtime.h"
#include "vsh-table.h"
#include "virenum.h"
#include "virsecureerase.h"

static virSecretPtr
virshCommandOptSecret(vshControl *ctl, const vshCmd *cmd, const char **name)
{
    virSecretPtr secret = NULL;
    const char *n = NULL;
    const char *optname = "secret";
    virshControlPtr priv = ctl->privData;

    if (vshCommandOptStringReq(ctl, cmd, optname, &n) < 0)
        return NULL;

    vshDebug(ctl, VSH_ERR_DEBUG,
             "%s: found option <%s>: %s\n", cmd->def->name, optname, n);

    if (name != NULL)
        *name = n;

    secret = virSecretLookupByUUIDString(priv->conn, n);

    if (secret == NULL)
        vshError(ctl, _("failed to get secret '%s'"), n);

    return secret;
}

/*
 * "secret-define" command
 */
static const vshCmdInfo info_secret_define[] = {
    {.name = "help",
     .data = N_("define or modify a secret from an XML file")
    },
    {.name = "desc",
     .data = N_("Define or modify a secret.")
    },
    {.name = NULL}
};

static const vshCmdOptDef opts_secret_define[] = {
    VIRSH_COMMON_OPT_FILE(N_("file containing secret attributes in XML")),
    {.name = NULL}
};

static bool
cmdSecretDefine(vshControl *ctl, const vshCmd *cmd)
{
    const char *from = NULL;
    char *buffer;
    virSecretPtr res;
    char uuid[VIR_UUID_STRING_BUFLEN];
    bool ret = false;
    virshControlPtr priv = ctl->privData;

    if (vshCommandOptStringReq(ctl, cmd, "file", &from) < 0)
        return false;

    if (virFileReadAll(from, VSH_MAX_XML_FILE, &buffer) < 0)
        return false;

    if (!(res = virSecretDefineXML(priv->conn, buffer, 0))) {
        vshError(ctl, _("Failed to set attributes from %s"), from);
        goto cleanup;
    }

    if (virSecretGetUUIDString(res, &(uuid[0])) < 0) {
        vshError(ctl, "%s", _("Failed to get UUID of created secret"));
        goto cleanup;
    }

    vshPrintExtra(ctl, _("Secret %s created\n"), uuid);
    ret = true;

 cleanup:
    VIR_FREE(buffer);
    virshSecretFree(res);
    return ret;
}

/*
 * "secret-dumpxml" command
 */
static const vshCmdInfo info_secret_dumpxml[] = {
    {.name = "help",
     .data = N_("secret attributes in XML")
    },
    {.name = "desc",
     .data = N_("Output attributes of a secret as an XML dump to stdout.")
    },
    {.name = NULL}
};

static const vshCmdOptDef opts_secret_dumpxml[] = {
    {.name = "secret",
     .type = VSH_OT_DATA,
     .flags = VSH_OFLAG_REQ,
     .help = N_("secret UUID"),
     .completer = virshSecretUUIDCompleter,
    },
    {.name = NULL}
};

static bool
cmdSecretDumpXML(vshControl *ctl, const vshCmd *cmd)
{
    virSecretPtr secret;
    bool ret = false;
    char *xml;

    secret = virshCommandOptSecret(ctl, cmd, NULL);
    if (secret == NULL)
        return false;

    xml = virSecretGetXMLDesc(secret, 0);
    if (xml == NULL)
        goto cleanup;
    vshPrint(ctl, "%s", xml);
    VIR_FREE(xml);
    ret = true;

 cleanup:
    virshSecretFree(secret);
    return ret;
}

/*
 * "secret-set-value" command
 */
static const vshCmdInfo info_secret_set_value[] = {
    {.name = "help",
     .data = N_("set a secret value")
    },
    {.name = "desc",
     .data = N_("Set a secret value.")
    },
    {.name = NULL}
};

static const vshCmdOptDef opts_secret_set_value[] = {
    {.name = "secret",
     .type = VSH_OT_DATA,
     .flags = VSH_OFLAG_REQ,
     .help = N_("secret UUID"),
     .completer = virshSecretUUIDCompleter,
    },
    {.name = "file",
     .type = VSH_OT_STRING,
     .flags = VSH_OFLAG_REQ_OPT,
     .help = N_("read secret from file"),
    },
    {.name = "plain",
     .type = VSH_OT_BOOL,
     .help = N_("read the secret from file without converting from base64")
    },
    {.name = "interactive",
     .type = VSH_OT_BOOL,
     .help = N_("read the secret from the terminal")
    },
    {.name = "base64",
     .type = VSH_OT_STRING,
     .help = N_("base64-encoded secret value")
    },
    {.name = NULL}
};

static bool
cmdSecretSetValue(vshControl *ctl, const vshCmd *cmd)
{
    g_autoptr(virshSecret) secret = NULL;
    const char *base64 = NULL;
    const char *filename = NULL;
    g_autofree char *secret_val = NULL;
    size_t secret_len = 0;
    bool plain = vshCommandOptBool(cmd, "plain");
    bool interactive = vshCommandOptBool(cmd, "interactive");
    int res;

    VSH_EXCLUSIVE_OPTIONS("file", "base64");
    VSH_EXCLUSIVE_OPTIONS("plain", "base64");
    VSH_EXCLUSIVE_OPTIONS("interactive", "base64");
    VSH_EXCLUSIVE_OPTIONS("interactive", "plain");
    VSH_EXCLUSIVE_OPTIONS("interactive", "file");

    if (!(secret = virshCommandOptSecret(ctl, cmd, NULL)))
        return false;

    if (vshCommandOptStringReq(ctl, cmd, "base64", &base64) < 0)
        return false;

    if (vshCommandOptStringReq(ctl, cmd, "file", &filename) < 0)
        return false;

    if (base64) {
        /* warn users that the --base64 option passed from command line is wrong */
        vshError(ctl, _("Passing secret value as command-line argument is insecure!"));
        secret_val = g_strdup(base64);
        secret_len = strlen(secret_val);
    } else if (filename) {
        ssize_t read_ret;
        if ((read_ret = virFileReadAll(filename, 1024, &secret_val)) < 0) {
            vshSaveLibvirtError();
            return false;
        }

        secret_len = read_ret;
    } else if (interactive) {
        vshPrint(ctl, "%s", _("Enter new value for secret:"));
        fflush(stdout);

        if (!(secret_val = virGetPassword())) {
            vshError(ctl, "%s", _("Failed to read secret"));
            return false;
        }
        secret_len = strlen(secret_val);
        plain = true;
    } else {
        vshError(ctl, _("Input secret value is missing"));
        return false;
    }

    if (!plain) {
        g_autofree char *tmp = g_steal_pointer(&secret_val);
        size_t tmp_len = secret_len;

        secret_val = (char *) g_base64_decode(tmp, &secret_len);
        virSecureErase(tmp, tmp_len);
    }

    res = virSecretSetValue(secret, (unsigned char *) secret_val, secret_len, 0);
    virSecureErase(secret_val, secret_len);

    if (res != 0) {
        vshError(ctl, "%s", _("Failed to set secret value"));
        return false;
    }
    vshPrintExtra(ctl, "%s", _("Secret value set\n"));
    return true;
}

/*
 * "secret-get-value" command
 */
static const vshCmdInfo info_secret_get_value[] = {
    {.name = "help",
     .data = N_("Output a secret value")
    },
    {.name = "desc",
     .data = N_("Output a secret value to stdout.")
    },
    {.name = NULL}
};

static const vshCmdOptDef opts_secret_get_value[] = {
    {.name = "secret",
     .type = VSH_OT_DATA,
{'fraction_non_alphanumeric': 0.13357377049180327, 'fraction_numerical': 0.007672131147540983, 'mean_word_length': 2.8194340095166543, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 16}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4699306', 'n_tokens_mistral': 5292, 'n_tokens_neox': 5106, 'n_words': 1431}