[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH 05/15] Generic module for handling SASL authentication & encryption



This provides two modules for handling SASL

 * virNetSASLContext provides the process-wide state, currently
   just a whitelist of usernames on the server and a one time
   library init call

 * virNetTLSSession provides the per-connection state, ie the
   SASL session itself. This also include APIs for providing
   data encryption/decryption once the session is established

* src/Makefile.am: Add to libvirt-net-rpc.la
* src/rpc/virnetsaslcontext.c, src/rpc/virnetsaslcontext.h: Generic
  SASL handling code
---
 po/POTFILES.in              |    1 +
 src/Makefile.am             |    3 +
 src/rpc/virnetsaslcontext.c |  525 +++++++++++++++++++++++++++++++++++++++++++
 src/rpc/virnetsaslcontext.h |  125 ++++++++++
 4 files changed, 654 insertions(+), 0 deletions(-)
 create mode 100644 src/rpc/virnetsaslcontext.c
 create mode 100644 src/rpc/virnetsaslcontext.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 39cecdd..bb8c0b2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -59,6 +59,7 @@ src/qemu/qemu_monitor_text.c
 src/qemu/qemu_security_dac.c
 src/remote/remote_driver.c
 src/rpc/virnetmessage.c
+src/rpc/virnetsaslcontext.c
 src/rpc/virnetsocket.c
 src/rpc/virnettlscontext.c
 src/secret/secret_driver.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 10fce7d..def7e0a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1124,13 +1124,16 @@ libvirt_net_rpc_la_SOURCES = \
 	../daemon/event.c \
 	rpc/virnetmessage.h rpc/virnetmessage.c \
 	rpc/virnetprotocol.h rpc/virnetprotocol.c \
+	rpc/virnetsaslcontext.h rpc/virnetsaslcontext.c \
 	rpc/virnetsocket.h rpc/virnetsocket.c \
 	rpc/virnettlscontext.h rpc/virnettlscontext.c
 libvirt_net_rpc_la_CFLAGS = \
 			$(GNUTLS_CFLAGS) \
+			$(SASL_CFLAGS) \
 			$(AM_CFLAGS)
 libvirt_net_rpc_la_LDFLAGS = \
 			$(GNUTLS_LIBS) \
+			$(SASL_LIBS) \
 			$(AM_LDFLAGS) \
 			$(CYGWIN_EXTRA_LDFLAGS) \
 			$(MINGW_EXTRA_LDFLAGS)
