2 * collectd - src/libcollectdclient/network_buffer.c
3 * Copyright (C) 2010-2015 Florian octo Forster
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * Florian octo Forster <octo at collectd.org>
29 #include <arpa/inet.h> /* htons */
41 /* default xcode compiler throws warnings even when deprecated functionality
42 * is not used. -Werror breaks the build because of erroneous warnings.
43 * http://stackoverflow.com/questions/10556299/compiler-warnings-with-libgcrypt-v1-5-0/12830209#12830209
45 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
47 /* FreeBSD's copy of libgcrypt extends the existing GCRYPT_NO_DEPRECATED
48 * to properly hide all deprecated functionality.
49 * http://svnweb.freebsd.org/ports/head/security/libgcrypt/files/patch-src__gcrypt.h.in
51 #define GCRYPT_NO_DEPRECATED
54 /* Re enable deprecation warnings */
55 #pragma GCC diagnostic warning "-Wdeprecated-declarations"
57 #if GCRYPT_VERSION_NUMBER < 0x010600
58 GCRY_THREAD_OPTION_PTHREAD_IMPL;
62 #include "collectd/network_buffer.h"
64 #define TYPE_HOST 0x0000
65 #define TYPE_TIME 0x0001
66 #define TYPE_TIME_HR 0x0008
67 #define TYPE_PLUGIN 0x0002
68 #define TYPE_PLUGIN_INSTANCE 0x0003
69 #define TYPE_TYPE 0x0004
70 #define TYPE_TYPE_INSTANCE 0x0005
71 #define TYPE_VALUES 0x0006
72 #define TYPE_INTERVAL 0x0007
73 #define TYPE_INTERVAL_HR 0x0009
75 /* Types to transmit notifications */
76 #define TYPE_MESSAGE 0x0100
77 #define TYPE_SEVERITY 0x0101
79 #define TYPE_SIGN_SHA256 0x0200
80 #define TYPE_ENCR_AES256 0x0210
82 #define PART_SIGNATURE_SHA256_SIZE 36
83 #define PART_ENCRYPTION_AES256_SIZE 42
85 #define ADD_GENERIC(nb, srcptr, size) \
87 assert((size) <= (nb)->free); \
88 memcpy((nb)->ptr, (srcptr), (size)); \
89 (nb)->ptr += (size); \
90 (nb)->free -= (size); \
93 #define ADD_STATIC(nb, var) ADD_GENERIC(nb, &(var), sizeof(var));
98 struct lcc_network_buffer_s {
102 lcc_value_list_t state;
106 lcc_security_level_t seclevel;
111 gcry_cipher_hd_t encr_cypher;
112 size_t encr_header_len;
117 #define SSTRNCPY(dst, src, sz) \
119 strncpy((dst), (src), (sz)); \
126 static bool have_gcrypt(void) /* {{{ */
129 static bool need_init = 1;
136 #if GCRYPT_VERSION_NUMBER < 0x010600
137 if (gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread))
141 if (!gcry_check_version(GCRYPT_VERSION))
144 if (!gcry_control(GCRYCTL_INIT_SECMEM, 32768, 0))
147 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
154 } /* }}} bool have_gcrypt */
157 static uint64_t htonll(uint64_t val) /* {{{ */
159 static int config = 0;
166 uint16_t n = htons(h);
177 hi = (uint32_t)(val >> 32);
178 lo = (uint32_t)(val & 0x00000000FFFFFFFF);
183 return (((uint64_t)lo) << 32) | ((uint64_t)hi);
184 } /* }}} uint64_t htonll */
187 static double htond(double val) /* {{{ */
189 static int config = 0;
201 double d = 8.642135e130;
206 if ((c[0] == 0x2f) && (c[1] == 0x25) && (c[2] == 0xc0) && (c[3] == 0xc7) &&
207 (c[4] == 0x43) && (c[5] == 0x2b) && (c[6] == 0x1f) && (c[7] == 0x5b))
208 config = 1; /* need nothing */
209 else if ((c[7] == 0x2f) && (c[6] == 0x25) && (c[5] == 0xc0) &&
210 (c[4] == 0xc7) && (c[3] == 0x43) && (c[2] == 0x2b) &&
211 (c[1] == 0x1f) && (c[0] == 0x5b))
212 config = 2; /* endian flip */
213 else if ((c[4] == 0x2f) && (c[5] == 0x25) && (c[6] == 0xc0) &&
214 (c[7] == 0xc7) && (c[0] == 0x43) && (c[1] == 0x2b) &&
215 (c[2] == 0x1f) && (c[3] == 0x5b))
216 config = 3; /* int swap */
222 out.byte[0] = out.byte[1] = out.byte[2] = out.byte[3] = 0x00;
223 out.byte[4] = out.byte[5] = 0x00;
227 } else if (config == 1)
229 else if (config == 2) {
231 out.byte[0] = in.byte[7];
232 out.byte[1] = in.byte[6];
233 out.byte[2] = in.byte[5];
234 out.byte[3] = in.byte[4];
235 out.byte[4] = in.byte[3];
236 out.byte[5] = in.byte[2];
237 out.byte[6] = in.byte[1];
238 out.byte[7] = in.byte[0];
240 } else if (config == 3) {
242 out.byte[0] = in.byte[4];
243 out.byte[1] = in.byte[5];
244 out.byte[2] = in.byte[6];
245 out.byte[3] = in.byte[7];
246 out.byte[4] = in.byte[0];
247 out.byte[5] = in.byte[1];
248 out.byte[6] = in.byte[2];
249 out.byte[7] = in.byte[3];
252 /* If in doubt, just copy the value back to the caller. */
255 } /* }}} double htond */
257 static int nb_add_values(char **ret_buffer, /* {{{ */
258 size_t *ret_buffer_len, const lcc_value_list_t *vl) {
259 if ((vl == NULL) || (vl->values_len < 1)) {
268 uint16_t pkg_num_values;
269 uint8_t pkg_values_types[vl->values_len];
270 value_t pkg_values[vl->values_len];
274 packet_len = sizeof(pkg_type) + sizeof(pkg_length) + sizeof(pkg_num_values) +
275 sizeof(pkg_values_types) + sizeof(pkg_values);
277 if (*ret_buffer_len < packet_len)
280 pkg_type = htons(TYPE_VALUES);
281 pkg_length = htons((uint16_t)packet_len);
282 pkg_num_values = htons((uint16_t)vl->values_len);
284 for (size_t i = 0; i < vl->values_len; i++) {
285 pkg_values_types[i] = (uint8_t)vl->values_types[i];
286 switch (vl->values_types[i]) {
287 case LCC_TYPE_COUNTER:
288 pkg_values[i].counter = (counter_t)htonll(vl->values[i].counter);
292 pkg_values[i].gauge = (gauge_t)htond(vl->values[i].gauge);
295 case LCC_TYPE_DERIVE:
296 pkg_values[i].derive = (derive_t)htonll(vl->values[i].derive);
299 case LCC_TYPE_ABSOLUTE:
300 pkg_values[i].absolute = (absolute_t)htonll(vl->values[i].absolute);
305 } /* switch (vl->values_types[i]) */
306 } /* for (vl->values_len) */
309 * Use `memcpy' to write everything to the buffer, because the pointer
310 * may be unaligned and some architectures, such as SPARC, can't handle
313 packet_ptr = *ret_buffer;
315 memcpy(packet_ptr + offset, &pkg_type, sizeof(pkg_type));
316 offset += sizeof(pkg_type);
317 memcpy(packet_ptr + offset, &pkg_length, sizeof(pkg_length));
318 offset += sizeof(pkg_length);
319 memcpy(packet_ptr + offset, &pkg_num_values, sizeof(pkg_num_values));
320 offset += sizeof(pkg_num_values);
321 memcpy(packet_ptr + offset, pkg_values_types, sizeof(pkg_values_types));
322 offset += sizeof(pkg_values_types);
323 memcpy(packet_ptr + offset, pkg_values, sizeof(pkg_values));
324 offset += sizeof(pkg_values);
326 assert(offset == packet_len);
328 *ret_buffer = packet_ptr + packet_len;
329 *ret_buffer_len -= packet_len;
331 } /* }}} int nb_add_values */
333 static int nb_add_number(char **ret_buffer, /* {{{ */
334 size_t *ret_buffer_len, uint16_t type,
345 packet_len = sizeof(pkg_type) + sizeof(pkg_length) + sizeof(pkg_value);
347 if (*ret_buffer_len < packet_len)
350 pkg_type = htons(type);
351 pkg_length = htons((uint16_t)packet_len);
352 pkg_value = htonll(value);
354 packet_ptr = *ret_buffer;
356 memcpy(packet_ptr + offset, &pkg_type, sizeof(pkg_type));
357 offset += sizeof(pkg_type);
358 memcpy(packet_ptr + offset, &pkg_length, sizeof(pkg_length));
359 offset += sizeof(pkg_length);
360 memcpy(packet_ptr + offset, &pkg_value, sizeof(pkg_value));
361 offset += sizeof(pkg_value);
363 assert(offset == packet_len);
365 *ret_buffer = packet_ptr + packet_len;
366 *ret_buffer_len -= packet_len;
368 } /* }}} int nb_add_number */
370 static int nb_add_time(char **ret_buffer, /* {{{ */
371 size_t *ret_buffer_len, uint16_t type, double value) {
372 /* Convert to collectd's "cdtime" representation. */
373 uint64_t cdtime_value = (uint64_t)(value * 1073741824.0);
374 return nb_add_number(ret_buffer, ret_buffer_len, type, cdtime_value);
375 } /* }}} int nb_add_time */
377 static int nb_add_string(char **ret_buffer, /* {{{ */
378 size_t *ret_buffer_len, uint16_t type, const char *str,
388 packet_len = sizeof(pkg_type) + sizeof(pkg_length) + str_len + 1;
389 if (*ret_buffer_len < packet_len)
392 pkg_type = htons(type);
393 pkg_length = htons((uint16_t)packet_len);
395 packet_ptr = *ret_buffer;
397 memcpy(packet_ptr + offset, &pkg_type, sizeof(pkg_type));
398 offset += sizeof(pkg_type);
399 memcpy(packet_ptr + offset, &pkg_length, sizeof(pkg_length));
400 offset += sizeof(pkg_length);
401 memcpy(packet_ptr + offset, str, str_len);
403 memset(packet_ptr + offset, 0, 1);
406 assert(offset == packet_len);
408 *ret_buffer = packet_ptr + packet_len;
409 *ret_buffer_len -= packet_len;
411 } /* }}} int nb_add_string */
413 static int nb_add_value_list(lcc_network_buffer_t *nb, /* {{{ */
414 const lcc_value_list_t *vl) {
415 char *buffer = nb->ptr;
416 size_t buffer_size = nb->free;
418 const lcc_identifier_t *ident_src;
419 lcc_identifier_t *ident_dst;
421 ident_src = &vl->identifier;
422 ident_dst = &nb->state.identifier;
424 if (strcmp(ident_dst->host, ident_src->host) != 0) {
425 if (nb_add_string(&buffer, &buffer_size, TYPE_HOST, ident_src->host,
426 strlen(ident_src->host)) != 0)
428 SSTRNCPY(ident_dst->host, ident_src->host, sizeof(ident_dst->host));
431 if (strcmp(ident_dst->plugin, ident_src->plugin) != 0) {
432 if (nb_add_string(&buffer, &buffer_size, TYPE_PLUGIN, ident_src->plugin,
433 strlen(ident_src->plugin)) != 0)
435 SSTRNCPY(ident_dst->plugin, ident_src->plugin, sizeof(ident_dst->plugin));
438 if (strcmp(ident_dst->plugin_instance, ident_src->plugin_instance) != 0) {
439 if (nb_add_string(&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
440 ident_src->plugin_instance,
441 strlen(ident_src->plugin_instance)) != 0)
443 SSTRNCPY(ident_dst->plugin_instance, ident_src->plugin_instance,
444 sizeof(ident_dst->plugin_instance));
447 if (strcmp(ident_dst->type, ident_src->type) != 0) {
448 if (nb_add_string(&buffer, &buffer_size, TYPE_TYPE, ident_src->type,
449 strlen(ident_src->type)) != 0)
451 SSTRNCPY(ident_dst->type, ident_src->type, sizeof(ident_dst->type));
454 if (strcmp(ident_dst->type_instance, ident_src->type_instance) != 0) {
455 if (nb_add_string(&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
456 ident_src->type_instance,
457 strlen(ident_src->type_instance)) != 0)
459 SSTRNCPY(ident_dst->type_instance, ident_src->type_instance,
460 sizeof(ident_dst->type_instance));
463 if (nb->state.time != vl->time) {
464 if (nb_add_time(&buffer, &buffer_size, TYPE_TIME_HR, vl->time))
466 nb->state.time = vl->time;
469 if (nb->state.interval != vl->interval) {
470 if (nb_add_time(&buffer, &buffer_size, TYPE_INTERVAL_HR, vl->interval))
472 nb->state.interval = vl->interval;
475 if (nb_add_values(&buffer, &buffer_size, vl) != 0)
479 nb->free = buffer_size;
481 } /* }}} int nb_add_value_list */
484 static int nb_add_signature(lcc_network_buffer_t *nb) /* {{{ */
492 const size_t hash_length = 32;
494 /* The type, length and username have already been filled in by
495 * "lcc_network_buffer_initialize". All we do here is calculate the hash over
496 * the username and the data and add the hash value to the buffer. */
498 buffer = nb->buffer + PART_SIGNATURE_SHA256_SIZE;
499 assert(nb->size >= (nb->free + PART_SIGNATURE_SHA256_SIZE));
500 buffer_size = nb->size - (nb->free + PART_SIGNATURE_SHA256_SIZE);
503 err = gcry_md_open(&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
507 assert(nb->password != NULL);
508 err = gcry_md_setkey(hd, nb->password, strlen(nb->password));
514 gcry_md_write(hd, buffer, buffer_size);
515 hash = gcry_md_read(hd, GCRY_MD_SHA256);
521 assert(((2 * sizeof(uint16_t)) + hash_length) == PART_SIGNATURE_SHA256_SIZE);
522 memcpy(nb->buffer + (2 * sizeof(uint16_t)), hash, hash_length);
526 } /* }}} int nb_add_signature */
528 static int nb_add_encryption(lcc_network_buffer_t *nb) /* {{{ */
530 size_t package_length;
531 char *encr_ptr; /* pointer to data being encrypted */
534 char *hash_ptr; /* pointer to data being hashed */
541 /* Fill in the package length */
542 package_length = nb->size - nb->free;
543 pkg_length = htons((uint16_t)package_length);
544 memcpy(nb->buffer + 2, &pkg_length, sizeof(pkg_length));
546 /* Calculate what to hash */
547 hash_ptr = nb->buffer + PART_ENCRYPTION_AES256_SIZE;
548 hash_size = package_length - nb->encr_header_len;
550 /* Calculate what to encrypt */
551 encr_ptr = hash_ptr - sizeof(hash);
552 encr_size = hash_size + sizeof(hash);
554 /* Calculate the SHA-1 hash */
555 gcry_md_hash_buffer(GCRY_MD_SHA1, hash, hash_ptr, hash_size);
556 memcpy(encr_ptr, hash, sizeof(hash));
558 if (nb->encr_cypher == NULL) {
559 unsigned char password_hash[32];
561 err = gcry_cipher_open(&nb->encr_cypher, GCRY_CIPHER_AES256,
562 GCRY_CIPHER_MODE_OFB, /* flags = */ 0);
566 /* Calculate our 256bit key used for AES */
567 gcry_md_hash_buffer(GCRY_MD_SHA256, password_hash, nb->password,
568 strlen(nb->password));
570 err = gcry_cipher_setkey(nb->encr_cypher, password_hash,
571 sizeof(password_hash));
573 gcry_cipher_close(nb->encr_cypher);
574 nb->encr_cypher = NULL;
577 } else /* if (nb->encr_cypher != NULL) */
579 gcry_cipher_reset(nb->encr_cypher);
582 /* Set the initialization vector */
583 err = gcry_cipher_setiv(nb->encr_cypher, nb->encr_iv, sizeof(nb->encr_iv));
585 gcry_cipher_close(nb->encr_cypher);
586 nb->encr_cypher = NULL;
590 /* Encrypt the buffer in-place */
591 err = gcry_cipher_encrypt(nb->encr_cypher, encr_ptr, encr_size,
592 /* in = */ NULL, /* in len = */ 0);
594 gcry_cipher_close(nb->encr_cypher);
595 nb->encr_cypher = NULL;
600 } /* }}} int nb_add_encryption */
606 lcc_network_buffer_t *lcc_network_buffer_create(size_t size) /* {{{ */
608 lcc_network_buffer_t *nb;
611 size = LCC_NETWORK_BUFFER_SIZE_DEFAULT;
618 nb = calloc(1, sizeof(*nb));
623 nb->buffer = calloc(1, nb->size);
624 if (nb->buffer == NULL) {
629 nb->ptr = nb->buffer;
637 } /* }}} lcc_network_buffer_t *lcc_network_buffer_create */
639 void lcc_network_buffer_destroy(lcc_network_buffer_t *nb) /* {{{ */
646 } /* }}} void lcc_network_buffer_destroy */
648 int lcc_network_buffer_set_security_level(lcc_network_buffer_t *nb, /* {{{ */
649 lcc_security_level_t level,
650 const char *username,
651 const char *password) {
661 lcc_network_buffer_initialize(nb);
668 username_copy = strdup(username);
669 password_copy = strdup(password);
670 if ((username_copy == NULL) || (password_copy == NULL)) {
678 nb->username = username_copy;
679 nb->password = password_copy;
680 nb->seclevel = level;
682 lcc_network_buffer_initialize(nb);
684 } /* }}} int lcc_network_buffer_set_security_level */
686 int lcc_network_buffer_initialize(lcc_network_buffer_t *nb) /* {{{ */
691 memset(nb->buffer, 0, nb->size);
692 memset(&nb->state, 0, sizeof(nb->state));
693 nb->ptr = nb->buffer;
697 if (nb->seclevel == SIGN) {
699 uint16_t pkg_type = htons(TYPE_SIGN_SHA256);
700 uint16_t pkg_length = PART_SIGNATURE_SHA256_SIZE;
702 assert(nb->username != NULL);
703 username_len = strlen(nb->username);
704 pkg_length = htons(pkg_length + ((uint16_t)username_len));
706 /* Fill in everything but the hash value here. */
707 memcpy(nb->ptr, &pkg_type, sizeof(pkg_type));
708 memcpy(nb->ptr + sizeof(pkg_type), &pkg_length, sizeof(pkg_length));
709 nb->ptr += PART_SIGNATURE_SHA256_SIZE;
710 nb->free -= PART_SIGNATURE_SHA256_SIZE;
712 memcpy(nb->ptr, nb->username, username_len);
713 nb->ptr += username_len;
714 nb->free -= username_len;
715 } else if (nb->seclevel == ENCRYPT) {
716 size_t username_length = strlen(nb->username);
717 uint16_t pkg_type = htons(TYPE_ENCR_AES256);
718 uint16_t pkg_length = 0; /* Filled in in finalize. */
719 uint16_t pkg_user_len = htons((uint16_t)username_length);
720 /* Filled in in finalize. */
723 nb->encr_header_len = username_length;
724 nb->encr_header_len += PART_ENCRYPTION_AES256_SIZE;
726 gcry_randomize((void *)&nb->encr_iv, sizeof(nb->encr_iv),
729 ADD_STATIC(nb, pkg_type);
730 ADD_STATIC(nb, pkg_length);
731 ADD_STATIC(nb, pkg_user_len);
732 ADD_GENERIC(nb, nb->username, username_length);
733 ADD_GENERIC(nb, nb->encr_iv, sizeof(nb->encr_iv));
734 ADD_GENERIC(nb, hash, sizeof(hash));
735 assert((nb->encr_header_len + nb->free) == nb->size);
740 } /* }}} int lcc_network_buffer_initialize */
742 int lcc_network_buffer_finalize(lcc_network_buffer_t *nb) /* {{{ */
748 if (nb->seclevel == SIGN)
749 return nb_add_signature(nb);
750 else if (nb->seclevel == ENCRYPT)
751 return nb_add_encryption(nb);
755 } /* }}} int lcc_network_buffer_finalize */
757 int lcc_network_buffer_add_value(lcc_network_buffer_t *nb, /* {{{ */
758 const lcc_value_list_t *vl) {
761 if ((nb == NULL) || (vl == NULL))
764 status = nb_add_value_list(nb, vl);
766 } /* }}} int lcc_network_buffer_add_value */
768 int lcc_network_buffer_get(lcc_network_buffer_t *nb, /* {{{ */
769 void *buffer, size_t *buffer_size) {
773 if ((nb == NULL) || (buffer_size == NULL))
776 assert(nb->size >= nb->free);
777 sz_required = nb->size - nb->free;
778 sz_available = *buffer_size;
780 *buffer_size = sz_required;
782 memcpy(buffer, nb->buffer,
783 (sz_available < sz_required) ? sz_available : sz_required);
786 } /* }}} int lcc_network_buffer_get */