mech-ntlm.c revision 8d6cb44a0161d88743756733f83c4fb278485987
2ronwalf/*
2ronwalf * NTLM and NTLMv2 authentication mechanism.
2ronwalf *
2ronwalf * Copyright (c) 2004 Andrey Panin <pazke@donpac.ru>
2ronwalf *
2ronwalf * This program is free software; you can redistribute it and/or modify
2ronwalf * it under the terms of the GNU Lesser General Public License as published
2ronwalf * by the Free Software Foundation; either version 2 of the License, or
2ronwalf * (at your option) any later version.
2ronwalf */
2ronwalf
2ronwalf#include "common.h"
2ronwalf#include "mech.h"
2ronwalf#include "passdb.h"
2ronwalf#include "str.h"
2ronwalf#include "buffer.h"
2ronwalf#include "hex-binary.h"
2ronwalf#include "safe-memset.h"
2ronwalf
2ronwalf#include "ntlm.h"
2ronwalf
2ronwalfstruct ntlm_auth_request {
2ronwalf struct auth_request auth_request;
2ronwalf
2ronwalf pool_t pool;
2ronwalf
2ronwalf /* requested: */
2ronwalf bool ntlm2_negotiated;
2ronwalf bool unicode_negotiated;
2ronwalf const unsigned char *challenge;
2ronwalf
2ronwalf /* received: */
2ronwalf struct ntlmssp_response *response;
2ronwalf};
2ronwalf
2ronwalfstatic int lm_verify_credentials(struct ntlm_auth_request *request,
2ronwalf const char *credentials)
2ronwalf{
2ronwalf const unsigned char *client_response;
2ronwalf unsigned char lm_response[LM_RESPONSE_SIZE];
2ronwalf unsigned char hash[LM_HASH_SIZE];
2ronwalf unsigned int response_length;
2ronwalf buffer_t *hash_buffer;
2ronwalf
2ronwalf response_length =
2ronwalf ntlmssp_buffer_length(request->response, lm_response);
2ronwalf client_response = ntlmssp_buffer_data(request->response, lm_response);
2ronwalf
2ronwalf if (response_length < LM_RESPONSE_SIZE) {
2ronwalf auth_request_log_error(&request->auth_request, "ntlm",
2ronwalf "LM response length is too small");
2ronwalf return FALSE;
2ronwalf }
2ronwalf
2ronwalf hash_buffer = buffer_create_data(request->auth_request.pool,
2ronwalf hash, sizeof(hash));
2ronwalf if (hex_to_binary(credentials, hash_buffer) < 0) {
2ronwalf auth_request_log_error(&request->auth_request, "ntlm",
2ronwalf "passdb credentials are not in hex");
2ronwalf return FALSE;
2ronwalf }
2ronwalf
2ronwalf ntlmssp_v1_response(hash, request->challenge, lm_response);
2ronwalf return memcmp(lm_response, client_response, LM_RESPONSE_SIZE) == 0;
2ronwalf}
2ronwalf
2ronwalfstatic void
2ronwalflm_credentials_callback(enum passdb_result result,
2ronwalf const char *credentials,
2ronwalf struct auth_request *auth_request)
2ronwalf{
2ronwalf struct ntlm_auth_request *request =
2ronwalf (struct ntlm_auth_request *)auth_request;
2ronwalf
2ronwalf switch (result) {
2ronwalf case PASSDB_RESULT_OK:
2ronwalf if (lm_verify_credentials(request, credentials))
2ronwalf auth_request_success(auth_request, NULL, 0);
2ronwalf else
2ronwalf auth_request_fail(auth_request);
2ronwalf break;
2ronwalf case PASSDB_RESULT_INTERNAL_FAILURE:
2ronwalf auth_request_internal_failure(auth_request);
2ronwalf break;
2ronwalf default:
2ronwalf auth_request_fail(auth_request);
2ronwalf break;
2ronwalf }
2ronwalf}
2ronwalf
2ronwalfstatic int ntlm_verify_credentials(struct ntlm_auth_request *request,
2ronwalf const char *credentials)
2ronwalf{
2ronwalf struct auth_request *auth_request = &request->auth_request;
2ronwalf const unsigned char *client_response;
2ronwalf unsigned char hash[NTLMSSP_HASH_SIZE];
2ronwalf unsigned int response_length;
2ronwalf buffer_t *hash_buffer;
2ronwalf
2ronwalf response_length =
2ronwalf ntlmssp_buffer_length(request->response, ntlm_response);
2ronwalf client_response = ntlmssp_buffer_data(request->response, ntlm_response);
2ronwalf
2ronwalf if (response_length == 0) {
2ronwalf /* try LM authentication unless NTLM2 was negotiated */
2ronwalf return request->ntlm2_negotiated ? -1 : 0;
2ronwalf }
2ronwalf
2ronwalf hash_buffer = buffer_create_data(auth_request->pool,
2ronwalf hash, sizeof(hash));
2ronwalf if (hex_to_binary(credentials, hash_buffer) < 0) {
2ronwalf auth_request_log_error(&request->auth_request, "ntlm",
2ronwalf "passdb credentials are not in hex");
2ronwalf return 0;
2ronwalf }
2ronwalf
2ronwalf if (response_length > NTLMSSP_RESPONSE_SIZE) {
2ronwalf unsigned char ntlm_v2_response[NTLMSSP_V2_RESPONSE_SIZE];
2ronwalf const unsigned char *blob =
2ronwalf client_response + NTLMSSP_V2_RESPONSE_SIZE;
2ronwalf
2ronwalf /*
2ronwalf * Authentication target == NULL because we are acting
2ronwalf * as a standalone server, not as NT domain member.
2ronwalf */
2ronwalf ntlmssp_v2_response(auth_request->user, NULL,
2ronwalf hash, request->challenge, blob,
2ronwalf response_length - NTLMSSP_V2_RESPONSE_SIZE,
2ronwalf ntlm_v2_response);
2ronwalf
2ronwalf return memcmp(ntlm_v2_response, client_response,
2ronwalf NTLMSSP_V2_RESPONSE_SIZE) == 0 ? 1 : -1;
2ronwalf } else {
2ronwalf unsigned char ntlm_response[NTLMSSP_RESPONSE_SIZE];
2ronwalf const unsigned char *client_lm_response =
2ronwalf ntlmssp_buffer_data(request->response, lm_response);
2ronwalf
2ronwalf if (request->ntlm2_negotiated)
2ronwalf ntlmssp2_response(hash, request->challenge,
2ronwalf client_lm_response,
2ronwalf ntlm_response);
2ronwalf else
2ronwalf ntlmssp_v1_response(hash, request->challenge,
2ronwalf ntlm_response);
2ronwalf
2ronwalf return memcmp(ntlm_response, client_response,
2ronwalf NTLMSSP_RESPONSE_SIZE) == 0 ? 1 : -1;
2ronwalf }
2ronwalf}
2ronwalf
2ronwalfstatic void
2ronwalfntlm_credentials_callback(enum passdb_result result,
2ronwalf const char *credentials,
2ronwalf struct auth_request *auth_request)
2ronwalf{
2ronwalf struct ntlm_auth_request *request =
2ronwalf (struct ntlm_auth_request *)auth_request;
2ronwalf int ret;
2ronwalf
2ronwalf switch (result) {
2ronwalf case PASSDB_RESULT_OK:
2ronwalf ret = ntlm_verify_credentials(request, credentials);
2ronwalf if (ret > 0) {
2ronwalf auth_request_success(auth_request, NULL, 0);
2ronwalf return;
2ronwalf }
2ronwalf if (ret < 0) {
2ronwalf auth_request_fail(auth_request);
2ronwalf return;
2ronwalf }
2ronwalf break;
case PASSDB_RESULT_INTERNAL_FAILURE:
auth_request_internal_failure(auth_request);
return;
default:
break;
}
/* NTLM credentials not found or didn't want to use them,
try with LM credentials */
auth_request_lookup_credentials(auth_request, PASSDB_CREDENTIALS_LANMAN,
lm_credentials_callback);
}
static void
mech_ntlm_auth_continue(struct auth_request *auth_request,
const unsigned char *data, size_t data_size)
{
struct ntlm_auth_request *request =
(struct ntlm_auth_request *)auth_request;
const char *error;
if (!request->challenge) {
const struct ntlmssp_request *ntlm_request =
(const struct ntlmssp_request *)data;
const struct ntlmssp_challenge *message;
size_t message_size;
uint32_t flags;
if (!ntlmssp_check_request(ntlm_request, data_size, &error)) {
auth_request_log_info(auth_request, "ntlm",
"invalid NTLM request: %s", error);
auth_request_fail(auth_request);
return;
}
message = ntlmssp_create_challenge(request->pool, ntlm_request,
&message_size);
flags = read_le32(&message->flags);
request->ntlm2_negotiated = flags & NTLMSSP_NEGOTIATE_NTLM2;
request->unicode_negotiated = flags & NTLMSSP_NEGOTIATE_UNICODE;
request->challenge = message->challenge;
auth_request->callback(auth_request,
AUTH_CLIENT_RESULT_CONTINUE,
message, message_size);
} else {
const struct ntlmssp_response *response =
(const struct ntlmssp_response *)data;
const char *username;
if (!ntlmssp_check_response(response, data_size, &error)) {
auth_request_log_info(auth_request, "ntlm",
"invalid NTLM response: %s", error);
auth_request_fail(auth_request);
return;
}
request->response = p_malloc(request->pool, data_size);
memcpy(request->response, response, data_size);
username = ntlmssp_t_str(request->response, user,
request->unicode_negotiated);
if (!auth_request_set_username(auth_request, username, &error)) {
auth_request_log_info(auth_request, "ntlm",
"%s", error);
auth_request_fail(auth_request);
return;
}
auth_request_lookup_credentials(auth_request,
PASSDB_CREDENTIALS_NTLM,
ntlm_credentials_callback);
}
}
static void
mech_ntlm_auth_initial(struct auth_request *request,
const unsigned char *data, size_t data_size)
{
if (data_size == 0) {
request->callback(request, AUTH_CLIENT_RESULT_CONTINUE,
NULL, 0);
} else {
mech_ntlm_auth_continue(request, data, data_size);
}
}
static void
mech_ntlm_auth_free(struct auth_request *request)
{
pool_unref(request->pool);
}
static struct auth_request *mech_ntlm_auth_new(void)
{
struct ntlm_auth_request *request;
pool_t pool;
pool = pool_alloconly_create("ntlm_auth_request", 1024);
request = p_new(pool, struct ntlm_auth_request, 1);
request->pool = pool;
request->auth_request.pool = pool;
return &request->auth_request;
}
const struct mech_module mech_ntlm = {
"NTLM",
MEMBER(flags) MECH_SEC_DICTIONARY | MECH_SEC_ACTIVE,
MEMBER(passdb_need_plain) FALSE,
MEMBER(passdb_need_credentials) TRUE,
mech_ntlm_auth_new,
mech_ntlm_auth_initial,
mech_ntlm_auth_continue,
mech_ntlm_auth_free
};