diff options
| author | Mandy Wu <mandy.wu@intel.com> | 2011-07-18 23:36:36 +0200 | 
|---|---|---|
| committer | Daniel Stenberg <daniel@haxx.se> | 2011-07-18 23:36:36 +0200 | 
| commit | a6d4807d028eb82f4a99587b0a8851727ca88566 (patch) | |
| tree | 0f2398259a279bd3d826eee2e78cc76aa1747e46 | |
| parent | 650a504b2f244ad323f1b45c2f36a2f967f7870f (diff) | |
NTLM single-sign on supported
With the use of the 'ntlm_auth' tool from the Samba project
| -rw-r--r-- | configure.ac | 31 | ||||
| -rw-r--r-- | include/curl/curl.h | 6 | ||||
| -rw-r--r-- | lib/http.c | 43 | ||||
| -rw-r--r-- | lib/http_ntlm.c | 283 | ||||
| -rw-r--r-- | lib/http_ntlm.h | 8 | ||||
| -rw-r--r-- | lib/setup.h | 5 | ||||
| -rw-r--r-- | lib/url.c | 11 | ||||
| -rw-r--r-- | lib/urldata.h | 9 | ||||
| -rw-r--r-- | lib/version.c | 3 | ||||
| -rw-r--r-- | src/main.c | 13 | 
10 files changed, 404 insertions, 8 deletions
diff --git a/configure.ac b/configure.ac index e9b49c788..b5adde5ec 100644 --- a/configure.ac +++ b/configure.ac @@ -2686,6 +2686,34 @@ then    USE_MANUAL="no";  fi +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);]) +   AC_HELP_STRING([--without-ntlm-auth], +        [disable ntlm single-sign-on by using ntlm_auth]), +ntlm_auth="$withval", +        [if test "$ac_cv_native_windows" = "yes"; then ntlm_auth="no"; else ntlm_auth="/usr/bin/ntlm_auth"; fi]) + +AC_MSG_CHECKING([if using ntlm_auth is requested]) + +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]) +  AC_SUBST(USE_NTLM_AUTH, [1]) +  if test "$ntlm_auth" = "yes"; then +    dnl --with-ntlm-auth (without path) used, use default path +    ntlm_auth="/usr/bin/ntlm_auth" +  fi +  AC_MSG_RESULT($ntlm_auth) +else +  AC_MSG_RESULT(no) +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 If the manual variable still is set, then we go with providing a built-in  dnl manual @@ -2969,6 +2997,9 @@ if test "x$CURL_DISABLE_HTTP" != "x1"; then    if test "x$USE_SSLEAY" = "x1" -o "x$USE_WINDOWS_SSPI" = "x1" \        -o "x$GNUTLS_ENABLED" = "x1" -o "x$NSS_ENABLED" = "x1"; then      SUPPORT_FEATURES="$SUPPORT_FEATURES NTLM" +    if test "x$USE_NTLM_AUTH" = "x1"; then +      SUPPORT_FEATURES="$SUPPORT_FEATURES NTLM_SSO" +    fi    fi  fi  if test "x$USE_TLS_SRP" = "x1"; then diff --git a/include/curl/curl.h b/include/curl/curl.h index a9d42fad6..998c109b9 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -600,6 +600,7 @@ typedef enum {  #define CURLAUTH_GSSNEGOTIATE (1<<2)  /* GSS-Negotiate */  #define CURLAUTH_NTLM         (1<<3)  /* NTLM */  #define CURLAUTH_DIGEST_IE    (1<<4)  /* Digest with IE flavour */ +#define CURLAUTH_NTLM_SSO     (1<<5)  /* NTLM single-sign-on */  #define CURLAUTH_ONLY         (1<<31) /* used together with a single other                                           type to force no auth or just that                                           single type */ @@ -2092,8 +2093,9 @@ typedef struct {  #define CURL_VERSION_CONV      (1<<12) /* character conversions supported */  #define CURL_VERSION_CURLDEBUG (1<<13) /* debug memory tracking supported */  #define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ - -/* +#define CURL_VERSION_NTLM_SSO  (1<<15)  /* NTLM single-sign-on is supported +                                           by using ntlm_auth */ + /*   * NAME curl_version_info()   *   * DESCRIPTION diff --git a/lib/http.c b/lib/http.c index 92f7c02a1..bb6af2cb4 100644 --- a/lib/http.c +++ b/lib/http.c @@ -307,6 +307,8 @@ static bool pickoneauth(struct auth *pick)      pick->picked = CURLAUTH_DIGEST;    else if(avail & CURLAUTH_NTLM)      pick->picked = CURLAUTH_NTLM; +  else if(avail & CURLAUTH_NTLM_SSO) +    pick->picked = CURLAUTH_NTLM_SSO;    else if(avail & CURLAUTH_BASIC)      pick->picked = CURLAUTH_BASIC;    else { @@ -393,7 +395,9 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)    if((expectsend == -1) || (expectsend > bytessent)) {      /* There is still data left to send */      if((data->state.authproxy.picked == CURLAUTH_NTLM) || -       (data->state.authhost.picked == CURLAUTH_NTLM)) { +       (data->state.authhost.picked == CURLAUTH_NTLM) || +       (data->state.authproxy.picked == CURLAUTH_NTLM_SSO) || +       (data->state.authhost.picked == CURLAUTH_NTLM_SSO)) {        if(((expectsend - bytessent) < 2000) ||           (conn->ntlm.state != NTLMSTATE_NONE)) {          /* The NTLM-negotiation has started *OR* there is just a little (<2K) @@ -554,6 +558,15 @@ output_auth_headers(struct connectdata *conn,    }    else  #endif +#ifdef USE_NTLM_SSO +  if(authstatus->picked == CURLAUTH_NTLM_SSO) { +    auth="NTLM_SSO"; +    result = Curl_output_ntlm_sso(conn, proxy); +    if(result) +      return result; +  } +  else +#endif  #ifndef CURL_DISABLE_CRYPTO_AUTH    if(authstatus->picked == CURLAUTH_DIGEST) {      auth="Digest"; @@ -766,13 +779,35 @@ CURLcode Curl_http_input_auth(struct connectdata *conn,      if(checkprefix("NTLM", start)) {        *availp |= CURLAUTH_NTLM;        authp->avail |= CURLAUTH_NTLM; -      if(authp->picked == CURLAUTH_NTLM) { +      if(authp->picked == CURLAUTH_NTLM || +         authp->picked == CURLAUTH_NTLM_SSO) {          /* NTLM authentication is picked and activated */          CURLntlm ntlm =            Curl_input_ntlm(conn, (bool)(httpcode == 407), start); - -        if(CURLNTLM_BAD != ntlm) +        if(CURLNTLM_BAD != ntlm) {            data->state.authproblem = FALSE; +#ifdef USE_NTLM_SSO +          if(authp->picked == CURLAUTH_NTLM_SSO) { +            *availp &= ~CURLAUTH_NTLM; +            authp->avail &= ~CURLAUTH_NTLM; +            *availp |= CURLAUTH_NTLM_SSO; +            authp->avail |= CURLAUTH_NTLM_SSO; + +            /* Get the challenge-message which will be passed to +             * ntlm_auth for generating the type 3 message later */ +            while(*start && ISSPACE(*start)) +              start++; +            if(checkprefix("NTLM", start)) { +              start += strlen("NTLM"); +              while(*start && ISSPACE(*start)) +                start++; +              if(*start) +                if((conn->challenge_header = strdup(start)) == NULL) +                  return CURLE_OUT_OF_MEMORY; +            } +          } +#endif +        }          else {            infof(data, "Authentication problem. Ignoring this.\n");            data->state.authproblem = TRUE; diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index c3ceadc35..85b4e937e 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -43,6 +43,15 @@  #include <unistd.h>  #endif +#ifdef USE_NTLM_SSO +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <errno.h> +#endif +  #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))  #include <netdb.h>  #endif @@ -674,6 +683,277 @@ static void unicodecpy(unsigned char *dest,  }  #endif +#ifdef USE_NTLM_SSO +static void sso_ntlm_close(struct connectdata *conn) +{ +  if(conn->fd_helper != -1) { +    close(conn->fd_helper); +    conn->fd_helper = -1; +  } + +  if(conn->pid) { +    int ret, i; +    for(i = 0; i < 4; i++) { +      ret = waitpid(conn->pid, NULL, WNOHANG); +      if(ret == conn->pid || errno == ECHILD) +        break; +      switch(i) { +      case 0: +        kill(conn->pid, SIGTERM); +        break; +      case 2: +        kill(conn->pid, SIGKILL); +        break; +      case 1: +      case 3: +        break; +      } +    } +    conn->pid = 0; +  } + +  Curl_safefree(conn->challenge_header); +  conn->challenge_header = NULL; +  Curl_safefree(conn->response_header); +  conn->response_header = NULL; +} + +static CURLcode sso_ntlm_initiate(struct connectdata *conn, +                                  const char *userp) +{ +  int sockfds[2]; +  pid_t pid; +  const char *username; +  char *slash, *domain = NULL; +  const char *ntlm_auth; + +  /* Return if communication with ntlm_auth already set up */ +  if(conn->fd_helper != -1 || conn->pid) { +    return CURLE_OK; +  } + +  username = userp; +  slash = strpbrk(username, "\\/"); +  if(slash) { +    if((domain = strdup(username)) == NULL) +      return CURLE_OUT_OF_MEMORY; +    slash = domain + (slash - username); +    *slash = '\0'; +    username = username + (slash - domain) + 1; +  } + +  /* When DEBUGBUILD is defined and environment variable NTLM_AUTH is set +   * (in test case 2005), use a fake_ntlm to do NTLM challenge/response, +   * which only accept commands and output strings pre-written/saved in +   * test case 2005 */ +#ifdef DEBUGBUILD +  ntlm_auth=getenv("NTLM_AUTH"); +#endif +  if(!ntlm_auth) +    ntlm_auth = NTLM_AUTH; + +  if(access(ntlm_auth, X_OK) != 0) +    goto done; + +  if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) +    goto done; + +  pid = fork(); +  if(!pid) { +    /* child process */ +    if(dup2(sockfds[1], 0) == -1 || dup2(sockfds[1], 1) == -1) +      exit(1); + +    execl(ntlm_auth, "--helper-protocol", "ntlmssp-client-1", +          "--use-cached-creds", "--username", username, +          domain?"--domain":NULL, domain, NULL); +    exit(1); +  } +  else if(pid == -1) { +    close(sockfds[0]); +    close(sockfds[1]); +    goto done; +  } + +  close(sockfds[1]); +  conn->fd_helper = sockfds[0]; +  conn->pid = pid; +  Curl_safefree(domain); +  return CURLE_OK; + +done: +  Curl_safefree(domain); +  return CURLE_REMOTE_ACCESS_DENIED; +} + +static CURLcode sso_ntlm_response(struct connectdata *conn, +                                  const char *input, curlntlm state) +{ +  ssize_t size; +  char buf[200]; /* enough, type 1, 3 message length is less then 200 */ +  char *tmpbuf = buf; +  size_t len_in = strlen(input), len_out = sizeof(buf); + +  while(len_in > 0) { +    int written = write(conn->fd_helper, 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_helper, 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: +  /* Samba/winbind installed but not configured */ +  if(state == NTLMSTATE_TYPE1 && +     size == 3 && +     buf[0] == 'P' && buf[1] == 'W') +    return CURLE_REMOTE_ACCESS_DENIED; +  /* invalid response */ +  if(size < 4) +    goto done; +  if(state == NTLMSTATE_TYPE1 && +     (buf[0]!='Y' || buf[1]!='R' || buf[2]!=' ')) +    goto done; +  if(state == NTLMSTATE_TYPE2 && +     (buf[0]!='K' || buf[1]!='K' || buf[2]!=' ') && +     (buf[0]!='A' || buf[1]!='F' || buf[2]!=' ')) +    goto done; + +  conn->response_header = aprintf("NTLM %.*s", size - 4, buf + 3); +  return CURLE_OK; +done: +  return CURLE_REMOTE_ACCESS_DENIED; +} + +/*this is for creating ntlm header output by delegating challenge/response + *to a Samba's daemon helper ntlm_auth */ +CURLcode Curl_output_ntlm_sso(struct connectdata *conn, +                              bool proxy) +{ +  /* point to the address of the pointer that holds the string to sent to the +     server, which is for a plain host or for a HTTP proxy */ +  char **allocuserpwd; +  /* point to the name and password for this */ +  const char *userp; +  /* point to the correct struct with this */ +  struct ntlmdata *ntlm; +  struct auth *authp; + +  CURLcode res = CURLE_OK; +  char *input; + +  DEBUGASSERT(conn); +  DEBUGASSERT(conn->data); + +  if(proxy) { +    allocuserpwd = &conn->allocptr.proxyuserpwd; +    userp = conn->proxyuser; +    ntlm = &conn->proxyntlm; +    authp = &conn->data->state.authproxy; +  } +  else { +    allocuserpwd = &conn->allocptr.userpwd; +    userp = conn->user; +    ntlm = &conn->ntlm; +    authp = &conn->data->state.authhost; +  } +  authp->done = FALSE; + +  /* not set means empty */ +  if(!userp) +    userp=""; + +  switch(ntlm->state) { +  case NTLMSTATE_TYPE1: +  default: +    /* 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. +     */ +    /* Clean data before using them */ +    conn->fd_helper = -1; +    conn->pid = 0; +    conn->challenge_header = NULL; +    conn->response_header = NULL; +    /* Create communication with ntlm_auth */ +    res = sso_ntlm_initiate(conn, userp); +    if(res) +      return res; +    res = sso_ntlm_response(conn, "YR\n", ntlm->state); +    if(res) +      return res; + +    Curl_safefree(*allocuserpwd); +    *allocuserpwd = aprintf("%sAuthorization: %s\r\n", +                            proxy?"Proxy-":"", +                            conn->response_header); +    DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd)); +    Curl_safefree(conn->response_header); +    conn->response_header = NULL; +    break; +  case NTLMSTATE_TYPE2: +    input = aprintf("TT %s\n", conn->challenge_header); +    res = sso_ntlm_response(conn, +                            input, +                            ntlm->state); +    if(res) +      return res; + +    Curl_safefree(*allocuserpwd); +    *allocuserpwd = aprintf("%sAuthorization: %s\r\n", +                            proxy?"Proxy-":"", +                            conn->response_header); +    DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd)); +    ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */ +    authp->done = TRUE; +    sso_ntlm_close(conn); +    free(input); +    break; +  case NTLMSTATE_TYPE3: +    /* connection is already authenticated, +     * don't send a header in future requests */ +    if(*allocuserpwd) { +      free(*allocuserpwd); +      *allocuserpwd=NULL; +    } +    authp->done = TRUE; +    break; +  } + +  return CURLE_OK; +} +#endif /* USE_NTLM_SSO */ +  /* this is for creating ntlm header output */  CURLcode Curl_output_ntlm(struct connectdata *conn,                            bool proxy) @@ -1320,6 +1600,9 @@ Curl_ntlm_cleanup(struct connectdata *conn)    ntlm_sspi_cleanup(&conn->ntlm);    ntlm_sspi_cleanup(&conn->proxyntlm);  #else +#ifdef USE_NTLM_SSO +  sso_ntlm_close(conn); +#endif    (void)conn;  #endif  } diff --git a/lib/http_ntlm.h b/lib/http_ntlm.h index c7422d701..faa7b0f31 100644 --- a/lib/http_ntlm.h +++ b/lib/http_ntlm.h @@ -7,7 +7,7 @@   *                            | (__| |_| |  _ <| |___   *                             \___|\___/|_| \_\_____|   * - * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.   *   * This software is licensed as described in the file COPYING, which   * you should have received as part of this distribution. The terms @@ -38,6 +38,12 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn, bool proxy,  /* this is for creating ntlm header output */  CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy); +#ifdef USE_NTLM_SSO +/* this is for creating ntlm header output by delegating challenge/response + * to a Samba's daemon helper ntlm_auth */ +CURLcode Curl_output_ntlm_sso(struct connectdata *conn, bool proxy); +#endif +  void Curl_ntlm_cleanup(struct connectdata *conn);  #ifndef USE_NTLM  #define Curl_ntlm_cleanup(x) diff --git a/lib/setup.h b/lib/setup.h index 35dfa5874..91a655713 100644 --- a/lib/setup.h +++ b/lib/setup.h @@ -567,6 +567,11 @@ int netware_init(void);  #if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) || \     defined(USE_GNUTLS) || defined(USE_NSS)  #define USE_NTLM +#if defined(USE_NTLM_AUTH) +/* Support NTLM single-sign-on by using Samba's winbind daemon helper +   'ntlm_auth' */ +#define USE_NTLM_SSO +#endif  #endif  #endif @@ -1406,6 +1406,10 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,  #ifndef USE_NTLM      auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */  #endif +#ifndef USE_NTLM_SSO +    auth &= ~CURLAUTH_NTLM_SSO; /* no NTLM single-sign-on without SSL +                                   and ntlm_auth */ +#endif  #ifndef USE_HTTP_NEGOTIATE      auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or                                         WINDOWS_SSPI */ @@ -1467,6 +1471,10 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,  #ifndef USE_NTLM      auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */  #endif +#ifndef USE_NTLM_SSO +    auth &= ~CURLAUTH_NTLM_SSO; /* no NTLM single-sign-on without SSL +                                   and ntlm_auth */ +#endif  #ifndef USE_HTTP_NEGOTIATE      auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or                                         WINDOWS_SSPI */ @@ -3002,7 +3010,8 @@ ConnectionExists(struct SessionHandle *data,          }          if((needle->handler->protocol & CURLPROTO_FTP) ||             ((needle->handler->protocol & CURLPROTO_HTTP) && -            (data->state.authhost.want==CURLAUTH_NTLM))) { +            ((data->state.authhost.want==CURLAUTH_NTLM) || +             (data->state.authhost.want==CURLAUTH_NTLM_SSO)))) {            /* This is FTP or HTTP+NTLM, verify that we're using the same name               and password as well */            if(!strequal(needle->user, check->user) || diff --git a/lib/urldata.h b/lib/urldata.h index d256968db..d2638aa93 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -905,6 +905,15 @@ struct connectdata {                                 single requests! */    struct ntlmdata proxyntlm; /* NTLM data for proxy */ +#ifdef USE_NTLM_SSO +  /* data used for communication with Samba's winbind daemon helper +     ntlm_auth */ +  int fd_helper; +  pid_t pid; +  char* challenge_header; +  char* response_header; +#endif +    char syserr_buf [256]; /* buffer for Curl_strerror() */  #ifdef CURLRES_ASYNCH diff --git a/lib/version.c b/lib/version.c index c471dc106..6b9ff5070 100644 --- a/lib/version.c +++ b/lib/version.c @@ -243,6 +243,9 @@ static curl_version_info_data version_info = {  #ifdef USE_NTLM    | CURL_VERSION_NTLM  #endif +#ifdef USE_NTLM_SSO +  | CURL_VERSION_NTLM_SSO +#endif  #ifdef USE_WINDOWS_SSPI    | CURL_VERSION_SSPI  #endif diff --git a/src/main.c b/src/main.c index 154694461..a68963df0 100644 --- a/src/main.c +++ b/src/main.c @@ -1869,6 +1869,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */      {"*k", "digest",     FALSE},      {"*l", "negotiate",  FALSE},      {"*m", "ntlm",       FALSE}, +    {"*M", "ntlm-sso",   FALSE},      {"*n", "basic",      FALSE},      {"*o", "anyauth",    FALSE},  #ifdef USE_WATT32 @@ -2223,6 +2224,17 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */            config->authtype &= ~CURLAUTH_NTLM;          break; +      case 'M': /* --ntlm-sso */ +        if(toggle) { +          if(curlinfo->features & CURL_VERSION_NTLM_SSO) +            config->authtype |= CURLAUTH_NTLM_SSO; +          else +            return PARAM_LIBCURL_DOESNT_SUPPORT; +        } +        else +          config->authtype &= ~CURLAUTH_NTLM_SSO; +        break; +        case 'n': /* --basic for completeness */          if(toggle)            config->authtype |= CURLAUTH_BASIC; @@ -3203,6 +3215,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */            {"IPv6", CURL_VERSION_IPV6},            {"Largefile", CURL_VERSION_LARGEFILE},            {"NTLM", CURL_VERSION_NTLM}, +          {"NTLM_SSO", CURL_VERSION_NTLM_SSO},            {"SPNEGO", CURL_VERSION_SPNEGO},            {"SSL",  CURL_VERSION_SSL},            {"SSPI",  CURL_VERSION_SSPI},  | 
