qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Max Reitz <mreitz@redhat.com>
To: qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	Markus Armbruster <armbru@redhat.com>,
	Max Reitz <mreitz@redhat.com>
Subject: [Qemu-devel] [PATCH for-2.4 4/5] chardev/irc: Add end-to-end encryption
Date: Wed,  1 Apr 2015 14:09:16 +0200	[thread overview]
Message-ID: <1427890157-18639-5-git-send-email-mreitz@redhat.com> (raw)
In-Reply-To: <1427890157-18639-1-git-send-email-mreitz@redhat.com>

IRC being usable for remote configuration, offering a secure channel is
indispensable. As can be seen from its alias "Caesar's cipher", ROT13
has been in use since ancient times and has been employed for state and
military secrets, so it is definitely well-tested, stable and secure
enough to manage a continent-spanning empire.

Its security is further proven by its ubiquity. For instance, vim
supports encryption and decryption by hfvat gur t? pbzznaq.

However, some concerns have been raised in the past whether ROT13 and
consequently all of Caesar's ciphers are actually secure for data with
non-maximum entropy. Another cipher has shown in the past how to deal
with these accusations: As DES is considered insecure, Triple DES (3DES)
has been introduced. Likewise, this patch implements Triple ROT13 in
addition to simple ROT13 to greatly improve security.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 qapi-schema.json |  17 +++++++-
 qemu-char.c      | 129 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 130 insertions(+), 16 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index e4f93fd..d365352 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2876,6 +2876,20 @@
 { 'type': 'ChardevRingbuf', 'data': { '*size'  : 'int' } }
 
 ##
+# @ChardevIrcEncryption
+#
+# Enumeration of supported encryption modes over IRC.
+#
+# @none:    No encryption
+# @weak:    ROT13
+# @strong:  Triple ROT13
+#
+# Since: 2.4
+##
+{ 'enum': 'ChardevIrcEncryption',
+  'data': [ 'none', 'weak', 'strong' ] }
+
+##
 # @ChardevIrc
 #
 # Configuration info for IRC chardevs.
@@ -2889,7 +2903,8 @@
 { 'type': 'ChardevIrc', 'data': { 'addr'        : 'SocketAddress',
                                   'nick'        : 'str',
                                   'channel'     : 'str',
-                                  'ssl'         : 'bool' } }
+                                  'ssl'         : 'bool',
+                                  'encryption'  : 'ChardevIrcEncryption' } }
 
 ##
 # @ChardevBackend:
diff --git a/qemu-char.c b/qemu-char.c
index 1030d5e..1fe149d 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -30,6 +30,7 @@
 #include "qmp-commands.h"
 #include "qapi/qmp-input-visitor.h"
 #include "qapi/qmp-output-visitor.h"
+#include "qapi/util.h"
 #include "qapi-visit.h"
 
 #include <unistd.h>
@@ -3321,8 +3322,78 @@ typedef struct {
     uint8_t *recvbuf;
     int recvbuf_idx;
     bool recv_line_skip;
+
+    ChardevIrcEncryption encryption;
 } IrcCharDriverState;
 