diff --git a/src/rpc/virnetsaslcontext.c b/src/rpc/virnetsaslcontext.c
new file mode 100644
index 0000000..8e72d2c
--- /dev/null
+++ b/src/rpc/virnetsaslcontext.c
@@ -0,0 +1,525 @@
+/*
+ * virnetsaslcontext.c: SASL encryption/auth handling
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#include <config.h>
+
+#include <fnmatch.h>
+
+#include "virnetsaslcontext.h"
+
+#include "virterror_internal.h"
+#include "memory.h"
+#include "logging.h"
+
+#define VIR_FROM_THIS VIR_FROM_RPC
+
+#define virNetError(code, ...)                                    \
+    virReportErrorHelper(NULL, VIR_FROM_RPC, code, __FILE__,      \
+                         __FUNCTION__, __LINE__, __VA_ARGS__)
+
+
+struct _virNetSASLContext {
+    const char *const*usernameWhitelist;
+    int refs;
+};
+
+struct _virNetSASLSession {
+    sasl_conn_t *conn;
+    int refs;
+};
+
+
+virNetSASLContextPtr virNetSASLContextNewClient(void)
+{
+    virNetSASLContextPtr ctxt;
+    int err;
+
+    err = sasl_client_init(NULL);
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("failed to initialize SASL library: %d (%s)"),
+                    err, sasl_errstring(err, NULL, NULL));
+        return NULL;
+    }
+
+    if (VIR_ALLOC(ctxt) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    ctxt->refs = 1;
+
+    return ctxt;
+}
+
+virNetSASLContextPtr virNetSASLContextNewServer(const char *const*usernameWhitelist)
+{
+    virNetSASLContextPtr ctxt;
+    int err;
+
+    err = sasl_server_init(NULL, "libvirt");
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("failed to initialize SASL library: %d (%s)"),
+                    err, sasl_errstring(err, NULL, NULL));
+        return NULL;
+    }
+
+    if (VIR_ALLOC(ctxt) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    ctxt->usernameWhitelist = usernameWhitelist;
+    ctxt->refs = 1;
+
+    return ctxt;
+}
+
+int virNetSASLContextCheckIdentity(virNetSASLContextPtr ctxt,
+                                   const char *identity)
+{
+    const char *const*wildcards;
+
+    /* If the list is not set, allow any DN. */
+    wildcards = ctxt->usernameWhitelist;
+    if (!wildcards)
+        return 1; /* No ACL, allow all */
+
+    while (*wildcards) {
+        if (fnmatch (*wildcards, identity, 0) == 0)
+            return 1; /* Allowed */
+        wildcards++;
+    }
+
+    /* Denied */
+    VIR_ERROR(_("SASL client %s not allowed in whitelist"), identity);
+    return 0;
+}
+
+
+void virNetSASLContextRef(virNetSASLContextPtr ctxt)
+{
+    ctxt->refs++;
+}
+
+void virNetSASLContextFree(virNetSASLContextPtr ctxt)
+{
+    if (!ctxt)
+        return;
+
+    ctxt->refs--;
+    if (ctxt->refs > 0)
+        return;
+
+    VIR_FREE(ctxt);
+}
+
+virNetSASLSessionPtr virNetSASLSessionNewClient(virNetSASLContextPtr ctxt ATTRIBUTE_UNUSED,
+                                                const char *service,
+                                                const char *hostname,
+                                                const char *localAddr,
+                                                const char *remoteAddr,
+                                                const sasl_callback_t *cbs)
+{
+    virNetSASLSessionPtr sasl = NULL;
+    int err;
+
+    if (VIR_ALLOC(sasl) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    sasl->refs = 1;
+
+    err = sasl_client_new(service,
+                          hostname,
+                          localAddr,
+                          remoteAddr,
+                          cbs,
+                          SASL_SUCCESS_DATA,
+                          &sasl->conn);
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("Failed to create SASL client context: %d (%s)"),
+                    err, sasl_errstring(err, NULL, NULL));
+        goto cleanup;
+    }
+
+    return sasl;
+
+cleanup:
+    virNetSASLSessionFree(sasl);
+    return NULL;
+}
+
+virNetSASLSessionPtr virNetSASLSessionNewServer(virNetSASLContextPtr ctxt ATTRIBUTE_UNUSED,
+                                                const char *service,
+                                                const char *localAddr,
+                                                const char *remoteAddr)
+{
+    virNetSASLSessionPtr sasl = NULL;
+    int err;
+
+    if (VIR_ALLOC(sasl) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    sasl->refs = 1;
+
+    err = sasl_server_new(service,
+                          NULL,
+                          NULL,
+                          localAddr,
+                          remoteAddr,
+                          NULL,
+                          SASL_SUCCESS_DATA,
+                          &sasl->conn);
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("Failed to create SASL client context: %d (%s)"),
+                    err, sasl_errstring(err, NULL, NULL));
+        goto cleanup;
+    }
+
+    return sasl;
+
+cleanup:
+    virNetSASLSessionFree(sasl);
+    return NULL;
+}
+
+void virNetSASLSessionRef(virNetSASLSessionPtr sasl)
+{
+    sasl->refs++;
+}
+
+int virNetSASLSessionExtKeySize(virNetSASLSessionPtr sasl,
+                                      int ssf)
+{
+    int err;
+
+    err = sasl_setprop(sasl->conn, SASL_SSF_EXTERNAL, &ssf);
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_INTERNAL_ERROR,
+                    _("cannot set external SSF %d (%s)"),
+                    err, sasl_errstring(err, NULL, NULL));
+        return -1;
+    }
+    return 0;
+}
+
+const char *virNetSASLSessionGetIdentity(virNetSASLSessionPtr sasl)
+{
+    const void *val;
+    int err;
+
+    err = sasl_getprop(sasl->conn, SASL_USERNAME, &val);
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("cannot query SASL username on connection %d (%s)"),
+                    err, sasl_errstring(err, NULL, NULL));
+        return NULL;
+    }
+    if (val == NULL) {
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("no client username was found"));
+        return NULL;
+    }
+    VIR_DEBUG("SASL client username %s", (const char *)val);
+
+    return (const char*)val;
+}
+
+
+int virNetSASLSessionGetKeySize(virNetSASLSessionPtr sasl)
+{
+    int err;
+    int ssf;
+    const void *val;
+    err = sasl_getprop(sasl->conn, SASL_SSF, &val);
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("cannot query SASL ssf on connection %d (%s)"),
+                    err, sasl_errstring(err, NULL, NULL));
+        return -1;
+    }
+    ssf = *(const int *)val;
+    return ssf;
+}
+
+int virNetSASLSessionSecProps(virNetSASLSessionPtr sasl,
+                              int minSSF,
+                              int maxSSF,
+                              bool allowAnonymous)
+{
+    sasl_security_properties_t secprops;
+    int err;
+
+    memset (&secprops, 0, sizeof secprops);
+
+    secprops.min_ssf = minSSF;
+    secprops.max_ssf = maxSSF;
+    secprops.maxbufsize = 100000;
+    secprops.security_flags = allowAnonymous ? 0 :
+        SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
+
+    err = sasl_setprop(sasl->conn, SASL_SEC_PROPS, &secprops);
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_INTERNAL_ERROR,
+                    _("cannot set security props %d (%s)"),
+                    err, sasl_errstring(err, NULL, NULL));
+        return -1;
+    }
+
+    return 0;
+}
+
+
+char *virNetSASLSessionListMechanisms(virNetSASLSessionPtr sasl)
+{
+    const char *mechlist;
+    char *ret;
+    int err;
+
+    err = sasl_listmech(sasl->conn,
+                        NULL, /* Don't need to set user */
+                        "", /* Prefix */
+                        ",", /* Separator */
+                        "", /* Suffix */
+                        &mechlist,
+                        NULL,
+                        NULL);
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_INTERNAL_ERROR,
+                    _("cannot list SASL mechanisms %d (%s)"),
+                    err, sasl_errdetail(sasl->conn));
+        return NULL;
+    }
+    if (!(ret = strdup(mechlist))) {
+        virReportOOMError();
+        return NULL;
+    }
+    return ret;
+}
+
+
+int virNetSASLSessionClientStart(virNetSASLSessionPtr sasl,
+                                 const char *mechlist,
+                                 sasl_interact_t **prompt_need,
+                                 const char **clientout,
+                                 size_t *clientoutlen,
+                                 const char **mech)
+{
+    unsigned outlen = 0;
+
+    int err = sasl_client_start(sasl->conn,
+                                mechlist,
+                                prompt_need,
+                                clientout,
+                                &outlen,
+                                mech);
+
+    *clientoutlen = outlen;
+
+    switch (err) {
+    case SASL_OK:
+        return VIR_NET_SASL_COMPLETE;
+    case SASL_CONTINUE:
+        return VIR_NET_SASL_CONTINUE;
+    case SASL_INTERACT:
+        return VIR_NET_SASL_INTERACT;
+
+    default:
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("Failed to start SASL negotiation: %d (%s)"),
+                    err, sasl_errdetail(sasl->conn));
+        return -1;
+    }
+}
+
+
+int virNetSASLSessionClientStep(virNetSASLSessionPtr sasl,
+                                const char *serverin,
+                                size_t serverinlen,
+                                sasl_interact_t **prompt_need,
+                                const char **clientout,
+                                size_t *clientoutlen)
+{
+    unsigned inlen = serverinlen;
+    unsigned outlen = 0;
+
+    int err = sasl_client_step(sasl->conn,
+                               serverin,
+                               inlen,
+                               prompt_need,
+                               clientout,
+                               &outlen);
+    *clientoutlen = outlen;
+
+    switch (err) {
+    case SASL_OK:
+        return VIR_NET_SASL_COMPLETE;
+    case SASL_CONTINUE:
+        return VIR_NET_SASL_CONTINUE;
+    case SASL_INTERACT:
+        return VIR_NET_SASL_INTERACT;
+
+    default:
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("Failed to start SASL negotiation: %d (%s)"),
+                    err, sasl_errdetail(sasl->conn));
+        return -1;
+    }
+}
+
+int virNetSASLSessionServerStart(virNetSASLSessionPtr sasl,
+                                 const char *mechname,
+                                 const char *clientin,
+                                 size_t clientinlen,
+                                 const char **serverout,
+                                 size_t *serveroutlen)
+{
+    unsigned inlen = clientinlen;
+    unsigned outlen = 0;
+    int err = sasl_server_start(sasl->conn,
+                                mechname,
+                                clientin,
+                                inlen,
+                                serverout,
+                                &outlen);
+
+    *serveroutlen = outlen;
+
+    switch (err) {
+    case SASL_OK:
+        return VIR_NET_SASL_COMPLETE;
+    case SASL_CONTINUE:
+        return VIR_NET_SASL_CONTINUE;
+    case SASL_INTERACT:
+        return VIR_NET_SASL_INTERACT;
+
+    default:
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("Failed to start SASL negotiation: %d (%s)"),
+                    err, sasl_errdetail(sasl->conn));
+        return -1;
+    }
+}
+
+
+int virNetSASLSessionServerStep(virNetSASLSessionPtr sasl,
+                                const char *clientin,
+                                size_t clientinlen,
+                                const char **serverout,
+                                size_t *serveroutlen)
+{
+    unsigned inlen = clientinlen;
+    unsigned outlen = 0;
+
+    int err = sasl_server_step(sasl->conn,
+                               clientin,
+                               inlen,
+                               serverout,
+                               &outlen);
+
+    *serveroutlen = outlen;
+
+    switch (err) {
+    case SASL_OK:
+        return VIR_NET_SASL_COMPLETE;
+    case SASL_CONTINUE:
+        return VIR_NET_SASL_CONTINUE;
+    case SASL_INTERACT:
+        return VIR_NET_SASL_INTERACT;
+
+    default:
+        VIR_DEBUG("Foo %s", sasl_errdetail(sasl->conn));
+        virNetError(VIR_ERR_AUTH_FAILED,
+                    _("Failed to start SASL negotiation: %d (%s)"),
+                    err, sasl_errdetail(sasl->conn));
+        return -1;
+    }
+}
+
+ssize_t virNetSASLSessionEncode(virNetSASLSessionPtr sasl,
+                                const char *input,
+                                size_t inputLen,
+                                const char **output,
+                                size_t *outputlen)
+{
+    unsigned inlen = inputLen;
+    unsigned outlen = 0;
+    int err;
+    err = sasl_encode(sasl->conn,
+                      input,
+                      inlen,
+                      output,
+                      &outlen);
+    *outputlen = outlen;
+
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_INTERNAL_ERROR,
+                    _("failed to encode SASL data: %d (%s)"),
+                    err, sasl_errstring(err, NULL, NULL));
+        return -1;
+    }
+    return 0;
+}
+
+ssize_t virNetSASLSessionDecode(virNetSASLSessionPtr sasl,
+                                const char *input,
+                                size_t inputLen,
+                                const char **output,
+                                size_t *outputlen)
+{
+    unsigned inlen = inputLen;
+    unsigned outlen = 0;
+    int err;
+    err = sasl_decode(sasl->conn,
+                      input,
+                      inlen,
+                      output,
+                      &outlen);
+    *outputlen = outlen;
+    if (err != SASL_OK) {
+        virNetError(VIR_ERR_INTERNAL_ERROR,
+                    _("failed to decode SASL data: %d (%s)"),
+                    err, sasl_errstring(err, NULL, NULL));
+        return -1;
+    }
+    return 0;
+}
+
+void virNetSASLSessionFree(virNetSASLSessionPtr sasl)
+{
+    if (!sasl)
+        return;
+
+    sasl->refs--;
+    if (sasl->refs > 0)
+        return;
+
+    if (sasl->conn)
+        sasl_dispose(&sasl->conn);
+
+    VIR_FREE(sasl);
+}
diff --git a/src/rpc/virnetsaslcontext.h b/src/rpc/virnetsaslcontext.h
new file mode 100644
index 0000000..9efa2cc
--- /dev/null
+++ b/src/rpc/virnetsaslcontext.h
@@ -0,0 +1,125 @@
+/*
+ * virnetsaslcontext.h: SASL encryption/auth handling
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#ifndef __VIR_NET_CLIENT_SASL_CONTEXT_H__
+# define __VIR_NET_CLIENT_SASL_CONTEXT_H__
+
+# ifdef HAVE_SASL
+/* Only needed for sasl_callback_t :-( */
+#  include <sasl/sasl.h>
+# endif
+
+# include <stdbool.h>
+# include <sys/types.h>
+
+# ifndef HAVE_SASL
+typedef sasl_callback_t void *;
+# endif
+
+typedef struct _virNetSASLContext virNetSASLContext;
+typedef virNetSASLContext *virNetSASLContextPtr;
+
+typedef struct _virNetSASLSession virNetSASLSession;
+typedef virNetSASLSession *virNetSASLSessionPtr;
+
+enum {
+    VIR_NET_SASL_COMPLETE,
+    VIR_NET_SASL_CONTINUE,
+    VIR_NET_SASL_INTERACT,
+};
+
+virNetSASLContextPtr virNetSASLContextNewClient(void);
+virNetSASLContextPtr virNetSASLContextNewServer(const char *const*usernameWhitelist);
+
+int virNetSASLContextCheckIdentity(virNetSASLContextPtr ctxt,
+                                   const char *identity);
+
+void virNetSASLContextRef(virNetSASLContextPtr sasl);
+void virNetSASLContextFree(virNetSASLContextPtr sasl);
+
+virNetSASLSessionPtr virNetSASLSessionNewClient(virNetSASLContextPtr ctxt,
+                                                const char *service,
+                                                const char *hostname,
+                                                const char *localAddr,
+                                                const char *remoteAddr,
+                                                const sasl_callback_t *cbs);
+virNetSASLSessionPtr virNetSASLSessionNewServer(virNetSASLContextPtr ctxt,
+                                                const char *service,
+                                                const char *localAddr,
+                                                const char *remoteAddr);
+
+char *virNetSASLSessionListMechanisms(virNetSASLSessionPtr sasl);
+
+void virNetSASLSessionRef(virNetSASLSessionPtr sasl);
+
+int virNetSASLSessionExtKeySize(virNetSASLSessionPtr sasl,
+                                int ssf);
+
+int virNetSASLSessionGetKeySize(virNetSASLSessionPtr sasl);
+
+const char *virNetSASLSessionGetIdentity(virNetSASLSessionPtr sasl);
+
+int virNetSASLSessionSecProps(virNetSASLSessionPtr sasl,
+                              int minSSF,
+                              int maxSSF,
+                              bool allowAnonymous);
+
+int virNetSASLSessionClientStart(virNetSASLSessionPtr sasl,
+                                 const char *mechlist,
+                                 sasl_interact_t **prompt_need,
+                                 const char **clientout,
+                                 size_t *clientoutlen,
+                                 const char **mech);
+
+int virNetSASLSessionClientStep(virNetSASLSessionPtr sasl,
+                                const char *serverin,
+                                size_t serverinlen,
+                                sasl_interact_t **prompt_need,
+                                const char **clientout,
+                                size_t *clientoutlen);
+
+int virNetSASLSessionServerStart(virNetSASLSessionPtr sasl,
+                                 const char *mechname,
+                                 const char *clientin,
+                                 size_t clientinlen,
+                                 const char **serverout,
+                                 size_t *serveroutlen);
+
+int virNetSASLSessionServerStep(virNetSASLSessionPtr sasl,
+                                const char *clientin,
+                                size_t clientinlen,
+                                const char **serverout,
+                                size_t *serveroutlen);
+
+ssize_t virNetSASLSessionEncode(virNetSASLSessionPtr sasl,
+                                const char *input,
+                                size_t inputLen,
+                                const char **output,
+                                size_t *outputlen);
+
+ssize_t virNetSASLSessionDecode(virNetSASLSessionPtr sasl,
+                                const char *input,
+                                size_t inputLen,
+                                const char **output,
+                                size_t *outputlen);
+
+void virNetSASLSessionFree(virNetSASLSessionPtr sasl);
+
+#endif /* __VIR_NET_CLIENT_SASL_CONTEXT_H__ */
-- 
1.7.2.3


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]