2003-08-12 Dan Winship <danw@ximian.com>
+ * libsoup/soup-auth.c: Make this an abstract GObject. Tweak some
+ of the interfaces around a little bit.
+
+ * libsoup/soup-auth-basic.c: subclass for Basic auth
+
+ * libsoup/soup-auth-digest.c: subclass for Digest auth
+
+ * libsoup/soup-auth-ntlm.c: subclass for NTLM auth. Move all of
+ the code from soup-ntlm.c here, and make it private.
+
+ * libsoup/soup-ntlm.c: gone
+
+ * libsoup/soup-misc.h: Remove the definition of SoupAuthType from
+ here, and change the signature of SoupAuthorizeFn.
+
+ * libsoup/soup-context.c: Use g_object_unref to free auths, use
+ methods instead of directly access private fields.
+
+ * libsoup/soup-queue.c: Likewise
+
+ * libsoup/soup-server-auth.c (soup_server_auth_free): Remove all
+ NTLM references. We have no plans to implement server-side NTLM
+ auth.
+
+ * tests/auth-test.c (identify_auth): Update for auth api changes
+
+2003-08-12 Dan Winship <danw@ximian.com>
+
* configure.in (GLIB): add gobject-2.0 to the PKG_CHECK_MODULES
call
soup-message.h \
soup-method.h \
soup-misc.h \
- soup-ntlm.h \
soup-server-auth.h \
soup-server.h \
soup-socket.h \
soup-address.c \
soup-auth.h \
soup-auth.c \
+ soup-auth-basic.h \
+ soup-auth-basic.c \
+ soup-auth-digest.h \
+ soup-auth-digest.c \
+ soup-auth-ntlm.h \
+ soup-auth-ntlm.c \
soup-context.c \
soup-dns.h \
soup-dns.c \
soup-message.c \
soup-method.c \
soup-misc.c \
- soup-ntlm.c \
soup-private.h \
soup-queue.h \
soup-queue.c \
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-basic.c: HTTP Basic Authentication
+ *
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-basic.h"
+#include "soup-headers.h"
+#include "soup-message.h"
+#include "soup-misc.h"
+#include "soup-private.h"
+#include "soup-uri.h"
+
+static void construct (SoupAuth *auth, const char *header);
+static GSList *get_protection_space (SoupAuth *auth, const SoupUri *source_uri);
+static const char *get_realm (SoupAuth *auth);
+static void authenticate (SoupAuth *auth, const char *username, const char *password);
+static gboolean invalidate (SoupAuth *auth);
+static gboolean is_authenticated (SoupAuth *auth);
+static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
+
+struct SoupAuthBasicPrivate {
+ char *realm, *token;
+};
+
+#define PARENT_TYPE SOUP_TYPE_AUTH
+static SoupAuthClass *parent_class;
+
+static void
+init (GObject *object)
+{
+ SoupAuthBasic *basic = SOUP_AUTH_BASIC (object);
+
+ basic->priv = g_new0 (SoupAuthBasicPrivate, 1);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthBasic *basic = SOUP_AUTH_BASIC (object);
+
+ g_free (basic->priv->realm);
+ g_free (basic->priv->token);
+ g_free (basic->priv);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+class_init (GObjectClass *object_class)
+{
+ SoupAuthClass *auth_class = SOUP_AUTH_CLASS (object_class);
+
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ auth_class->scheme_name = "Basic";
+
+ auth_class->construct = construct;
+ auth_class->get_protection_space = get_protection_space;
+ auth_class->get_realm = get_realm;
+ auth_class->authenticate = authenticate;
+ auth_class->invalidate = invalidate;
+ auth_class->is_authenticated = is_authenticated;
+ auth_class->get_authorization = get_authorization;
+
+ object_class->finalize = finalize;
+}
+
+SOUP_MAKE_TYPE (soup_auth_basic, SoupAuthBasic, class_init, init, PARENT_TYPE)
+
+
+static void
+construct (SoupAuth *auth, const char *header)
+{
+ SoupAuthBasic *basic = SOUP_AUTH_BASIC (auth);
+ GHashTable *tokens;
+
+ header += sizeof ("Basic");
+
+ tokens = soup_header_param_parse_list (header);
+ if (!tokens)
+ return;
+
+ basic->priv->realm = soup_header_param_copy_token (tokens, "realm");
+ soup_header_param_destroy_hash (tokens);
+}
+
+static GSList *
+get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
+{
+ char *space, *p;
+
+ space = g_strdup (source_uri->path);
+
+ /* Strip query and filename component */
+ p = strrchr (space, '/');
+ if (p && p != space && p[1])
+ *p = '\0';
+
+ return g_slist_prepend (NULL, space);
+}
+
+static const char *
+get_realm (SoupAuth *auth)
+{
+ SoupAuthBasic *basic = SOUP_AUTH_BASIC (auth);
+
+ return basic->priv->realm;
+}
+
+static void
+authenticate (SoupAuth *auth, const char *username, const char *password)
+{
+ SoupAuthBasic *basic = SOUP_AUTH_BASIC (auth);
+ char *user_pass;
+ int len;
+
+ g_return_if_fail (username != NULL);
+ g_return_if_fail (password != NULL);
+
+ user_pass = g_strdup_printf ("%s:%s", username, password);
+ len = strlen (user_pass);
+
+ basic->priv->token = soup_base64_encode (user_pass, len);
+
+ memset (user_pass, 0, len);
+ g_free (user_pass);
+}
+
+static gboolean
+invalidate (SoupAuth *auth)
+{
+ SoupAuthBasic *basic = SOUP_AUTH_BASIC (auth);
+
+ g_free (basic->priv->token);
+ basic->priv->token = NULL;
+
+ return TRUE;
+}
+
+static gboolean
+is_authenticated (SoupAuth *auth)
+{
+ SoupAuthBasic *basic = SOUP_AUTH_BASIC (auth);
+
+ return basic->priv->token != NULL;
+}
+
+static char *
+get_authorization (SoupAuth *auth, SoupMessage *msg)
+{
+ SoupAuthBasic *basic = SOUP_AUTH_BASIC (auth);
+
+ return g_strdup_printf ("Basic %s", basic->priv->token);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_AUTH_BASIC_H
+#define SOUP_AUTH_BASIC_H 1
+
+#include "soup-auth.h"
+
+#define SOUP_TYPE_AUTH_BASIC (soup_auth_basic_get_type ())
+#define SOUP_AUTH_BASIC(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_BASIC, SoupAuthBasic))
+#define SOUP_AUTH_BASIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_BASIC, SoupAuthBasicClass))
+#define SOUP_IS_AUTH_BASIC(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_BASIC))
+#define SOUP_IS_AUTH_BASIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_BASIC))
+#define SOUP_AUTH_BASIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_BASIC, SoupAuthBasicClass))
+
+typedef struct SoupAuthBasicPrivate SoupAuthBasicPrivate;
+
+typedef struct {
+ SoupAuth parent;
+
+ SoupAuthBasicPrivate *priv;
+} SoupAuthBasic;
+
+typedef struct {
+ SoupAuthClass parent_class;
+
+} SoupAuthBasicClass;
+
+GType soup_auth_basic_get_type (void);
+
+#endif /*SOUP_AUTH_BASIC_H*/
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-digest-offset: 8 -*- */
+/*
+ * soup-auth-digest.c: HTTP Digest Authentication
+ *
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "soup-auth-digest.h"
+#include "soup-headers.h"
+#include "soup-message.h"
+#include "soup-misc.h"
+#include "soup-private.h"
+#include "soup-uri.h"
+#include "md5-utils.h"
+
+static void construct (SoupAuth *auth, const char *header);
+static GSList *get_protection_space (SoupAuth *auth, const SoupUri *source_uri);
+static const char *get_realm (SoupAuth *auth);
+static void authenticate (SoupAuth *auth, const char *username, const char *password);
+static gboolean invalidate (SoupAuth *auth);
+static gboolean is_authenticated (SoupAuth *auth);
+static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
+
+typedef enum {
+ QOP_NONE = 0,
+ QOP_AUTH = 1 << 0,
+ QOP_AUTH_INT = 1 << 1
+} QOPType;
+
+typedef enum {
+ ALGORITHM_MD5 = 1 << 0,
+ ALGORITHM_MD5_SESS = 1 << 1
+} AlgorithmType;
+
+struct SoupAuthDigestPrivate {
+ char *user;
+ guchar hex_a1[33];
+
+ /* These are provided by the server */
+ char *realm;
+ char *nonce;
+ QOPType qop_options;
+ AlgorithmType algorithm;
+ char *domain;
+
+ /* These are generated by the client */
+ char *cnonce;
+ int nc;
+ QOPType qop;
+};
+
+#define PARENT_TYPE SOUP_TYPE_AUTH
+static SoupAuthClass *parent_class;
+
+static void
+init (GObject *object)
+{
+ SoupAuthDigest *digest = SOUP_AUTH_DIGEST (object);
+
+ digest->priv = g_new0 (SoupAuthDigestPrivate, 1);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthDigest *digest = SOUP_AUTH_DIGEST (object);
+
+ if (digest->priv->user)
+ g_free (digest->priv->user);
+ if (digest->priv->realm)
+ g_free (digest->priv->realm);
+ if (digest->priv->nonce)
+ g_free (digest->priv->nonce);
+ if (digest->priv->domain)
+ g_free (digest->priv->domain);
+ if (digest->priv->cnonce)
+ g_free (digest->priv->cnonce);
+
+ g_free (digest->priv);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+class_init (GObjectClass *object_class)
+{
+ SoupAuthClass *auth_class = SOUP_AUTH_CLASS (object_class);
+
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ auth_class->scheme_name = "Digest";
+
+ auth_class->get_protection_space = get_protection_space;
+ auth_class->get_realm = get_realm;
+ auth_class->construct = construct;
+ auth_class->authenticate = authenticate;
+ auth_class->invalidate = invalidate;
+ auth_class->is_authenticated = is_authenticated;
+ auth_class->get_authorization = get_authorization;
+
+ object_class->finalize = finalize;
+}
+
+SOUP_MAKE_TYPE (soup_auth_digest, SoupAuthDigest, class_init, init, PARENT_TYPE)
+
+typedef struct {
+ char *name;
+ guint type;
+} DataType;
+
+static DataType qop_types[] = {
+ { "auth", QOP_AUTH },
+ { "auth-int", QOP_AUTH_INT }
+};
+
+static DataType algorithm_types[] = {
+ { "MD5", ALGORITHM_MD5 },
+ { "MD5-sess", ALGORITHM_MD5_SESS }
+};
+
+static guint
+decode_data_type (DataType *dtype, const char *name)
+{
+ int i;
+
+ if (!name)
+ return 0;
+
+ for (i = 0; dtype[i].name; i++) {
+ if (!g_strcasecmp (dtype[i].name, name))
+ return dtype[i].type;
+ }
+
+ return 0;
+}
+
+static inline guint
+decode_qop (const char *name)
+{
+ return decode_data_type (qop_types, name);
+}
+
+static inline guint
+decode_algorithm (const char *name)
+{
+ return decode_data_type (algorithm_types, name);
+}
+
+static void
+construct (SoupAuth *auth, const char *header)
+{
+ SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
+ GHashTable *tokens;
+ char *tmp, *ptr;
+
+ header += sizeof ("Digest");
+
+ tokens = soup_header_param_parse_list (header);
+ if (!tokens)
+ return;
+
+ digest->priv->nc = 1;
+ /* We're just going to do qop=auth for now */
+ digest->priv->qop = QOP_AUTH;
+
+ digest->priv->realm = soup_header_param_copy_token (tokens, "realm");
+ digest->priv->domain = soup_header_param_copy_token (tokens, "domain");
+ digest->priv->nonce = soup_header_param_copy_token (tokens, "nonce");
+
+ tmp = soup_header_param_copy_token (tokens, "qop");
+ ptr = tmp;
+
+ while (ptr && *ptr) {
+ char *token;
+
+ token = soup_header_param_decode_token ((char **)&ptr);
+ if (token)
+ digest->priv->qop_options |= decode_qop (token);
+ g_free (token);
+
+ if (*ptr == ',')
+ ptr++;
+ }
+ g_free (tmp);
+
+ tmp = soup_header_param_copy_token (tokens, "algorithm");
+ digest->priv->algorithm = decode_algorithm (tmp);
+ g_free (tmp);
+
+ soup_header_param_destroy_hash (tokens);
+}
+
+static GSList *
+get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
+{
+ SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
+ GSList *space = NULL;
+ SoupUri *uri;
+ char *domain, *d, *lasts, *dir, *slash;
+
+ if (!digest->priv->domain || !*digest->priv->domain) {
+ /* If no domain directive, the protection space is the
+ * whole server.
+ */
+ return g_slist_prepend (NULL, g_strdup (""));
+ }
+
+ domain = g_strdup (digest->priv->domain);
+ for (d = strtok_r (domain, " ", &lasts); d; d = strtok_r (NULL, " ", &lasts)) {
+ if (*d == '/')
+ dir = g_strdup (d);
+ else {
+ uri = soup_uri_new (d);
+ if (uri && uri->protocol == source_uri->protocol &&
+ uri->port == source_uri->port &&
+ !strcmp (uri->host, source_uri->host))
+ dir = g_strdup (uri->path);
+ else
+ dir = NULL;
+ if (uri)
+ soup_uri_free (uri);
+ }
+
+ if (dir) {
+ slash = strrchr (dir, '/');
+ if (slash && !slash[1])
+ *slash = '\0';
+
+ space = g_slist_prepend (space, dir);
+ }
+ }
+ g_free (domain);
+
+ return space;
+}
+
+static const char *
+get_realm (SoupAuth *auth)
+{
+ SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
+
+ return digest->priv->realm;
+}
+
+static void
+digest_hex (guchar *digest, guchar hex[33])
+{
+ guchar *s, *p;
+
+ /* lowercase hexify that bad-boy... */
+ for (s = digest, p = hex; p < hex + 32; s++, p += 2)
+ sprintf (p, "%.2x", *s);
+}
+
+static void
+authenticate (SoupAuth *auth, const char *username, const char *password)
+{
+ SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
+ MD5Context ctx;
+ guchar d[16];
+ char *bgen;
+
+ g_return_if_fail (username != NULL);
+
+ bgen = g_strdup_printf ("%p:%lu:%lu",
+ auth,
+ (unsigned long) getpid (),
+ (unsigned long) time (0));
+ digest->priv->cnonce = soup_base64_encode (bgen, strlen (bgen));
+ g_free (bgen);
+
+ digest->priv->user = g_strdup (username);
+
+ /* compute A1 */
+ md5_init (&ctx);
+
+ md5_update (&ctx, username, strlen (username));
+
+ md5_update (&ctx, ":", 1);
+ if (digest->priv->realm)
+ md5_update (&ctx, digest->priv->realm, strlen (digest->priv->realm));
+
+ md5_update (&ctx, ":", 1);
+ if (password)
+ md5_update (&ctx, password, strlen (password));
+
+ if (digest->priv->algorithm == ALGORITHM_MD5_SESS) {
+ md5_final (&ctx, d);
+
+ md5_init (&ctx);
+ md5_update (&ctx, d, 16);
+ md5_update (&ctx, ":", 1);
+ md5_update (&ctx, digest->priv->nonce, strlen (digest->priv->nonce));
+ md5_update (&ctx, ":", 1);
+ md5_update (&ctx, digest->priv->cnonce, strlen (digest->priv->cnonce));
+ }
+
+ /* hexify A1 */
+ md5_final (&ctx, d);
+ digest_hex (d, digest->priv->hex_a1);
+}
+
+static gboolean
+invalidate (SoupAuth *auth)
+{
+ /* An invalidated Digest auth is useless. You need to get
+ * a new nonce from the server before you can start using it
+ * again.
+ */
+ return FALSE;
+}
+
+static gboolean
+is_authenticated (SoupAuth *auth)
+{
+ SoupAuthDigest *digest = SOUP_AUTH_DIGEST (auth);
+
+ return digest->priv->cnonce != NULL;
+}
+
+static char *
+compute_response (SoupAuthDigest *digest, SoupMessage *msg)
+{
+ guchar hex_a2[33], o[33];
+ guchar d[16];
+ MD5Context md5;
+ char *url;
+ SoupContext *ctx;
+
+ ctx = soup_message_get_context (msg);
+ g_return_val_if_fail (ctx != NULL, NULL);
+ url = soup_uri_to_string (soup_context_get_uri (ctx), TRUE);
+
+ /* compute A2 */
+ md5_init (&md5);
+ md5_update (&md5, msg->method, strlen (msg->method));
+ md5_update (&md5, ":", 1);
+ md5_update (&md5, url, strlen (url));
+
+ g_free (url);
+
+ if (digest->priv->qop == QOP_AUTH_INT) {
+ /* FIXME: Actually implement. Ugh. */
+ md5_update (&md5, ":", 1);
+ md5_update (&md5, "00000000000000000000000000000000", 32);
+ }
+
+ /* now hexify A2 */
+ md5_final (&md5, d);
+ digest_hex (d, hex_a2);
+
+ /* compute KD */
+ md5_init (&md5);
+ md5_update (&md5, digest->priv->hex_a1, 32);
+ md5_update (&md5, ":", 1);
+ md5_update (&md5, digest->priv->nonce, strlen (digest->priv->nonce));
+ md5_update (&md5, ":", 1);
+
+ if (digest->priv->qop) {
+ char *tmp;
+
+ tmp = g_strdup_printf ("%.8x", digest->priv->nc);
+
+ md5_update (&md5, tmp, strlen (tmp));
+ g_free (tmp);
+ md5_update (&md5, ":", 1);
+ md5_update (&md5, digest->priv->cnonce, strlen (digest->priv->cnonce));
+ md5_update (&md5, ":", 1);
+
+ if (digest->priv->qop == QOP_AUTH)
+ tmp = "auth";
+ else if (digest->priv->qop == QOP_AUTH_INT)
+ tmp = "auth-int";
+ else
+ g_assert_not_reached ();
+
+ md5_update (&md5, tmp, strlen (tmp));
+ md5_update (&md5, ":", 1);
+ }
+
+ md5_update (&md5, hex_a2, 32);
+ md5_final (&md5, d);
+
+ digest_hex (d, o);
+
+ return g_strdup (o);
+}
+
+static char *
+get_authorization (SoupAuth *auth, SoupMessage *msg)
+{
+ SoupAuthDigest *digest = (SoupAuthDigest *) auth;
+ char *response;
+ char *qop = NULL;
+ char *nc;
+ char *url;
+ char *out;
+ SoupContext *ctx;
+
+ ctx = soup_message_get_context (msg);
+ g_return_val_if_fail (ctx != NULL, NULL);
+ url = soup_uri_to_string (soup_context_get_uri (ctx), TRUE);
+
+ response = compute_response (digest, msg);
+
+ if (digest->priv->qop == QOP_AUTH)
+ qop = "auth";
+ else if (digest->priv->qop == QOP_AUTH_INT)
+ qop = "auth-int";
+ else
+ g_assert_not_reached ();
+
+ nc = g_strdup_printf ("%.8x", digest->priv->nc);
+
+ out = g_strdup_printf (
+ "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", %s%s%s "
+ "%s%s%s %s%s%s uri=\"%s\", response=\"%s\"",
+ digest->priv->user,
+ digest->priv->realm,
+ digest->priv->nonce,
+
+ digest->priv->qop ? "cnonce=\"" : "",
+ digest->priv->qop ? digest->priv->cnonce : "",
+ digest->priv->qop ? "\"," : "",
+
+ digest->priv->qop ? "nc=" : "",
+ digest->priv->qop ? nc : "",
+ digest->priv->qop ? "," : "",
+
+ digest->priv->qop ? "qop=" : "",
+ digest->priv->qop ? qop : "",
+ digest->priv->qop ? "," : "",
+
+ url,
+ response);
+
+ g_free (response);
+ g_free (url);
+ g_free (nc);
+
+ digest->priv->nc++;
+
+ return out;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_AUTH_DIGEST_H
+#define SOUP_AUTH_DIGEST_H 1
+
+#include "soup-auth.h"
+
+#define SOUP_TYPE_AUTH_DIGEST (soup_auth_digest_get_type ())
+#define SOUP_AUTH_DIGEST(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_DIGEST, SoupAuthDigest))
+#define SOUP_AUTH_DIGEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_DIGEST, SoupAuthDigestClass))
+#define SOUP_IS_AUTH_DIGEST(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_DIGEST))
+#define SOUP_IS_AUTH_DIGEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_DIGEST))
+#define SOUP_AUTH_DIGEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_DIGEST, SoupAuthDigestClass))
+
+typedef struct SoupAuthDigestPrivate SoupAuthDigestPrivate;
+
+typedef struct {
+ SoupAuth parent;
+
+ SoupAuthDigestPrivate *priv;
+} SoupAuthDigest;
+
+typedef struct {
+ SoupAuthClass parent_class;
+
+} SoupAuthDigestClass;
+
+GType soup_auth_digest_get_type (void);
+
+#endif /*SOUP_AUTH_DIGEST_H*/
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-ntlm-offset: 8 -*- */
/*
- * soup-ntlm.c: Microsoft Windows NTLM authentication support
+ * soup-auth-ntlm.c: HTTP NTLM Authentication
*
- * Authors:
- * Dan Winship (danw@ximian.com)
- *
- * Public domain DES implementation from Phil Karn.
- *
- * All else Copyright (C) 2001-2002, Ximian, Inc.
+ * Copyright (C) 2001-2003, Ximian, Inc.
*/
#ifdef HAVE_CONFIG_H
#endif
#include <ctype.h>
-#include <stdlib.h>
#include <string.h>
-#include "soup-ntlm.h"
-#include "soup-misc.h"
+#include "soup-auth-ntlm.h"
+#include "soup-private.h"
+#include "soup-uri.h"
+
+static void construct (SoupAuth *auth, const char *header);
+static GSList *get_protection_space (SoupAuth *auth, const SoupUri *source_uri);
+static const char *get_realm (SoupAuth *auth);
+static void authenticate (SoupAuth *auth, const char *username, const char *password);
+static gboolean invalidate (SoupAuth *auth);
+static gboolean is_authenticated (SoupAuth *auth);
+static char *get_authorization (SoupAuth *auth, SoupMessage *msg);
+
+char *soup_ntlm_request (void);
+gboolean soup_ntlm_parse_challenge (const char *challenge,
+ char **nonce,
+ char **default_domain);
+char *soup_ntlm_response (const char *nonce,
+ const char *user,
+ const char *password,
+ const char *host,
+ const char *domain);
+
+struct SoupAuthNTLMPrivate {
+ gboolean authenticated;
+ char *header;
+ char *response;
+};
+
+#define PARENT_TYPE SOUP_TYPE_AUTH
+static SoupAuthClass *parent_class;
+
+static void
+init (GObject *object)
+{
+ SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (object);
+
+ ntlm->priv = g_new0 (SoupAuthNTLMPrivate, 1);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (object);
+
+ g_free (ntlm->priv->header);
+ g_free (ntlm->priv->response);
+ g_free (ntlm->priv);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+class_init (GObjectClass *object_class)
+{
+ SoupAuthClass *auth_class = SOUP_AUTH_CLASS (object_class);
+
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ auth_class->scheme_name = "NTLM";
+
+ auth_class->construct = construct;
+ auth_class->get_protection_space = get_protection_space;
+ auth_class->get_realm = get_realm;
+ auth_class->authenticate = authenticate;
+ auth_class->invalidate = invalidate;
+ auth_class->is_authenticated = is_authenticated;
+ auth_class->get_authorization = get_authorization;
+
+ object_class->finalize = finalize;
+}
+
+SOUP_MAKE_TYPE (soup_auth_ntlm, SoupAuthNTLM, class_init, init, PARENT_TYPE)
+
+
+SoupAuth *
+soup_auth_ntlm_new (void)
+{
+ SoupAuth *auth;
+
+ auth = g_object_new (SOUP_TYPE_AUTH_NTLM, NULL);
+ construct (auth, "");
+ return auth;
+}
+
+static void
+construct (SoupAuth *auth, const char *header)
+{
+ SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
+
+ ntlm->priv->header = g_strdup (header);
+}
+
+static GSList *
+get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
+{
+ /* The protection space is the whole server. */
+ return g_slist_prepend (NULL, g_strdup (""));
+}
+
+static const char *
+get_realm (SoupAuth *auth)
+{
+ return "";
+}
+
+static void
+authenticate (SoupAuth *auth, const char *username, const char *password)
+{
+ SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
+ char *domain, *nonce;
+
+ if (!ntlm->priv->header || strlen (ntlm->priv->header) < sizeof ("NTLM"))
+ return;
+
+ if (ntlm->priv->response)
+ g_free (ntlm->priv->response);
+
+ if (soup_ntlm_parse_challenge (ntlm->priv->header, &nonce, &domain)) {
+ ntlm->priv->response =
+ soup_ntlm_response (nonce, username, password,
+ NULL, domain);
+ g_free (nonce);
+ g_free (domain);
+ } else
+ ntlm->priv->response = NULL;
+
+ g_free (ntlm->priv->header);
+ ntlm->priv->header = NULL;
+
+ ntlm->priv->authenticated = TRUE;
+}
+
+static gboolean
+invalidate (SoupAuth *auth)
+{
+ SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
+
+ g_free (ntlm->priv->response);
+ ntlm->priv->response = NULL;
+ g_free (ntlm->priv->header);
+ ntlm->priv->header = NULL;
+
+ ntlm->priv->authenticated = FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+is_authenticated (SoupAuth *auth)
+{
+ SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
+
+ return ntlm->priv->authenticated;
+}
+
+static char *
+get_authorization (SoupAuth *auth, SoupMessage *msg)
+{
+ SoupAuthNTLM *ntlm = SOUP_AUTH_NTLM (auth);
+ char *ret;
+
+ if (!ntlm->priv->authenticated)
+ return soup_ntlm_request ();
+
+ /* Otherwise, return the response; but only once */
+ ret = ntlm->priv->response;
+ ntlm->priv->response = NULL;
+ return ret;
+}
+
+
/* MD4 */
static void md4sum (const unsigned char *in,
"\x4B\x47\x53\x21\x40\x23\x24\x25" \
"\x00\x00\x00\x00\x00"
-void
-soup_ntlm_lanmanager_hash (const char *password, guchar hash[21])
+static void
+lanmanager_hash (const char *password, guchar hash[21])
{
guchar lm_password [15];
DES_KS ks;
des (ks, hash + 8);
}
-void
-soup_ntlm_nt_hash (const char *password, guchar hash[21])
+static void
+nt_hash (const char *password, guchar hash[21])
{
unsigned char *buf, *p;
unsigned char *out, *p;
int state, save;
- soup_ntlm_lanmanager_hash (password, hash);
+ lanmanager_hash (password, hash);
calc_response (hash, nonce, lm_resp);
- soup_ntlm_nt_hash (password, hash);
+ nt_hash (password, hash);
calc_response (hash, nonce, nt_resp);
memset (&resp, 0, sizeof (resp));
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-ntlm-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_AUTH_NTLM_H
+#define SOUP_AUTH_NTLM_H 1
+
+#include "soup-auth.h"
+
+#define SOUP_TYPE_AUTH_NTLM (soup_auth_ntlm_get_type ())
+#define SOUP_AUTH_NTLM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLM))
+#define SOUP_AUTH_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
+#define SOUP_IS_AUTH_NTLM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_NTLM))
+#define SOUP_IS_AUTH_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_NTLM))
+#define SOUP_AUTH_NTLM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
+
+typedef struct SoupAuthNTLMPrivate SoupAuthNTLMPrivate;
+
+typedef struct {
+ SoupAuth parent;
+
+ SoupAuthNTLMPrivate *priv;
+} SoupAuthNTLM;
+
+typedef struct {
+ SoupAuthClass parent_class;
+
+} SoupAuthNTLMClass;
+
+GType soup_auth_ntlm_get_type (void);
+
+SoupAuth *soup_auth_ntlm_new (void);
+
+#endif /*SOUP_AUTH_NTLM_H*/
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * soup-auth.c: HTTP Authentication schemes (basic and digest)
+ * soup-auth.c: HTTP Authentication framework
*
- * Authors:
- * Joe Shaw (joe@ximian.com)
- * Jeffrey Steadfast (fejj@ximian.com)
- * Alex Graveley (alex@ximian.com)
- *
- * Copyright (C) 2001-2002, Ximian, Inc.
+ * Copyright (C) 2001-2003, Ximian, Inc.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
-#include <unistd.h>
#include <string.h>
-#include <stdlib.h>
-#include <glib.h>
-
-#include <stdio.h>
-#include <time.h>
-#include "md5-utils.h"
#include "soup-auth.h"
-#include "soup-context.h"
-#include "soup-headers.h"
-#include "soup-message.h"
+#include "soup-auth-basic.h"
+#include "soup-auth-digest.h"
+#include "soup-auth-ntlm.h"
#include "soup-private.h"
-#include "soup-ntlm.h"
-
-/*
- * Basic Authentication Support
- */
-
-typedef struct {
- SoupAuth auth;
- gchar *token;
-} SoupAuthBasic;
-
-static char *
-basic_auth_func (SoupAuth *auth, SoupMessage *message)
-{
- SoupAuthBasic *basic = (SoupAuthBasic *) auth;
-
- return g_strconcat ("Basic ", basic->token, NULL);
-}
-
-static void
-basic_parse_func (SoupAuth *auth, const char *header)
-{
- GHashTable *tokens;
-
- header += sizeof ("Basic");
-
- tokens = soup_header_param_parse_list (header);
- if (!tokens) return;
-
- auth->realm = soup_header_param_copy_token (tokens, "realm");
-
- soup_header_param_destroy_hash (tokens);
-}
-
-static GSList *
-basic_pspace_func (SoupAuth *auth, const SoupUri *source_uri)
-{
- char *space, *p;
-
- space = g_strdup (source_uri->path);
-
- /* Strip query and filename component */
- p = strrchr (space, '/');
- if (p && p != space && p[1])
- *p = '\0';
-
- return g_slist_prepend (NULL, space);
-}
-
-static void
-basic_init_func (SoupAuth *auth, const SoupUri *uri)
-{
- SoupAuthBasic *basic = (SoupAuthBasic *) auth;
- char *user_pass;
-
- user_pass = g_strdup_printf ("%s:%s", uri->user, uri->passwd);
- basic->token = soup_base64_encode (user_pass, strlen (user_pass));
- g_free (user_pass);
-
- auth->authenticated = TRUE;
-}
-
-static gboolean
-basic_invalidate_func (SoupAuth *auth)
-{
- SoupAuthBasic *basic = (SoupAuthBasic *) auth;
-
- g_free (basic->token);
- basic->token = NULL;
- auth->authenticated = FALSE;
-
- return TRUE;
-}
-
-static void
-basic_free (SoupAuth *auth)
-{
- SoupAuthBasic *basic = (SoupAuthBasic *) auth;
-
- g_free (basic->token);
- g_free (basic);
-}
-
-static SoupAuth *
-soup_auth_new_basic (void)
-{
- SoupAuthBasic *basic;
- SoupAuth *auth;
-
- basic = g_new0 (SoupAuthBasic, 1);
- auth = (SoupAuth *) basic;
- auth->type = SOUP_AUTH_TYPE_BASIC;
- auth->authenticated = FALSE;
-
- auth->parse_func = basic_parse_func;
- auth->init_func = basic_init_func;
- auth->invalidate_func = basic_invalidate_func;
- auth->pspace_func = basic_pspace_func;
- auth->auth_func = basic_auth_func;
- auth->free_func = basic_free;
-
- return auth;
-}
-
-
-/*
- * Digest Authentication Support
- */
-
-typedef enum {
- QOP_NONE = 0,
- QOP_AUTH = 1 << 0,
- QOP_AUTH_INT = 1 << 1
-} QOPType;
-
-typedef enum {
- ALGORITHM_MD5 = 1 << 0,
- ALGORITHM_MD5_SESS = 1 << 1
-} AlgorithmType;
-
-typedef struct {
- SoupAuth auth;
-
- gchar *user;
- guchar hex_a1 [33];
-
- /* These are provided by the server */
- char *nonce;
- QOPType qop_options;
- AlgorithmType algorithm;
- char *domain;
-
- /* These are generated by the client */
- char *cnonce;
- int nc;
- QOPType qop;
-} SoupAuthDigest;
-
-static void
-digest_hex (guchar *digest, guchar hex[33])
-{
- guchar *s, *p;
-
- /* lowercase hexify that bad-boy... */
- for (s = digest, p = hex; p < hex + 32; s++, p += 2)
- sprintf (p, "%.2x", *s);
-}
-
-static char *
-compute_response (SoupAuthDigest *digest, SoupMessage *msg)
-{
- const SoupUri *uri;
- guchar hex_a2[33], o[33];
- guchar d[16];
- MD5Context ctx;
- char *url;
-
- uri = soup_context_get_uri (msg->context);
- url = soup_uri_to_string (uri, TRUE);
-
- /* compute A2 */
- md5_init (&ctx);
- md5_update (&ctx, msg->method, strlen (msg->method));
- md5_update (&ctx, ":", 1);
- md5_update (&ctx, url, strlen (url));
- g_free (url);
-
- if (digest->qop == QOP_AUTH_INT) {
- /* FIXME: Actually implement. Ugh. */
- md5_update (&ctx, ":", 1);
- md5_update (&ctx, "00000000000000000000000000000000", 32);
- }
-
- /* now hexify A2 */
- md5_final (&ctx, d);
- digest_hex (d, hex_a2);
-
- /* compute KD */
- md5_init (&ctx);
- md5_update (&ctx, digest->hex_a1, 32);
- md5_update (&ctx, ":", 1);
- md5_update (&ctx, digest->nonce, strlen (digest->nonce));
- md5_update (&ctx, ":", 1);
-
- if (digest->qop) {
- char *tmp;
-
- tmp = g_strdup_printf ("%.8x", digest->nc);
-
- md5_update (&ctx, tmp, strlen (tmp));
- g_free (tmp);
- md5_update (&ctx, ":", 1);
- md5_update (&ctx, digest->cnonce, strlen (digest->cnonce));
- md5_update (&ctx, ":", 1);
-
- if (digest->qop == QOP_AUTH)
- tmp = "auth";
- else if (digest->qop == QOP_AUTH_INT)
- tmp = "auth-int";
- else
- g_assert_not_reached ();
-
- md5_update (&ctx, tmp, strlen (tmp));
- md5_update (&ctx, ":", 1);
- }
-
- md5_update (&ctx, hex_a2, 32);
- md5_final (&ctx, d);
-
- digest_hex (d, o);
-
- return g_strdup (o);
-}
-
-static char *
-digest_auth_func (SoupAuth *auth, SoupMessage *message)
-{
- SoupAuthDigest *digest = (SoupAuthDigest *) auth;
- const SoupUri *uri;
- char *response;
- char *qop = NULL;
- char *nc;
- char *url;
- char *out;
-
- g_return_val_if_fail (message, NULL);
-
- response = compute_response (digest, message);
-
- if (digest->qop == QOP_AUTH)
- qop = "auth";
- else if (digest->qop == QOP_AUTH_INT)
- qop = "auth-int";
- else
- g_assert_not_reached ();
-
- uri = soup_context_get_uri (message->context);
- url = soup_uri_to_string (uri, TRUE);
-
- nc = g_strdup_printf ("%.8x", digest->nc);
-
- out = g_strdup_printf (
- "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", %s%s%s "
- "%s%s%s %s%s%s uri=\"%s\", response=\"%s\"",
- digest->user,
- auth->realm,
- digest->nonce,
-
- digest->qop ? "cnonce=\"" : "",
- digest->qop ? digest->cnonce : "",
- digest->qop ? "\"," : "",
-
- digest->qop ? "nc=" : "",
- digest->qop ? nc : "",
- digest->qop ? "," : "",
-
- digest->qop ? "qop=" : "",
- digest->qop ? qop : "",
- digest->qop ? "," : "",
-
- url,
- response);
-
- g_free (response);
- g_free (url);
- g_free (nc);
-
- digest->nc++;
-
- return out;
-}
-
-typedef struct {
- char *name;
- guint type;
-} DataType;
-
-static DataType qop_types[] = {
- { "auth", QOP_AUTH },
- { "auth-int", QOP_AUTH_INT }
-};
-
-static DataType algorithm_types[] = {
- { "MD5", ALGORITHM_MD5 },
- { "MD5-sess", ALGORITHM_MD5_SESS }
-};
-
-static guint
-decode_data_type (DataType *dtype, const char *name)
-{
- int i;
-
- if (!name)
- return 0;
-
- for (i = 0; dtype[i].name; i++) {
- if (!g_strcasecmp (dtype[i].name, name))
- return dtype[i].type;
- }
-
- return 0;
-}
-
-static inline guint
-decode_qop (const char *name)
-{
- return decode_data_type (qop_types, name);
-}
-
-static inline guint
-decode_algorithm (const char *name)
-{
- return decode_data_type (algorithm_types, name);
-}
-
-static void
-digest_parse_func (SoupAuth *auth, const char *header)
-{
- SoupAuthDigest *digest = (SoupAuthDigest *) auth;
- GHashTable *tokens;
- char *tmp, *ptr;
-
- header += sizeof ("Digest");
-
- tokens = soup_header_param_parse_list (header);
- if (!tokens) return;
-
- auth->realm = soup_header_param_copy_token (tokens, "realm");
-
- digest->nonce = soup_header_param_copy_token (tokens, "nonce");
- digest->domain = soup_header_param_copy_token (tokens, "domain");
- tmp = soup_header_param_copy_token (tokens, "qop");
- ptr = tmp;
-
- while (ptr && *ptr) {
- char *token;
-
- token = soup_header_param_decode_token (&ptr);
- if (token)
- digest->qop_options |= decode_qop (token);
- g_free (token);
-
- if (*ptr == ',')
- ptr++;
- }
-
- g_free (tmp);
-
- tmp = soup_header_param_copy_token (tokens, "algorithm");
- digest->algorithm = decode_algorithm (tmp);
- g_free (tmp);
-
- soup_header_param_destroy_hash (tokens);
-}
-
-static GSList *
-digest_pspace_func (SoupAuth *auth, const SoupUri *source_uri)
-{
- SoupAuthDigest *digest = (SoupAuthDigest *) auth;
- GSList *space = NULL;
- SoupUri *uri;
- char *domain, *d, *lasts, *dir, *slash;
-
- if (!digest->domain || !*digest->domain) {
- /* If no domain directive, the protection space is the
- * whole server.
- */
- return g_slist_prepend (NULL, g_strdup (""));
- }
-
- domain = g_strdup (digest->domain);
- for (d = strtok_r (domain, " ", &lasts); d; d = strtok_r (NULL, " ", &lasts)) {
- if (*d == '/')
- dir = g_strdup (d);
- else {
- uri = soup_uri_new (d);
- if (uri && uri->protocol == source_uri->protocol &&
- uri->port == source_uri->port &&
- !strcmp (uri->host, source_uri->host))
- dir = g_strdup (uri->path);
- else
- dir = NULL;
- if (uri)
- soup_uri_free (uri);
- }
-
- if (dir) {
- slash = strrchr (dir, '/');
- if (slash && !slash[1])
- *slash = '\0';
-
- space = g_slist_prepend (space, dir);
- }
- }
- g_free (domain);
-
- return space;
-}
-
-static void
-digest_init_func (SoupAuth *auth, const SoupUri *uri)
-{
- SoupAuthDigest *digest = (SoupAuthDigest *) auth;
- MD5Context ctx;
- guchar d[16];
-
- digest->user = g_strdup (uri->user);
-
- /* compute A1 */
- md5_init (&ctx);
-
- md5_update (&ctx, uri->user, strlen (uri->user));
-
- md5_update (&ctx, ":", 1);
- if (auth->realm)
- md5_update (&ctx, auth->realm, strlen (auth->realm));
-
- md5_update (&ctx, ":", 1);
- if (uri->passwd)
- md5_update (&ctx, uri->passwd, strlen (uri->passwd));
-
- if (digest->algorithm == ALGORITHM_MD5_SESS) {
- md5_final (&ctx, d);
-
- md5_init (&ctx);
- md5_update (&ctx, d, 16);
- md5_update (&ctx, ":", 1);
- md5_update (&ctx, digest->nonce, strlen (digest->nonce));
- md5_update (&ctx, ":", 1);
- md5_update (&ctx, digest->cnonce, strlen (digest->cnonce));
- }
-
- /* hexify A1 */
- md5_final (&ctx, d);
- digest_hex (d, digest->hex_a1);
-
- auth->authenticated = TRUE;
-}
-
-static gboolean
-digest_invalidate_func (SoupAuth *auth)
-{
- /* If we failed, we need to get a new nonce from the server
- * next time, so this can't be reused.
- */
- return FALSE;
-}
-
-static void
-digest_free (SoupAuth *auth)
-{
- SoupAuthDigest *digest = (SoupAuthDigest *) auth;
-
- g_free (digest->user);
- g_free (digest->domain);
-
- g_free (digest->nonce);
- g_free (digest->cnonce);
- g_free (digest);
-}
-
-static SoupAuth *
-soup_auth_new_digest (void)
-{
- SoupAuthDigest *digest;
- SoupAuth *auth;
- char *bgen;
-
- digest = g_new0 (SoupAuthDigest, 1);
-
- auth = (SoupAuth *) digest;
- auth->type = SOUP_AUTH_TYPE_DIGEST;
- auth->authenticated = FALSE;
-
- auth->parse_func = digest_parse_func;
- auth->init_func = digest_init_func;
- auth->invalidate_func = digest_invalidate_func;
- auth->pspace_func = digest_pspace_func;
- auth->auth_func = digest_auth_func;
- auth->free_func = digest_free;
-
- bgen = g_strdup_printf ("%p:%lu:%lu",
- auth,
- (unsigned long) getpid (),
- (unsigned long) time (0));
- digest->cnonce = soup_base64_encode (bgen, strlen (bgen));
- digest->nc = 1;
- /* We're just going to do qop=auth for now */
- digest->qop = QOP_AUTH;
-
- g_free (bgen);
-
- return auth;
-}
-
-
-/*
- * NTLM Authentication Support
- */
-
-typedef struct {
- SoupAuth auth;
- gchar *response;
- gchar *header;
-} SoupAuthNTLM;
-
-static gchar *
-ntlm_auth (SoupAuth *sa, SoupMessage *msg)
-{
- SoupAuthNTLM *auth = (SoupAuthNTLM *) sa;
- char *ret;
-
- if (!sa->authenticated)
- return soup_ntlm_request ();
-
- /* Otherwise, return the response; but only once */
- ret = auth->response;
- auth->response = NULL;
- return ret;
-}
-
-static inline gchar *
-ntlm_get_authmech_token (const SoupUri *uri, gchar *key)
-{
- gchar *idx;
- gint len;
-
- if (!uri->authmech) return NULL;
-
- idx = strstr (uri->authmech, key);
- if (idx) {
- idx += strlen (key);
-
- len = strcspn (idx, ",; ");
- if (len)
- return g_strndup (idx, len);
- else
- return g_strdup (idx);
- }
-
- return NULL;
-}
-
-static void
-ntlm_parse (SoupAuth *sa, const char *header)
-{
- SoupAuthNTLM *auth = (SoupAuthNTLM *) sa;
-
- auth->header = g_strdup (header);
- g_strstrip (auth->header);
-}
-
-static GSList *
-ntlm_pspace (SoupAuth *auth, const SoupUri *source_uri)
-{
- /* The protection space is the whole server. */
- return g_slist_prepend (NULL, g_strdup (""));
-}
-
-static void
-ntlm_init (SoupAuth *sa, const SoupUri *uri)
-{
- SoupAuthNTLM *auth = (SoupAuthNTLM *) sa;
- gchar *host, *domain, *nonce;
-
- if (!auth->header || strlen (auth->header) < sizeof ("NTLM"))
- return;
-
- if (auth->response)
- g_free (auth->response);
-
- host = ntlm_get_authmech_token (uri, "host=");
- domain = ntlm_get_authmech_token (uri, "domain=");
-
- if (!soup_ntlm_parse_challenge (auth->header, &nonce,
- domain ? NULL : &domain))
- auth->response = NULL;
- else {
- auth->response = soup_ntlm_response (nonce,
- uri->user,
- uri->passwd,
- host,
- domain);
- g_free (nonce);
- }
-
- g_free (host);
- g_free (domain);
-
- g_free (auth->header);
- auth->header = NULL;
-
- sa->authenticated = TRUE;
-}
-
-static gboolean
-ntlm_invalidate (SoupAuth *sa)
-{
- SoupAuthNTLM *auth = (SoupAuthNTLM *) sa;
-
- g_free (auth->response);
- auth->response = NULL;
- g_free (auth->header);
- auth->header = NULL;
-
- sa->authenticated = FALSE;
- return TRUE;
-}
+#define PARENT_TYPE G_TYPE_OBJECT
+static GObjectClass *parent_class;
static void
-ntlm_free (SoupAuth *sa)
-{
- SoupAuthNTLM *auth = (SoupAuthNTLM *) sa;
-
- g_free (auth->response);
- g_free (auth->header);
- g_free (auth);
-}
-
-SoupAuth *
-soup_auth_new_ntlm (void)
+class_init (GObjectClass *object_class)
{
- SoupAuthNTLM *auth;
-
- auth = g_new0 (SoupAuthNTLM, 1);
- auth->auth.type = SOUP_AUTH_TYPE_NTLM;
- auth->auth.authenticated = FALSE;
- auth->auth.realm = g_strdup ("");
-
- auth->auth.parse_func = ntlm_parse;
- auth->auth.init_func = ntlm_init;
- auth->auth.invalidate_func = ntlm_invalidate;
- auth->auth.pspace_func = ntlm_pspace;
- auth->auth.auth_func = ntlm_auth;
- auth->auth.free_func = ntlm_free;
-
- return (SoupAuth *) auth;
+ parent_class = g_type_class_ref (PARENT_TYPE);
}
+SOUP_MAKE_TYPE (soup_auth, SoupAuth, class_init, NULL, PARENT_TYPE)
-/*
- * Generic Authentication Interface
- */
-
-typedef SoupAuth *(*SoupAuthNewFn) (void);
typedef struct {
- const gchar *scheme;
- SoupAuthNewFn ctor;
- gint strength;
+ const char *scheme;
+ GType (*type_func) (void);
+ int strength;
} AuthScheme;
static AuthScheme known_auth_schemes [] = {
- { "Basic", soup_auth_new_basic, 0 },
- { "NTLM", soup_auth_new_ntlm, 2 },
- { "Digest", soup_auth_new_digest, 3 },
+ { "Basic", soup_auth_basic_get_type, 0 },
+ { "NTLM", soup_auth_ntlm_get_type, 2 },
+ { "Digest", soup_auth_digest_get_type, 3 },
{ NULL }
};
SoupAuth *
-soup_auth_new_from_header_list (const SoupUri *uri,
- const GSList *vals)
+soup_auth_new_from_header_list (const GSList *vals, const char *pref)
{
- gchar *header = NULL;
+ char *header = NULL;
AuthScheme *scheme = NULL, *iter;
SoupAuth *auth = NULL;
g_return_val_if_fail (vals != NULL, NULL);
while (vals) {
- gchar *tryheader = vals->data;
+ char *tryheader = vals->data;
for (iter = known_auth_schemes; iter->scheme; iter++) {
- if (uri->authmech &&
- g_strncasecmp (uri->authmech,
- iter->scheme,
+ if (pref &&
+ g_strncasecmp (pref, iter->scheme,
strlen (iter->scheme)) != 0)
continue;
- if (!g_strncasecmp (tryheader,
- iter->scheme,
+ if (!g_strncasecmp (tryheader, iter->scheme,
strlen (iter->scheme))) {
if (!scheme ||
scheme->strength < iter->strength) {
vals = vals->next;
}
- if (!scheme) return NULL;
+ if (!scheme)
+ return NULL;
- auth = scheme->ctor ();
- if (!auth) return NULL;
-
- if (!auth->parse_func ||
- !auth->init_func ||
- !auth->auth_func ||
- !auth->free_func)
- g_error ("Faulty Auth Created!!");
-
- auth->parse_func (auth, header);
+ auth = g_object_new (scheme->type_func (), NULL);
+ if (!auth)
+ return NULL;
+ SOUP_AUTH_GET_CLASS (auth)->construct (auth, header);
return auth;
}
void
-soup_auth_initialize (SoupAuth *auth, const SoupUri *uri)
+soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password)
+{
+ g_return_if_fail (SOUP_IS_AUTH (auth));
+ g_return_if_fail (username != NULL);
+ g_return_if_fail (password != NULL);
+
+ SOUP_AUTH_GET_CLASS (auth)->authenticate (auth, username, password);
+}
+
+const char *
+soup_auth_get_scheme_name (SoupAuth *auth)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+
+ return SOUP_AUTH_GET_CLASS (auth)->scheme_name;
+}
+
+const char *
+soup_auth_get_realm (SoupAuth *auth)
{
- g_return_if_fail (auth != NULL);
- g_return_if_fail (uri != NULL);
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
- auth->init_func (auth, uri);
+ return SOUP_AUTH_GET_CLASS (auth)->get_realm (auth);
}
gboolean
soup_auth_invalidate (SoupAuth *auth)
{
- g_return_val_if_fail (auth != NULL, FALSE);
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
- return auth->invalidate_func (auth);
+ return SOUP_AUTH_GET_CLASS (auth)->invalidate (auth);
}
-gchar *
-soup_auth_authorize (SoupAuth *auth, SoupMessage *msg)
+gboolean
+soup_auth_is_authenticated (SoupAuth *auth)
{
- g_return_val_if_fail (auth != NULL, NULL);
- g_return_val_if_fail (msg != NULL, NULL);
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
- return auth->auth_func (auth, msg);
+ return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
}
-void
-soup_auth_free (SoupAuth *auth)
+char *
+soup_auth_get_authorization (SoupAuth *auth, SoupMessage *msg)
{
- g_return_if_fail (auth != NULL);
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
+ g_return_val_if_fail (msg != NULL, NULL);
- g_free (auth->realm);
- auth->free_func (auth);
+ return SOUP_AUTH_GET_CLASS (auth)->get_authorization (auth, msg);
}
GSList *
soup_auth_get_protection_space (SoupAuth *auth, const SoupUri *source_uri)
{
- g_return_val_if_fail (auth != NULL, NULL);
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
g_return_val_if_fail (source_uri != NULL, NULL);
- return auth->pspace_func (auth, source_uri);
+ return SOUP_AUTH_GET_CLASS (auth)->get_protection_space (auth, source_uri);
}
void
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * soup-auth.h: Authentication schemes
- *
- * Authors:
- * Joe Shaw (joe@ximian.com)
- *
- * Copyright (C) 2001-2002, Ximian, Inc.
+ * Copyright (C) 2001-2003, Ximian, Inc.
*/
#ifndef SOUP_AUTH_H
#define SOUP_AUTH_H 1
-#include <libsoup/soup-context.h>
+#include <glib-object.h>
#include <libsoup/soup-message.h>
-#include <libsoup/soup-misc.h>
+#include <libsoup/soup-uri.h>
-typedef enum _SoupAuthStatus SoupAuthStatus;
-typedef struct _SoupAuth SoupAuth;
+#define SOUP_TYPE_AUTH (soup_auth_get_type ())
+#define SOUP_AUTH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_AUTH, SoupAuth))
+#define SOUP_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH, SoupAuthClass))
+#define SOUP_IS_AUTH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_AUTH))
+#define SOUP_IS_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_AUTH))
+#define SOUP_AUTH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH, SoupAuthClass))
-struct _SoupAuth {
- SoupAuthType type;
- char *realm;
- gboolean authenticated;
+typedef struct {
+ GObject parent;
- void (*parse_func) (SoupAuth *auth,
- const gchar *header);
+} SoupAuth;
- void (*init_func) (SoupAuth *auth,
- const SoupUri *uri);
+typedef struct {
+ GObjectClass parent_class;
- gboolean (*invalidate_func) (SoupAuth *auth);
+ const char *scheme_name;
- char *(*auth_func) (SoupAuth *auth,
- SoupMessage *message);
+ void (*construct) (SoupAuth *auth,
+ const char *header);
- GSList *(*pspace_func) (SoupAuth *auth,
- const SoupUri *source_uri);
+ GSList * (*get_protection_space) (SoupAuth *auth,
+ const SoupUri *source_uri);
- void (*free_func) (SoupAuth *auth);
-};
+ const char * (*get_realm) (SoupAuth *auth);
-SoupAuth *soup_auth_new_from_header_list (const SoupUri *uri,
- const GSList *header);
+ void (*authenticate) (SoupAuth *auth,
+ const char *username,
+ const char *password);
+ gboolean (*invalidate) (SoupAuth *auth);
+ gboolean (*is_authenticated) (SoupAuth *auth);
-SoupAuth *soup_auth_new_ntlm (void);
+ char * (*get_authorization) (SoupAuth *auth,
+ SoupMessage *msg);
+} SoupAuthClass;
-void soup_auth_initialize (SoupAuth *auth,
- const SoupUri *uri);
+GType soup_auth_get_type (void);
-gboolean soup_auth_invalidate (SoupAuth *auth);
-void soup_auth_free (SoupAuth *auth);
+SoupAuth *soup_auth_new_from_header_list (const GSList *header,
+ const char *pref);
+
+const char *soup_auth_get_scheme_name (SoupAuth *auth);
+const char *soup_auth_get_realm (SoupAuth *auth);
-gchar *soup_auth_authorize (SoupAuth *auth,
+void soup_auth_authenticate (SoupAuth *auth,
+ const char *username,
+ const char *password);
+gboolean soup_auth_invalidate (SoupAuth *auth);
+gboolean soup_auth_is_authenticated (SoupAuth *auth);
+
+char *soup_auth_get_authorization (SoupAuth *auth,
SoupMessage *msg);
GSList *soup_auth_get_protection_space (SoupAuth *auth,
void soup_auth_free_protection_space (SoupAuth *auth,
GSList *space);
-
#endif /* SOUP_AUTH_H */
#include <netinet/in.h>
#include "soup-auth.h"
+#include "soup-auth-ntlm.h"
#include "soup-context.h"
#include "soup-private.h"
#include "soup-misc.h"
free_auth (gpointer realm, gpointer auth, gpointer unused)
{
g_free (realm);
- soup_auth_free (auth);
+ g_object_unref (auth);
}
/**
g_slist_remove (conn->server->connections, conn);
if (conn->auth)
- soup_auth_free (conn->auth);
+ g_object_unref (conn->auth);
g_io_channel_unref (conn->channel);
soup_context_unref (conn->context);
if (ctx->server->use_ntlm && msg && msg->connection) {
if (!msg->connection->auth)
- msg->connection->auth = soup_auth_new_ntlm ();
+ msg->connection->auth = soup_auth_ntlm_new ();
return msg->connection->auth;
}
GSList *pspace, *p;
if (server->use_ntlm && conn && conn->auth) {
- if (conn->auth->authenticated) {
+ if (soup_auth_is_authenticated (conn->auth)) {
/* This is a "permission denied", not a
* "password incorrect". There's nothing more
* we can do.
}
/* Free the intermediate auth */
- soup_auth_free (conn->auth);
+ g_object_unref (conn->auth);
conn->auth = NULL;
}
/* Try to construct a new auth from the headers; if we can't,
* there's no way we'll be able to authenticate.
*/
- new_auth = soup_auth_new_from_header_list (ctx->uri, headers);
+ new_auth = soup_auth_new_from_header_list (headers, ctx->uri->authmech);
if (!new_auth)
return FALSE;
/* See if this auth is the same auth we used last time */
prior_auth = soup_context_lookup_auth (ctx, NULL);
- if (prior_auth && prior_auth->type == new_auth->type &&
- !strcmp (prior_auth->realm, new_auth->realm)) {
- soup_auth_free (new_auth);
+ if (prior_auth &&
+ G_OBJECT_TYPE (prior_auth) == G_OBJECT_TYPE (new_auth) &&
+ !strcmp (soup_auth_get_realm (prior_auth),
+ soup_auth_get_realm (new_auth))) {
+ g_object_unref (new_auth);
if (prior_auth_failed) {
/* The server didn't like the username/password
* we provided before.
}
}
- if (new_auth->type == SOUP_AUTH_TYPE_NTLM) {
+ if (SOUP_IS_AUTH_NTLM (new_auth)) {
server->use_ntlm = TRUE;
if (conn) {
conn->auth = new_auth;
return soup_context_authenticate_auth (ctx, new_auth);
} else {
- soup_auth_free (new_auth);
+ g_object_unref (new_auth);
return FALSE;
}
}
}
/* Record where this auth realm is used */
- realm = g_strdup_printf ("%d:%s", new_auth->type, new_auth->realm);
+ realm = g_strdup_printf ("%s:%s",
+ soup_auth_get_scheme_name (new_auth),
+ soup_auth_get_realm (new_auth));
pspace = soup_auth_get_protection_space (new_auth, ctx->uri);
for (p = pspace; p; p = p->next) {
path = p->data;
old_auth = g_hash_table_lookup (server->auths, realm);
if (old_auth) {
g_free (realm);
- soup_auth_free (new_auth);
+ g_object_unref (new_auth);
new_auth = old_auth;
} else
g_hash_table_insert (server->auths, realm, new_auth);
/* Try to authenticate if needed. */
- if (!new_auth->authenticated)
+ if (!soup_auth_is_authenticated (new_auth))
return soup_context_authenticate_auth (ctx, new_auth);
return TRUE;
const SoupUri *uri = ctx->uri;
if (!uri->user && soup_auth_fn) {
- (*soup_auth_fn) (auth->type,
+ (*soup_auth_fn) (soup_auth_get_scheme_name (auth),
(SoupUri *) uri,
- auth->realm,
+ soup_auth_get_realm (auth),
soup_auth_fn_user_data);
}
if (!uri->user)
return FALSE;
- soup_auth_initialize (auth, uri);
+ soup_auth_authenticate (auth, uri->user, uri->passwd);
return TRUE;
}
return;
/* Nope, need to remove it completely */
- realm = g_strdup_printf ("%d:%s", auth->type, auth->realm);
+ realm = g_strdup_printf ("%s:%s",
+ soup_auth_get_scheme_name (auth),
+ soup_auth_get_realm (auth));
if (g_hash_table_lookup_extended (ctx->server->auths, realm,
&key, &value) &&
auth == (SoupAuth *)value) {
g_hash_table_remove (ctx->server->auths, realm);
g_free (key);
- soup_auth_free (auth);
+ g_object_unref (auth);
}
g_free (realm);
}
/* Authentication callback */
-typedef enum {
- SOUP_AUTH_TYPE_BASIC = 1,
- SOUP_AUTH_TYPE_DIGEST,
- SOUP_AUTH_TYPE_NTLM,
-} SoupAuthType;
-
-typedef void (*SoupAuthorizeFn) (SoupAuthType type,
+typedef void (*SoupAuthorizeFn) (const char *scheme_name,
SoupUri *uri,
- const gchar *realm,
+ const char *realm,
gpointer user_data);
void soup_set_authorize_callback (SoupAuthorizeFn authfn,
+++ /dev/null
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-ntlm.h: Microsoft Windows NTLM authentication support
- *
- * Authors:
- * Dan Winship (danw@ximian.com)
- *
- * Copyright (C) 2001-2002, Ximian, Inc.
- */
-
-#ifndef NTLM_H
-#define NTLM_H 1
-
-#include <glib.h>
-
-void soup_ntlm_lanmanager_hash (const char *password,
- guchar hash[21]);
-
-void soup_ntlm_nt_hash (const char *password,
- guchar hash[21]);
-
-char *soup_ntlm_request (void);
-
-gboolean soup_ntlm_parse_challenge (const char *challenge,
- char **nonce,
- char **default_domain);
-
-char *soup_ntlm_response (const char *nonce,
- const char *user,
- const char *password,
- const char *host,
- const char *domain);
-
-#endif /* NTLM_H */
auth = soup_context_lookup_auth (ctx, msg);
if (!auth)
return;
- if (!auth->authenticated &&
+ if (!soup_auth_is_authenticated (auth) &&
!soup_context_authenticate_auth (ctx, auth))
return;
- token = soup_auth_authorize (auth, msg);
+ token = soup_auth_get_authorization (auth, msg);
if (token) {
g_string_sprintfa (header, "%s: %s\r\n",
proxy_auth ?
#include "md5-utils.h"
#include "soup-headers.h"
-#include "soup-ntlm.h"
typedef struct {
const gchar *scheme;
static AuthScheme known_auth_schemes [] = {
{ "Basic", SOUP_AUTH_TYPE_BASIC, 0 },
- { "NTLM", SOUP_AUTH_TYPE_NTLM, 2 },
{ "Digest", SOUP_AUTH_TYPE_DIGEST, 3 },
{ NULL }
};
return passwd == auth->basic.passwd;
case SOUP_AUTH_TYPE_DIGEST:
return check_digest_passwd (&auth->digest, passwd);
- case SOUP_AUTH_TYPE_NTLM:
- if (passwd) {
- gchar lm_hash [21], nt_hash [21];
-
- soup_ntlm_lanmanager_hash (passwd, lm_hash);
- soup_ntlm_nt_hash (passwd, nt_hash);
-
- if (memcmp (lm_hash,
- auth->ntlm.lm_hash,
- sizeof (lm_hash)) != 0)
- return FALSE;
-
- if (memcmp (nt_hash,
- auth->ntlm.nt_hash,
- sizeof (nt_hash)) != 0)
- return FALSE;
-
- return TRUE;
- }
- return FALSE;
}
return FALSE;
return auth->basic.user;
case SOUP_AUTH_TYPE_DIGEST:
return auth->digest.user;
- case SOUP_AUTH_TYPE_NTLM:
- return auth->ntlm.user;
}
return NULL;
if (parse_digest (auth_ctx, header, msg, ret))
return ret;
break;
- case SOUP_AUTH_TYPE_NTLM:
- g_warning ("NTLM server authentication not yet implemented\n");
- break;
}
g_free (ret);
g_free ((gchar *) auth->digest.digest_uri);
g_free ((gchar *) auth->digest.digest_response);
break;
- case SOUP_AUTH_TYPE_NTLM:
- g_free ((gchar *) auth->ntlm.host);
- g_free ((gchar *) auth->ntlm.domain);
- g_free ((gchar *) auth->ntlm.user);
- g_free ((gchar *) auth->ntlm.lm_hash);
- g_free ((gchar *) auth->ntlm.nt_hash);
- break;
}
g_free (auth);
gchar *header_name);
+typedef enum {
+ SOUP_AUTH_TYPE_BASIC = 1,
+ SOUP_AUTH_TYPE_DIGEST,
+} SoupAuthType;
+
typedef struct {
SoupAuthType type;
const gchar *user;
const gchar *request_method;
} SoupServerAuthDigest;
-typedef struct {
- SoupAuthType type;
- const gchar *host;
- const gchar *domain;
- const gchar *user;
- const gchar *lm_hash;
- const gchar *nt_hash;
-} SoupServerAuthNTLM;
-
union _SoupServerAuth {
SoupAuthType type;
SoupServerAuthBasic basic;
SoupServerAuthDigest digest;
- SoupServerAuthNTLM ntlm;
};
SoupServerAuth *soup_server_auth_new (SoupServerAuthContext *auth_ctx,
int num;
auth = soup_context_lookup_auth (msg->context, msg);
- if (!auth || !auth->authenticated)
+ if (!auth || !soup_auth_is_authenticated (auth))
return 0;
- header = soup_auth_authorize (auth, msg);
+ header = soup_auth_get_authorization (auth, msg);
if (!g_ascii_strncasecmp (header, "Basic ", 6)) {
char *token;
int len;