[libsoup/wip/tpopela/negotiate: 15/16] Make the libgssapi a hard dependency of libsoup



commit 0092a0acb02701469dc2d140ccbc3ed6ea42ebc9
Author: Tomas Popela <tpopela redhat com>
Date:   Wed Oct 21 15:42:12 2015 +0200

    Make the libgssapi a hard dependency of libsoup

 configure.ac                   |    1 -
 examples/get.c                 |   13 ++-
 libsoup/Makefile.am            |   23 +---
 libsoup/libsoup-gssapi-2.4.sym |    5 -
 libsoup/soup-auth-manager.c    |    7 +-
 libsoup/soup-auth-negotiate.c  |  314 +++++++++++++++++++++++++++++++---------
 libsoup/soup-auth-negotiate.h  |    4 +
 libsoup/soup-auth.h            |    2 +
 libsoup/soup-gssapi.c          |  240 ------------------------------
 libsoup/soup-gssapi.h          |   41 -----
 10 files changed, 272 insertions(+), 378 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 7d46af3..06f7542 100644
--- a/configure.ac
+++ b/configure.ac
@@ -330,7 +330,6 @@ if test "x$KRB5_CONFIG" != "xnone"; then
         AC_DEFINE(HAVE_GSSAPI, 1, [Whether or not gssapi libs are available])
     fi
 fi
-AM_CONDITIONAL(BUILD_LIBSOUP_GSSAPI, test $KRB5_CONFIG != none)
 
 dnl ****************************************************
 dnl *** Warnings to show if using GCC                ***
diff --git a/examples/get.c b/examples/get.c
index 6f761af..63ac0b4 100644
--- a/examples/get.c
+++ b/examples/get.c
@@ -4,6 +4,10 @@
  * Copyright (C) 2013 Igalia, S.L.
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -89,7 +93,10 @@ get_url (const char *url)
 }
 
 static const char *ca_file, *proxy;
