X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source%2Futils%2Fntlm_auth.c;h=f99c9db35bfcdb332166cdbdd6b00a1a6533c3bc;hb=b65be8874a2efe5a4b167448960a4fcf6bd995e2;hp=fbe32768a3a34733d82a7032fa928f230e820840;hpb=946695242f0a6db0e8c712f9bd97ac3cb99c4119;p=kai%2Fsamba-autobuild%2F.git diff --git a/source/utils/ntlm_auth.c b/source/utils/ntlm_auth.c index fbe32768a3a..f99c9db35bf 100644 --- a/source/utils/ntlm_auth.c +++ b/source/utils/ntlm_auth.c @@ -3,8 +3,8 @@ Winbind status program. - Copyright (C) Tim Potter 2000-2002 - Copyright (C) Andrew Bartlett 2003 + Copyright (C) Tim Potter 2000-2003 + Copyright (C) Andrew Bartlett 2003-2004 Copyright (C) Francesco Chemolli 2000 This program is free software; you can redistribute it and/or modify @@ -23,32 +23,74 @@ */ #include "includes.h" +#include "utils/ntlm_auth.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND #define SQUID_BUFFER_SIZE 2010 -enum squid_mode { +enum stdio_helper_mode { SQUID_2_4_BASIC, SQUID_2_5_BASIC, SQUID_2_5_NTLMSSP, - GSS_SPNEGO + NTLMSSP_CLIENT_1, + GSS_SPNEGO, + GSS_SPNEGO_CLIENT, + NTLM_SERVER_1, + NUM_HELPER_MODES +}; + +typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static const struct { + enum stdio_helper_mode mode; + const char *name; + stdio_helper_function fn; +} stdio_helper_protocols[] = { + { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request}, + { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request}, + { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request}, + { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request}, + { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request}, + { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request}, + { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request}, + { NUM_HELPER_MODES, NULL, NULL} }; - extern int winbindd_fd; -static const char *opt_username; -static const char *opt_domain; -static const char *opt_workstation; -static const char *opt_password; +const char *opt_username; +const char *opt_domain; +const char *opt_workstation; +const char *opt_password; static DATA_BLOB opt_challenge; static DATA_BLOB opt_lm_response; static DATA_BLOB opt_nt_response; static int request_lm_key; -static int request_nt_key; +static int request_user_session_key; +static const char *require_membership_of; +static const char *require_membership_of_sid; static char winbind_separator(void) { @@ -63,10 +105,10 @@ static char winbind_separator(void) /* Send off request */ - if (winbindd_request(WINBINDD_INFO, NULL, &response) != + if (winbindd_request_response(WINBINDD_INFO, NULL, &response) != NSS_STATUS_SUCCESS) { d_printf("could not obtain winbind separator!\n"); - return '\\'; + return *lp_winbind_separator(); } sep = response.data.info.winbind_separator; @@ -74,13 +116,13 @@ static char winbind_separator(void) if (!sep) { d_printf("winbind separator was NULL!\n"); - return '\\'; + return *lp_winbind_separator(); } return sep; } -static const char *get_winbind_domain(void) +const char *get_winbind_domain(void) { struct winbindd_response response; @@ -93,10 +135,10 @@ static const char *get_winbind_domain(void) /* Send off request */ - if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) != + if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) != NSS_STATUS_SUCCESS) { - d_printf("could not obtain winbind domain name!\n"); - return NULL; + DEBUG(0, ("could not obtain winbind domain name!\n")); + return lp_workgroup(); } fstrcpy(winbind_domain, response.data.domain_name); @@ -105,7 +147,7 @@ static const char *get_winbind_domain(void) } -static const char *get_winbind_netbios_name(void) +const char *get_winbind_netbios_name(void) { struct winbindd_response response; @@ -119,10 +161,10 @@ static const char *get_winbind_netbios_name(void) /* Send off request */ - if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) != + if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) != NSS_STATUS_SUCCESS) { - d_printf("could not obtain winbind netbios name!\n"); - return NULL; + DEBUG(0, ("could not obtain winbind netbios name!\n")); + return global_myname(); } fstrcpy(winbind_netbios_name, response.data.netbios_name); @@ -131,14 +173,91 @@ static const char *get_winbind_netbios_name(void) } +DATA_BLOB get_challenge(void) +{ + static DATA_BLOB chal; + if (opt_challenge.length) + return opt_challenge; + + chal = data_blob(NULL, 8); + + generate_random_buffer(chal.data, chal.length); + return chal; +} + +/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the + form DOMAIN/user into a domain and a user */ + +static BOOL parse_ntlm_auth_domain_user(const char *domuser, fstring domain, + fstring user) +{ + + char *p = strchr(domuser,winbind_separator()); + + if (!p) { + return False; + } + + fstrcpy(user, p+1); + fstrcpy(domain, domuser); + domain[PTR_DIFF(p, domuser)] = 0; + strupper_m(domain); + + return True; +} + +static BOOL get_require_membership_sid(void) { + struct winbindd_request request; + struct winbindd_response response; + + if (!require_membership_of) { + return True; + } + + if (require_membership_of_sid) { + return True; + } + + /* Otherwise, ask winbindd for the name->sid request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + if (!parse_ntlm_auth_domain_user(require_membership_of, + request.data.name.dom_name, + request.data.name.name)) { + DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n", + require_membership_of)); + return False; + } + + if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) != + NSS_STATUS_SUCCESS) { + DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n", + require_membership_of)); + return False; + } + + require_membership_of_sid = SMB_STRDUP(response.data.sid.sid); + + if (require_membership_of_sid) + return True; + + return False; +} /* Authenticate a user with a plaintext password */ -static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics) +static BOOL check_plaintext_auth(const char *user, const char *pass, + BOOL stdout_diagnostics) { struct winbindd_request request; struct winbindd_response response; NSS_STATUS result; + if (!get_require_membership_sid()) { + return False; + } + /* Send off request */ ZERO_STRUCT(request); @@ -146,8 +265,10 @@ static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout fstrcpy(request.data.auth.user, user); fstrcpy(request.data.auth.pass, pass); + if (require_membership_of_sid) + fstrcpy(request.data.auth.require_membership_of_sid, require_membership_of_sid); - result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response); + result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response); /* Display response */ @@ -176,47 +297,60 @@ static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout /* authenticate a user with an encrypted username/password */ -static NTSTATUS contact_winbind_auth_crap(const char *username, - const char *domain, - const char *workstation, - const DATA_BLOB *challenge, - const DATA_BLOB *lm_response, - const DATA_BLOB *nt_response, - uint32 flags, - uint8 lm_key[8], - uint8 nt_key[16], - char **error_string) +NTSTATUS contact_winbind_auth_crap(const char *username, + const char *domain, + const char *workstation, + const DATA_BLOB *challenge, + const DATA_BLOB *lm_response, + const DATA_BLOB *nt_response, + uint32 flags, + uint8 lm_key[8], + uint8 user_session_key[16], + char **error_string, + char **unix_name) { NTSTATUS nt_status; NSS_STATUS result; struct winbindd_request request; struct winbindd_response response; - static uint8 zeros[16]; + if (!get_require_membership_sid()) { + return NT_STATUS_INVALID_PARAMETER; + } ZERO_STRUCT(request); ZERO_STRUCT(response); request.flags = flags; - fstrcpy(request.data.auth_crap.user, username); + request.data.auth_crap.logon_parameters = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT; + if (require_membership_of_sid) + fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid); + + fstrcpy(request.data.auth_crap.user, username); fstrcpy(request.data.auth_crap.domain, domain); - fstrcpy(request.data.auth_crap.workstation, workstation); + + fstrcpy(request.data.auth_crap.workstation, + workstation); memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8)); if (lm_response && lm_response->length) { - memcpy(request.data.auth_crap.lm_resp, lm_response->data, MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp))); + memcpy(request.data.auth_crap.lm_resp, + lm_response->data, + MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp))); request.data.auth_crap.lm_resp_len = lm_response->length; } if (nt_response && nt_response->length) { - memcpy(request.data.auth_crap.nt_resp, nt_response->data, MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp))); + memcpy(request.data.auth_crap.nt_resp, + nt_response->data, + MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp))); request.data.auth_crap.nt_resp_len = nt_response->length; } - result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response); + result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response); /* Display response */ @@ -224,6 +358,7 @@ static NTSTATUS contact_winbind_auth_crap(const char *username, nt_status = NT_STATUS_UNSUCCESSFUL; if (error_string) *error_string = smb_xstrdup("Reading winbind reply failed!"); + free_response(&response); return nt_status; } @@ -231,38 +366,174 @@ static NTSTATUS contact_winbind_auth_crap(const char *username, if (!NT_STATUS_IS_OK(nt_status)) { if (error_string) *error_string = smb_xstrdup(response.data.auth.error_string); + free_response(&response); return nt_status; } - if ((flags & WBFLAG_PAM_LMKEY) && lm_key - && (memcmp(zeros, response.data.auth.first_8_lm_hash, - sizeof(response.data.auth.first_8_lm_hash)) != 0)) { + if ((flags & WBFLAG_PAM_LMKEY) && lm_key) { memcpy(lm_key, response.data.auth.first_8_lm_hash, - sizeof(response.data.auth.first_8_lm_hash)); + sizeof(response.data.auth.first_8_lm_hash)); } - if ((flags & WBFLAG_PAM_NTKEY) && nt_key - && (memcmp(zeros, response.data.auth.nt_session_key, - sizeof(response.data.auth.nt_session_key)) != 0)) { - memcpy(nt_key, response.data.auth.nt_session_key, - sizeof(response.data.auth.nt_session_key)); + if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) { + memcpy(user_session_key, response.data.auth.user_session_key, + sizeof(response.data.auth.user_session_key)); + } + + if (flags & WBFLAG_PAM_UNIX_NAME) { + *unix_name = SMB_STRDUP((char *)response.extra_data); + if (!*unix_name) { + free_response(&response); + return NT_STATUS_NO_MEMORY; + } } + + free_response(&response); return nt_status; } -static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state) +static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) +{ + static const char zeros[16]; + NTSTATUS nt_status; + char *error_string; + uint8 lm_key[8]; + uint8 user_sess_key[16]; + char *unix_name; + + nt_status = contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain, + ntlmssp_state->workstation, + &ntlmssp_state->chal, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME, + lm_key, user_sess_key, + &error_string, &unix_name); + + if (NT_STATUS_IS_OK(nt_status)) { + if (memcmp(lm_key, zeros, 8) != 0) { + *lm_session_key = data_blob(NULL, 16); + memcpy(lm_session_key->data, lm_key, 8); + memset(lm_session_key->data+8, '\0', 8); + } + + if (memcmp(user_sess_key, zeros, 16) != 0) { + *user_session_key = data_blob(user_sess_key, 16); + } + ntlmssp_state->auth_context = talloc_strdup(ntlmssp_state->mem_ctx, unix_name); + SAFE_FREE(unix_name); + } else { + DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3, + ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", + ntlmssp_state->domain, ntlmssp_state->user, + ntlmssp_state->workstation, + error_string ? error_string : "unknown error (NULL)")); + ntlmssp_state->auth_context = NULL; + } + return nt_status; +} + +static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) +{ + NTSTATUS nt_status; + uint8 lm_pw[16], nt_pw[16]; + + nt_lm_owf_gen (opt_password, nt_pw, lm_pw); + + nt_status = ntlm_password_check(ntlmssp_state->mem_ctx, + &ntlmssp_state->chal, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + NULL, NULL, + ntlmssp_state->user, + ntlmssp_state->user, + ntlmssp_state->domain, + lm_pw, nt_pw, user_session_key, lm_session_key); + + if (NT_STATUS_IS_OK(nt_status)) { + ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx, + "%s%c%s", ntlmssp_state->domain, + *lp_winbind_separator(), + ntlmssp_state->user); + } else { + DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", + ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation, + nt_errstr(nt_status))); + ntlmssp_state->auth_context = NULL; + } + return nt_status; +} + +static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_state) +{ + NTSTATUS status; + if ( (opt_username == NULL) || (opt_domain == NULL) ) { + status = NT_STATUS_UNSUCCESSFUL; + DEBUG(1, ("Need username and domain for NTLMSSP\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + status = ntlmssp_client_start(client_ntlmssp_state); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not start NTLMSSP client: %s\n", + nt_errstr(status))); + ntlmssp_end(client_ntlmssp_state); + return status; + } + + status = ntlmssp_set_username(*client_ntlmssp_state, opt_username); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set username: %s\n", + nt_errstr(status))); + ntlmssp_end(client_ntlmssp_state); + return status; + } + + status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set domain: %s\n", + nt_errstr(status))); + ntlmssp_end(client_ntlmssp_state); + return status; + } + + status = ntlmssp_set_password(*client_ntlmssp_state, opt_password); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set password: %s\n", + nt_errstr(status))); + ntlmssp_end(client_ntlmssp_state); + return status; + } + return NT_STATUS_OK; +} + +static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state) { - return contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain, - ntlmssp_state->workstation, - &ntlmssp_state->chal, - &ntlmssp_state->lm_resp, - &ntlmssp_state->nt_resp, - 0, - NULL, - NULL, - NULL); + NTSTATUS status = ntlmssp_server_start(ntlmssp_state); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not start NTLMSSP client: %s\n", + nt_errstr(status))); + return status; + } + + /* Have we been given a local password, or should we ask winbind? */ + if (opt_password) { + (*ntlmssp_state)->check_password = local_pw_check; + (*ntlmssp_state)->get_domain = lp_workgroup; + (*ntlmssp_state)->get_global_myname = global_myname; + } else { + (*ntlmssp_state)->check_password = winbind_pw_check; + (*ntlmssp_state)->get_domain = get_winbind_domain; + (*ntlmssp_state)->get_global_myname = get_winbind_netbios_name; + } + return NT_STATUS_OK; } -static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, +static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, char *buf, int length) { static NTLMSSP_STATE *ntlmssp_state = NULL; @@ -277,10 +548,32 @@ static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, if (strlen(buf) > 3) { request = base64_decode_data_blob(buf + 3); - } else if (strcmp(buf, "YR") == 0) { + } else { request = data_blob(NULL, 0); + } + + if ((strncmp(buf, "PW ", 3) == 0)) { + /* The calling application wants us to use a local password (rather than winbindd) */ + + opt_password = SMB_STRNDUP((const char *)request.data, request.length); + + if (opt_password == NULL) { + DEBUG(1, ("Out of memory\n")); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&request); + return; + } + + x_fprintf(x_stdout, "OK\n"); + data_blob_free(&request); + return; + } + + if (strncmp(buf, "YR", 2) == 0) { if (ntlmssp_state) - ntlmssp_server_end(&ntlmssp_state); + ntlmssp_end(&ntlmssp_state); + } else if (strncmp(buf, "KK", 2) == 0) { + } else { DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); x_fprintf(x_stdout, "BH\n"); @@ -288,16 +581,16 @@ static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, } if (!ntlmssp_state) { - ntlmssp_server_start(&ntlmssp_state); - ntlmssp_state->check_password = winbind_pw_check; - ntlmssp_state->get_domain = get_winbind_domain; - ntlmssp_state->get_global_myname = get_winbind_netbios_name; + if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); + return; + } } DEBUG(10, ("got NTLMSSP packet:\n")); - dump_data(10, request.data, request.length); + dump_data(10, (const char *)request.data, request.length); - nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply); + nt_status = ntlmssp_update(ntlmssp_state, request, &reply); if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { char *reply_base64 = base64_encode_data_blob(reply); @@ -305,18 +598,120 @@ static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, SAFE_FREE(reply_base64); data_blob_free(&reply); DEBUG(10, ("NTLMSSP challenge\n")); + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); + DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status))); + + ntlmssp_end(&ntlmssp_state); } else if (!NT_STATUS_IS_OK(nt_status)) { x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status)); DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status))); } else { - x_fprintf(x_stdout, "AF %s\\%s\n", ntlmssp_state->domain, ntlmssp_state->user); + x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context); + DEBUG(10, ("NTLMSSP OK!\n")); + } + + data_blob_free(&request); +} + +static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length) +{ + static NTLMSSP_STATE *ntlmssp_state = NULL; + DATA_BLOB request, reply; + NTSTATUS nt_status; + BOOL first = False; + + if (strlen(buf) < 2) { + DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (strlen(buf) > 3) { + request = base64_decode_data_blob(buf + 3); + } else { + request = data_blob(NULL, 0); + } + + if (strncmp(buf, "PW ", 3) == 0) { + /* We asked for a password and obviously got it :-) */ + + opt_password = SMB_STRNDUP((const char *)request.data, request.length); + + if (opt_password == NULL) { + DEBUG(1, ("Out of memory\n")); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&request); + return; + } + + x_fprintf(x_stdout, "OK\n"); + data_blob_free(&request); + return; + } + + if (opt_password == NULL) { + + /* Request a password from the calling process. After + sending it, the calling process should retry asking for the negotiate. */ + + DEBUG(10, ("Requesting password\n")); + x_fprintf(x_stdout, "PW\n"); + return; + } + + if (strncmp(buf, "YR", 2) == 0) { + if (ntlmssp_state) + ntlmssp_end(&ntlmssp_state); + } else if (strncmp(buf, "TT", 2) == 0) { + + } else { + DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (!ntlmssp_state) { + if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); + return; + } + first = True; + } + + DEBUG(10, ("got NTLMSSP packet:\n")); + dump_data(10, (const char *)request.data, request.length); + + nt_status = ntlmssp_update(ntlmssp_state, request, &reply); + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + char *reply_base64 = base64_encode_data_blob(reply); + if (first) { + x_fprintf(x_stdout, "YR %s\n", reply_base64); + } else { + x_fprintf(x_stdout, "KK %s\n", reply_base64); + } + SAFE_FREE(reply_base64); + data_blob_free(&reply); + DEBUG(10, ("NTLMSSP challenge\n")); + } else if (NT_STATUS_IS_OK(nt_status)) { + char *reply_base64 = base64_encode_data_blob(reply); + x_fprintf(x_stdout, "AF %s\n", reply_base64); DEBUG(10, ("NTLMSSP OK!\n")); + if (ntlmssp_state) + ntlmssp_end(&ntlmssp_state); + } else { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); + DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status))); + if (ntlmssp_state) + ntlmssp_end(&ntlmssp_state); } data_blob_free(&request); } -static void manage_squid_basic_request(enum squid_mode squid_mode, +static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, char *buf, int length) { char *user, *pass; @@ -325,13 +720,13 @@ static void manage_squid_basic_request(enum squid_mode squid_mode, pass=memchr(buf,' ',length); if (!pass) { DEBUG(2, ("Password not found. Denying access\n")); - x_fprintf(x_stderr, "ERR\n"); + x_fprintf(x_stdout, "ERR\n"); return; } *pass='\0'; pass++; - if (squid_mode == SQUID_2_5_BASIC) { + if (stdio_helper_mode == SQUID_2_5_BASIC) { rfc1738_unescape(user); rfc1738_unescape(pass); } @@ -346,27 +741,35 @@ static void manage_squid_basic_request(enum squid_mode squid_mode, static void offer_gss_spnego_mechs(void) { DATA_BLOB token; - ASN1_DATA asn1; SPNEGO_DATA spnego; ssize_t len; char *reply_base64; + pstring principal; + pstring myname_lower; + ZERO_STRUCT(spnego); + pstrcpy(myname_lower, global_myname()); + strlower_m(myname_lower); + + pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm()); + /* Server negTokenInit (mech offerings) */ spnego.type = SPNEGO_NEG_TOKEN_INIT; - spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 2); + spnego.negTokenInit.mechTypes = SMB_XMALLOC_ARRAY(const char *, 2); +#ifdef HAVE_KRB5 + spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD); + spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP); + spnego.negTokenInit.mechTypes[2] = NULL; +#else spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP); spnego.negTokenInit.mechTypes[1] = NULL; +#endif - ZERO_STRUCT(asn1); - asn1_push_tag(&asn1, ASN1_SEQUENCE(0)); - asn1_push_tag(&asn1, ASN1_CONTEXT(0)); - asn1_write_GeneralString(&asn1, "NONE"); - asn1_pop_tag(&asn1); - asn1_pop_tag(&asn1); - spnego.negTokenInit.mechListMIC = data_blob(asn1.data, asn1.length); - asn1_free(&asn1); + + spnego.negTokenInit.mechListMIC = data_blob(principal, + strlen(principal)); len = write_spnego_data(&token, &spnego); free_spnego_data(&spnego); @@ -378,7 +781,7 @@ static void offer_gss_spnego_mechs(void) { } reply_base64 = base64_encode_data_blob(token); - x_fprintf(x_stdout, "TT %s\n", reply_base64); + x_fprintf(x_stdout, "TT %s *\n", reply_base64); SAFE_FREE(reply_base64); data_blob_free(&token); @@ -386,52 +789,59 @@ static void offer_gss_spnego_mechs(void) { return; } -static void manage_gss_spnego_request(enum squid_mode squid_mode, +static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode, char *buf, int length) { static NTLMSSP_STATE *ntlmssp_state = NULL; - SPNEGO_DATA spnego; - DATA_BLOB request, token; + SPNEGO_DATA request, response; + DATA_BLOB token; NTSTATUS status; - char *reply_base64; - pstring reply; ssize_t len; - if (strlen(buf) < 2) { + char *user = NULL; + char *domain = NULL; - if (ntlmssp_state != NULL) { - DEBUG(1, ("Request for initial SPNEGO request where " - "we already have a state\n")); - x_fprintf(x_stdout, "BH\n"); - return; - } + const char *reply_code; + char *reply_base64; + pstring reply_argument; - DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); + if (strlen(buf) < 2) { + DEBUG(1, ("SPENGO query [%s] invalid", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (strncmp(buf, "YR", 2) == 0) { + if (ntlmssp_state) + ntlmssp_end(&ntlmssp_state); + } else if (strncmp(buf, "KK", 2) == 0) { + + } else { + DEBUG(1, ("SPENGO query [%s] invalid", buf)); x_fprintf(x_stdout, "BH\n"); return; } - if ( (strlen(buf) == 2) && (strcmp(buf, "YR") == 0) ) { + if ( (strlen(buf) == 2)) { - /* Initial request, get the negTokenInit offering + /* no client data, get the negTokenInit offering mechanisms */ offer_gss_spnego_mechs(); return; } - /* All subsequent requests are "KK" (Knock, Knock ;)) and have - a blob. This might be negTokenInit or negTokenTarg */ + /* All subsequent requests have a blob. This might be negTokenInit or negTokenTarg */ - if ( (strlen(buf) <= 3) || (strncmp(buf, "KK", 2) != 0) ) { + if (strlen(buf) <= 3) { DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf)); x_fprintf(x_stdout, "BH\n"); return; } - request = base64_decode_data_blob(buf + 3); - - len = read_spnego_data(request, &spnego); + token = base64_decode_data_blob(buf + 3); + len = read_spnego_data(token, &request); + data_blob_free(&token); if (len == -1) { DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf)); @@ -439,106 +849,159 @@ static void manage_gss_spnego_request(enum squid_mode squid_mode, return; } - if (spnego.type == SPNEGO_NEG_TOKEN_INIT) { + if (request.type == SPNEGO_NEG_TOKEN_INIT) { /* Second request from Client. This is where the - client offers its mechanism to use. We currently - only support NTLMSSP, the decision for Kerberos - would be taken here. */ + client offers its mechanism to use. */ - if ( (spnego.negTokenInit.mechTypes == NULL) || - (spnego.negTokenInit.mechTypes[0] == NULL) ) { + if ( (request.negTokenInit.mechTypes == NULL) || + (request.negTokenInit.mechTypes[0] == NULL) ) { DEBUG(1, ("Client did not offer any mechanism")); x_fprintf(x_stdout, "BH\n"); return; } - if ( strcmp(spnego.negTokenInit.mechTypes[0], OID_NTLMSSP) != 0 ) { - DEBUG(1, ("Client did not choose NTLMSSP but %s\n", - spnego.negTokenInit.mechTypes[0])); - x_fprintf(x_stdout, "BH\n"); - return; - } + status = NT_STATUS_UNSUCCESSFUL; + if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) { - if ( spnego.negTokenInit.mechToken.data == NULL ) { - DEBUG(1, ("Client did not provide NTLMSSP data\n")); - x_fprintf(x_stdout, "BH\n"); - return; - } + if ( request.negTokenInit.mechToken.data == NULL ) { + DEBUG(1, ("Client did not provide NTLMSSP data\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } - if ( ntlmssp_state != NULL ) { - DEBUG(1, ("Client wants a new NTLMSSP challenge, but " - "already got one\n")); - x_fprintf(x_stdout, "BH\n"); + if ( ntlmssp_state != NULL ) { + DEBUG(1, ("Client wants a new NTLMSSP challenge, but " + "already got one\n")); + x_fprintf(x_stdout, "BH\n"); + ntlmssp_end(&ntlmssp_state); + return; + } - ntlmssp_server_end(&ntlmssp_state); - return; + if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(status)); + return; + } + + DEBUG(10, ("got NTLMSSP packet:\n")); + dump_data(10, (const char *)request.negTokenInit.mechToken.data, + request.negTokenInit.mechToken.length); + + response.type = SPNEGO_NEG_TOKEN_TARG; + response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP); + response.negTokenTarg.mechListMIC = data_blob(NULL, 0); + + status = ntlmssp_update(ntlmssp_state, + request.negTokenInit.mechToken, + &response.negTokenTarg.responseToken); } - ntlmssp_server_start(&ntlmssp_state); - ntlmssp_state->check_password = winbind_pw_check; - ntlmssp_state->get_domain = get_winbind_domain; - ntlmssp_state->get_global_myname = get_winbind_netbios_name; +#ifdef HAVE_KRB5 + if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) { - DEBUG(10, ("got NTLMSSP packet:\n")); - dump_data(10, spnego.negTokenInit.mechToken.data, - spnego.negTokenInit.mechToken.length); + TALLOC_CTX *mem_ctx = talloc_init("manage_gss_spnego_request"); + char *principal; + DATA_BLOB ap_rep; + DATA_BLOB session_key; - free_spnego_data(&spnego); + if ( request.negTokenInit.mechToken.data == NULL ) { + DEBUG(1, ("Client did not provide Kerberos data\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } - spnego.type = SPNEGO_NEG_TOKEN_TARG; - spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; - spnego.negTokenTarg.supportedMech = OID_NTLMSSP; + response.type = SPNEGO_NEG_TOKEN_TARG; + response.negTokenTarg.supportedMech = SMB_STRDUP(OID_KERBEROS5_OLD); + response.negTokenTarg.mechListMIC = data_blob(NULL, 0); + response.negTokenTarg.responseToken = data_blob(NULL, 0); - status = ntlmssp_server_update(ntlmssp_state, - spnego.negTokenInit.mechToken, - &spnego.negTokenTarg.responseToken); + status = ads_verify_ticket(mem_ctx, lp_realm(), + &request.negTokenInit.mechToken, + &principal, NULL, &ap_rep, + &session_key); - } else { + talloc_destroy(mem_ctx); - /* spnego.type == SPNEGO_NEG_TOKEN_TARG */ + /* Now in "principal" we have the name we are + authenticated as. */ - DATA_BLOB response; + if (NT_STATUS_IS_OK(status)) { - if (spnego.negTokenTarg.responseToken.data == NULL) { - DEBUG(1, ("Got a negTokenArg without a responseToken!\n")); - x_fprintf(x_stdout, "BH\n"); - return; - } + domain = strchr_m(principal, '@'); - status = ntlmssp_server_update(ntlmssp_state, - spnego.negTokenTarg.responseToken, - &response); + if (domain == NULL) { + DEBUG(1, ("Did not get a valid principal " + "from ads_verify_ticket\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } - data_blob_free(&spnego.negTokenTarg.responseToken); + *domain++ = '\0'; + domain = SMB_STRDUP(domain); + user = SMB_STRDUP(principal); - spnego.negTokenTarg.responseToken = response; + data_blob_free(&ap_rep); - } + SAFE_FREE(principal); + } + } +#endif - if ( !NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ) { + } else { - /* Neither ok nor more work to do, so reject */ + if ( (request.negTokenTarg.supportedMech == NULL) || + ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) { + /* Kerberos should never send a negTokenTarg, OID_NTLMSSP + is the only one we support that sends this stuff */ + DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n", + request.negTokenTarg.supportedMech)); + x_fprintf(x_stdout, "BH\n"); + return; + } - x_fprintf(x_stdout, "NA %s\n", nt_errstr(status)); - DEBUG(10, ("NTLMSSP %s\n", nt_errstr(status))); - return; - } + if (request.negTokenTarg.responseToken.data == NULL) { + DEBUG(1, ("Got a negTokenTarg without a responseToken!\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } - pstr_sprintf(reply, "TT"); + status = ntlmssp_update(ntlmssp_state, + request.negTokenTarg.responseToken, + &response.negTokenTarg.responseToken); - if (NT_STATUS_IS_OK(status)) { - pstr_sprintf(reply, "AF %s\\%s", - ntlmssp_state->domain, ntlmssp_state->user); + response.type = SPNEGO_NEG_TOKEN_TARG; + response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP); + response.negTokenTarg.mechListMIC = data_blob(NULL, 0); - spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED; + if (NT_STATUS_IS_OK(status)) { + user = SMB_STRDUP(ntlmssp_state->user); + domain = SMB_STRDUP(ntlmssp_state->domain); + ntlmssp_end(&ntlmssp_state); + } + } - DEBUG(10, ("NTLMSSP OK!\n")); + free_spnego_data(&request); + + if (NT_STATUS_IS_OK(status)) { + response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED; + reply_code = "AF"; + pstr_sprintf(reply_argument, "%s\\%s", domain, user); + } else if (NT_STATUS_EQUAL(status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; + reply_code = "TT"; + pstr_sprintf(reply_argument, "*"); + } else { + response.negTokenTarg.negResult = SPNEGO_REJECT; + reply_code = "NA"; + pstrcpy(reply_argument, nt_errstr(status)); } - len = write_spnego_data(&token, &spnego); - free_spnego_data(&spnego); + SAFE_FREE(user); + SAFE_FREE(domain); + + len = write_spnego_data(&token, &response); + free_spnego_data(&response); if (len == -1) { DEBUG(1, ("Could not write SPNEGO data blob\n")); @@ -547,876 +1010,690 @@ static void manage_gss_spnego_request(enum squid_mode squid_mode, } reply_base64 = base64_encode_data_blob(token); - x_fprintf(x_stdout, "%s %s\n", reply, reply_base64); + + x_fprintf(x_stdout, "%s %s %s\n", + reply_code, reply_base64, reply_argument); + SAFE_FREE(reply_base64); data_blob_free(&token); - if (NT_STATUS_IS_OK(status)) { - ntlmssp_server_end(&ntlmssp_state); - } - return; } -static void manage_squid_request(enum squid_mode squid_mode) -{ - char buf[SQUID_BUFFER_SIZE+1]; - int length; - char *c; - static BOOL err; +static NTLMSSP_STATE *client_ntlmssp_state = NULL; - /* this is not a typo - x_fgets doesn't work too well under squid */ - if (fgets(buf, sizeof(buf)-1, stdin) == NULL) { - DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin), - strerror(ferror(stdin)))); - exit(1); /* BIIG buffer */ - } - - c=memchr(buf,'\n',sizeof(buf)-1); - if (c) { - *c = '\0'; - length = c-buf; - } else { - err = 1; - return; - } - if (err) { - DEBUG(2, ("Oversized message\n")); - x_fprintf(x_stderr, "ERR\n"); - err = 0; - return; - } +static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego) +{ + NTSTATUS status; + DATA_BLOB null_blob = data_blob(NULL, 0); + DATA_BLOB to_server; + char *to_server_base64; + const char *my_mechs[] = {OID_NTLMSSP, NULL}; - DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length)); + DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n")); - if (buf[0] == '\0') { - DEBUG(2, ("Invalid Request\n")); - x_fprintf(x_stderr, "ERR\n"); - return; + if (client_ntlmssp_state != NULL) { + DEBUG(1, ("Request for initial SPNEGO request where " + "we already have a state\n")); + return False; } - - if (squid_mode == SQUID_2_5_BASIC || squid_mode == SQUID_2_4_BASIC) { - manage_squid_basic_request(squid_mode, buf, length); - } else if (squid_mode == SQUID_2_5_NTLMSSP) { - manage_squid_ntlmssp_request(squid_mode, buf, length); - } else if (squid_mode == GSS_SPNEGO) { - manage_gss_spnego_request(squid_mode, buf, length); + + if (!client_ntlmssp_state) { + if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(status)); + return False; + } } -} -static void squid_stream(enum squid_mode squid_mode) { - /* initialize FDescs */ - x_setbuf(x_stdout, NULL); - x_setbuf(x_stderr, NULL); - while(1) { - manage_squid_request(squid_mode); - } -} + if (opt_password == NULL) { + /* Request a password from the calling process. After + sending it, the calling process should retry with + the negTokenInit. */ -/* Authenticate a user with a challenge/response */ + DEBUG(10, ("Requesting password\n")); + x_fprintf(x_stdout, "PW\n"); + return True; + } -static BOOL check_auth_crap(void) -{ - NTSTATUS nt_status; - uint32 flags = 0; - char lm_key[8]; - char nt_key[16]; - char *hex_lm_key; - char *hex_nt_key; - char *error_string; - static uint8 zeros[16]; + spnego.type = SPNEGO_NEG_TOKEN_INIT; + spnego.negTokenInit.mechTypes = my_mechs; + spnego.negTokenInit.reqFlags = 0; + spnego.negTokenInit.mechListMIC = null_blob; + + status = ntlmssp_update(client_ntlmssp_state, null_blob, + &spnego.negTokenInit.mechToken); + + if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || + NT_STATUS_IS_OK(status)) ) { + DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n", + nt_errstr(status))); + ntlmssp_end(&client_ntlmssp_state); + return False; + } - x_setbuf(x_stdout, NULL); + write_spnego_data(&to_server, &spnego); + data_blob_free(&spnego.negTokenInit.mechToken); - if (request_lm_key) - flags |= WBFLAG_PAM_LMKEY; + to_server_base64 = base64_encode_data_blob(to_server); + data_blob_free(&to_server); + x_fprintf(x_stdout, "KK %s\n", to_server_base64); + SAFE_FREE(to_server_base64); + return True; +} - if (request_nt_key) - flags |= WBFLAG_PAM_NTKEY; +static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego) +{ + NTSTATUS status; + DATA_BLOB null_blob = data_blob(NULL, 0); + DATA_BLOB request; + DATA_BLOB to_server; + char *to_server_base64; - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &opt_challenge, - &opt_lm_response, - &opt_nt_response, - flags, - lm_key, - nt_key, - &error_string); + DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n")); - if (!NT_STATUS_IS_OK(nt_status)) { - x_fprintf(x_stdout, "%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; + if (client_ntlmssp_state == NULL) { + DEBUG(1, ("Got NTLMSSP tArg without a client state\n")); + x_fprintf(x_stdout, "BH\n"); + ntlmssp_end(&client_ntlmssp_state); + return; } - if (request_lm_key - && (memcmp(zeros, lm_key, - sizeof(lm_key)) != 0)) { - hex_encode(lm_key, - sizeof(lm_key), - &hex_lm_key); - x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key); - SAFE_FREE(hex_lm_key); - } - if (request_nt_key - && (memcmp(zeros, nt_key, - sizeof(nt_key)) != 0)) { - hex_encode(nt_key, - sizeof(nt_key), - &hex_nt_key); - x_fprintf(x_stdout, "NT_KEY: %s\n", hex_nt_key); - SAFE_FREE(hex_nt_key); + if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) { + x_fprintf(x_stdout, "NA\n"); + ntlmssp_end(&client_ntlmssp_state); + return; } - return True; -} + if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) { + x_fprintf(x_stdout, "AF\n"); + ntlmssp_end(&client_ntlmssp_state); + return; + } -/* - Authenticate a user with a challenge/response, checking session key - and valid authentication types -*/ + status = ntlmssp_update(client_ntlmssp_state, + spnego.negTokenTarg.responseToken, + &request); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from " + "ntlmssp_client_update, got: %s\n", + nt_errstr(status))); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&request); + ntlmssp_end(&client_ntlmssp_state); + return; + } -static DATA_BLOB get_challenge(void) -{ - static DATA_BLOB chal; - if (opt_challenge.length) - return opt_challenge; + spnego.type = SPNEGO_NEG_TOKEN_TARG; + spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; + spnego.negTokenTarg.supportedMech = (char *)OID_NTLMSSP; + spnego.negTokenTarg.responseToken = request; + spnego.negTokenTarg.mechListMIC = null_blob; - chal = data_blob(NULL, 8); + write_spnego_data(&to_server, &spnego); + data_blob_free(&request); - generate_random_buffer(chal.data, chal.length, False); - return chal; + to_server_base64 = base64_encode_data_blob(to_server); + data_blob_free(&to_server); + x_fprintf(x_stdout, "KK %s\n", to_server_base64); + SAFE_FREE(to_server_base64); + return; } -/* - * Test LM authentication, no NT response supplied - */ +#ifdef HAVE_KRB5 -static BOOL test_lm(void) +static BOOL manage_client_krb5_init(SPNEGO_DATA spnego) { - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB lm_response = data_blob(NULL, 24); - - uchar lm_key[8]; - uchar nt_key[16]; - uchar lm_hash[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; + char *principal; + DATA_BLOB tkt, to_server; + DATA_BLOB session_key_krb5 = data_blob(NULL, 0); + SPNEGO_DATA reply; + char *reply_base64; + int retval; - ZERO_STRUCT(lm_key); - ZERO_STRUCT(nt_key); - - flags |= WBFLAG_PAM_LMKEY; - flags |= WBFLAG_PAM_NTKEY; + const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL}; + ssize_t len; - SMBencrypt(opt_password, chall.data, lm_response.data); - E_deshash(opt_password, lm_hash); + if ( (spnego.negTokenInit.mechListMIC.data == NULL) || + (spnego.negTokenInit.mechListMIC.length == 0) ) { + DEBUG(1, ("Did not get a principal for krb5\n")); + return False; + } - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, opt_workstation, - &chall, - &lm_response, - NULL, - flags, - lm_key, - nt_key, - &error_string); - - data_blob_free(&lm_response); + principal = SMB_MALLOC(spnego.negTokenInit.mechListMIC.length+1); - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); + if (principal == NULL) { + DEBUG(1, ("Could not malloc principal\n")); return False; } - if (memcmp(lm_hash, lm_key, - sizeof(lm_key)) != 0) { - DEBUG(1, ("LM Key does not match expectations!\n")); - DEBUG(1, ("lm_key:\n")); - dump_data(1, lm_key, 8); - DEBUG(1, ("expected:\n")); - dump_data(1, lm_hash, 8); - } - if (memcmp(lm_hash, nt_key, 8) != 0) { - DEBUG(1, ("Session Key (first 8, lm hash) does not match expectations!\n")); - DEBUG(1, ("nt_key:\n")); - dump_data(1, nt_key, 8); - DEBUG(1, ("expected:\n")); - dump_data(1, lm_hash, 8); - } - return True; -} + memcpy(principal, spnego.negTokenInit.mechListMIC.data, + spnego.negTokenInit.mechListMIC.length); + principal[spnego.negTokenInit.mechListMIC.length] = '\0'; -/* - * Test the normal 'LM and NTLM' combination - */ + retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL); -static BOOL test_lm_ntlm(void) -{ - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB lm_response = data_blob(NULL, 24); - DATA_BLOB nt_response = data_blob(NULL, 24); - DATA_BLOB session_key = data_blob(NULL, 16); - - uchar lm_key[8]; - uchar nt_key[16]; - uchar lm_hash[16]; - uchar nt_hash[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; - - ZERO_STRUCT(lm_key); - ZERO_STRUCT(nt_key); + if (retval) { - flags |= WBFLAG_PAM_LMKEY; - flags |= WBFLAG_PAM_NTKEY; + pstring user; - SMBencrypt(opt_password,chall.data,lm_response.data); - E_deshash(opt_password, lm_hash); + /* Let's try to first get the TGT, for that we need a + password. */ - SMBNTencrypt(opt_password,chall.data,nt_response.data); + if (opt_password == NULL) { + DEBUG(10, ("Requesting password\n")); + x_fprintf(x_stdout, "PW\n"); + return True; + } - E_md4hash(opt_password, nt_hash); - SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + pstr_sprintf(user, "%s@%s", opt_username, opt_domain); - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &lm_response, - &nt_response, - flags, - lm_key, - nt_key, - &error_string); - - data_blob_free(&lm_response); + if ((retval = kerberos_kinit_password(user, opt_password, + 0, NULL, NULL, NULL, False, 0))) { + DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval))); + return False; + } - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; + retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL); + + if (retval) { + DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval))); + return False; + } } - if (memcmp(lm_hash, lm_key, - sizeof(lm_key)) != 0) { - DEBUG(1, ("LM Key does not match expectations!\n")); - DEBUG(1, ("lm_key:\n")); - dump_data(1, lm_key, 8); - DEBUG(1, ("expected:\n")); - dump_data(1, lm_hash, 8); - pass = False; - } - if (memcmp(session_key.data, nt_key, - sizeof(nt_key)) != 0) { - DEBUG(1, ("NT Session Key does not match expectations!\n")); - DEBUG(1, ("nt_key:\n")); - dump_data(1, nt_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, session_key.data, session_key.length); - pass = False; - } - return pass; -} + data_blob_free(&session_key_krb5); -/* - * Test the NTLM response only, no LM. - */ + ZERO_STRUCT(reply); -static BOOL test_ntlm(void) -{ - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB nt_response = data_blob(NULL, 24); - DATA_BLOB session_key = data_blob(NULL, 16); + reply.type = SPNEGO_NEG_TOKEN_INIT; + reply.negTokenInit.mechTypes = my_mechs; + reply.negTokenInit.reqFlags = 0; + reply.negTokenInit.mechToken = tkt; + reply.negTokenInit.mechListMIC = data_blob(NULL, 0); - char lm_key[8]; - char nt_key[16]; - char lm_hash[16]; - char nt_hash[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; - - ZERO_STRUCT(lm_key); - ZERO_STRUCT(nt_key); - - flags |= WBFLAG_PAM_LMKEY; - flags |= WBFLAG_PAM_NTKEY; + len = write_spnego_data(&to_server, &reply); + data_blob_free(&tkt); - SMBNTencrypt(opt_password,chall.data,nt_response.data); - E_md4hash(opt_password, nt_hash); - SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + if (len == -1) { + DEBUG(1, ("Could not write SPNEGO data blob\n")); + return False; + } - E_deshash(opt_password, lm_hash); + reply_base64 = base64_encode_data_blob(to_server); + x_fprintf(x_stdout, "KK %s *\n", reply_base64); - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - NULL, - &nt_response, - flags, - lm_key, - nt_key, - &error_string); - - data_blob_free(&nt_response); + SAFE_FREE(reply_base64); + data_blob_free(&to_server); + DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n")); + return True; +} - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; +static void manage_client_krb5_targ(SPNEGO_DATA spnego) +{ + switch (spnego.negTokenTarg.negResult) { + case SPNEGO_ACCEPT_INCOMPLETE: + DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n")); + x_fprintf(x_stdout, "BH\n"); + break; + case SPNEGO_ACCEPT_COMPLETED: + DEBUG(10, ("Accept completed\n")); + x_fprintf(x_stdout, "AF\n"); + break; + case SPNEGO_REJECT: + DEBUG(10, ("Rejected\n")); + x_fprintf(x_stdout, "NA\n"); + break; + default: + DEBUG(1, ("Got an invalid negTokenTarg\n")); + x_fprintf(x_stdout, "AF\n"); } - - if (memcmp(lm_hash, lm_key, - sizeof(lm_key)) != 0) { - DEBUG(1, ("LM Key does not match expectations!\n")); - DEBUG(1, ("lm_key:\n")); - dump_data(1, lm_key, 8); - DEBUG(1, ("expected:\n")); - dump_data(1, lm_hash, 8); - pass = False; - } - if (memcmp(session_key.data, nt_key, - sizeof(nt_key)) != 0) { - DEBUG(1, ("NT Session Key does not match expectations!\n")); - DEBUG(1, ("nt_key:\n")); - dump_data(1, nt_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, session_key.data, session_key.length); - pass = False; - } - return pass; } -/* - * Test the NTLM response only, but in the LM field. - */ +#endif -static BOOL test_ntlm_in_lm(void) +static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length) { - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB nt_response = data_blob(NULL, 24); + DATA_BLOB request; + SPNEGO_DATA spnego; + ssize_t len; - uchar lm_key[8]; - uchar lm_hash[16]; - uchar nt_key[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; - - ZERO_STRUCT(nt_key); + if (strlen(buf) <= 3) { + DEBUG(1, ("SPNEGO query [%s] too short\n", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } - flags |= WBFLAG_PAM_LMKEY; - flags |= WBFLAG_PAM_NTKEY; + request = base64_decode_data_blob(buf+3); - SMBNTencrypt(opt_password,chall.data,nt_response.data); + if (strncmp(buf, "PW ", 3) == 0) { - E_deshash(opt_password, lm_hash); + /* We asked for a password and obviously got it :-) */ - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &nt_response, - NULL, - flags, - lm_key, - nt_key, - &error_string); - - data_blob_free(&nt_response); + opt_password = SMB_STRNDUP((const char *)request.data, request.length); + + if (opt_password == NULL) { + DEBUG(1, ("Out of memory\n")); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&request); + return; + } - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; + x_fprintf(x_stdout, "OK\n"); + data_blob_free(&request); + return; } - if (memcmp(lm_hash, lm_key, - sizeof(lm_key)) != 0) { - DEBUG(1, ("LM Key does not match expectations!\n")); - DEBUG(1, ("lm_key:\n")); - dump_data(1, lm_key, 8); - DEBUG(1, ("expected:\n")); - dump_data(1, lm_hash, 8); - pass = False; - } - if (memcmp(lm_hash, nt_key, 8) != 0) { - DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n")); - DEBUG(1, ("nt_key:\n")); - dump_data(1, nt_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, lm_hash, 8); - pass = False; - } - return pass; -} + if ( (strncmp(buf, "TT ", 3) != 0) && + (strncmp(buf, "AF ", 3) != 0) && + (strncmp(buf, "NA ", 3) != 0) ) { + DEBUG(1, ("SPNEGO request [%s] invalid\n", buf)); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&request); + return; + } -/* - * Test the NTLM response only, but in the both the NT and LM fields. - */ + /* So we got a server challenge to generate a SPNEGO + client-to-server request... */ -static BOOL test_ntlm_in_both(void) -{ - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB nt_response = data_blob(NULL, 24); - DATA_BLOB session_key = data_blob(NULL, 16); + len = read_spnego_data(request, &spnego); + data_blob_free(&request); - char lm_key[8]; - char lm_hash[16]; - char nt_key[16]; - char nt_hash[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; - - ZERO_STRUCT(lm_key); - ZERO_STRUCT(nt_key); + if (len == -1) { + DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } - flags |= WBFLAG_PAM_LMKEY; - flags |= WBFLAG_PAM_NTKEY; + if (spnego.type == SPNEGO_NEG_TOKEN_INIT) { - SMBNTencrypt(opt_password,chall.data,nt_response.data); - E_md4hash(opt_password, nt_hash); - SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + /* The server offers a list of mechanisms */ - E_deshash(opt_password, lm_hash); + const char **mechType = (const char **)spnego.negTokenInit.mechTypes; - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &nt_response, - &nt_response, - flags, - lm_key, - nt_key, - &error_string); - - data_blob_free(&nt_response); + while (*mechType != NULL) { - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; - } +#ifdef HAVE_KRB5 + if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) || + (strcmp(*mechType, OID_KERBEROS5) == 0) ) { + if (manage_client_krb5_init(spnego)) + goto out; + } +#endif - if (memcmp(lm_hash, lm_key, - sizeof(lm_key)) != 0) { - DEBUG(1, ("LM Key does not match expectations!\n")); - DEBUG(1, ("lm_key:\n")); - dump_data(1, lm_key, 8); - DEBUG(1, ("expected:\n")); - dump_data(1, lm_hash, 8); - pass = False; - } - if (memcmp(session_key.data, nt_key, - sizeof(nt_key)) != 0) { - DEBUG(1, ("NT Session Key does not match expectations!\n")); - DEBUG(1, ("nt_key:\n")); - dump_data(1, nt_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, session_key.data, session_key.length); - pass = False; - } + if (strcmp(*mechType, OID_NTLMSSP) == 0) { + if (manage_client_ntlmssp_init(spnego)) + goto out; + } + mechType++; + } - return pass; -} + DEBUG(1, ("Server offered no compatible mechanism\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } -/* - * Test the NTLMv2 response only - */ + if (spnego.type == SPNEGO_NEG_TOKEN_TARG) { + + if (spnego.negTokenTarg.supportedMech == NULL) { + /* On accept/reject Windows does not send the + mechanism anymore. Handle that here and + shut down the mechanisms. */ + + switch (spnego.negTokenTarg.negResult) { + case SPNEGO_ACCEPT_COMPLETED: + x_fprintf(x_stdout, "AF\n"); + break; + case SPNEGO_REJECT: + x_fprintf(x_stdout, "NA\n"); + break; + default: + DEBUG(1, ("Got a negTokenTarg with no mech and an " + "unknown negResult: %d\n", + spnego.negTokenTarg.negResult)); + x_fprintf(x_stdout, "BH\n"); + } -static BOOL test_ntlmv2(void) -{ - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB ntlmv2_response = data_blob(NULL, 0); - DATA_BLOB nt_session_key = data_blob(NULL, 0); - DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain()); + ntlmssp_end(&client_ntlmssp_state); + goto out; + } - uchar nt_key[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; + if (strcmp(spnego.negTokenTarg.supportedMech, + OID_NTLMSSP) == 0) { + manage_client_ntlmssp_targ(spnego); + goto out; + } - ZERO_STRUCT(nt_key); - - flags |= WBFLAG_PAM_NTKEY; +#if HAVE_KRB5 + if (strcmp(spnego.negTokenTarg.supportedMech, + OID_KERBEROS5_OLD) == 0) { + manage_client_krb5_targ(spnego); + goto out; + } +#endif - if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall, - &names_blob, - NULL, &ntlmv2_response, - &nt_session_key)) { - data_blob_free(&names_blob); - return False; } - data_blob_free(&names_blob); - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - NULL, - &ntlmv2_response, - flags, - NULL, - nt_key, - &error_string); - - data_blob_free(&ntlmv2_response); - - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; - } + DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf)); + x_fprintf(x_stdout, "BH\n"); + return; - if (memcmp(nt_session_key.data, nt_key, - sizeof(nt_key)) != 0) { - DEBUG(1, ("NT Session Key does not match expectations!\n")); - DEBUG(1, ("nt_key:\n")); - dump_data(1, nt_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, nt_session_key.data, nt_session_key.length); - pass = False; - } - return pass; + out: + free_spnego_data(&spnego); + return; } -/* - * Test the NTLMv2 and LMv2 responses - */ - -static BOOL test_lmv2_ntlmv2(void) +static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length) { - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB ntlmv2_response = data_blob(NULL, 0); - DATA_BLOB lmv2_response = data_blob(NULL, 0); - DATA_BLOB nt_session_key = data_blob(NULL, 0); - DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain()); + char *request, *parameter; + static DATA_BLOB challenge; + static DATA_BLOB lm_response; + static DATA_BLOB nt_response; + static char *full_username; + static char *username; + static char *domain; + static char *plaintext_password; + static BOOL ntlm_server_1_user_session_key; + static BOOL ntlm_server_1_lm_session_key; + + if (strequal(buf, ".")) { + if (!full_username && !username) { + x_fprintf(x_stdout, "Error: No username supplied!\n"); + } else if (plaintext_password) { + /* handle this request as plaintext */ + if (!full_username) { + if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) { + x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n"); + return; + } + } + if (check_plaintext_auth(full_username, plaintext_password, False)) { + x_fprintf(x_stdout, "Authenticated: Yes\n"); + } else { + x_fprintf(x_stdout, "Authenticated: No\n"); + } + } else if (!lm_response.data && !nt_response.data) { + x_fprintf(x_stdout, "Error: No password supplied!\n"); + } else if (!challenge.data) { + x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n"); + } else { + char *error_string = NULL; + uchar lm_key[8]; + uchar user_session_key[16]; + uint32 flags = 0; + + if (full_username && !username) { + fstring fstr_user; + fstring fstr_domain; + + if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) { + /* username might be 'tainted', don't print into our new-line deleimianted stream */ + x_fprintf(x_stdout, "Error: Could not parse into domain and username\n"); + } + SAFE_FREE(username); + SAFE_FREE(domain); + username = smb_xstrdup(fstr_user); + domain = smb_xstrdup(fstr_domain); + } - uchar nt_key[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; + if (!domain) { + domain = smb_xstrdup(get_winbind_domain()); + } - ZERO_STRUCT(nt_key); - - flags |= WBFLAG_PAM_NTKEY; + if (ntlm_server_1_lm_session_key) + flags |= WBFLAG_PAM_LMKEY; + + if (ntlm_server_1_user_session_key) + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + if (!NT_STATUS_IS_OK( + contact_winbind_auth_crap(username, + domain, + global_myname(), + &challenge, + &lm_response, + &nt_response, + flags, + lm_key, + user_session_key, + &error_string, + NULL))) { + + x_fprintf(x_stdout, "Authenticated: No\n"); + x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string); + SAFE_FREE(error_string); + } else { + static char zeros[16]; + char *hex_lm_key; + char *hex_user_session_key; + + x_fprintf(x_stdout, "Authenticated: Yes\n"); + + if (ntlm_server_1_lm_session_key + && (memcmp(zeros, lm_key, + sizeof(lm_key)) != 0)) { + hex_lm_key = hex_encode(NULL, + (const unsigned char *)lm_key, + sizeof(lm_key)); + x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key); + TALLOC_FREE(hex_lm_key); + } + + if (ntlm_server_1_user_session_key + && (memcmp(zeros, user_session_key, + sizeof(user_session_key)) != 0)) { + hex_user_session_key = hex_encode(NULL, + (const unsigned char *)user_session_key, + sizeof(user_session_key)); + x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key); + TALLOC_FREE(hex_user_session_key); + } + } + } + /* clear out the state */ + challenge = data_blob(NULL, 0); + nt_response = data_blob(NULL, 0); + lm_response = data_blob(NULL, 0); + SAFE_FREE(full_username); + SAFE_FREE(username); + SAFE_FREE(domain); + SAFE_FREE(plaintext_password); + ntlm_server_1_user_session_key = False; + ntlm_server_1_lm_session_key = False; + x_fprintf(x_stdout, ".\n"); - if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall, - &names_blob, - &lmv2_response, &ntlmv2_response, - &nt_session_key)) { - data_blob_free(&names_blob); - return False; + return; } - data_blob_free(&names_blob); - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &lmv2_response, - &ntlmv2_response, - flags, - NULL, - nt_key, - &error_string); - - data_blob_free(&lmv2_response); - data_blob_free(&ntlmv2_response); + request = buf; - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; - } + /* Indicates a base64 encoded structure */ + parameter = strstr_m(request, ":: "); + if (!parameter) { + parameter = strstr_m(request, ": "); + + if (!parameter) { + DEBUG(0, ("Parameter not found!\n")); + x_fprintf(x_stdout, "Error: Parameter not found!\n.\n"); + return; + } + + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; - if (memcmp(nt_session_key.data, nt_key, - sizeof(nt_key)) != 0) { - DEBUG(1, ("NT Session Key does not match expectations!\n")); - DEBUG(1, ("nt_key:\n")); - dump_data(1, nt_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, nt_session_key.data, nt_session_key.length); - pass = False; + } else { + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + + base64_decode_inplace(parameter); + } + + if (strequal(request, "LANMAN-Challenge")) { + challenge = strhex_to_data_blob(NULL, parameter); + if (challenge.length != 8) { + x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n", + parameter, + (int)challenge.length); + challenge = data_blob(NULL, 0); + } + } else if (strequal(request, "NT-Response")) { + nt_response = strhex_to_data_blob(NULL, parameter); + if (nt_response.length < 24) { + x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n", + parameter, + (int)nt_response.length); + nt_response = data_blob(NULL, 0); + } + } else if (strequal(request, "LANMAN-Response")) { + lm_response = strhex_to_data_blob(NULL, parameter); + if (lm_response.length != 24) { + x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n", + parameter, + (int)lm_response.length); + lm_response = data_blob(NULL, 0); + } + } else if (strequal(request, "Password")) { + plaintext_password = smb_xstrdup(parameter); + } else if (strequal(request, "NT-Domain")) { + domain = smb_xstrdup(parameter); + } else if (strequal(request, "Username")) { + username = smb_xstrdup(parameter); + } else if (strequal(request, "Full-Username")) { + full_username = smb_xstrdup(parameter); + } else if (strequal(request, "Request-User-Session-Key")) { + ntlm_server_1_user_session_key = strequal(parameter, "Yes"); + } else if (strequal(request, "Request-LanMan-Session-Key")) { + ntlm_server_1_lm_session_key = strequal(parameter, "Yes"); + } else { + x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request); } - return pass; } -/* - * Test the LMv2 response only - */ - -static BOOL test_lmv2(void) +static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) { - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB lmv2_response = data_blob(NULL, 0); - - DATA_BLOB chall = get_challenge(); - char *error_string; + char buf[SQUID_BUFFER_SIZE+1]; + int length; + char *c; + static BOOL err; - if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall, - NULL, - &lmv2_response, NULL, - NULL)) { - return False; + /* this is not a typo - x_fgets doesn't work too well under squid */ + if (fgets(buf, sizeof(buf)-1, stdin) == NULL) { + if (ferror(stdin)) { + DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin), + strerror(ferror(stdin)))); + + exit(1); /* BIIG buffer */ + } + exit(0); } - - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &lmv2_response, - NULL, - flags, - NULL, - NULL, - &error_string); - - data_blob_free(&lmv2_response); - - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; + + c=memchr(buf,'\n',sizeof(buf)-1); + if (c) { + *c = '\0'; + length = c-buf; + } else { + err = 1; + return; + } + if (err) { + DEBUG(2, ("Oversized message\n")); + x_fprintf(x_stderr, "ERR\n"); + err = 0; + return; } - return pass; -} - -/* - * Test the normal 'LM and NTLM' combination but deliberately break one - */ + DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length)); -static BOOL test_ntlm_broken(BOOL break_lm) -{ - BOOL pass = True; - NTSTATUS nt_status; - uint32 flags = 0; - DATA_BLOB lm_response = data_blob(NULL, 24); - DATA_BLOB nt_response = data_blob(NULL, 24); - DATA_BLOB session_key = data_blob(NULL, 16); - - uchar lm_key[8]; - uchar nt_key[16]; - uchar lm_hash[16]; - uchar nt_hash[16]; - DATA_BLOB chall = get_challenge(); - char *error_string; + if (buf[0] == '\0') { + DEBUG(2, ("Invalid Request\n")); + x_fprintf(x_stderr, "ERR\n"); + return; + } - ZERO_STRUCT(lm_key); - ZERO_STRUCT(nt_key); - - flags |= WBFLAG_PAM_LMKEY; - flags |= WBFLAG_PAM_NTKEY; - - SMBencrypt(opt_password,chall.data,lm_response.data); - E_deshash(opt_password, lm_hash); - - SMBNTencrypt(opt_password,chall.data,nt_response.data); - - E_md4hash(opt_password, nt_hash); - SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); - - if (break_lm) - lm_response.data[0]++; - else - nt_response.data[0]++; + fn(helper_mode, buf, length); +} - nt_status = contact_winbind_auth_crap(opt_username, opt_domain, - opt_workstation, - &chall, - &lm_response, - &nt_response, - flags, - lm_key, - nt_key, - &error_string); - - data_blob_free(&lm_response); - if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); - SAFE_FREE(error_string); - return False; +static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) { + /* initialize FDescs */ + x_setbuf(x_stdout, NULL); + x_setbuf(x_stderr, NULL); + while(1) { + manage_squid_request(stdio_mode, fn); } - - if (memcmp(lm_hash, lm_key, - sizeof(lm_key)) != 0) { - DEBUG(1, ("LM Key does not match expectations!\n")); - DEBUG(1, ("lm_key:\n")); - dump_data(1, lm_key, 8); - DEBUG(1, ("expected:\n")); - dump_data(1, lm_hash, 8); - pass = False; - } - if (memcmp(session_key.data, nt_key, - sizeof(nt_key)) != 0) { - DEBUG(1, ("NT Session Key does not match expectations!\n")); - DEBUG(1, ("nt_key:\n")); - dump_data(1, nt_key, 16); - DEBUG(1, ("expected:\n")); - dump_data(1, session_key.data, session_key.length); - pass = False; - } - return pass; } -static BOOL test_ntlm_lm_broken(void) -{ - return test_ntlm_broken(True); -} -static BOOL test_ntlm_ntlm_broken(void) -{ - return test_ntlm_broken(False); -} +/* Authenticate a user with a challenge/response */ -static BOOL test_ntlmv2_broken(BOOL break_lmv2) +static BOOL check_auth_crap(void) { - BOOL pass = True; NTSTATUS nt_status; uint32 flags = 0; - DATA_BLOB ntlmv2_response = data_blob(NULL, 0); - DATA_BLOB lmv2_response = data_blob(NULL, 0); - DATA_BLOB nt_session_key = data_blob(NULL, 0); - DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain()); - - uchar nt_key[16]; - DATA_BLOB chall = get_challenge(); + char lm_key[8]; + char user_session_key[16]; + char *hex_lm_key; + char *hex_user_session_key; char *error_string; + static uint8 zeros[16]; - ZERO_STRUCT(nt_key); - - flags |= WBFLAG_PAM_NTKEY; - - if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall, - &names_blob, - &lmv2_response, &ntlmv2_response, - &nt_session_key)) { - data_blob_free(&names_blob); - return False; - } - data_blob_free(&names_blob); + x_setbuf(x_stdout, NULL); - /* Heh - this should break the appropriate password hash nicely! */ + if (request_lm_key) + flags |= WBFLAG_PAM_LMKEY; - if (break_lmv2) - lmv2_response.data[0]++; - else - ntlmv2_response.data[0]++; + if (request_user_session_key) + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + flags |= WBFLAG_PAM_NT_STATUS_SQUASH; nt_status = contact_winbind_auth_crap(opt_username, opt_domain, opt_workstation, - &chall, - &lmv2_response, - &ntlmv2_response, + &opt_challenge, + &opt_lm_response, + &opt_nt_response, flags, - NULL, - nt_key, - &error_string); - - data_blob_free(&lmv2_response); - data_blob_free(&ntlmv2_response); + (unsigned char *)lm_key, + (unsigned char *)user_session_key, + &error_string, NULL); if (!NT_STATUS_IS_OK(nt_status)) { - d_printf("%s (0x%x)\n", - error_string, - NT_STATUS_V(nt_status)); + x_fprintf(x_stdout, "%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); SAFE_FREE(error_string); return False; } - return pass; -} - -static BOOL test_ntlmv2_lmv2_broken(void) -{ - return test_ntlmv2_broken(True); -} - -static BOOL test_ntlmv2_ntlmv2_broken(void) -{ - return test_ntlmv2_broken(False); -} - -/* - Tests: - - - LM only - - NT and LM - - NT - - NT in LM field - - NT in both fields - - NTLMv2 - - NTLMv2 and LMv2 - - LMv2 - - check we get the correct session key in each case - check what values we get for the LM session key - -*/ - -struct ntlm_tests { - BOOL (*fn)(void); - const char *name; -} test_table[] = { - {test_lm, "LM"}, - {test_lm_ntlm, "LM and NTLM"}, - {test_ntlm, "NTLM"}, - {test_ntlm_in_lm, "NTLM in LM"}, - {test_ntlm_in_both, "NTLM in both"}, - {test_ntlmv2, "NTLMv2"}, - {test_lmv2_ntlmv2, "NTLMv2 and LMv2"}, - {test_lmv2, "LMv2"}, - {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"}, - {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"}, - {test_ntlm_lm_broken, "NTLM and LM, LM broken"}, - {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"} -}; - -static BOOL diagnose_ntlm_auth(void) -{ - unsigned int i; - BOOL pass = True; - - for (i=0; test_table[i].fn; i++) { - if (!test_table[i].fn()) { - DEBUG(1, ("Test %s failed!\n", test_table[i].name)); - pass = False; - } + if (request_lm_key + && (memcmp(zeros, lm_key, + sizeof(lm_key)) != 0)) { + hex_lm_key = hex_encode(NULL, (const unsigned char *)lm_key, + sizeof(lm_key)); + x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key); + TALLOC_FREE(hex_lm_key); + } + if (request_user_session_key + && (memcmp(zeros, user_session_key, + sizeof(user_session_key)) != 0)) { + hex_user_session_key = hex_encode(NULL, (const unsigned char *)user_session_key, + sizeof(user_session_key)); + x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key); + TALLOC_FREE(hex_user_session_key); } - return pass; + return True; } /* Main program */ @@ -1431,8 +1708,9 @@ enum { OPT_NT, OPT_PASSWORD, OPT_LM_KEY, - OPT_NT_KEY, - OPT_DIAGNOSTICS + OPT_USER_SESSION_KEY, + OPT_DIAGNOSTICS, + OPT_REQUIRE_MEMBERSHIP }; int main(int argc, const char **argv) @@ -1444,12 +1722,6 @@ enum { static const char *hex_challenge; static const char *hex_lm_response; static const char *hex_nt_response; - char *challenge; - char *lm_response; - char *nt_response; - size_t challenge_len; - size_t lm_response_len; - size_t nt_response_len; poptContext pc; @@ -1471,21 +1743,23 @@ enum { { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"}, { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"}, { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"}, - { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"}, - { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"}, + { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"}, + { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"}, { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"}, + { "require-membership-of", 0, POPT_ARG_STRING, &require_membership_of, OPT_REQUIRE_MEMBERSHIP, "Require that a user be a member of this group (either name or SID) for authentication to succeed" }, POPT_COMMON_SAMBA POPT_TABLEEND }; /* Samba client initialisation */ + load_case_tables(); dbf = x_stderr; /* Samba client initialisation */ - if (!lp_load(dyn_CONFIGFILE, True, False, False)) { - d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n", + if (!lp_load(dyn_CONFIGFILE, True, False, False, True)) { + d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n", dyn_CONFIGFILE, strerror(errno)); exit(1); } @@ -1507,57 +1781,57 @@ enum { while((opt = poptGetNextOpt(pc)) != -1) { switch (opt) { case OPT_CHALLENGE: - challenge = smb_xmalloc((strlen(hex_challenge))/2+1); - if ((challenge_len = strhex_to_str(challenge, - strlen(hex_challenge), - hex_challenge)) != 8) { - x_fprintf(x_stderr, "hex decode of %s failed (only got %u bytes)!\n", - hex_challenge, challenge_len); + opt_challenge = strhex_to_data_blob(NULL, hex_challenge); + if (opt_challenge.length != 8) { + x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", + hex_challenge, + (int)opt_challenge.length); exit(1); } - opt_challenge = data_blob(challenge, challenge_len); - SAFE_FREE(challenge); break; case OPT_LM: - lm_response = smb_xmalloc((strlen(hex_lm_response))/2+1); - lm_response_len = strhex_to_str(lm_response, - strlen(hex_lm_response), - hex_lm_response); - if (lm_response_len != 24) { - x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response); + opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response); + if (opt_lm_response.length != 24) { + x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", + hex_lm_response, + (int)opt_lm_response.length); exit(1); } - opt_lm_response = data_blob(lm_response, lm_response_len); - SAFE_FREE(lm_response); break; + case OPT_NT: - nt_response = smb_xmalloc((strlen(hex_nt_response)+2)/2+1); - nt_response_len = strhex_to_str(nt_response, - strlen(hex_nt_response), - hex_nt_response); - if (nt_response_len < 24) { - x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response); + opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response); + if (opt_nt_response.length < 24) { + x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", + hex_nt_response, + (int)opt_nt_response.length); exit(1); } - opt_nt_response = data_blob(nt_response, nt_response_len); - SAFE_FREE(nt_response); + break; + + case OPT_REQUIRE_MEMBERSHIP: + if (StrnCaseCmp("S-", require_membership_of, 2) == 0) { + require_membership_of_sid = require_membership_of; + } break; } } if (helper_protocol) { - if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) { - squid_stream(SQUID_2_5_NTLMSSP); - } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) { - squid_stream(SQUID_2_5_BASIC); - } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) { - squid_stream(SQUID_2_4_BASIC); - } else if (strcmp(helper_protocol, "gss-spnego")== 0) { - squid_stream(GSS_SPNEGO); - } else { - x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol); - exit(1); + int i; + for (i=0; i