Skip to content

Commit

Permalink
Initial NTLM auth support
Browse files Browse the repository at this point in the history
This only works with the ntlm_auth helper. Implementing NTLM manually is
left as an exercise for the user.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Jun 18, 2014
1 parent 57c2546 commit 583ee4b
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 2 deletions.
170 changes: 168 additions & 2 deletions http.c
Expand Up @@ -29,6 +29,7 @@
#include <stdarg.h>
#ifndef _WIN32
#include <pwd.h>
#include <sys/wait.h>
#endif

#include "openconnect-internal.h"
Expand Down Expand Up @@ -1556,15 +1557,177 @@ static void b64_frag(struct oc_text_buf *buf, int len, unsigned char *in)
}

/* State in vpninfo->proxy_auth_state */
#define AUTH_FAILED -1 /* Failed */
#define AUTH_UNSEEN 0 /* Server has not offered it */
#define AUTH_AVAILABLE 1 /* Server has offered it, we have not tried it */
#define AUTH_IN_PROGRESS 2 /* In-progress attempt */
#define AUTH_FAILED 3 /* Failed */

/* Generate Proxy-Authorization: header for request if appropriate */

#define NTLM_SSO_REQ 2 /* SSO type1 packet sent */
#define NTLM_MANUAL 3 /* SSO challenge/response sent or skipped; manual next */
#define NTLM_MANUAL_REQ 4 /* manual type1 packet sent */

#ifndef _WIN32
static int ntlm_helper_spawn(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
{
char *username;
int pipefd[2];
pid_t pid;
char helperbuf[MAX_BUF_LEN];
int len;

if (access("/usr/bin/ntlm_auth", X_OK))
return -errno;

username = vpninfo->proxy_user;
if (!username)
username = getenv("NTLMUSER");
if (!username)
username = getenv("USER");
if (!username)
return -EINVAL;

#ifdef SOCK_CLOEXEC
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, pipefd))
#endif
{
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd))
return -errno;
set_fd_cloexec(pipefd[0]);
set_fd_cloexec(pipefd[1]);
}
pid = fork();
if (pid == -1)
return -errno;

if (!pid) {
int i;
char *p;
const char *argv[9];

/* Fork again to detach grandchild */
if (fork())
exit(1);

close(pipefd[1]);
/* The duplicated fd does not have O_CLOEXEC */
dup2(pipefd[0], 0);
dup2(pipefd[0], 1);
/* Should we leave stderr open? */
for (i = 3; i < 1024 ; i++)
close(i);


i = 0;
argv[i++] = "/usr/bin/ntlm_auth";
argv[i++] = "--helper-protocol";
argv[i++] = "ntlmssp-client-1";
argv[i++] = "--use-cached-creds";
argv[i++] = "--username";
p = strchr(username, '\\');
if (p) {
argv[i++] = p+1;
argv[i++] = "--domain";
argv[i++] = strndup(username, p - username);
} else
argv[i++] = username;
argv[i++] = NULL;

execv(argv[0], (char **)argv);
exit(1);
}
waitpid(pid, NULL, 0);
close(pipefd[0]);

if (write(pipefd[1], "YR\n", 3) != 3) {
close(pipefd[1]);
return -EIO;
}

len = read(pipefd[1], helperbuf, sizeof(helperbuf));
if (len < 4 || helperbuf[0] != 'Y' || helperbuf[1] != 'R' ||
helperbuf[2] != ' ' || helperbuf[len - 1] != '\n') {
close(pipefd[1]);
return -EIO;
}
helperbuf[len - 1] = 0;
buf_append(buf, "Proxy-Authorization: NTLM %s\r\n", helperbuf + 3);
vpninfo->ntlm_helper_fd = pipefd[1];
return 0;
}

static int ntlm_helper_challenge(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
{
char helperbuf[MAX_BUF_LEN];
int len;

if (!vpninfo->ntlm_auth.challenge ||
write(vpninfo->ntlm_helper_fd, "TT ", 3) != 3 ||
write(vpninfo->ntlm_helper_fd, vpninfo->ntlm_auth.challenge,
strlen(vpninfo->ntlm_auth.challenge)) != strlen(vpninfo->ntlm_auth.challenge) ||
write(vpninfo->ntlm_helper_fd, "\n", 1) != 1) {
err:
close(vpninfo->ntlm_helper_fd);
vpninfo->ntlm_helper_fd = -1;
return -EIO;
}
len = read(vpninfo->ntlm_helper_fd, helperbuf, sizeof(helperbuf));
if (len < 4 || helperbuf[0] != 'K' || helperbuf[1] != 'K' ||
helperbuf[2] != ' ' || helperbuf[len - 1] != '\n') {
goto err;
}
helperbuf[len - 1] = 0;
buf_append(buf, "Proxy-Authorization: NTLM %s\r\n", helperbuf + 3);
close(vpninfo->ntlm_helper_fd);
vpninfo->ntlm_helper_fd = -1;
return 0;

}
#endif /* !_WIN32 */

static int ntlm_manual_challenge(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
{
return -EIO;
}

static int ntlm_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
{
if (vpninfo->ntlm_auth.state == AUTH_AVAILABLE) {
vpninfo->ntlm_auth.state = NTLM_MANUAL;
#ifndef _WIN32
if (!ntlm_helper_spawn(vpninfo, buf)) {
vpninfo->ntlm_auth.state = NTLM_SSO_REQ;
return 0;
}
}
if (vpninfo->ntlm_auth.state == NTLM_SSO_REQ) {
vpninfo->ntlm_auth.state = NTLM_MANUAL;
if (!ntlm_helper_challenge(vpninfo, buf))
return 0;
#endif
}
if (vpninfo->ntlm_auth.state == NTLM_MANUAL && vpninfo->proxy_user &&
vpninfo->proxy_pass) {
buf_append(buf, "Proxy-Authorization: NTLM %s\r\n",
"TlRMTVNTUAABAAAABoIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
vpninfo->ntlm_auth.state = NTLM_MANUAL_REQ;
return 0;
}
if (vpninfo->ntlm_auth.state == NTLM_MANUAL_REQ) {
vpninfo->ntlm_auth.state = AUTH_FAILED;
return ntlm_manual_challenge(vpninfo, buf);

}
return -EINVAL;
}

/* Generate Proxy-Authorization: header for request if appropriate */
static int proxy_authorization(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
{
if (vpninfo->ntlm_auth.state > AUTH_UNSEEN &&
!ntlm_authorization(vpninfo, buf))
return 0;

if (vpninfo->basic_auth.state == AUTH_AVAILABLE &&
vpninfo->proxy_user && vpninfo->proxy_pass) {
char *p = vpninfo->proxy_user;
Expand Down Expand Up @@ -1695,6 +1858,9 @@ static int process_http_proxy(struct openconnect_info *vpninfo)
if (buf_error(reqbuf))
return buf_free(reqbuf);

if (vpninfo->dump_http_traffic)
dump_buf(vpninfo, '>', reqbuf->data);

result = proxy_write(vpninfo, reqbuf->data, reqbuf->pos);
buf_free(reqbuf);

Expand Down
1 change: 1 addition & 0 deletions openconnect-internal.h
Expand Up @@ -178,6 +178,7 @@ struct openconnect_info {
struct proxy_auth_state basic_auth;
struct proxy_auth_state ntlm_auth;
struct proxy_auth_state gssapi_auth;
int ntlm_helper_fd;

char *localname;
char *hostname;
Expand Down

0 comments on commit 583ee4b

Please sign in to comment.