diff --git a/daemon/admin.c b/daemon/admin.c index fcbdee5fa3..3de09ca900 100644 --- a/daemon/admin.c +++ b/daemon/admin.c @@ -236,4 +236,63 @@ adminDispatchServerSetThreadpoolParameters(virNetServerPtr server ATTRIBUTE_UNUS virObjectUnref(srv); return rv; } + +static int +adminDispatchClientGetInfo(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + struct admin_client_get_info_args *args, + struct admin_client_get_info_ret *ret) +{ + int rv = -1; + virNetServerPtr srv = NULL; + virNetServerClientPtr clnt = NULL; + virTypedParameterPtr params = NULL; + int nparams = 0; + struct daemonAdmClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!(srv = virNetDaemonGetServer(priv->dmn, args->clnt.srv.name))) { + virReportError(VIR_ERR_NO_SERVER, + _("no server with matching name '%s' found"), + args->clnt.srv.name); + goto cleanup; + } + + if (!(clnt = virNetServerGetClient(srv, args->clnt.id))) { + virReportError(VIR_ERR_NO_CLIENT, + _("no client with matching id '%lu' found"), + args->clnt.id); + goto cleanup; + } + + if (adminClientGetInfo(clnt, ¶ms, &nparams, args->flags) < 0) + goto cleanup; + + if (nparams > ADMIN_CLIENT_INFO_PARAMETERS_MAX) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Number of client info parameters %d exceeds max " + "allowed limit: %d"), nparams, + ADMIN_CLIENT_INFO_PARAMETERS_MAX); + goto cleanup; + } + + if (virTypedParamsSerialize(params, nparams, + (virTypedParameterRemotePtr *) &ret->params.params_val, + &ret->params.params_len, + VIR_TYPED_PARAM_STRING_OKAY) < 0) + goto cleanup; + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + + virTypedParamsFree(params, nparams); + virObjectUnref(clnt); + virObjectUnref(srv); + return rv; +} #include "admin_dispatch.h" diff --git a/daemon/admin_server.c b/daemon/admin_server.c index 9ea1164571..2fc467539c 100644 --- a/daemon/admin_server.c +++ b/daemon/admin_server.c @@ -27,6 +27,7 @@ #include "datatypes.h" #include "viralloc.h" #include "virerror.h" +#include "viridentity.h" #include "virlog.h" #include "virnetdaemon.h" #include "virnetserver.h" @@ -211,3 +212,94 @@ adminServerLookupClient(virNetServerPtr srv, return virNetServerGetClient(srv, id); } + +int +adminClientGetInfo(virNetServerClientPtr client, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int ret = -1; + int maxparams = 0; + bool readonly; + const char *sock_addr = NULL; + const char *attr = NULL; + virTypedParameterPtr tmpparams = NULL; + virIdentityPtr identity = NULL; + + virCheckFlags(0, -1); + + if (virNetServerClientGetInfo(client, &readonly, + &sock_addr, &identity) < 0) + goto cleanup; + + if (virTypedParamsAddBoolean(&tmpparams, nparams, &maxparams, + VIR_CLIENT_INFO_READONLY, + readonly) < 0) + goto cleanup; + + if (!virNetServerClientIsLocal(client)) { + if (virTypedParamsAddString(&tmpparams, nparams, &maxparams, + VIR_CLIENT_INFO_SOCKET_ADDR, + sock_addr) < 0) + goto cleanup; + + if (virIdentityGetSASLUserName(identity, &attr) < 0 || + (attr && + virTypedParamsAddString(&tmpparams, nparams, &maxparams, + VIR_CLIENT_INFO_SASL_USER_NAME, + attr) < 0)) + goto cleanup; + + if (virIdentityGetX509DName(identity, &attr) < 0 || + (attr && + virTypedParamsAddString(&tmpparams, nparams, &maxparams, + VIR_CLIENT_INFO_X509_DISTINGUISHED_NAME, + attr) < 0)) + goto cleanup; + } else { + pid_t pid; + uid_t uid; + gid_t gid; + if (virIdentityGetUNIXUserID(identity, &uid) < 0 || + virTypedParamsAddInt(&tmpparams, nparams, &maxparams, + VIR_CLIENT_INFO_UNIX_USER_ID, uid) < 0) + goto cleanup; + + if (virIdentityGetUNIXUserName(identity, &attr) < 0 || + virTypedParamsAddString(&tmpparams, nparams, &maxparams, + VIR_CLIENT_INFO_UNIX_USER_NAME, + attr) < 0) + goto cleanup; + + if (virIdentityGetUNIXGroupID(identity, &gid) < 0 || + virTypedParamsAddInt(&tmpparams, nparams, &maxparams, + VIR_CLIENT_INFO_UNIX_GROUP_ID, gid) < 0) + goto cleanup; + + if (virIdentityGetUNIXGroupName(identity, &attr) < 0 || + virTypedParamsAddString(&tmpparams, nparams, &maxparams, + VIR_CLIENT_INFO_UNIX_GROUP_NAME, + attr) < 0) + goto cleanup; + + if (virIdentityGetUNIXProcessID(identity, &pid) < 0 || + virTypedParamsAddInt(&tmpparams, nparams, &maxparams, + VIR_CLIENT_INFO_UNIX_PROCESS_ID, pid) < 0) + goto cleanup; + } + + if (virIdentityGetSELinuxContext(identity, &attr) < 0 || + (attr && + virTypedParamsAddString(&tmpparams, nparams, &maxparams, + VIR_CLIENT_INFO_SELINUX_CONTEXT, attr) < 0)) + goto cleanup; + + *params = tmpparams; + tmpparams = NULL; + ret = 0; + + cleanup: + virObjectUnref(identity); + return ret; +} diff --git a/daemon/admin_server.h b/daemon/admin_server.h index a593251d7a..95c76b9e0f 100644 --- a/daemon/admin_server.h +++ b/daemon/admin_server.h @@ -54,4 +54,9 @@ virNetServerClientPtr adminServerLookupClient(virNetServerPtr srv, unsigned long long id, unsigned int flags); +int adminClientGetInfo(virNetServerClientPtr client, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags); + #endif /* __LIBVIRTD_ADMIN_SERVER_H__ */ diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 5c30aae125..0a1ea61e33 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -344,6 +344,11 @@ virAdmServerLookupClient(virAdmServerPtr srv, # define VIR_CLIENT_INFO_SELINUX_CONTEXT "selinux_context" +int virAdmClientGetInfo(virAdmClientPtr client, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags); + # ifdef __cplusplus } # endif diff --git a/src/admin/admin_protocol.x b/src/admin/admin_protocol.x index da21db8d39..67bdbf30ee 100644 --- a/src/admin/admin_protocol.x +++ b/src/admin/admin_protocol.x @@ -42,6 +42,9 @@ const ADMIN_SERVER_THREADPOOL_PARAMETERS_MAX = 32; /* Upper limit on list of clients */ const ADMIN_CLIENT_LIST_MAX = 16384; +/* Upper limit on number of client info parameters */ +const ADMIN_CLIENT_INFO_PARAMETERS_MAX = 64; + /* A long string, which may NOT be NULL. */ typedef string admin_nonnull_string; @@ -148,6 +151,15 @@ struct admin_server_lookup_client_ret { admin_nonnull_client clnt; }; +struct admin_client_get_info_args { + admin_nonnull_client clnt; + unsigned int flags; +}; + +struct admin_client_get_info_ret { /* insert@1 */ + admin_typed_param params; +}; + /* Define the program number, protocol version and procedure numbers here. */ const ADMIN_PROGRAM = 0x06900690; const ADMIN_PROTOCOL_VERSION = 1; @@ -213,5 +225,10 @@ enum admin_procedure { /** * @generate: both */ - ADMIN_PROC_SERVER_LOOKUP_CLIENT = 9 + ADMIN_PROC_SERVER_LOOKUP_CLIENT = 9, + + /** + * @generate: none + */ + ADMIN_PROC_CLIENT_GET_INFO = 10 }; diff --git a/src/admin/admin_remote.c b/src/admin/admin_remote.c index b833ea4e15..40fcddb5fb 100644 --- a/src/admin/admin_remote.c +++ b/src/admin/admin_remote.c @@ -67,6 +67,16 @@ make_nonnull_server(admin_nonnull_server *srv_dst, virAdmServerPtr srv_src) srv_dst->name = srv_src->name; } +static void +make_nonnull_client(admin_nonnull_client *client_dst, + virAdmClientPtr client_src) +{ + client_dst->id = client_src->id; + client_dst->transport = client_src->transport; + client_dst->timestamp = client_src->timestamp; + make_nonnull_server(&client_dst->srv, client_src->srv); +} + static int callFull(virAdmConnectPtr conn ATTRIBUTE_UNUSED, remoteAdminPrivPtr priv, @@ -308,3 +318,40 @@ remoteAdminServerSetThreadPoolParameters(virAdmServerPtr srv, virObjectUnlock(priv); return rv; } + +static int +remoteAdminClientGetInfo(virAdmClientPtr client, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int rv = -1; + remoteAdminPrivPtr priv = client->srv->conn->privateData; + admin_client_get_info_args args; + admin_client_get_info_ret ret; + + args.flags = flags; + make_nonnull_client(&args.clnt, client); + + memset(&ret, 0, sizeof(ret)); + virObjectLock(priv); + + if (call(client->srv->conn, 0, ADMIN_PROC_CLIENT_GET_INFO, + (xdrproc_t)xdr_admin_client_get_info_args, (char *) &args, + (xdrproc_t)xdr_admin_client_get_info_ret, (char *) &ret) == -1) + goto cleanup; + + if (virTypedParamsDeserialize((virTypedParameterRemotePtr) ret.params.params_val, + ret.params.params_len, + ADMIN_CLIENT_INFO_PARAMETERS_MAX, + params, + nparams) < 0) + goto cleanup; + + rv = 0; + xdr_free((xdrproc_t)xdr_admin_client_get_info_ret, (char *) &ret); + + cleanup: + virObjectUnlock(priv); + return rv; +} diff --git a/src/admin_protocol-structs b/src/admin_protocol-structs index dc2220e3ef..ea9adf6c3c 100644 --- a/src/admin_protocol-structs +++ b/src/admin_protocol-structs @@ -95,6 +95,16 @@ struct admin_server_lookup_client_args { struct admin_server_lookup_client_ret { admin_nonnull_client clnt; }; +struct admin_client_get_info_args { + admin_nonnull_client clnt; + u_int flags; +}; +struct admin_client_get_info_ret { + struct { + u_int params_len; + admin_typed_param * params_val; + } params; +}; enum admin_procedure { ADMIN_PROC_CONNECT_OPEN = 1, ADMIN_PROC_CONNECT_CLOSE = 2, @@ -105,4 +115,5 @@ enum admin_procedure { ADMIN_PROC_SERVER_SET_THREADPOOL_PARAMETERS = 7, ADMIN_PROC_SERVER_LIST_CLIENTS = 8, ADMIN_PROC_SERVER_LOOKUP_CLIENT = 9, + ADMIN_PROC_CLIENT_GET_INFO = 10, }; diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index d9cdb811f1..a94f5dd1d3 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -925,3 +925,53 @@ virAdmServerLookupClient(virAdmServerPtr srv, virDispatchError(NULL); return NULL; } + +/** + * virAdmClientGetInfo: + * @client: a client object reference + * @params: pointer to a list of typed parameters which will be allocated + * to store all returned parameters + * @nparams: pointer which will hold the number of params returned in @params + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Extract identity information about a client. Attributes returned in @params + * are mostly transport-dependent, i.e. some attributes including client + * process's pid, gid, uid, or remote side's socket address are only available + * for a specific connection type - local vs remote. + * Other identity attributes like authentication method used + * (if authentication is enabled on the remote host), SELinux context, or + * an indicator whether client is connected via a read-only connection are + * independent of the connection transport. + * + * Note that the read-only connection indicator returns false for TCP/TLS + * clients because libvirt treats such connections as read-write by default, + * even though a TCP client is able to restrict access to certain APIs for + * itself. + * + * Returns 0 if the information has been successfully retrieved or -1 in case + * of an error. + */ +int +virAdmClientGetInfo(virAdmClientPtr client, + virTypedParameterPtr *params, + int *nparams, + unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("client=%p, params=%p, nparams=%p, flags=%x", + client, params, nparams, flags); + + virResetLastError(); + virCheckAdmClientReturn(client, -1); + virCheckNonNullArgGoto(params, error); + virCheckFlagsGoto(0, error); + + if ((ret = remoteAdminClientGetInfo(client, params, nparams, flags)) < 0) + goto error; + + return ret; + error: + virDispatchError(NULL); + return -1; +} diff --git a/src/libvirt_admin_private.syms b/src/libvirt_admin_private.syms index 3d7ecbcb71..affe8c1133 100644 --- a/src/libvirt_admin_private.syms +++ b/src/libvirt_admin_private.syms @@ -6,6 +6,8 @@ # # admin/admin_protocol.x +xdr_admin_client_get_info_args; +xdr_admin_client_get_info_ret; xdr_admin_connect_get_lib_version_ret; xdr_admin_connect_list_servers_args; xdr_admin_connect_list_servers_ret; diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index 066ae0ce98..27e4a1deed 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -33,4 +33,5 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectLookupServer; virAdmServerSetThreadPoolParameters; virAdmServerListClients; + virAdmClientGetInfo; };