[libvirt] [PATCH 07/20] Secret manipulation step 7: Local driver

Miloslav Trmac mitr at redhat.com
Wed Aug 19 14:38:54 UTC 2009


----- "Daniel Veillard" <veillard at redhat.com> wrote:
> On Sun, Aug 16, 2009 at 10:48:00PM +0200, Miloslav Trmač wrote:
> > This implementation stores the secrets in an unencrypted text file,
> > for simplicity in implementation and debugging.
> > 
> > (Symmetric encryption, e.g. using gpgme, will not be difficult to add.
> > Because the TLS private key used by libvirtd is stored unencrypted,
> > encrypting the secrets file does not currently provide much additional
> > security.)
> 
>   What if we change our mind in some time, would there be any obstacle
> to dynamically detecting things are not encrypted and switching to
> a crypted file transparently?
Two simple ways:
1) use a different file name, e,g. secrets.gpg
2) check the start of the file, currently the file always starts with "id ";
   I can add a proper magic number if necessary.


> > +static int
> > +writeBase64Data(virConnectPtr conn, int fd, const char *field,
> > +                const void *data, size_t size)
> > +{
> > +    int ret = -1;
> > +    char *base64 = NULL;
> > +
> > +    if (writeString(conn, fd, field) < 0 || writeString(conn, fd, "
> ") < 0)
> > +        goto cleanup;
> > +
> > +    base64_encode_alloc(data, size, &base64);
> 
>   where is base64_encode_alloc coming from ?
gnulib - in a later patch I'm afraid, I'll reorder it.

> > +static int
> > +saveSecrets(virConnectPtr conn, virSecretDriverStatePtr driver)
> > +{
> > +    const virSecretEntry *secret;
> > +    char *tmp_path = NULL;
> > +    int fd = -1, ret = -1;
> > +
> > +    if (virAsprintf(&tmp_path, "%sXXXXXX", driver->filename) < 0)
> {
> > +        virReportOOMError(conn);
> > +        goto cleanup;
> > +    }
> > +    fd = mkstemp (tmp_path);
> 
>   Hum even if unencrypted, we should make sure of the mode of the file
> beforehand...
Good point.
>  isn't there a safer equivalent (possibly made postable by gnulib ?)
Grepping of gnulib does not reveal any.

> > +    if (fd == -1) {
> > +        virReportSystemError (conn, errno, _("mkstemp(\"%s\") failed"),
> > +                              tmp_path);
> > +        goto cleanup;
> > +    }
> > +
> > +    for (secret = driver->secrets; secret != NULL;
> > +         secret = secret->next) {
> > +        if (!secret->ephemeral && writeSecret(conn, fd, secret) < 0)
> > +            goto cleanup;
> > +    }
> > +    close(fd);
> > +    fd = -1;
> > +    if (rename(tmp_path, driver->filename) < 0) {
> > +        virReportSystemError (conn, errno, _("rename(%s, %s) failed"), tmp_path,
> > +                              driver->filename);
> > +        goto cleanup;
> > +    }
> 
>    Hum, the whole set smells fishy, we are creating a temp file, without
> mode checked, then moving that file somewhere else. I would ratehr have
> internal APIsdeling with a dump to memory and then a single open, safe
> and directly to the driver->filename.
"Somewhere else" is in the same directory.  The mkstemp() + rename() is used to make sure the secrets file is replaced atomically, without losing any data if a save is interrupted in the middle.

> > +static int
> > +parseBase64String(virConnectPtr conn, const char *base64, char
> **string)
> > +{
> > +    char *tmp;
> > +    size_t size;
> > +
> > +    if (!base64_decode_alloc(base64, strlen(base64), &tmp, &size))
> {
> 
>   where is base64_decode_alloc coming from ?
gnulib


> > +    if (stat(driver->filename, &st) < 0) {
> > +        if (errno == ENOENT)
> > +            return 0;
> > +        virReportSystemError (conn, errno, _("cannot stat '%s'"),
> > +                              driver->filename);
> > +        goto cleanup;
> > +    }
> > +    if ((size_t)st.st_size != st.st_size || (size_t)(st.st_size + 1) == 0) {
> > +        virSecretReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
> > +                             _("secrets file does not fit in memory"));
> > +        goto cleanup;
> > +    }
> > +
> > +    fd = open(driver->filename, O_RDONLY);
> > +    if (fd == -1) {
> > +        virReportSystemError (conn, errno, _("cannot open '%s'"),
> > +                              driver->filename);
> > +        goto cleanup;
> > +    }
> 
>    stat()/open() introduces a small race, I think open() and
> then fdstat() is a bit cleaner, not a big deal though
Technically there is still a race with fstat() - but fstat is a bit better.

> > +    if (VIR_ALLOC_N(contents, st.st_size + 1) < 0) {
> > +        virReportOOMError(conn);
> > +        goto cleanup;
> > +    }
> > +    if (saferead(fd, contents, st.st_size) != st.st_size) {
> > +        virReportSystemError (conn, errno, _("cannot read '%s'"),
> > +                              driver->filename);
> > +        goto cleanup;
> > +    }
> > +    close(fd);
> > +    fd = -1;
> 
>   contents[st.st_size] = 0;
>   needed here.
VIR_ALLOC_N automatically zeroes the memory.


I'll fix all other issues you have mentioned.  Thanks for the review.
    Mirek




More information about the libvir-list mailing list