secret_conf.c 9.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * secret_conf.c: internal <secret> XML handling
 *
 * Copyright (C) 2009 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * Red Hat Author: Miloslav Trmač <mitr@redhat.com>
 */

#include <config.h>

#include "internal.h"
#include "buf.h"
#include "datatypes.h"
#include "logging.h"
#include "memory.h"
#include "secret_conf.h"
#include "virterror_internal.h"
#include "util.h"
#include "xml.h"
34
#include "uuid.h"
35 36 37

#define VIR_FROM_THIS VIR_FROM_SECRET

38
VIR_ENUM_IMPL(virSecretUsageType, VIR_SECRET_USAGE_TYPE_VOLUME + 1, "none", "volume")
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62

void
virSecretDefFree(virSecretDefPtr def)
{
    if (def == NULL)
        return;

    VIR_FREE(def->description);
    switch (def->usage_type) {
    case VIR_SECRET_USAGE_TYPE_NONE:
        break;

    case VIR_SECRET_USAGE_TYPE_VOLUME:
        VIR_FREE(def->usage.volume);
        break;

    default:
        VIR_ERROR(_("unexpected secret usage type %d"), def->usage_type);
        break;
    }
    VIR_FREE(def);
}

static int
63
virSecretDefParseUsage(xmlXPathContextPtr ctxt,
64 65 66 67 68
                       virSecretDefPtr def)
{
    char *type_str;
    int type;

69
    type_str = virXPathString("string(./usage/@type)", ctxt);
70
    if (type_str == NULL) {
71
        virSecretReportError(VIR_ERR_XML_ERROR, "%s",
72 73 74 75 76
                             _("unknown secret usage type"));
        return -1;
    }
    type = virSecretUsageTypeTypeFromString(type_str);
    if (type < 0) {
77
        virSecretReportError(VIR_ERR_XML_ERROR,
78 79 80 81 82 83 84 85 86 87 88
                             _("unknown secret usage type %s"), type_str);
        VIR_FREE(type_str);
        return -1;
    }
    VIR_FREE(type_str);
    def->usage_type = type;
    switch (def->usage_type) {
    case VIR_SECRET_USAGE_TYPE_NONE:
        break;

    case VIR_SECRET_USAGE_TYPE_VOLUME:
89
        def->usage.volume = virXPathString("string(./usage/volume)", ctxt);
90
        if (!def->usage.volume) {
91
            virSecretReportError(VIR_ERR_INTERNAL_ERROR, "%s",
92 93 94
                                 _("volume usage specified, but volume path is missing"));
            return -1;
        }
95 96 97
        break;

    default:
98
        virSecretReportError(VIR_ERR_INTERNAL_ERROR,
99 100 101 102 103 104 105 106
                             _("unexpected secret usage type %d"),
                             def->usage_type);
        return -1;
    }
    return 0;
}

static virSecretDefPtr
107
secretXMLParseNode(xmlDocPtr xml, xmlNodePtr root)
108 109 110 111
{
    xmlXPathContextPtr ctxt = NULL;
    virSecretDefPtr def = NULL, ret = NULL;
    char *prop = NULL;
112
    char *uuidstr = NULL;
113 114

    if (!xmlStrEqual(root->name, BAD_CAST "secret")) {
115
        virSecretReportError(VIR_ERR_XML_ERROR, "%s",
116 117 118 119 120 121
                             _("incorrect root element"));
        goto cleanup;
    }

    ctxt = xmlXPathNewContext(xml);
    if (ctxt == NULL) {
122
        virReportOOMError();
123 124 125 126 127
        goto cleanup;
    }
    ctxt->node = root;

    if (VIR_ALLOC(def) < 0) {
128
        virReportOOMError();
129 130 131
        goto cleanup;
    }

132
    prop = virXPathString("string(./@ephemeral)", ctxt);
133 134 135 136 137 138
    if (prop != NULL) {
        if (STREQ(prop, "yes"))
            def->ephemeral = 1;
        else if (STREQ(prop, "no"))
            def->ephemeral = 0;
        else {
139
            virSecretReportError(VIR_ERR_XML_ERROR, "%s",
140 141 142 143 144 145
                                 _("invalid value of 'ephemeral'"));
            goto cleanup;
        }
        VIR_FREE(prop);
    }

146
    prop = virXPathString("string(./@private)", ctxt);
147 148 149 150 151 152
    if (prop != NULL) {
        if (STREQ(prop, "yes"))
            def->private = 1;
        else if (STREQ(prop, "no"))
            def->private = 0;
        else {
153
            virSecretReportError(VIR_ERR_XML_ERROR, "%s",
154 155 156 157 158 159
                                 _("invalid value of 'private'"));
            goto cleanup;
        }
        VIR_FREE(prop);
    }

160
    uuidstr = virXPathString("string(./uuid)", ctxt);
161 162
    if (!uuidstr) {
        if (virUUIDGenerate(def->uuid)) {
163
            virSecretReportError(VIR_ERR_INTERNAL_ERROR,
164 165 166 167 168
                                 "%s", _("Failed to generate UUID"));
            goto cleanup;
        }
    } else {
        if (virUUIDParse(uuidstr, def->uuid) < 0) {
169
            virSecretReportError(VIR_ERR_INTERNAL_ERROR,
170 171 172 173 174 175
                                 "%s", _("malformed uuid element"));
            goto cleanup;
        }
        VIR_FREE(uuidstr);
    }

176 177
    def->description = virXPathString("string(./description)", ctxt);
    if (virXPathNode("./usage", ctxt) != NULL
178
        && virSecretDefParseUsage(ctxt, def) < 0)
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
        goto cleanup;
    ret = def;
    def = NULL;

 cleanup:
    VIR_FREE(prop);
    virSecretDefFree(def);
    xmlXPathFreeContext(ctxt);
    return ret;
}