+static void caesar_cipher(uint8_t *dest, const uint8_t *src, size_t len,
+                          int shift)
+{
+    size_t i;
+
+    shift %= 26;
+    if (shift < 0) {
+        shift += 26;
+    }
+
+    for (i = 0; i < len; i++) {
+        if (isascii(src[i]) && isalpha(src[i])) {
+            dest[i] = (((src[i] & 0x1f) - 1 + shift) % 26 + 1)
+                    | (src[i] & ~0x1f);
+        } else {
+            dest[i] = src[i];
+        }
+    }
+}
+
+static void irc_encrypt(IrcCharDriverState *irc, uint8_t *buffer, size_t len)
+{
+    switch (irc->encryption) {
+    /* no encryption */
+    case CHARDEV_IRC_ENCRYPTION_NONE:
+        break;
+
+    /* ROT13 */
+    case CHARDEV_IRC_ENCRYPTION_WEAK:
+        caesar_cipher(buffer, buffer, len, 13);
+        break;
+
+    /* Triple ROT13 */
+    case CHARDEV_IRC_ENCRYPTION_STRONG:
+        caesar_cipher(buffer, buffer, len, 13);
+        caesar_cipher(buffer, buffer, len, 13);
+        caesar_cipher(buffer, buffer, len, 13);
+        break;
+
+    default:
+        return;
+    }
+}
+
+static void irc_decrypt(IrcCharDriverState *irc, uint8_t *buffer, size_t len)
+{
+    switch (irc->encryption) {
+    /* no encryption */
+    case CHARDEV_IRC_ENCRYPTION_NONE:
+        break;
+
+    /* ROT13 */
+    case CHARDEV_IRC_ENCRYPTION_WEAK:
+        caesar_cipher(buffer, buffer, len, -13);
+        break;
+
+    /* Triple ROT13 */
+    case CHARDEV_IRC_ENCRYPTION_STRONG:
+        caesar_cipher(buffer, buffer, len, -13);
+        caesar_cipher(buffer, buffer, len, -13);
+        caesar_cipher(buffer, buffer, len, -13);
+        break;
+
+    default:
+        return;
+    }
+}
+
 #ifdef CONFIG_GNUTLS
 static ssize_t irc_send(IrcCharDriverState *irc, const void *buf, size_t len)
 {
@@ -3447,6 +3518,9 @@ static int irc_chr_write(CharDriverState *s, const uint8_t *buf, int len)
         } else if (buf[i] == '\n') {
             if (irc->sendbuf) {
                 if (!irc->send_line_skip) {
+                    irc_encrypt(irc, irc->sendbuf + irc->sendbuf_prefixlen,
+                                irc->sendbuf_idx);
+
                     irc->sendbuf[irc->sendbuf_idx++] = '\r';
                     irc->sendbuf[irc->sendbuf_idx++] = '\n';
 
@@ -3486,6 +3560,9 @@ static int irc_chr_write(CharDriverState *s, const uint8_t *buf, int len)
                     irc->sendbuf[break_index] = '\r';
                     irc->sendbuf[break_index + 1] = '\n';
 
+                    irc_encrypt(irc, irc->sendbuf + irc->sendbuf_prefixlen,
+                                break_index);
+
                     irc_send(irc, irc->sendbuf, break_index + 2);
 
                     irc->sendbuf[break_index + 1] = replaced;
@@ -3573,22 +3650,27 @@ static gboolean irc_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
 
     in_query = !strcmp(real_dest, irc->nick);
 
-    /* ignore CTCP */
-    if (!strcmp(cmd, "PRIVMSG") && msg[0] != 1 &&
-        (in_query || (!strncmp(msg, irc->nick, strlen(irc->nick)) &&
-                      !isalnum(msg[strlen(irc->nick)]))))
-    {
-        const char *start = msg;
-
-        if (!in_query) {
-            start = msg + strlen(irc->nick) + 1;
-            while (*start && isspace(*start)) {
-                start++;
+    /* ignore plain-text CTCP */
+    if (!strcmp(cmd, "PRIVMSG") && msg[0] != 1) {
+        irc_decrypt(irc, (uint8_t *)msg, strlen(msg));
+
+        /* ignore encrypted CTCP as well */
+        if (in_query ? msg[0] != 1 :
+            !strncmp(msg, irc->nick, strlen(irc->nick)) &&
+            !isalnum(msg[strlen(irc->nick)]))
+        {
+            const char *start = msg;
+
+            if (!in_query) {
+                start = msg + strlen(irc->nick) + 1;
+                while (*start && isspace(*start)) {
+                    start++;
+                }
             }
-        }
 
-        qemu_chr_be_write(s, (uint8_t *)start, strlen(start));
-        qemu_chr_be_write(s, (uint8_t *)"\n", 1);
+            qemu_chr_be_write(s, (uint8_t *)start, strlen(start));
+            qemu_chr_be_write(s, (uint8_t *)"\n", 1);
+        }
     }
 
     g_free(buffer);
@@ -3985,9 +4067,11 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
 static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
                                Error **errp)
 {
-    const char *host, *port, *nick, *channel, *sockfd;
+    const char *host, *port, *nick, *channel, *sockfd, *encryption_raw;
+    ChardevIrcEncryption encryption;
     bool ssl;
     SocketAddress *addr;
+    Error *local_err = NULL;
 
     host    = qemu_opt_get(opts, "host");
     port    = qemu_opt_get(opts, "port") ?: "6667";
@@ -4000,6 +4084,15 @@ static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
     ssl     = false;
 #endif
 
+    encryption_raw = qemu_opt_get(opts, "encryption") ?: "none";
+    encryption = qapi_enum_parse(ChardevIrcEncryption_lookup, encryption_raw,
+                                 CHARDEV_IRC_ENCRYPTION_MAX,
+                                 CHARDEV_IRC_ENCRYPTION_NONE, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
     if ((!host && !sockfd) || !nick || !channel) {
         error_setg(errp, "chardev: irc: Missing options");
         return;
@@ -4035,6 +4128,7 @@ static void qemu_chr_parse_irc(QemuOpts *opts, ChardevBackend *backend,
     backend->irc->nick = g_strdup(nick);
     backend->irc->channel = g_strdup(channel);
     backend->irc->ssl = ssl;
+    backend->irc->encryption = encryption;
 }
 
 typedef struct CharDriver {
@@ -4409,6 +4503,9 @@ QemuOptsList qemu_chardev_opts = {
             .name = "ssl",
             .type = QEMU_OPT_BOOL,
 #endif
+        },{
+            .name = "encryption",
+            .type = QEMU_OPT_STRING,
         },
         { /* end of list */ }
     },
@@ -4677,6 +4774,7 @@ static CharDriverState *qemu_chr_open_irc(ChardevIrc *irc, Error **errp)
 
     irc_cds->recvbuf = g_malloc(1024);
     irc_cds->recvbuf_idx = 0;
+    irc_cds->encryption = CHARDEV_IRC_ENCRYPTION_NONE;
 
 #ifdef CONFIG_GNUTLS
     if (irc->ssl) {
@@ -4800,6 +4898,7 @@ static CharDriverState *qemu_chr_open_irc(ChardevIrc *irc, Error **errp)
     }
 
     irc_cds->query = query;
+    irc_cds->encryption = irc->encryption;
 
     s = qemu_chr_alloc();
     s->chr_write = irc_chr_write;
-- 
2.3.4

  parent reply	other threads:[~2015-04-01 12:09 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-04-01 12:09 [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add IRC char driver Max Reitz
2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 1/5] " Max Reitz
2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 2/5] chardev/irc: Add sockfd option Max Reitz
2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 3/5] chardev/irc: Add SSL support Max Reitz
2015-04-01 12:09 ` Max Reitz [this message]
2015-04-01 12:09 ` [Qemu-devel] [PATCH for-2.4 5/5] Documentation: Document IRC char driver Max Reitz
2015-04-02  8:57 ` [Qemu-devel] [PATCH for-2.4 0/5] chardev: Add " Stefan Hajnoczi
2015-04-02 14:04   ` Max Reitz
2015-04-02 19:19 ` Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1427890157-18639-5-git-send-email-mreitz@redhat.com \
    --to=mreitz@redhat.com \
    --cc=armbru@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).