diff options
author | Mandy Wu <mandy.wu@intel.com> | 2011-08-02 21:31:10 +0800 |
---|---|---|
committer | Dan Winship <danw@gnome.org> | 2011-08-04 08:21:41 -0400 |
commit | ade9d549960cef9642c56df05bba94b9ec6eebf3 (patch) | |
tree | 8399c25c2afe0405a6a5d7b5ccf68a6b9fd9e3a6 | |
parent | 22762aa044389783bf8ecc889e2346d664a69d5e (diff) | |
download | libsoup-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.ac | 17 | ||||
-rw-r--r-- | libsoup/soup-auth-manager-ntlm.c | 240 |
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; |