/* Called from SAX on parsing errors in the XML. */
static void
catchXMLError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...)
{
    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;

    if (ctxt) {
        if (virGetLastError() == NULL &&
            ctxt->lastError.level == XML_ERR_FATAL &&
            ctxt->lastError.message != NULL) {
200
            virSecretReportError(VIR_ERR_XML_DETAIL, _("at line %d: %s"),
201 202 203 204 205 206
                                 ctxt->lastError.line, ctxt->lastError.message);
        }
    }
}

static virSecretDefPtr
207
virSecretDefParse(const char *xmlStr, const char *filename)
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
{
    xmlParserCtxtPtr pctxt;
    xmlDocPtr xml = NULL;
    xmlNodePtr root;
    virSecretDefPtr ret = NULL;

    pctxt = xmlNewParserCtxt();
    if (pctxt == NULL || pctxt->sax == NULL)
        goto cleanup;
    pctxt->sax->error = catchXMLError;

    if (filename != NULL)
        xml = xmlCtxtReadFile(pctxt, filename, NULL,
                              XML_PARSE_NOENT | XML_PARSE_NONET |
                              XML_PARSE_NOWARNING);
    else
        xml = xmlCtxtReadDoc(pctxt, BAD_CAST xmlStr, "secret.xml", NULL,
                             XML_PARSE_NOENT | XML_PARSE_NONET |
                             XML_PARSE_NOWARNING);
    if (xml == NULL) {
228 229
        if (virGetLastError() == NULL)
            virSecretReportError(VIR_ERR_XML_ERROR, "%s",
230 231 232 233 234 235
                                 _("failed to parse xml document"));
        goto cleanup;
    }

    root = xmlDocGetRootElement(xml);
    if (root == NULL) {
236
        virSecretReportError(VIR_ERR_INTERNAL_ERROR, "%s",
237 238 239 240
                             _("missing root element"));
        goto cleanup;
    }

241
    ret = secretXMLParseNode(xml, root);
242 243 244 245 246 247 248 249

 cleanup:
    xmlFreeDoc(xml);
    xmlFreeParserCtxt(pctxt);
    return ret;
}

virSecretDefPtr
250
virSecretDefParseString(const char *xmlStr)
251
{
252
    return virSecretDefParse(xmlStr, NULL);
253 254 255
}

virSecretDefPtr
256
virSecretDefParseFile(const char *filename)
257
{
258
    return virSecretDefParse(NULL, filename);
259 260 261
}

static int
262
virSecretDefFormatUsage(virBufferPtr buf,
263 264 265 266 267 268
                        const virSecretDefPtr def)
{
    const char *type;

    type = virSecretUsageTypeTypeToString(def->usage_type);
    if (type == NULL) {
269
        virSecretReportError(VIR_ERR_INTERNAL_ERROR,
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
                             _("unexpected secret usage type %d"),
                             def->usage_type);
        return -1;
    }
    virBufferVSprintf(buf, "  <usage type='%s'>\n", type);
    switch (def->usage_type) {
    case VIR_SECRET_USAGE_TYPE_NONE:
        break;

    case VIR_SECRET_USAGE_TYPE_VOLUME:
        if (def->usage.volume != NULL)
            virBufferEscapeString(buf, "    <volume>%s</volume>\n",
                                  def->usage.volume);
        break;

    default:
286
        virSecretReportError(VIR_ERR_INTERNAL_ERROR,
287 288 289 290 291 292 293 294 295 296
                             _("unexpected secret usage type %d"),
                             def->usage_type);
        return -1;
    }
    virBufferAddLit(buf, "  </usage>\n");

    return 0;
}

char *
297
virSecretDefFormat(const virSecretDefPtr def)
298 299
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;
300 301
    unsigned char *uuid;
    char uuidstr[VIR_UUID_STRING_BUFLEN];
302 303 304 305

    virBufferVSprintf(&buf, "<secret ephemeral='%s' private='%s'>\n",
                      def->ephemeral ? "yes" : "no",
                      def->private ? "yes" : "no");
306 307 308 309

    uuid = def->uuid;
    virUUIDFormat(uuid, uuidstr);
    virBufferEscapeString(&buf, "  <uuid>%s</uuid>\n", uuidstr);
310 311 312 313
    if (def->description != NULL)
        virBufferEscapeString(&buf, "  <description>%s</description>\n",
                              def->description);
    if (def->usage_type != VIR_SECRET_USAGE_TYPE_NONE &&
314
        virSecretDefFormatUsage(&buf, def) < 0)
315 316 317 318 319 320 321 322 323
        goto error;
    virBufferAddLit(&buf, "</secret>\n");

    if (virBufferError(&buf))
        goto no_memory;

    return virBufferContentAndReset(&buf);

 no_memory:
324
    virReportOOMError();
325
 error:
326
    virBufferFreeAndReset(&buf);
327 328
    return NULL;
}