diff --git a/docs/internals/rpc.html.in b/docs/internals/rpc.html.in index 2c0c9836db7cdc5c0fee731f5261a8623735d734..4cf1e380c534151ab8cf6e4866d91006e779a22f 100644 --- a/docs/internals/rpc.html.in +++ b/docs/internals/rpc.html.in @@ -163,14 +163,28 @@ +

+ With the two packet types that support passing file descriptors, in + between the header and the payload there will be a 4-byte integer + specifying the number of file descriptors which are being sent. + The actual file handles are sent after the payload has been sent. + Each file handle has a single dummy byte transmitted as a carrier + for the out of band file descriptor. While the sender should always + send '\0' as the dummy byte value, the receiver ought to ignore the + value for the sake of robustness. +

+

For the exact payload information for each procedure, consult the XDR protocol definition for the program+version in question @@ -339,6 +353,27 @@ +--+-----------------------+--------+ +

Method call with passed FD

+ +

+ A single method call with 2 passed file descriptors and successful + reply, for a program=8, version=1, procedure=3, which 10 bytes worth + of input args, and 4 bytes worth of return values. The number of + file descriptors is encoded as a 32-bit int. Each file descriptor + then has a 1 byte dummy payload. The overall input + packet length is 4 + 24 + 4 + 2 + 10 == 44, and output packet length 32. +

+ +
+          +--+-----------------------+---------------+-------+
+   C -->  |44| 8 | 1 | 3 | 0 | 1 | 0 | 2 | .o.oOo.o. | 0 | 0 |  --> S  (call)
+          +--+-----------------------+---------------+-------+
+
+          +--+-----------------------+--------+
+   C <--  |32| 8 | 1 | 3 | 1 | 1 | 0 | .o.oOo |  <-- S  (reply)
+          +--+-----------------------+--------+
+    
+

