[libvirt] Authentication with virConnectAuthPtr

Daniel P. Berrange berrange at redhat.com
Tue May 5 20:50:49 UTC 2009


On Tue, May 05, 2009 at 04:06:12PM -0300, Eduardo Otubo wrote:
> Hello all,
> 
> I'll start using virConnectAuthPtr to handle the authentication proccess
> at the phyp driver I'm wrinting
> <https://www.redhat.com/archives/libvir-list/2009-April/msg00493.html>
> and I have some doubts in my mind:
> 
> 1) What are the types of authentication that 'int *credtype' can hold?
> And who fills it with the information I'll need?

These are all defined in libvirt.h - we basically copied the SASL
credential types

    VIR_CRED_USERNAME = 1,     /* Identity to act as */
    VIR_CRED_AUTHNAME = 2,     /* Identify to authorize as */
    VIR_CRED_LANGUAGE = 3,     /* RFC 1766 languages, comma separated */
    VIR_CRED_CNONCE = 4,       /* client supplies a nonce */
    VIR_CRED_PASSPHRASE = 5,   /* Passphrase secret */
    VIR_CRED_ECHOPROMPT = 6,   /* Challenge response */
    VIR_CRED_NOECHOPROMPT = 7, /* Challenge response */
    VIR_CRED_REALM = 8,        /* Authentication realm */
    VIR_CRED_EXTERNAL = 9,     /* Externally managed credential */

The client application filles in the data in virConnectAuth struct,
providing the function callback pointer, an opaque data blob (cddata)
and the list of credential types it knows how to handle.

> 2) Once known the credential type, I need to use the function pointer
> 'virConnectAuthCallbackPtr cb' to get the information whatever it is,
> right? I mean, it can be a password, a pubkey or anything else, right?
> Is there a callback able to handle password or pubkeys?

Yep, once your driver knows what credentials it needs to collect, it
should check to see if the client has declared it supports the required
credentials. If it does, then the driver should populate  an array of
virConnectCredential objects detailing what credentials needs to be
collected. The callback is then invoked, and the client app will collect
the credentials, putting the results in the 'result' / resultlen fields.

When the callback returns, the driver can get the credentials fro mthe
result/resultlen field - though it should check the callback return
status to see if the client app had any errors.

> 3) And finally I'll be able to use the 'void *cbdata' to manage the
> authentication in my way. In my case, using libssh. In fact, in the end
> of the process, I'll just need a password or a key to get things
> working.

The 'cbdata' field is something used by the client app. Both a password
or private key passphrase would map to VIR_CRED_PASSPHRASE credential
type. You'd just have a different prompt / challenge 

> There is no driver authenticatin agaist ssh channel, that's why I got so
> confused with this topic.
> 
> I don't know if my thoughts are pretty clear here. But I would like to
> confirm the information 2 and 3, and check how 1 works. Could anyone
> help me?

Let me give you a close-to-working example that tries to illustrate 
the general idea from the driver point of view.

First off, libvirt.so provides a generic callback that works for command
line based apps, and knows how to collect credentials of type

  static int virConnectCredTypeDefault[] = {
    VIR_CRED_AUTHNAME,
    VIR_CRED_ECHOPROMPT,
    VIR_CRED_REALM,
    VIR_CRED_PASSPHRASE,
    VIR_CRED_NOECHOPROMPT,
    VIR_CRED_EXTERNAL,
  };

  static virConnectAuth virConnectAuthDefault = {
    virConnectCredTypeDefault,
    sizeof(virConnectCredTypeDefault)/sizeof(int),
    virConnectAuthCallbackDefault,
    NULL,
  };


This is what virsh uses when connecting to libvirt

    ctl->conn = virConnectOpenAuth(ctl->name, virConnectAuthPtrDefault, 0)



Now, in your Phyp  driver, lets assume at some point in time libssh tells
it wants a username+password. 

These will map to VIR_CRED_AUTHNAME and VIR_CRED_PASSPHRASE, so first
step is to ensure the callback passed in by the client app supports
these

  static virDrvOpenStatus phypOpen(virConnectPtr conn,
                                   virConnectAuthPtr auth,
                                   int flags) {
 
   .... start ssh conection ...

   if (need password) {
     int i;
     int hasPassphrase = 0
     int hasAuthname = 0;
     /* The creds we're about to ask the application for... */
     virConnectCredentials creds[] = {
        { VIR_CRED_AUTHNAME, "Enter username:", "Username", NULL, NULL, 0 },
        { VIR_CRED_PASSPHRASE, "Enter password:", "Password", NULL, NULL, 0 },
     };

     /* If no auth callback info was provided, must fail now */
     if (!auth || !auth->cb) {
       virPhypRaiseError(conn, VIR_ERR_AUTH_FAILED, "%s",
                         _("no authentication callback provided"));
       return VIR_OPEN_FAILED;
     }

     /* Check if desired credentials are supported */
     for  (i = 0 ; i < auth.ncredtype ; i++) {
       if (auth.credtype[i] == VIR_CRED_AUTHNAME)
            hasAuthName = 1;
       if (auth.credtype[i] == VIR_CRED_PASSPHRASE)
            hasPassphrase = 1;
     }
     if (!hasPassphrase || !hasAuthname) {
         virPhypRaiseError(conn, VIR_ERR_AUTH_FAILED, "%s",
                           _("required credentials are not supported"));
         return VIR_OPEN_FAILED;
     }


     /* Credential supported, to try to get them from user */
     int res = (auth->cb)(creds, ARRAY_CARDINALITY(creds), auth->cbdata);

     if (res < 0) {
         virPhypRaiseError(conn, VIR_ERR_AUTH_FAILED, "%s",
                           _("unable to fetch credentials"));
         return VIR_OPEN_FAILED;
     }

     char *username = creds[0]->result;
     char *password = creds[1]->result;
       .... give these back to libssh now ....
    }


This example code is quite inflexible & hardcoded to the specifics of
getting a username + password. Depending on libssh's API you may want
or need todo things slightly differently.  For example, you probably
won't need to actually prompt for username, since I imagine you're
getting that from the connection URI directly. 

You may have to try several credentials in turn, requiring multiple
invocations of the callback. For example, first time you may prompt
for a passphrase for a SSH private key. If private key auth fails,
you may then continue onto password based auth, and need to prompt
for login password. So in that cae you'd be invoking the callback
twice asking for different types of data each time.

Also, don't assume that the 'result' field is NULL terminated, or
that 'result' is even non-NULL - sanity check  this data you get
back from the client app before using it. 

Regards,
Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list