-static gboolean synchronous, ntlm, negotiate;
+static gboolean synchronous, ntlm;
+#if HAVE_GSSAPI
+static gboolean negotiate;
+#endif
 
 static GOptionEntry entries[] = {
        { "ca-file", 'c', 0,
@@ -104,9 +111,11 @@ static GOptionEntry entries[] = {
        { "ntlm", 'n', 0,
          G_OPTION_ARG_NONE, &ntlm,
          "Use NTLM authentication", NULL },
+#if HAVE_GSSAPI
        { "negotiate", 'N', 0,
          G_OPTION_ARG_NONE, &negotiate,
          "Use Negotiate authentication", NULL },
+#endif
        { "output", 'o', 0,
          G_OPTION_ARG_STRING, &output_file_path,
          "Write the received data to FILE instead of stdout", "FILE" },
@@ -186,10 +195,12 @@ main (int argc, char **argv)
                soup_uri_free (proxy_uri);
        }
 
+#if HAVE_GSSAPI
        if (negotiate) {
                soup_session_add_feature_by_type(session,
                                                 SOUP_TYPE_AUTH_NEGOTIATE);
        }
+#endif
 
        if (!synchronous)
                loop = g_main_loop_new (NULL, TRUE);
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index 00ac40b..510c3c3 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -99,7 +99,8 @@ libsoup_2_4_la_LIBADD =                       \
        $(GLIB_LIBS)                    \
        $(LIBWS2_32)                    \
        $(XML_LIBS)                     \
-       $(SQLITE_LIBS)
+       $(SQLITE_LIBS)                  \
+       $(KRB5_LIBS)
 
 libsoup_2_4_la_SOURCES =               \
        gconstructor.h                  \
@@ -245,26 +246,6 @@ libsoup_gnome_2_4_la_SOURCES =             \
 
 endif
 
-if BUILD_LIBSOUP_GSSAPI
-
-lib_LTLIBRARIES += libsoup-gssapi-2.4.la
-
-libsoup_gssapi_2_4_la_LDFLAGS =        \
-       -version-info $(SOUP_CURRENT):$(SOUP_REVISION):$(SOUP_AGE) \
-       -no-undefined \
-       -export-symbols $(srcdir)/libsoup-gssapi-2.4.sym
-
-EXTRA_DIST += libsoup-gssapi-2.4.sym
-
-libsoup_gssapi_2_4_la_LIBADD =         \
-       $(KRB5_LIBS)                    \
-       $(GLIB_LIBS)
-
-libsoup_gssapi_2_4_la_SOURCES =                \
-       soup-gssapi.c
-
-endif
-
 GLIB_GENERATED = soup-enum-types.c soup-enum-types.h
 BUILT_SOURCES = \
        $(GLIB_GENERATED)   \
diff --git a/libsoup/soup-auth-manager.c b/libsoup/soup-auth-manager.c
index c17b4e1..95a0c38 100644
--- a/libsoup/soup-auth-manager.c
+++ b/libsoup/soup-auth-manager.c
@@ -13,13 +13,16 @@
 
 #include "soup-auth-manager.h"
 #include "soup.h"
-#include "soup-auth-negotiate.h"
 #include "soup-connection-auth.h"
 #include "soup-message-private.h"
 #include "soup-message-queue.h"
 #include "soup-path-map.h"
 #include "soup-session-private.h"
 
+#if HAVE_GSSAPI
+#include "soup-auth-negotiate.h"
+#endif
+
 /**
  * SECTION:soup-auth-manager
  * @short_description: HTTP client-side authentication handler
@@ -486,8 +489,10 @@ authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
        } else
                uri = soup_message_get_uri (msg);
 
+#if HAVE_GSSAPI
        if (SOUP_IS_AUTH_NEGOTIATE (auth))
                return;
+#endif
 
        /* If a password is specified explicitly in the URI, use it
         * even if the auth had previously already been authenticated.
diff --git a/libsoup/soup-auth-negotiate.c b/libsoup/soup-auth-negotiate.c
index 7727b74..013f9a4 100644
--- a/libsoup/soup-auth-negotiate.c
+++ b/libsoup/soup-auth-negotiate.c
@@ -9,47 +9,60 @@
 #include <config.h>
 #endif
 
-#ifdef HAVE_GSSAPI
-# include <gssapi/gssapi.h>
-#endif
+#if HAVE_GSSAPI
 
 #include <string.h>
-#include <stdio.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
 
 #include "soup-auth-negotiate.h"
-#include "soup-gssapi.h"
 #include "soup-headers.h"
 #include "soup-message.h"
 #include "soup-message-private.h"
 #include "soup-misc.h"
 #include "soup-uri.h"
 
+#define AUTH_GSS_ERROR      -1
+#define AUTH_GSS_COMPLETE    1
+#define AUTH_GSS_CONTINUE    0
+
+typedef enum {
+       SOUP_NEGOTIATE_NEW,
+       SOUP_NEGOTIATE_RECEIVED_CHALLENGE, /* received intial negotiate header */
+       SOUP_NEGOTIATE_SENT_RESPONSE,      /* sent response to server */
+       SOUP_NEGOTIATE_FAILED
+} SoupNegotiateState;
+
+typedef struct {
+       SoupNegotiateState state;
+
+       gss_ctx_id_t context;
+       gss_name_t   server_name;
+
+       gchar *response_header;
+} SoupNegotiateConnectionState;
+
 static gboolean soup_gss_build_response (SoupNegotiateConnectionState *conn,
                                         SoupAuth *auth, GError **err);
 static void parse_trusted_uris (void);
 static gboolean check_auth_trusted_uri (SoupAuthNegotiate *negotiate,
                                        SoupMessage *msg);
+static void soup_gss_client_cleanup (SoupNegotiateConnectionState *conn);
+static gboolean soup_gss_client_init (SoupNegotiateConnectionState *conn,
+                                     const char *host, GError **err);
+static gboolean soup_gss_client_inquire_cred (SoupAuth *auth, GError **err);
+static gchar * soup_gss_client_get_name (SoupAuth *auth, GError **err);
+static int soup_gss_client_step (SoupNegotiateConnectionState *conn,
+                                const char *host, GError **err);
 
-G_DEFINE_TYPE (SoupAuthNegotiate, soup_auth_negotiate, SOUP_TYPE_CONNECTION_AUTH)
-
-/* Function pointers to dlopen'ed libsoup-gssapi */
-struct {
-       void (*client_cleanup)(SoupNegotiateConnectionState *conn);
-       char * (*client_get_name)(SoupAuth *auth,
-                                 GError **err);
-       int (*client_init)(SoupNegotiateConnectionState *conn,
-                          const char *host,
-                          GError **err);
-       int (*client_inquire_cred)(SoupAuth *auth,
-                                  GError **err);
-       int (*client_step)(SoupNegotiateConnectionState *conn,
-                          const char *challenge,
-                          GError **err);
-} soup_gssapi_syms;
-gboolean have_gssapi;
+static const char spnego_OID[] = "\x2b\x06\x01\x05\x05\x02";
+static const gss_OID_desc gss_mech_spnego = { 6, (void *) &spnego_OID };
 
 static GSList *trusted_uris;
 
+G_DEFINE_TYPE (SoupAuthNegotiate, soup_auth_negotiate, SOUP_TYPE_CONNECTION_AUTH)
+
 static void
 soup_auth_negotiate_init (SoupAuthNegotiate *negotiate)
 {
@@ -67,8 +80,8 @@ soup_auth_negotiate_free_connection_state (SoupConnectionAuth *auth,
 {
        SoupNegotiateConnectionState *conn = state;
 
-       if (have_gssapi)
-               soup_gssapi_syms.client_cleanup (conn);
+       soup_gss_client_cleanup (conn);
+
        g_free (conn->response_header);
 }
 
@@ -139,8 +152,7 @@ soup_auth_negotiate_is_authenticated (SoupAuth *auth)
        gboolean has_credentials = FALSE;
        GError *err = NULL;
 
-       if (have_gssapi)
-               has_credentials = soup_gssapi_syms.client_inquire_cred (auth, &err);
+       has_credentials = soup_gss_client_inquire_cred (auth, &err);
 
        if (err)
                g_warning ("%s", err->message);
@@ -170,7 +182,7 @@ check_server_response (SoupMessage *msg, gpointer state)
                goto out;
        }
 
-       ret = soup_gssapi_syms.client_step (conn, auth_headers + 10, &err);
+       ret = soup_gss_client_step (conn, auth_headers + 10, &err);
 
        if (ret != AUTH_GSS_COMPLETE) {
                g_warning ("%s", err->message);
@@ -226,34 +238,6 @@ soup_auth_negotiate_is_connection_ready (SoupConnectionAuth *auth,
        return conn->state != SOUP_NEGOTIATE_FAILED;
 }
 
-static gboolean
-soup_gssapi_load (void)
-{
-       GModule *gssapi;
-       const char *modulename = PACKAGE "-gssapi-2.4." G_MODULE_SUFFIX;
-
-       if (!g_module_supported ())
-               return FALSE;
-
-       gssapi = g_module_open (modulename, G_MODULE_BIND_LOCAL);
-       if (!gssapi) {
-               g_warning ("Failed to load %s - negotiate support will "
-                          "be disabled.", modulename);
-               return FALSE;
-       }
-
-#define GSSAPI_BIND_SYMBOL(name) \
-       g_return_val_if_fail (g_module_symbol (gssapi, "soup_gss_" #name, (gpointer)&soup_gssapi_syms.name), 
FALSE)
-
-       GSSAPI_BIND_SYMBOL(client_cleanup);
-       GSSAPI_BIND_SYMBOL(client_get_name);
-       GSSAPI_BIND_SYMBOL(client_init);
-       GSSAPI_BIND_SYMBOL(client_inquire_cred);
-       GSSAPI_BIND_SYMBOL(client_step);
-#undef GSSPI_BIND_SYMBOL
-       return TRUE;
-}
-
 static void
 soup_auth_negotiate_class_init (SoupAuthNegotiateClass *auth_negotiate_class)
 {
@@ -275,26 +259,15 @@ soup_auth_negotiate_class_init (SoupAuthNegotiateClass *auth_negotiate_class)
        conn_auth_class->is_connection_ready = soup_auth_negotiate_is_connection_ready;
 
        parse_trusted_uris ();
-       have_gssapi = soup_gssapi_load();
 }
 
 static gboolean
 soup_gss_build_response (SoupNegotiateConnectionState *conn, SoupAuth *auth, GError **err)
 {
-       if (!have_gssapi) {
-               if (err && *err == NULL) {
-                       g_set_error (err,
-                                    SOUP_HTTP_ERROR,
-                                    SOUP_STATUS_GSSAPI_FAILED,
-                                    "GSSAPI unavailable");
-               }
-               return FALSE;
-       }
-
-       if (!soup_gssapi_syms.client_init (conn, soup_auth_get_host (SOUP_AUTH (auth)), err))
+       if (!soup_gss_client_init (conn, soup_auth_get_host (SOUP_AUTH (auth)), err))
                return FALSE;
 
-       if (soup_gssapi_syms.client_step (conn, "", err) != AUTH_GSS_CONTINUE)
+       if (soup_gss_client_step (conn, "", err) != AUTH_GSS_CONTINUE)
                return FALSE;
 
        return TRUE;
@@ -391,3 +364,208 @@ check_auth_trusted_uri (SoupAuthNegotiate *negotiate, SoupMessage *msg)
 
        return matched ? TRUE : FALSE;
 }
+
+static void
+soup_gss_error (OM_uint32 err_maj, OM_uint32 err_min, GError **err)
+{
+       OM_uint32 maj_stat, min_stat, msg_ctx = 0;
+       gss_buffer_desc status;
+       gchar *buf_maj = NULL, *buf_min = NULL;
+
+       do {
+               maj_stat = gss_display_status (&min_stat,
+                                              err_maj,
+                                              GSS_C_GSS_CODE,
+                                              (gss_OID) &gss_mech_spnego,
+                                              &msg_ctx,
+                                              &status);
+               if (GSS_ERROR (maj_stat))
+                       break;
+
+               buf_maj = g_strdup ((gchar *) status.value);
+               gss_release_buffer (&min_stat, &status);
+
+               maj_stat = gss_display_status (&min_stat,
+                                              err_min,
+                                              GSS_C_MECH_CODE,
+                                              GSS_C_NULL_OID,
+                                              &msg_ctx,
+                                              &status);
+               if (!GSS_ERROR (maj_stat)) {
+                       buf_min = g_strdup ((gchar *) status.value);
+                       gss_release_buffer (&min_stat, &status);
+               }
+
+               if (err && *err == NULL) {
+                       g_set_error (err,
+                                    SOUP_HTTP_ERROR,
+                                    SOUP_STATUS_GSSAPI_FAILED,
+                                    "%s %s",
+                                    buf_maj,
+                                    buf_min ? buf_min : "");
+               }
+               g_free (buf_maj);
+               g_free (buf_min);
+               buf_min = buf_maj = NULL;
+       } while (!GSS_ERROR (maj_stat) && msg_ctx != 0);
+}
+
+static gboolean
+soup_gss_client_inquire_cred (SoupAuth *auth, GError **err)
+{
+       gboolean ret = FALSE;
+       OM_uint32 maj_stat, min_stat;
+
+       maj_stat = gss_inquire_cred (&min_stat,
+                                    GSS_C_NO_CREDENTIAL,
+                                    NULL,
+                                    NULL,
+                                    NULL,
+                                    NULL);
+
+       if (GSS_ERROR (maj_stat))
+               soup_gss_error (maj_stat, min_stat, err);
+
+       ret = maj_stat == GSS_S_COMPLETE;
+
+       return ret;
+}
+
+static gchar *
+soup_gss_client_get_name (SoupAuth *auth, GError **err)
+{
+       gchar *name = NULL;
+       OM_uint32 maj_stat, min_stat;
+       gss_name_t gss_name;
+       gss_buffer_desc out = GSS_C_EMPTY_BUFFER;
+
+       maj_stat = gss_inquire_cred (&min_stat,
+                                    GSS_C_NO_CREDENTIAL,
+                                    &gss_name,
+                                    NULL,
+                                    NULL,
+                                    NULL);
+
+       if (GSS_ERROR (maj_stat)) {
+               soup_gss_error (maj_stat, min_stat, err);
+               goto out;
+       }
+
+       if (maj_stat != GSS_S_COMPLETE)
+               goto out;
+
+       maj_stat = gss_display_name (&min_stat,
+                                    gss_name,
+                                    &out,
+                                    NULL);
+
+       if (GSS_ERROR (maj_stat)) {
+               soup_gss_error (maj_stat, min_stat, err);
+               goto out;
+       }
+
+       if (maj_stat == GSS_S_COMPLETE)
+               name = g_strndup (out.value, out.length);
+
+       maj_stat = gss_release_buffer (&min_stat, &out);
+ out:
+       maj_stat = gss_release_name (&min_stat, &gss_name);
+
+       return name;
+}
+
+static gboolean
+soup_gss_client_init (SoupNegotiateConnectionState *conn, const gchar *host, GError **err)
+{
+       OM_uint32 maj_stat, min_stat;
+       gchar *service = NULL;
+       gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
+       gboolean ret = FALSE;
+       gchar *h;
+
+       conn->server_name = GSS_C_NO_NAME;
+       conn->context = GSS_C_NO_CONTEXT;
+
+       h = g_ascii_strdown (host, -1);
+       service = g_strconcat ("HTTP@", h, NULL);
+       token.length = strlen (service);
+       token.value = (gchar *) service;
+
+       maj_stat = gss_import_name (&min_stat,
+                                   &token,
+                                   (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
+                                   &conn->server_name);
+
+       if (GSS_ERROR (maj_stat)) {
+               soup_gss_error (maj_stat, min_stat, err);
+               ret = FALSE;
+               goto out;
+       }
+
+       ret = TRUE;
+out:
+       g_free (h);
+       g_free (service);
+       return ret;
+}
+
+static gint
+soup_gss_client_step (SoupNegotiateConnectionState *conn, const gchar *challenge, GError **err)
+{
+       OM_uint32 maj_stat, min_stat;
+       gss_buffer_desc in = GSS_C_EMPTY_BUFFER;
+       gss_buffer_desc out = GSS_C_EMPTY_BUFFER;
+       gint ret = AUTH_GSS_CONTINUE;
+
+       g_clear_pointer (&conn->response_header, g_free);
+
+       if (challenge && *challenge) {
+               size_t len;
+               in.value = g_base64_decode (challenge, &len);
+               in.length = len;
+       }
+
+       maj_stat = gss_init_sec_context (&min_stat,
+                                        GSS_C_NO_CREDENTIAL,
+                                        &conn->context,
+                                        conn->server_name,
+                                        (gss_OID) &gss_mech_spnego,
+                                        GSS_C_MUTUAL_FLAG,
+                                        GSS_C_INDEFINITE,
+                                        GSS_C_NO_CHANNEL_BINDINGS,
+                                        &in,
+                                        NULL,
+                                        &out,
+                                        NULL,
+                                        NULL);
+
+       if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) {
+               soup_gss_error (maj_stat, min_stat, err);
+               ret = AUTH_GSS_ERROR;
+               goto out;
+       }
+
+       ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE;
+       if (out.length) {
+               gchar *response = g_base64_encode ((const guchar *) out.value, out.length);
+               conn->response_header = g_strconcat ("Negotiate ", response, NULL);
+               g_free (response);
+               maj_stat = gss_release_buffer (&min_stat, &out);
+       }
+
+out:
+       if (out.value)
+               gss_release_buffer (&min_stat, &out);
+       if (in.value)
+               g_free (in.value);
+       return ret;
+}
+
+G_MODULE_EXPORT void
+soup_gss_client_cleanup (SoupNegotiateConnectionState *conn)
+{
+       OM_uint32 min_stat;
+
+       gss_release_name (&min_stat, &conn->server_name);
+}
+#endif /* HAVE_GSSAPI */
diff --git a/libsoup/soup-auth-negotiate.h b/libsoup/soup-auth-negotiate.h
index dbdfc3c..d6d373f 100644
--- a/libsoup/soup-auth-negotiate.h
+++ b/libsoup/soup-auth-negotiate.h
@@ -6,6 +6,8 @@
 #ifndef SOUP_AUTH_NEGOTIATE_H
 #define SOUP_AUTH_NEGOTIATE_H 1
 
+#if HAVE_GSSAPI
+
 #include "soup-connection-auth.h"
 
 #define SOUP_AUTH_NEGOTIATE(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_NEGOTIATE, 
SoupAuthNegotiate))
@@ -24,4 +26,6 @@ typedef struct {
 
 } SoupAuthNegotiateClass;
 
+#endif /* HAVE_GSSAPI */
+
 #endif /* SOUP_AUTH_NEGOTIATE_H */
diff --git a/libsoup/soup-auth.h b/libsoup/soup-auth.h
index 8db2ccc..e471587 100644
--- a/libsoup/soup-auth.h
+++ b/libsoup/soup-auth.h
@@ -99,8 +99,10 @@ GType soup_auth_basic_get_type  (void);
 GType soup_auth_digest_get_type (void);
 #define SOUP_TYPE_AUTH_NTLM   (soup_auth_ntlm_get_type ())
 GType soup_auth_ntlm_get_type   (void);
+#if HAVE_GSSAPI
 #define SOUP_TYPE_AUTH_NEGOTIATE  (soup_auth_negotiate_get_type ())
 GType soup_auth_negotiate_get_type   (void);
+#endif
 
 /* Deprecated SoupPasswordManager-related APIs: all are now no-ops */
 SOUP_AVAILABLE_IN_2_28


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