commit d0cd173e2b9d26a3af6e28746003453500f8f655 Author: Andrew Beekhof Date: Thu Oct 31 08:18:11 2013 +1100 Fix: remote: Handle endian changes between client and server and improve forward compatibility (cherry picked from commit 0c8cb451bb31335e242da328c8a537ab60095a0f) diff --git a/configure.ac b/configure.ac index cfc1b1f..b94c26e 100644 --- a/configure.ac +++ b/configure.ac @@ -727,6 +727,7 @@ AC_CHECK_HEADERS(glib.h) AC_CHECK_HEADERS(grp.h) AC_CHECK_HEADERS(limits.h) AC_CHECK_HEADERS(linux/errqueue.h) +AC_CHECK_HEADERS(linux/swab.h) AC_CHECK_HEADERS(malloc.h) AC_CHECK_HEADERS(netdb.h) AC_CHECK_HEADERS(netinet/in.h) diff --git a/lib/common/remote.c b/lib/common/remote.c index c991761..47f0205 100644 --- a/lib/common/remote.c +++ b/lib/common/remote.c @@ -43,9 +43,7 @@ #ifdef HAVE_GNUTLS_GNUTLS_H # undef KEYFILE # include -#endif -#ifdef HAVE_GNUTLS_GNUTLS_H const int psk_tls_kx_order[] = { GNUTLS_KX_DHE_PSK, GNUTLS_KX_PSK, @@ -58,17 +56,87 @@ const int anon_tls_kx_order[] = { GNUTLS_KX_RSA, 0 }; +#endif + +/* Swab macros from linux/swab.h */ +#ifdef HAVE_LINUX_SWAB_H +# include +#else +/* + * casts are necessary for constants, because we never know how for sure + * how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way. + */ +#define ___swab16(x) ((__u16)( \ + (((__u16)(x) & (__u16)0x00ffU) << 8) | \ + (((__u16)(x) & (__u16)0xff00U) >> 8))) + +#define ___swab32(x) ((__u32)( \ + (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \ + (((__u32)(x) & (__u32)0x0000ff00UL) << 8) | \ + (((__u32)(x) & (__u32)0x00ff0000UL) >> 8) | \ + (((__u32)(x) & (__u32)0xff000000UL) >> 24))) + +#define ___swab64(x) ((__u64)( \ + (((__u64)(x) & (__u64)0x00000000000000ffULL) << 56) | \ + (((__u64)(x) & (__u64)0x000000000000ff00ULL) << 40) | \ + (((__u64)(x) & (__u64)0x0000000000ff0000ULL) << 24) | \ + (((__u64)(x) & (__u64)0x00000000ff000000ULL) << 8) | \ + (((__u64)(x) & (__u64)0x000000ff00000000ULL) >> 8) | \ + (((__u64)(x) & (__u64)0x0000ff0000000000ULL) >> 24) | \ + (((__u64)(x) & (__u64)0x00ff000000000000ULL) >> 40) | \ + (((__u64)(x) & (__u64)0xff00000000000000ULL) >> 56))) +#endif + +#define REMOTE_MSG_VERSION 1 +#define ENDIAN_LOCAL 0xBADADBBD struct crm_remote_header_v0 { + uint32_t endian; /* Detect messages from hosts with different endian-ness */ + uint32_t version; uint64_t id; uint64_t flags; - uint32_t error; - uint32_t version; uint32_t size_total; - uint32_t payload_uncompressed; + uint32_t payload_offset; uint32_t payload_compressed; -}; + uint32_t payload_uncompressed; + + /* New fields get added here */ + +} __attribute__ ((packed)); + +static struct crm_remote_header_v0 * +crm_remote_header(crm_remote_t * remote) +{ + struct crm_remote_header_v0 *header = (struct crm_remote_header_v0 *)remote->buffer; + if(remote->buffer_offset < sizeof(struct crm_remote_header_v0)) { + return NULL; + + } else if(header->endian != ENDIAN_LOCAL) { + uint32_t endian = __swab32(header->endian); + + CRM_LOG_ASSERT(endian == ENDIAN_LOCAL); + if(endian != ENDIAN_LOCAL) { + crm_err("Invalid message detected, endian mismatch: %lx is neither %lx nor the swab'd %lx", + ENDIAN_LOCAL, header->endian, endian); + return NULL; + } + + header->id = __swab64(header->id); + header->flags = __swab64(header->flags); + header->endian = __swab32(header->endian); + + header->version = __swab32(header->version); + header->size_total = __swab32(header->size_total); + header->payload_offset = __swab32(header->payload_offset); + header->payload_compressed = __swab32(header->payload_compressed); + header->payload_uncompressed = __swab32(header->payload_uncompressed); + } + + return header; +} + +#ifdef HAVE_GNUTLS_GNUTLS_H int crm_initiate_client_tls_handshake(crm_remote_t * remote, int timeout_ms) @@ -253,13 +321,11 @@ crm_remote_sendv(crm_remote_t * remote, struct iovec * iov, int iovs) return rc; } -#define PCMK_TLS_VERSION 1 - int crm_remote_send(crm_remote_t * remote, xmlNode * msg) { - static uint64_t id = 0; int rc = -1; + static uint64_t id = 0; char *xml_text = dump_xml_unformatted(msg); struct iovec iov[2]; @@ -278,19 +344,25 @@ crm_remote_send(crm_remote_t * remote, xmlNode * msg) id++; header->id = id; - header->version = PCMK_TLS_VERSION; - header->size_total = iov[0].iov_len + iov[1].iov_len; + header->endian = ENDIAN_LOCAL; + header->version = REMOTE_MSG_VERSION; + header->payload_offset = iov[0].iov_len; header->payload_uncompressed = iov[1].iov_len; + header->size_total = iov[0].iov_len + iov[1].iov_len; + crm_trace("Sending len[0]=%d, start=%x\n", + (int)iov[0].iov_len, *(int*)xml_text); rc = crm_remote_sendv(remote, iov, 2); if (rc < 0) { crm_err("Failed to send remote msg, rc = %d", rc); } - free(xml_text); + free(iov[0].iov_base); + free(iov[1].iov_base); return rc; } + /*! * \internal * \brief handles the recv buffer and parsing out msgs. @@ -300,34 +372,35 @@ xmlNode * crm_remote_parse_buffer(crm_remote_t * remote) { xmlNode *xml = NULL; - size_t offset = sizeof(struct crm_remote_header_v0); - struct crm_remote_header_v0 *header = NULL; + struct crm_remote_header_v0 *header = crm_remote_header(remote); - if (remote->buffer == NULL) { - return NULL; - - } else if(remote->buffer_offset < sizeof(struct crm_remote_header_v0)) { + if (remote->buffer == NULL || header == NULL) { return NULL; } /* take ownership of the buffer */ remote->buffer_offset = 0; - header = (struct crm_remote_header_v0 *)remote->buffer; /* Support compression on the receiving end now, in case we ever want to add it later */ if (header->payload_compressed) { int rc = 0; unsigned int size_u = 1 + header->payload_uncompressed; - char *uncompressed = calloc(1, offset + size_u); + char *uncompressed = calloc(1, header->payload_offset + size_u); crm_trace("Decompressing message data %d bytes into %d bytes", header->payload_compressed, size_u); - rc = BZ2_bzBuffToBuffDecompress(uncompressed + offset, &size_u, - remote->buffer + offset, + rc = BZ2_bzBuffToBuffDecompress(uncompressed + header->payload_offset, &size_u, + remote->buffer + header->payload_offset, header->payload_compressed, 1, 0); - if (rc != BZ_OK) { + if (rc != BZ_OK && header->version > REMOTE_MSG_VERSION) { + crm_warn("Couldn't decompress v%d message, we only understand v%d", + header->version, REMOTE_MSG_VERSION); + free(uncompressed); + return NULL; + + } else if (rc != BZ_OK) { crm_err("Decompression failed: %s (%d)", bz2_strerror(rc), rc); free(uncompressed); return NULL; @@ -335,19 +408,23 @@ crm_remote_parse_buffer(crm_remote_t * remote) CRM_ASSERT(size_u == header->payload_uncompressed); - memcpy(uncompressed, remote->buffer, offset); /* Preserve the header */ - header = (struct crm_remote_header_v0 *)uncompressed; + memcpy(uncompressed, remote->buffer, header->payload_offset); /* Preserve the header */ + remote->buffer_size = header->payload_offset + size_u; free(remote->buffer); - remote->buffer_size = offset + size_u; remote->buffer = uncompressed; + header = crm_remote_header(remote); } - CRM_ASSERT(remote->buffer[sizeof(struct crm_remote_header_v0) + header->payload_uncompressed - 1] == 0); + CRM_LOG_ASSERT(remote->buffer[sizeof(struct crm_remote_header_v0) + header->payload_uncompressed - 1] == 0); + + xml = string2xml(remote->buffer + header->payload_offset); + if (xml == NULL && header->version > REMOTE_MSG_VERSION) { + crm_warn("Couldn't parse v%d message, we only understand v%d", + header->version, REMOTE_MSG_VERSION); - xml = string2xml(remote->buffer + offset); - if (xml == NULL) { - crm_err("Couldn't parse: '%.120s'", remote->buffer + offset); + } else if (xml == NULL) { + crm_err("Couldn't parse: '%.120s'", remote->buffer + header->payload_offset); } return xml; @@ -424,16 +501,16 @@ crm_remote_recv_once(crm_remote_t * remote) { int rc = 0; size_t read_len = sizeof(struct crm_remote_header_v0); + struct crm_remote_header_v0 *header = crm_remote_header(remote); - if(remote->buffer_offset >= sizeof(struct crm_remote_header_v0)) { - struct crm_remote_header_v0 *hdr = (struct crm_remote_header_v0 *)remote->buffer; - - read_len = hdr->size_total; + if(header) { + /* Stop at the end of the current message */ + read_len = header->size_total; } /* automatically grow the buffer when needed */ if(remote->buffer_size < read_len) { - remote->buffer_size = 2 * read_len; + remote->buffer_size = 2 * read_len; crm_trace("Expanding buffer to %u bytes", remote->buffer_size); remote->buffer = realloc(remote->buffer, remote->buffer_size + 1); @@ -488,23 +565,18 @@ crm_remote_recv_once(crm_remote_t * remote) return -ENOTCONN; } - if(remote->buffer_offset < sizeof(struct crm_remote_header_v0)) { - crm_trace("Not enough data to fill header: %u < %u bytes", - remote->buffer_offset, sizeof(struct crm_remote_header_v0)); - return -EAGAIN; - - } else { - struct crm_remote_header_v0 *hdr = (struct crm_remote_header_v0 *)remote->buffer; - - if(remote->buffer_offset < hdr->size_total) { + header = crm_remote_header(remote); + if(header) { + if(remote->buffer_offset < header->size_total) { crm_trace("Read less than the advertised length: %u < %u bytes", - remote->buffer_offset, hdr->size_total); - return -EAGAIN; + remote->buffer_offset, header->size_total); + } else { + crm_trace("Read full message of %u bytes", remote->buffer_offset); + return remote->buffer_offset; } } - crm_trace("Read full message of %u bytes", remote->buffer_offset); - return remote->buffer_offset; + return -EAGAIN; } /*! diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c index d037022..d99d7c8 100644 --- a/lib/lrmd/lrmd_client.c +++ b/lib/lrmd/lrmd_client.c @@ -414,9 +414,6 @@ lrmd_poll(lrmd_t * lrmd, int timeout) case CRM_CLIENT_TLS: if (native->pending_notify) { return 1; - } else if (native->remote->buffer - && strstr(native->remote->buffer, REMOTE_MSG_TERMINATOR)) { - return 1; } return crm_remote_ready(native->remote, 0);