summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMandy Wu <mandy.wu@intel.com>2011-08-02 21:31:10 +0800
committerDan Winship <danw@gnome.org>2011-08-04 08:21:41 -0400
commitade9d549960cef9642c56df05bba94b9ec6eebf3 (patch)
tree8399c25c2afe0405a6a5d7b5ccf68a6b9fd9e3a6
parent22762aa044389783bf8ecc889e2346d664a69d5e (diff)
downloadlibsoup-ade9d549960cef9642c56df05bba94b9ec6eebf3.tar.gz
Support NTLM Single Sign On
Support NTLM Single Sign On by using Samba's 'winbind' daemon helper /usr/bin/ntlm_auth https://bugzilla.gnome.org/show_bug.cgi?id=650940
-rw-r--r--configure.ac17
-rw-r--r--libsoup/soup-auth-manager-ntlm.c240
2 files changed, 256 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index 01917478..b1b4c1d4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -326,6 +326,23 @@ AM_CONDITIONAL(HAVE_CURL, test "$CURL" != no)
AC_SUBST(MISSING_REGRESSION_TEST_PACKAGES)
AM_CONDITIONAL(MISSING_REGRESSION_TEST_PACKAGES, test -n "$MISSING_REGRESSION_TEST_PACKAGES")
+dnl **********************************************************
+dnl *** path of NTLM single-sign-on helper ntlm_auth
+dnl **********************************************************
+AC_ARG_WITH(ntlm-auth,
+ AC_HELP_STRING([--with-ntlm-auth=PATH],[Where to look for ntlm_auth, path points to ntlm_auth installation (default: /usr/bin/ntlm_auth)]),
+ ntlm_auth="$withval",
+ [if test $os_win32 = yes; then ntlm_auth="no"; else ntlm_auth="/usr/bin/ntlm_auth"; fi])
+if test "$ntlm_auth" != "no"; then
+ AC_DEFINE(USE_NTLM_AUTH, 1, [Whether or not use Samba's 'winbind' daemon helper 'ntlm_auth' for NTLM single-sign-on])
+ if test "$ntlm_auth" = "yes"; then
+ dnl --with-ntlm-auth (without path) used, use default path
+ ntlm_auth="/usr/bin/ntlm_auth"
+ fi
+fi
+AC_SUBST(ntlm_auth)
+AC_DEFINE_UNQUOTED(NTLM_AUTH, "$ntlm_auth", [Samba's 'winbind' daemon helper 'ntlm_auth' which can be used for NTLM single-sign-on])
+
dnl *************************
dnl *** Output Everything ***
dnl *************************
diff --git a/libsoup/soup-auth-manager-ntlm.c b/libsoup/soup-auth-manager-ntlm.c
index 49db6133..7c3f2398 100644
--- a/libsoup/soup-auth-manager-ntlm.c
+++ b/libsoup/soup-auth-manager-ntlm.c
@@ -13,6 +13,10 @@
#include <ctype.h>
#include <string.h>
+#ifdef USE_NTLM_AUTH
+#include <stdlib.h>
+#include <errno.h>
+#endif
#include "soup-auth-manager-ntlm.h"
#include "soup-auth-ntlm.h"
#include "soup-message.h"
@@ -42,6 +46,12 @@ G_DEFINE_TYPE_WITH_CODE (SoupAuthManagerNTLM, soup_auth_manager_ntlm, SOUP_TYPE_
typedef enum {
SOUP_NTLM_NEW,
+#ifdef USE_NTLM_AUTH
+ SOUP_NTLM_SENT_SSO_REQUEST,
+ SOUP_NTLM_RECEIVED_SSO_CHALLENGE,
+ SOUP_NTLM_SENT_SSO_RESPONSE,
+ SOUP_NTLM_SSO_FAILED,
+#endif
SOUP_NTLM_SENT_REQUEST,
SOUP_NTLM_RECEIVED_CHALLENGE,
SOUP_NTLM_SENT_RESPONSE,
@@ -55,6 +65,11 @@ typedef struct {
char *nonce, *domain;
SoupAuth *auth;
+#ifdef USE_NTLM_AUTH
+ char *challenge_header;
+ int fd_in;
+ int fd_out;
+#endif
} SoupNTLMConnection;
typedef struct {
@@ -63,6 +78,9 @@ typedef struct {
SoupSession *session;
GHashTable *connections_by_msg;
GHashTable *connections_by_id;
+#ifdef USE_NTLM_AUTH
+ gboolean ntlm_auth_accessible;
+#endif
} SoupAuthManagerNTLMPrivate;
#define SOUP_AUTH_MANAGER_NTLM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER_NTLM, SoupAuthManagerNTLMPrivate))
@@ -75,6 +93,9 @@ static char *soup_ntlm_response (const char *nonce,
const char *password,
const char *host,
const char *domain);
+#ifdef USE_NTLM_AUTH
+static void sso_ntlm_close (SoupNTLMConnection *conn);
+#endif
static void
soup_auth_manager_ntlm_init (SoupAuthManagerNTLM *ntlm)
@@ -84,6 +105,9 @@ soup_auth_manager_ntlm_init (SoupAuthManagerNTLM *ntlm)
priv->connections_by_id = g_hash_table_new (NULL, NULL);
priv->connections_by_msg = g_hash_table_new (NULL, NULL);
+#ifdef USE_NTLM_AUTH
+ priv->ntlm_auth_accessible = (access (NTLM_AUTH, X_OK) == 0);
+#endif
}
static void
@@ -94,6 +118,10 @@ free_ntlm_connection (SoupNTLMConnection *conn)
g_free (conn->domain);
if (conn->auth)
g_object_unref (conn->auth);
+#ifdef USE_NTLM_AUTH
+ g_free (conn->challenge_header);
+ sso_ntlm_close (conn);
+#endif
g_slice_free (SoupNTLMConnection, conn);
}
@@ -180,6 +208,10 @@ get_connection (SoupAuthManagerNTLMPrivate *priv, SoupSocket *socket)
conn = g_slice_new0 (SoupNTLMConnection);
conn->socket = socket;
conn->state = SOUP_NTLM_NEW;
+#ifdef USE_NTLM_AUTH
+ conn->fd_in = -1;
+ conn->fd_out = -1;
+#endif
g_hash_table_insert (priv->connections_by_id, socket, conn);
g_signal_connect (socket, "disconnected",
@@ -217,6 +249,137 @@ get_connection_for_msg (SoupAuthManagerNTLMPrivate *priv, SoupMessage *msg)
return g_hash_table_lookup (priv->connections_by_msg, msg);
}
+#ifdef USE_NTLM_AUTH
+static void
+sso_ntlm_close (SoupNTLMConnection *conn)
+{
+ if (conn->fd_in != -1) {
+ close (conn->fd_in);
+ conn->fd_in = -1;
+ }
+
+ if (conn->fd_out != -1) {
+ close (conn->fd_out);
+ conn->fd_out = -1;
+ }
+}
+
+static gboolean
+sso_ntlm_initiate (SoupNTLMConnection *conn, SoupAuthManagerNTLMPrivate *priv)
+{
+ char *username = NULL, *slash, *domain = NULL;
+ char *argv[9];
+ gboolean ret;
+
+ /* Return if ntlm_auth execution process exist already */
+ if (conn->fd_in != -1 && conn->fd_out != -1)
+ return TRUE;
+ else
+ /* Clean all sso data before re-initiate */
+ sso_ntlm_close (conn);
+
+ if (!priv->ntlm_auth_accessible)
+ goto done;
+
+ username = getenv ("NTLMUSER");
+ if (!username)
+ username = getenv ("USER");
+ if (!username)
+ goto done;
+
+ slash = strpbrk (username, "\\/");
+ if (slash) {
+ domain = g_strdup (username);
+ slash = domain + (slash - username);
+ *slash = '\0';
+ username = slash + 1;
+ }
+
+ argv[0] = NTLM_AUTH;
+ argv[1] = "--helper-protocol";
+ argv[2] = "ntlmssp-client-1";
+ argv[3] = "--use-cached-creds";
+ argv[4] = "--username";
+ argv[5] = username;
+ argv[6] = domain ? "--domain" : NULL;
+ argv[7] = domain;
+ argv[8] = NULL;
+ /* Spawn child process */
+ ret = g_spawn_async_with_pipes (NULL, argv, NULL,
+ G_SPAWN_FILE_AND_ARGV_ZERO,
+ NULL, NULL,
+ NULL, &conn->fd_in, &conn->fd_out,
+ NULL, NULL);
+ if (!ret)
+ goto done;
+ g_free (domain);
+ return TRUE;
+done:
+ g_free (domain);
+ return FALSE;
+}
+
+static char *
+sso_ntlm_response (SoupNTLMConnection *conn, const char *input, SoupNTLMState conn_state)
+{
+ ssize_t size;
+ char buf[1024], *response = NULL;
+ char *tmpbuf = buf;
+ size_t len_in = strlen (input), len_out = sizeof (buf);
+
+ while (len_in > 0) {
+ int written = write (conn->fd_in, input, len_in);
+ if (written == -1) {
+ /* Interrupted by a signal, retry it */
+ if (errno == EINTR)
+ continue;
+ /* write failed if other errors happen */
+ goto done;
+ }
+ input += written;
+ len_in -= written;
+ }
+ /* Read one line */
+ while (len_out > 0) {
+ size = read (conn->fd_out, tmpbuf, len_out);
+ if (size == -1) {
+ if (errno == EINTR)
+ continue;
+ goto done;
+ } else if (size == 0)
+ goto done;
+ else if (tmpbuf[size - 1] == '\n') {
+ tmpbuf[size - 1] = '\0';
+ goto wrfinish;
+ }
+ tmpbuf += size;
+ len_out -= size;
+ }
+ goto done;
+wrfinish:
+ if (conn_state == SOUP_NTLM_NEW &&
+ g_ascii_strcasecmp (buf, "PW") == 0) {
+ /* Samba/winbind installed but not configured */
+ response = g_strdup ("PW");
+ goto done;
+ }
+ if (conn_state == SOUP_NTLM_NEW &&
+ g_ascii_strncasecmp (buf, "YR ", 3) != 0)
+ /* invalid response for type 1 message */
+ goto done;
+ if (conn_state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE &&
+ g_ascii_strncasecmp (buf, "KK ", 3) != 0 &&
+ g_ascii_strncasecmp (buf, "AF ", 3) != 0)
+ /* invalid response for type 3 message */
+ goto done;
+
+ response = g_strdup_printf ("NTLM %.*s", (int)(size - 4), buf + 3);
+ goto done;
+done:
+ return response;
+}
+#endif /* USE_NTLM_AUTH */
+
static void
ntlm_authorize_pre (SoupMessage *msg, gpointer ntlm)
{
@@ -250,9 +413,16 @@ ntlm_authorize_pre (SoupMessage *msg, gpointer ntlm)
goto done;
}
- conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
conn->auth = soup_auth_ntlm_new (conn->domain,
soup_message_get_uri (msg)->host);
+#ifdef USE_NTLM_AUTH
+ conn->challenge_header = g_strdup (val + 5);
+ if (conn->state == SOUP_NTLM_SENT_SSO_REQUEST) {
+ conn->state = SOUP_NTLM_RECEIVED_SSO_CHALLENGE;
+ goto done;
+ }
+#endif
+ conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
soup_auth_manager_emit_authenticate (SOUP_AUTH_MANAGER (ntlm), msg,
conn->auth, FALSE);
@@ -276,6 +446,31 @@ ntlm_authorize_post (SoupMessage *msg, gpointer ntlm)
if (!conn || !conn->auth)
return;
+#ifdef USE_NTLM_AUTH
+ if (conn->state == SOUP_NTLM_RECEIVED_SSO_CHALLENGE) {
+ char *input;
+ input = g_strdup_printf ("TT %s\n", conn->challenge_header);
+ /* Re-Initiate ntlm_auth process in case it was closed/killed abnormally */
+ if (sso_ntlm_initiate (conn, priv)) {
+ conn->response_header = sso_ntlm_response (conn,
+ input,
+ conn->state);
+ /* Close ntlm_auth as it is no longer needed for current connection */
+ sso_ntlm_close (conn);
+ if (!conn->response_header) {
+ g_free (input);
+ goto ssofailure;
+ }
+ soup_session_requeue_message (priv->session, msg);
+ g_free (input);
+ goto done;
+ }
+ssofailure:
+ conn->state = SOUP_NTLM_SSO_FAILED;
+ soup_session_requeue_message (priv->session, msg);
+ goto done;
+ }
+#endif
username = soup_auth_ntlm_get_username (conn->auth);
password = soup_auth_ntlm_get_password (conn->auth);
if (!username || !password)
@@ -341,9 +536,52 @@ request_started (SoupSessionFeature *ntlm, SoupSession *session,
switch (conn->state) {
case SOUP_NTLM_NEW:
+#ifdef USE_NTLM_AUTH
+ /* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
+ * by delegating the NTLM challenge/response protocal to a helper
+ * in ntlm_auth.
+ * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
+ * http://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
+ * http://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
+ * The preprocessor variable 'USE_NTLM_AUTH' indicates whether
+ * this feature is enabled. Another one 'NTLM_AUTH' contains absolute
+ * path of it.
+ * If NTLM single-sign-on fails, go back to original request handling process.
+ */
+ if (sso_ntlm_initiate (conn, priv)) {
+ header = sso_ntlm_response (conn, "YR\n", conn->state);
+ if (header) {
+ if (g_ascii_strcasecmp (header, "PW") != 0) {
+ conn->state = SOUP_NTLM_SENT_SSO_REQUEST;
+ break;
+ } else {
+ g_free (header);
+ header = NULL;
+ goto ssofailure;
+ }
+ } else {
+ g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
+ goto ssofailure;
+ }
+ }
+ssofailure:
+#endif
header = soup_ntlm_request ();
conn->state = SOUP_NTLM_SENT_REQUEST;
break;
+#ifdef USE_NTLM_AUTH
+ case SOUP_NTLM_RECEIVED_SSO_CHALLENGE:
+ header = conn->response_header;
+ conn->response_header = NULL;
+ conn->state = SOUP_NTLM_SENT_SSO_RESPONSE;
+ break;
+ case SOUP_NTLM_SSO_FAILED:
+ /* Restart request without SSO */
+ g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
+ header = soup_ntlm_request ();
+ conn->state = SOUP_NTLM_SENT_REQUEST;
+ break;
+#endif
case SOUP_NTLM_RECEIVED_CHALLENGE:
header = conn->response_header;
conn->response_header = NULL;