RPC security

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3b4b429cc930afe18d360db433837b138995c3ae..8c74f56bce819e71423620e04203dd14b790f4f5 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1186,8 +1186,10 @@ virFileRewrite; # virnetmessage.h virNetMessageClear; +virNetMessageDecodeNumFDs; virNetMessageEncodeHeader; virNetMessageEncodePayload; +virNetMessageEncodeNumFDs; virNetMessageFree; virNetMessageNew; virNetMessageQueuePush; diff --git a/src/rpc/virnetmessage.c b/src/rpc/virnetmessage.c index a1ae9c4d7b5a32c83d1aff2695e165fe573143d7..8abf5ab5bcfea9b9377a79148018fb49e8d3f1c2 100644 --- a/src/rpc/virnetmessage.c +++ b/src/rpc/virnetmessage.c @@ -21,11 +21,14 @@ #include #include +#include #include "virnetmessage.h" #include "memory.h" #include "virterror_internal.h" #include "logging.h" +#include "virfile.h" +#include "util.h" #define VIR_FROM_THIS VIR_FROM_RPC #define virNetError(code, ...) \ @@ -51,6 +54,13 @@ virNetMessagePtr virNetMessageNew(bool tracked) void virNetMessageClear(virNetMessagePtr msg) { bool tracked = msg->tracked; + size_t i; + + VIR_DEBUG("msg=%p nfds=%zu", msg, msg->nfds); + + for (i = 0 ; i < msg->nfds ; i++) + VIR_FORCE_CLOSE(msg->fds[i]); + VIR_FREE(msg->fds); memset(msg, 0, sizeof(*msg)); msg->tracked = tracked; } @@ -58,14 +68,18 @@ void virNetMessageClear(virNetMessagePtr msg) void virNetMessageFree(virNetMessagePtr msg) { + size_t i; if (!msg) return; + VIR_DEBUG("msg=%p nfds=%zu cb=%p", msg, msg->nfds, msg->cb); + if (msg->cb) msg->cb(msg, msg->opaque); - VIR_DEBUG("msg=%p", msg); - + for (i = 0 ; i < msg->nfds ; i++) + VIR_FORCE_CLOSE(msg->fds[i]); + VIR_FREE(msg->fds); VIR_FREE(msg); } @@ -239,6 +253,78 @@ cleanup: } +int virNetMessageEncodeNumFDs(virNetMessagePtr msg) +{ + XDR xdr; + unsigned int numFDs = msg->nfds; + int ret = -1; + + xdrmem_create(&xdr, msg->buffer + msg->bufferOffset, + msg->bufferLength - msg->bufferOffset, XDR_ENCODE); + + if (numFDs > VIR_NET_MESSAGE_NUM_FDS_MAX) { + virNetError(VIR_ERR_RPC, + _("Too many FDs to send %d, expected %d maximum"), + numFDs, VIR_NET_MESSAGE_NUM_FDS_MAX); + goto cleanup; + } + + if (!xdr_u_int(&xdr, &numFDs)) { + virNetError(VIR_ERR_RPC, "%s", _("Unable to encode number of FDs")); + goto cleanup; + } + msg->bufferOffset += xdr_getpos(&xdr); + + VIR_DEBUG("Send %zu FDs to peer", msg->nfds); + + ret = 0; + +cleanup: + xdr_destroy(&xdr); + return ret; +} + + +int virNetMessageDecodeNumFDs(virNetMessagePtr msg) +{ + XDR xdr; + unsigned int numFDs; + int ret = -1; + size_t i; + + xdrmem_create(&xdr, msg->buffer + msg->bufferOffset, + msg->bufferLength - msg->bufferOffset, XDR_DECODE); + if (!xdr_u_int(&xdr, &numFDs)) { + virNetError(VIR_ERR_RPC, "%s", _("Unable to decode number of FDs")); + goto cleanup; + } + msg->bufferOffset += xdr_getpos(&xdr); + + if (numFDs > VIR_NET_MESSAGE_NUM_FDS_MAX) { + virNetError(VIR_ERR_RPC, + _("Received too many FDs %d, expected %d maximum"), + numFDs, VIR_NET_MESSAGE_NUM_FDS_MAX); + goto cleanup; + } + + msg->nfds = numFDs; + if (VIR_ALLOC_N(msg->fds, msg->nfds) < 0) { + virReportOOMError(); + goto cleanup; + } + for (i = 0 ; i < msg->nfds ; i++) + msg->fds[i] = -1; + + VIR_DEBUG("Got %zu FDs from peer", msg->nfds); + + ret = 0; + +cleanup: + xdr_destroy(&xdr); + return ret; +} + + int virNetMessageEncodePayload(virNetMessagePtr msg, xdrproc_t filter, void *data) @@ -403,3 +489,31 @@ void virNetMessageSaveError(virNetMessageErrorPtr rerr) rerr->level = VIR_ERR_ERROR; } } + + +int virNetMessageDupFD(virNetMessagePtr msg, + size_t slot) +{ + int fd; + + if (slot >= msg->nfds) { + virNetError(VIR_ERR_INTERNAL_ERROR, + _("No FD available at slot %zu"), slot); + return -1; + } + + if ((fd = dup(msg->fds[slot])) < 0) { + virReportSystemError(errno, + _("Unable to duplicate FD %d"), + msg->fds[slot]); + return -1; + } + if (virSetInherit(fd, false) < 0) { + VIR_FORCE_CLOSE(fd); + virReportSystemError(errno, + _("Cannot set close-on-exec %d"), + fd); + return -1; + } + return fd; +} diff --git a/src/rpc/virnetmessage.h b/src/rpc/virnetmessage.h index 307a0413ef4e90521c971fe50b00c4b9e91de557..ad634099d83f5218f3f9ea24c3adcd46b8cbb3bc 100644 --- a/src/rpc/virnetmessage.h +++ b/src/rpc/virnetmessage.h @@ -46,6 +46,9 @@ struct _virNetMessage { virNetMessageFreeCallback cb; void *opaque; + size_t nfds; + int *fds; + virNetMessagePtr next; }; @@ -78,6 +81,9 @@ int virNetMessageDecodePayload(virNetMessagePtr msg, void *data) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +int virNetMessageEncodeNumFDs(virNetMessagePtr msg); +int virNetMessageDecodeNumFDs(virNetMessagePtr msg); + int virNetMessageEncodePayloadRaw(virNetMessagePtr msg, const char *buf, size_t len) @@ -88,4 +94,7 @@ int virNetMessageEncodePayloadEmpty(virNetMessagePtr msg) void virNetMessageSaveError(virNetMessageErrorPtr rerr) ATTRIBUTE_NONNULL(1); +int virNetMessageDupFD(virNetMessagePtr msg, + size_t slot); + #endif /* __VIR_NET_MESSAGE_H__ */ diff --git a/src/rpc/virnetprotocol.x b/src/rpc/virnetprotocol.x index 306fafad5044684118cc8e6d53df54f3175ee520..4520e380da237d72684ea0d8a3c33270b9cff875 100644 --- a/src/rpc/virnetprotocol.x +++ b/src/rpc/virnetprotocol.x @@ -62,6 +62,11 @@ const VIR_NET_MESSAGE_LEN_MAX = 4; */ const VIR_NET_MESSAGE_STRING_MAX = 65536; +/* Limit on number of File Descriptors allowed to be + * passed per message + */ +const VIR_NET_MESSAGE_NUM_FDS_MAX = 32; + /* * RPC wire format * @@ -126,6 +131,18 @@ const VIR_NET_MESSAGE_STRING_MAX = 65536; * remote_error error information * * status == VIR_NET_OK * + * + * - type == VIR_NET_CALL_WITH_FDS + * int8 - number of FDs + * XXX_args for procedure + * + * - type == VIR_NET_REPLY_WITH_FDS + * int8 - number of FDs + * * status == VIR_NET_OK + * XXX_ret for procedure + * * status == VIR_NET_ERROR + * remote_error Error information + * */ enum virNetMessageType { /* client -> server. args from a method call */ @@ -135,7 +152,11 @@ enum virNetMessageType { /* either direction. async notification */ VIR_NET_MESSAGE = 2, /* either direction. stream data packet */ - VIR_NET_STREAM = 3 + VIR_NET_STREAM = 3, + /* client -> server. args from a method call, with passed FDs */ + VIR_NET_CALL_WITH_FDS = 4, + /* server -> client. reply/error from a method call, with passed FDs */ + VIR_NET_REPLY_WITH_FDS = 5 }; enum virNetMessageStatus { diff --git a/src/virnetprotocol-structs b/src/virnetprotocol-structs index 4d97e9c55a2b85277c9982473707fc114875cece..af4526c9011ad33655cf216b49f9aec06b5d1085 100644 --- a/src/virnetprotocol-structs +++ b/src/virnetprotocol-structs @@ -4,6 +4,8 @@ enum virNetMessageType { VIR_NET_REPLY = 1, VIR_NET_MESSAGE = 2, VIR_NET_STREAM = 3, + VIR_NET_CALL_WITH_FDS = 4, + VIR_NET_REPLY_WITH_FDS = 5, }; enum virNetMessageStatus { VIR_NET_OK = 0,