This is the 2nd iteration of my remote authentication / SASL patches
previously provided here:
http://www.redhat.com/archives/libvir-list/2007-October/msg00131.html
In this iteration I've changed the wire protocol to give better compatability
with older libvirt clients. My previous version clients would get a cryptic
"unknown status 3"
if the server was mandating authentication. This is because I extended the
reply codes for RPC to include a specific 'auth required' code which old
clients are not expecting. The idea was that the new client would run the
REMOTE_PROC_OPEN rpc call & it would return a special NEED_AUTH code, the
client would then authenticate & redo the REMOTE_PROC_OPEN.
In the new way of doing things, instead of calling REMOTE_PROC_OPEN as the
first RPC, a new client will do REMOTE_PROC_AUTH_LIST to query auth types
required by the server. It will choose an auth method (there may be several
to choose from), then complete the auth sequence. Only then will it try to
do the REMOTE_PROC_OPEN. For compatability with older servers which do not
implement the REMOTE_PROC_AUTH_LIST rpc, it will catch & ignore the error
this may generate. Old clients which don't know to call REMOTE_PROC_AUTH_LIST
will get a proper virErrorPtr object returned from REMOTE_PROC_OPEN with
a clear 'authentication required' message.
The end result is that new client <-> new server has same level of functionality
as my previous patches, and old client <-> new server gets friendly errors
reported (if server mandates auth - if no auth is enabled old client works
just fine).
The main outstanding item to be finalized before these patches can be added
to CVS is the question of callbacks. For some auth methods we need to be
able to gather credentials from the user. There is no way for the caller
to know ahead of time what credentials are needed, because this is deterined
by the config of the server. Thus we need callbacks in some form.
I've come up with a couple of ideas...
1. A global method to supply a list of callbacks per credential type:
enum {
VIR_CONN_AUTH_PASSWORD,
VIR_CONN_AUTH_USERNAME,
VIR_CONN_AUTH_LANG,
VIR_CONN_AUTH_CHALLENGE,
VIR_CONN_AUTH_REALM,
} virConnectAuthToken;
typedef int (virConnectAuthCBSimple)(const char **result, unsigned *len);
typedef int (virConnectAuthCBPrompt)(const char *challenge, const char *prompt,
const char *defresult,
const char *result, unsigned *len);
typedef int (virConnetAuthCBRealm)(const char **availrealms, const char *result);
typedef struct {
int token;
union {
virConnectAuthCBSimple simple;
virConnectAuthCBPrompt prompt;
virConnectAuthCBRealm realm;
} cb;
} virConnectAuthCallback;
virConnectSetAuthCallbacks(virConnectAuthCallback *cbs, int ncbs)
This is described a little more here:
http://www.redhat.com/archives/libvir-list/2007-January/msg00024.html
2. A new variant of virConnectOpen which takes a callback. The callback
gives invoked with a list of required credentials
struct _virConnectInteract {
int type; /* One of virConnectInteractType constants */
const char *prompt;
const char *challenge;
const char *defresult;
char *result;
unsigned int resultlen;
};
typedef struct _virConnectInteract virConnectInteract;
typedef virConnectInteract *virConnectInteractPtr;
/**
* When authentication requires one or more interactions, this callback
* is invoked. For each interaction supplied, data must be gathered
* from the user and filled in to the 'result' and 'resultlen' fields.
* If an interaction can not be filled, fill in NULL and 0.
*
* Return 0 if all interactions were filled, or -1 upon error
*/
typedef int (*virConnectAuthCallbackPtr)(const char *uri,
virConnectInteractPtr interact,
unsigned int ninteract);
virConnectPtr virConnectOpenAuth (const char *name,
virConnectAuthCallbackPtr cb,
int flags);
Iternally the virConnectOpen & virConnectOpenReadOnly can both just be
delegating to this new virConnectOpenAuth call. Existing users of libvirt
won't know about this new call, but that doesn't matter because they're
not loosing any functionality - merely not able to put up UI to prompt
for auth credentials/
The compelling thing about the 2nd way of doing things is that the callback
gets a complete list of all required data items at once. This makes it
very easy to present a UI with form entry fields for all items. The first
way where there is a separate callback per item makes UI very hard.
There is one issue not addressed by those two options though - there may be
a choice of authentication methods to use. eg SASL vs PolicyKit. The callback
only provides a way to supply credentials, no way to choose between auth
types if a server offers more than one.
Again there's a couple of ways to address this.
1. A global method to set preferred auth type. Simply a sorted list of
auth types. Internally, we pick the first auth type in this list that
the server supports
virConnectSetAuthPriority(int types[], int ntypes);
2. A 2nd callback providing the int types[] and letting the client app
choose which one they want to use per connection.
3. Separate out the operation of creating a virConnectPtr object, from the
act of connecting, eg
/* Allocate a handle */
virConnectPtr virConnectNew(const char *uri);
/* List available auth methods */
int virConnectListAuth(virConnectPtr conn, int **types, int *ntypes);
/* Connect to HV, with a speciifc auth method */
int virConnectInit(virConnectPtr conn, int auth);
Any of these three, can be combined with either of the 2 options for supplying
auth credentials. So we've got a choice of 6 :-)
My preference, is to go for option 2, of the credentials choice, and
option 1 of the auth type priority choice. eg a static priority list for
auth types, and a single callback passed into a new virConnectOpenAuth()
call.
The final tedious bit, is the question of how we deal with underlying HV
which may need further authentication. eg, if connecting to XenAPI we may
need to provide a username & password. If the XenAPI connection is being
made through a local libvirt driver hooking up the callbacks as described
is fairly easy. If the XenAPI connection is made indirectly via the
remote driver, then we have two drivers requiring auth - the initial remote
driver, and then the remotely run XenAPi driver. This would need us to
figure out a way to hook up the callbacks to the REMOTE_PROC_OPEN api
call. Not pleasant !
Dan.
--
|=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=|
|=- Perl modules:
http://search.cpan.org/~danberr/ -=|
|=- Projects:
http://freshmeat.net/~danielpb/ -=|
|=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|