From 6dce8c678a806add23c9bc05be65a050f7fedf0a Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 7 Apr 2003 07:10:53 +0000 Subject: Merge the ntlm_auth updates (refactor, add --diagnostics) into Samba 3.0 Andrew Bartlett (This used to be commit d711a1c95c92f5a89b43bf29bba8460b870d3b3a) --- source3/utils/ntlm_auth.c | 546 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 425 insertions(+), 121 deletions(-) (limited to 'source3/utils/ntlm_auth.c') diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c index ac456769f2..0bead042fd 100644 --- a/source3/utils/ntlm_auth.c +++ b/source3/utils/ntlm_auth.c @@ -4,7 +4,7 @@ Winbind status program. Copyright (C) Tim Potter 2000-2002 - Copyright (C) Andrew Bartlett 2003 + Copyright (C) Andrew Bartlett 2003 Copyright (C) Francesco Chemolli 2000 This program is free software; you can redistribute it and/or modify @@ -39,22 +39,17 @@ enum squid_mode { extern int winbindd_fd; static const char *helper_protocol; -static const char *username; -static const char *domain; -static const char *workstation; -static const char *hex_challenge; -static const char *hex_lm_response; -static const char *hex_nt_response; -static unsigned char *challenge; -static size_t challenge_len; -static unsigned char *lm_response; -static size_t lm_response_len; -static unsigned char *nt_response; -static size_t nt_response_len; +static const char *opt_username; +static const char *opt_domain; +static const char *opt_workstation; +static 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 diagnostics; -static char *password; static char winbind_separator(void) { @@ -162,58 +157,110 @@ static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout d_printf("Reading winbind reply failed! (0x01)\n"); } - d_printf("%s (0x%x)\n", + d_printf("%s: %s (0x%x)\n", response.data.auth.nt_status_string, + response.data.auth.error_string, response.data.auth.nt_status); } else { if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) { DEBUG(1, ("Reading winbind reply failed! (0x01)\n")); } - DEBUG(3, ("%s (0x%x)\n", - response.data.auth.nt_status_string, - response.data.auth.nt_status)); + DEBUG(3, ("%s: %s (0x%x)\n", + response.data.auth.nt_status_string, + response.data.auth.error_string, + response.data.auth.nt_status)); } return (result == NSS_STATUS_SUCCESS); } -static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state) +/* 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[16], + uint8 nt_key[16], + char **error_string) { + NTSTATUS nt_status; + NSS_STATUS result; struct winbindd_request request; struct winbindd_response response; - NSS_STATUS result; - /* Send off request */ + + static uint8 zeros[16]; ZERO_STRUCT(request); ZERO_STRUCT(response); - fstrcpy(request.data.auth_crap.user, ntlmssp_state->user); + request.data.auth_crap.flags = flags; - fstrcpy(request.data.auth_crap.domain, ntlmssp_state->domain); - fstrcpy(request.data.auth_crap.workstation, ntlmssp_state->workstation); - - memcpy(request.data.auth_crap.chal, ntlmssp_state->chal.data, - MIN(ntlmssp_state->chal.length, 8)); - - memcpy(request.data.auth_crap.lm_resp, ntlmssp_state->lm_resp.data, - MIN(ntlmssp_state->lm_resp.length, sizeof(request.data.auth_crap.lm_resp))); - - memcpy(request.data.auth_crap.nt_resp, ntlmssp_state->nt_resp.data, - MIN(ntlmssp_state->nt_resp.length, sizeof(request.data.auth_crap.nt_resp))); - - request.data.auth_crap.lm_resp_len = ntlmssp_state->lm_resp.length; - request.data.auth_crap.nt_resp_len = ntlmssp_state->nt_resp.length; + fstrcpy(request.data.auth_crap.user, username); + + fstrcpy(request.data.auth_crap.domain, domain); + 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))); + 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))); + request.data.auth_crap.nt_resp_len = nt_response->length; + } + result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response); /* Display response */ if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) { - return NT_STATUS_UNSUCCESSFUL; + nt_status = NT_STATUS_UNSUCCESSFUL; + if (error_string) + *error_string = smb_xstrdup("Reading winbind reply failed!"); + return nt_status; + } + + nt_status = (NT_STATUS(response.data.auth.nt_status)); + if (!NT_STATUS_IS_OK(nt_status)) { + if (error_string) + *error_string = smb_xstrdup(response.data.auth.error_string); + return nt_status; } - return NT_STATUS(response.data.auth.nt_status); + if ((flags & WINBIND_PAM_LMKEY) && lm_key + && (memcmp(zeros, response.data.auth.first_8_lm_hash, + sizeof(response.data.auth.first_8_lm_hash)) != 0)) { + memcpy(lm_key, response.data.auth.first_8_lm_hash, + sizeof(response.data.auth.first_8_lm_hash)); + } + if ((flags & WINBIND_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)); + } + return nt_status; +} + +static NTSTATUS winbind_pw_check(struct 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); } static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, @@ -356,72 +403,283 @@ static void squid_stream(enum squid_mode squid_mode) { static BOOL check_auth_crap(void) { - struct winbindd_request request; - struct winbindd_response response; - char *lm_key; - char *nt_key; + 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]; - NSS_STATUS result; - /* Send off request */ - - ZERO_STRUCT(request); - ZERO_STRUCT(response); - if (request_lm_key) - request.data.auth_crap.flags |= WINBIND_PAM_LMKEY; + flags |= WINBIND_PAM_LMKEY; if (request_nt_key) - request.data.auth_crap.flags |= WINBIND_PAM_NTKEY; + flags |= WINBIND_PAM_NTKEY; + + 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); + + 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; + } - fstrcpy(request.data.auth_crap.user, username); + if (request_lm_key + && (memcmp(zeros, lm_key, + sizeof(lm_key)) != 0)) { + hex_encode(lm_key, + sizeof(lm_key), + &hex_lm_key); + d_printf("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); + d_printf("NT_KEY: %s\n", hex_nt_key); + SAFE_FREE(hex_nt_key); + } - fstrcpy(request.data.auth_crap.domain, domain); - fstrcpy(request.data.auth_crap.workstation, workstation); + return True; +} + +/* + Authenticate a user with a challenge/response, checking session key + and valid authentication types +*/ + +static const DATA_BLOB get_challenge(void) +{ + static DATA_BLOB chal; + if (opt_challenge.length) + return opt_challenge; - memcpy(request.data.auth_crap.chal, challenge, MIN(challenge_len, 8)); + chal = data_blob(NULL, 8); - memcpy(request.data.auth_crap.lm_resp, lm_response, MIN(lm_response_len, sizeof(request.data.auth_crap.lm_resp))); - - memcpy(request.data.auth_crap.nt_resp, nt_response, MIN(nt_response_len, sizeof(request.data.auth_crap.nt_resp))); - - request.data.auth_crap.lm_resp_len = lm_response_len; - request.data.auth_crap.nt_resp_len = nt_response_len; + generate_random_buffer(chal.data, chal.length, False); + return chal; +} - result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response); +static BOOL test_lm(void) +{ + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB lm_response = data_blob(NULL, 24); - /* Display response */ + uchar lm_key[8]; + uchar lm_hash[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + flags |= WINBIND_PAM_LMKEY; + + SMBencrypt(opt_password,chall.data,lm_response.data); + E_deshash(opt_password, lm_hash); + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, opt_workstation, + &chall, + &lm_response, + NULL, + flags, + lm_key, + NULL, + &error_string); + + data_blob_free(&lm_response); - if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) { - d_printf("Reading winbind reply failed! (0x01)\n"); - } - - d_printf("%s (0x%x)\n", - response.data.auth.nt_status_string, - response.data.auth.nt_status); - - if (response.data.auth.nt_status == 0) { - if (request_lm_key - && (memcmp(zeros, response.data.auth.first_8_lm_hash, - sizeof(response.data.auth.first_8_lm_hash)) != 0)) { - hex_encode(response.data.auth.first_8_lm_hash, - sizeof(response.data.auth.first_8_lm_hash), - &lm_key); - d_printf("LM_KEY: %s\n", lm_key); - SAFE_FREE(lm_key); - } - if (request_nt_key - && (memcmp(zeros, response.data.auth.nt_session_key, - sizeof(response.data.auth.nt_session_key)) != 0)) { - hex_encode(response.data.auth.nt_session_key, - sizeof(response.data.auth.nt_session_key), - &nt_key); - d_printf("NT_KEY: %s\n", nt_key); - SAFE_FREE(nt_key); + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + 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); + } + return True; +} + +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; + + flags |= WINBIND_PAM_LMKEY; + flags |= WINBIND_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); + + 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; + } + + 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(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); + + char nt_key[16]; + char nt_hash[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + flags |= WINBIND_PAM_NTKEY; + + SMBNTencrypt(opt_password,chall.data,nt_response.data); + E_md4hash(opt_password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + NULL, + &nt_response, + flags, + NULL, + nt_key, + &error_string); + + data_blob_free(&nt_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; + } + + 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; +} + +/* + Tests: + + - LM only + - NT and LM + - NT + - 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)(); + const char *name; +} test_table[] = { + {test_lm, "test LM"}, + {test_lm_ntlm, "test LM and NTLM"}, + {test_ntlm, "test NTLM"} +/* {test_lm_ntlmv2, "test NTLMv2"}, */ +/* {test_lm_ntlmv2, "test NTLMv2 and LMv2"}, */ +/* {test_lm_ntlmv2, "test LMv2"} */ +}; + +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; } } - return result == NSS_STATUS_SUCCESS; + return pass; } /* Main program */ @@ -436,31 +694,48 @@ enum { OPT_NT, OPT_PASSWORD, OPT_LM_KEY, - OPT_NT_KEY + OPT_NT_KEY, + OPT_DIAGNOSTICS }; -int main(int argc, const char **argv) + int main(int argc, const char **argv) { int opt; + 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; + + /* NOTE: DO NOT change this interface without considering the implications! + This is an external interface, which other programs will use to interact + with this helper. + */ + + /* We do not use single-letter command abbreviations, because they harm future + interface stability. */ + struct poptOption long_options[] = { POPT_AUTOHELP - { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"}, - { "username", 0, POPT_ARG_STRING, &username, OPT_USERNAME, "username"}, - { "domain", 0, POPT_ARG_STRING, &domain, OPT_DOMAIN, "domain name"}, - { "workstation", 0, POPT_ARG_STRING, &domain, OPT_WORKSTATION, "workstation"}, + { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"}, + { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"}, + { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"}, { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"}, { "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, &password, OPT_PASSWORD, "User's plaintext password"}, + { "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"}, - { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug }, - { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile }, - { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version}, - { 0, 0, 0, 0 } + { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"}, + { NULL, 0, 0, NULL, 0, NULL} }; /* Samba client initialisation */ @@ -484,29 +759,40 @@ int main(int argc, const char **argv) while((opt = poptGetNextOpt(pc)) != -1) { switch (opt) { case OPT_CHALLENGE: - challenge_len = strlen(hex_challenge); - challenge = smb_xmalloc((challenge_len+1)/2); - if ((challenge_len = strhex_to_str(challenge, challenge_len, hex_challenge)) != 8) { - fprintf(stderr, "hex decode of %s failed (only got %u bytes)!\n", + challenge = smb_xmalloc((strlen(hex_challenge)+1)/2); + 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); exit(1); } + opt_challenge = data_blob(challenge, challenge_len); + SAFE_FREE(challenge); break; case OPT_LM: - lm_response_len = strlen(hex_lm_response); - lm_response = smb_xmalloc((lm_response_len+1)/2); - if ((lm_response_len = strhex_to_str(lm_response, lm_response_len, hex_lm_response)) != 24) { - fprintf(stderr, "hex decode of %s failed!\n", hex_lm_response); + lm_response = smb_xmalloc((strlen(hex_lm_response)+1)/2); + 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); exit(1); } + opt_lm_response = data_blob(lm_response, lm_response_len); + SAFE_FREE(lm_response); break; case OPT_NT: - nt_response_len = strlen(hex_nt_response); - nt_response = smb_xmalloc((nt_response_len+1)/2); - if ((nt_response_len = strhex_to_str(nt_response, nt_response_len, hex_nt_response)) < 24) { - fprintf(stderr, "hex decode of %s failed!\n", hex_nt_response); + nt_response = smb_xmalloc((strlen(hex_nt_response)+1)/2); + 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); exit(1); } + opt_nt_response = data_blob(nt_response, nt_response_len); + SAFE_FREE(nt_response); break; } } @@ -519,27 +805,45 @@ int main(int argc, const char **argv) } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) { squid_stream(SQUID_2_4_BASIC); } else { - fprintf(stderr, "unknown helper protocol [%s]\n", helper_protocol); + x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol); exit(1); } } - if (domain == NULL) { - domain = get_winbind_domain(); + if (!opt_username) { + x_fprintf(x_stderr, "username must be specified!\n\n"); + poptPrintHelp(pc, stderr, 0); + exit(1); + } + + if (opt_domain == NULL) { + opt_domain = get_winbind_domain(); } - if (workstation == NULL) { - workstation = ""; + if (opt_workstation == NULL) { + opt_workstation = ""; } - if (challenge) { + if (opt_challenge.length) { if (!check_auth_crap()) { exit(1); } - } else if (password) { + exit(0); + } + + if (!opt_password) { + opt_password = getpass("password: "); + } + + if (diagnostics) { + if (!diagnose_ntlm_auth()) { + exit(1); + } + } else { fstring user; - snprintf(user, sizeof(user)-1, "%s%c%s", domain, winbind_separator(), username); - if (!check_plaintext_auth(user, password, True)) { + + snprintf(user, sizeof(user)-1, "%s%c%s", opt_domain, winbind_separator(), opt_username); + if (!check_plaintext_auth(user, opt_password, True)) { exit(1); } } -- cgit