+/* Forward declaration: parse_part_sign_sha256 and parse_part_encr_aes256 call
+ * parse_packet and vice versa. */
+#define PP_SIGNED 0x01
+#define PP_ENCRYPTED 0x02
+static int parse_packet (sockent_t *se,
+ void *buffer, size_t buffer_size, int flags);
+
+#define BUFFER_READ(p,s) do { \
+ memcpy ((p), buffer + buffer_offset, (s)); \
+ buffer_offset += (s); \
+} while (0)
+
+#if HAVE_LIBGCRYPT
+static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */
+ void **ret_buffer, size_t *ret_buffer_len, int flags)
+{
+ char *buffer;
+ size_t buffer_len;
+ size_t buffer_offset;
+
+ size_t username_len;
+ char *secret;
+
+ part_signature_sha256_t pss;
+ uint16_t pss_head_length;
+ char hash[sizeof (pss.hash)];
+
+ gcry_md_hd_t hd;
+ gcry_error_t err;
+ unsigned char *hash_ptr;
+
+ buffer = *ret_buffer;
+ buffer_len = *ret_buffer_len;
+ buffer_offset = 0;
+
+ if (se->data.server.userdb == NULL)
+ {
+ NOTICE ("network plugin: Received signed network packet but can't verify "
+ "it because no user DB has been configured. Will accept it.");
+ return (0);
+ }
+
+ /* Check if the buffer has enough data for this structure. */
+ if (buffer_len <= PART_SIGNATURE_SHA256_SIZE)
+ return (-ENOMEM);
+
+ /* Read type and length header */
+ BUFFER_READ (&pss.head.type, sizeof (pss.head.type));
+ BUFFER_READ (&pss.head.length, sizeof (pss.head.length));
+ pss_head_length = ntohs (pss.head.length);
+
+ /* Check if the `pss_head_length' is within bounds. */
+ if ((pss_head_length <= PART_SIGNATURE_SHA256_SIZE)
+ || (pss_head_length > buffer_len))
+ {
+ ERROR ("network plugin: HMAC-SHA-256 with invalid length received.");
+ return (-1);
+ }
+
+ /* Copy the hash. */
+ BUFFER_READ (pss.hash, sizeof (pss.hash));
+
+ /* Calculate username length (without null byte) and allocate memory */
+ username_len = pss_head_length - PART_SIGNATURE_SHA256_SIZE;
+ pss.username = malloc (username_len + 1);
+ if (pss.username == NULL)
+ return (-ENOMEM);
+
+ /* Read the username */
+ BUFFER_READ (pss.username, username_len);
+ pss.username[username_len] = 0;
+
+ assert (buffer_offset == pss_head_length);
+
+ /* Query the password */
+ secret = fbh_get (se->data.server.userdb, pss.username);
+ if (secret == NULL)
+ {
+ ERROR ("network plugin: Unknown user: %s", pss.username);
+ sfree (pss.username);
+ return (-ENOENT);
+ }
+
+ /* Create a hash device and check the HMAC */
+ hd = NULL;
+ err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err != 0)
+ {
+ ERROR ("network plugin: Creating HMAC-SHA-256 object failed: %s",
+ gcry_strerror (err));
+ sfree (secret);
+ sfree (pss.username);
+ return (-1);
+ }
+
+ err = gcry_md_setkey (hd, secret, strlen (secret));
+ if (err != 0)
+ {
+ ERROR ("network plugin: gcry_md_setkey failed: %s", gcry_strerror (err));
+ gcry_md_close (hd);
+ return (-1);
+ }
+
+ gcry_md_write (hd,
+ buffer + PART_SIGNATURE_SHA256_SIZE,
+ buffer_len - PART_SIGNATURE_SHA256_SIZE);
+ hash_ptr = gcry_md_read (hd, GCRY_MD_SHA256);
+ if (hash_ptr == NULL)
+ {
+ ERROR ("network plugin: gcry_md_read failed.");
+ gcry_md_close (hd);
+ sfree (secret);
+ sfree (pss.username);
+ return (-1);
+ }
+ memcpy (hash, hash_ptr, sizeof (hash));
+
+ /* Clean up */
+ gcry_md_close (hd);
+ hd = NULL;
+
+ sfree (secret);
+ sfree (pss.username);
+
+ if (memcmp (pss.hash, hash, sizeof (pss.hash)) != 0)
+ {
+ WARNING ("network plugin: Verifying HMAC-SHA-256 signature failed: "
+ "Hash mismatch.");
+ }
+ else
+ {
+ parse_packet (se, buffer + buffer_offset, buffer_len - buffer_offset,
+ flags | PP_SIGNED);
+ }
+
+ *ret_buffer = buffer + buffer_len;
+ *ret_buffer_len = 0;
+
+ return (0);
+} /* }}} int parse_part_sign_sha256 */
+/* #endif HAVE_LIBGCRYPT */
+
+#else /* if !HAVE_LIBGCRYPT */
+static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */
+ void **ret_buffer, size_t *ret_buffer_size, int flags)
+{
+ static int warning_has_been_printed = 0;
+
+ char *buffer;
+ size_t buffer_size;
+ size_t buffer_offset;
+ uint16_t part_len;
+
+ part_signature_sha256_t pss;
+
+ buffer = *ret_buffer;
+ buffer_size = *ret_buffer_size;
+ buffer_offset = 0;
+
+ if (buffer_size <= PART_SIGNATURE_SHA256_SIZE)
+ return (-ENOMEM);
+
+ BUFFER_READ (&pss.head.type, sizeof (pss.head.type));
+ BUFFER_READ (&pss.head.length, sizeof (pss.head.length));
+ part_len = ntohs (pss.head.length);
+
+ if ((part_len <= PART_SIGNATURE_SHA256_SIZE)
+ || (part_len > buffer_size))
+ return (-EINVAL);
+
+ if (warning_has_been_printed == 0)
+ {
+ WARNING ("network plugin: Received signed packet, but the network "
+ "plugin was not linked with libgcrypt, so I cannot "
+ "verify the signature. The packet will be accepted.");
+ warning_has_been_printed = 1;
+ }
+
+ parse_packet (se, buffer + part_len, buffer_size - part_len, flags);
+
+ *ret_buffer = buffer + buffer_size;
+ *ret_buffer_size = 0;
+
+ return (0);
+} /* }}} int parse_part_sign_sha256 */
+#endif /* !HAVE_LIBGCRYPT */
+
+#if HAVE_LIBGCRYPT
+static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */
+ void **ret_buffer, size_t *ret_buffer_len,
+ int flags)
+{
+ char *buffer = *ret_buffer;
+ size_t buffer_len = *ret_buffer_len;
+ size_t payload_len;
+ size_t part_size;
+ size_t buffer_offset;
+ uint16_t username_len;
+ part_encryption_aes256_t pea;
+ unsigned char hash[sizeof (pea.hash)];
+
+ gcry_cipher_hd_t cypher;
+ gcry_error_t err;
+
+ /* Make sure at least the header if available. */
+ if (buffer_len <= PART_ENCRYPTION_AES256_SIZE)
+ {
+ NOTICE ("network plugin: parse_part_encr_aes256: "
+ "Discarding short packet.");
+ return (-1);
+ }
+
+ buffer_offset = 0;
+
+ /* Copy the unencrypted information into `pea'. */
+ BUFFER_READ (&pea.head.type, sizeof (pea.head.type));
+ BUFFER_READ (&pea.head.length, sizeof (pea.head.length));
+
+ /* Check the `part size'. */
+ part_size = ntohs (pea.head.length);
+ if ((part_size <= PART_ENCRYPTION_AES256_SIZE)
+ || (part_size > buffer_len))
+ {
+ NOTICE ("network plugin: parse_part_encr_aes256: "
+ "Discarding part with invalid size.");
+ return (-1);
+ }
+
+ /* Read the username */
+ BUFFER_READ (&username_len, sizeof (username_len));
+ username_len = ntohs (username_len);
+
+ if ((username_len <= 0)
+ || (username_len > (part_size - (PART_ENCRYPTION_AES256_SIZE + 1))))
+ {
+ NOTICE ("network plugin: parse_part_encr_aes256: "
+ "Discarding part with invalid username length.");
+ return (-1);
+ }
+
+ assert (username_len > 0);
+ pea.username = malloc (username_len + 1);
+ if (pea.username == NULL)
+ return (-ENOMEM);
+ BUFFER_READ (pea.username, username_len);
+ pea.username[username_len] = 0;
+
+ /* Last but not least, the initialization vector */
+ BUFFER_READ (pea.iv, sizeof (pea.iv));
+
+ /* Make sure we are at the right position */
+ assert (buffer_offset == (username_len +
+ PART_ENCRYPTION_AES256_SIZE - sizeof (pea.hash)));
+
+ cypher = network_get_aes256_cypher (se, pea.iv, sizeof (pea.iv),
+ pea.username);
+ if (cypher == NULL)
+ return (-1);
+
+ payload_len = part_size - (PART_ENCRYPTION_AES256_SIZE + username_len);
+ assert (payload_len > 0);
+
+ /* Decrypt the packet in-place */
+ err = gcry_cipher_decrypt (cypher,
+ buffer + buffer_offset,
+ part_size - buffer_offset,
+ /* in = */ NULL, /* in len = */ 0);
+ if (err != 0)
+ {
+ ERROR ("network plugin: gcry_cipher_decrypt returned: %s",
+ gcry_strerror (err));
+ return (-1);
+ }
+
+ /* Read the hash */
+ BUFFER_READ (pea.hash, sizeof (pea.hash));
+
+ /* Make sure we're at the right position - again */
+ assert (buffer_offset == (username_len + PART_ENCRYPTION_AES256_SIZE));
+ assert (buffer_offset == (part_size - payload_len));
+
+ /* Check hash sum */
+ memset (hash, 0, sizeof (hash));
+ gcry_md_hash_buffer (GCRY_MD_SHA1, hash,
+ buffer + buffer_offset, payload_len);
+ if (memcmp (hash, pea.hash, sizeof (hash)) != 0)
+ {
+ ERROR ("network plugin: Decryption failed: Checksum mismatch.");
+ return (-1);
+ }
+
+ parse_packet (se, buffer + buffer_offset, payload_len,
+ flags | PP_ENCRYPTED);
+
+ /* Update return values */
+ *ret_buffer = buffer + part_size;
+ *ret_buffer_len = buffer_len - part_size;
+
+ return (0);
+} /* }}} int parse_part_encr_aes256 */
+/* #endif HAVE_LIBGCRYPT */
+
+#else /* if !HAVE_LIBGCRYPT */
+static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */
+ void **ret_buffer, size_t *ret_buffer_size, int flags)
+{
+ static int warning_has_been_printed = 0;
+
+ char *buffer;
+ size_t buffer_size;
+ size_t buffer_offset;
+
+ part_header_t ph;
+ size_t ph_length;
+
+ buffer = *ret_buffer;
+ buffer_size = *ret_buffer_size;
+ buffer_offset = 0;
+
+ /* parse_packet assures this minimum size. */
+ assert (buffer_size >= (sizeof (ph.type) + sizeof (ph.length)));
+
+ BUFFER_READ (&ph.type, sizeof (ph.type));
+ BUFFER_READ (&ph.length, sizeof (ph.length));
+ ph_length = ntohs (ph.length);
+
+ if ((ph_length <= PART_ENCRYPTION_AES256_SIZE)
+ || (ph_length > buffer_size))
+ {
+ ERROR ("network plugin: AES-256 encrypted part "
+ "with invalid length received.");
+ return (-1);
+ }
+
+ if (warning_has_been_printed == 0)
+ {
+ WARNING ("network plugin: Received encrypted packet, but the network "
+ "plugin was not linked with libgcrypt, so I cannot "
+ "decrypt it. The part will be discarded.");
+ warning_has_been_printed = 1;
+ }
+
+ *ret_buffer += ph_length;
+ *ret_buffer_size -= ph_length;
+
+ return (0);
+} /* }}} int parse_part_encr_aes256 */
+#endif /* !HAVE_LIBGCRYPT */
+
+#undef BUFFER_READ
+
+static int parse_packet (sockent_t *se, /* {{{ */
+ void *buffer, size_t buffer_size, int flags)