wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# add valgrind suppressions for the build farm here. Get the format
|
||||
# from the build farm log
|
||||
|
||||
{
|
||||
samba_dlopen1
|
||||
Memcheck:Cond
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen2
|
||||
Memcheck:Cond
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen3
|
||||
Memcheck:Addr4
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen4
|
||||
Memcheck:Cond
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen5
|
||||
Memcheck:Addr4
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen6
|
||||
Memcheck:Cond
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen7
|
||||
Memcheck:Addr4
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_libc_dlsym1
|
||||
Memcheck:Addr4
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:__libc_dlsym
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
########################################################
|
||||
# SAMBA Version #
|
||||
# #
|
||||
# Samba versions are as follows #
|
||||
# 3.0.x New production series #
|
||||
# 3.0.x{tp,pre,rc}y Preview/Testing & RC #
|
||||
# 3.0.x[a-z] Patch releases #
|
||||
# 3.0.x[a-z]-VENDOR-z Vendor patch releases #
|
||||
# #
|
||||
# script/mkversion.sh #
|
||||
# will use this file to create #
|
||||
# include/version.h #
|
||||
# #
|
||||
########################################################
|
||||
|
||||
########################################################
|
||||
# This are the main SAMBA version numbers #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE> #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_MAJOR=3 #
|
||||
# SAMBA_VERSION_MINOR=0 #
|
||||
# SAMBA_VERSION_RELEASE=0 #
|
||||
# -> "3.0.0" #
|
||||
########################################################
|
||||
SAMBA_VERSION_MAJOR=4
|
||||
SAMBA_VERSION_MINOR=0
|
||||
SAMBA_VERSION_RELEASE=0
|
||||
|
||||
########################################################
|
||||
# If a official release has a serious bug #
|
||||
# a security release will have 'a' sufffix #
|
||||
# #
|
||||
# so SAMBA's version will be #
|
||||
# <MAJOR>.<MINOR>.<RELEASE><REVISION> #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_REVISION=a #
|
||||
# -> "2.2.8a" #
|
||||
########################################################
|
||||
SAMBA_VERSION_REVISION=
|
||||
|
||||
########################################################
|
||||
# For 'tp' releases the version will be #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE>tp<TP_RELEASE> #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_TP_RELEASE=1 #
|
||||
# -> "4.0.0tp1" #
|
||||
########################################################
|
||||
SAMBA_VERSION_TP_RELEASE=4
|
||||
|
||||
########################################################
|
||||
# For 'pre' releases the version will be #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE>pre<PRE_RELEASE> #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_PRE_RELEASE=1 #
|
||||
# -> "2.2.9pre1" #
|
||||
########################################################
|
||||
SAMBA_VERSION_PRE_RELEASE=
|
||||
|
||||
########################################################
|
||||
# For 'rc' releases the version will be #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE>rc<RC_RELEASE> #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_RC_RELEASE=1 #
|
||||
# -> "3.0.0rc1" #
|
||||
########################################################
|
||||
SAMBA_VERSION_RC_RELEASE=
|
||||
|
||||
########################################################
|
||||
# To mark SVN snapshots this should be set to 'yes' #
|
||||
# in the development BRANCH, and set to 'no' only in #
|
||||
# the SAMBA_X_X_RELEASE BRANCH #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE>[...]-SVN-build-xxx #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_IS_SVN_SNAPSHOT=yes #
|
||||
# -> "3.0.0-SVN-build-199" #
|
||||
########################################################
|
||||
SAMBA_VERSION_IS_SVN_SNAPSHOT=yes
|
||||
|
||||
########################################################
|
||||
# This is for specifying a release nickname #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_RELEASE_NICKNAME=Nicky Nickname #
|
||||
# smbd --version will then give: #
|
||||
# -> "4.0.0-tp1-VendorVersion (Nicky Nickname)" #
|
||||
########################################################
|
||||
SAMBA_VERSION_RELEASE_NICKNAME=
|
||||
|
||||
########################################################
|
||||
# This can be set by vendors if they want... #
|
||||
# This can be a string constant or a function which #
|
||||
# returns a string (const char *) #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE>[...]-<VENDOR_SUFFIX> #
|
||||
# #
|
||||
# Note the '-' is automaticaly added #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_VENDOR_SUFFIX=VendorVersion #
|
||||
# -> "3.0.0rc2-VendorVersion" #
|
||||
# #
|
||||
########################################################
|
||||
SAMBA_VERSION_VENDOR_SUFFIX=
|
||||
SAMBA_VERSION_VENDOR_PATCH=
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
|
||||
dnl Copied from libtool.m4
|
||||
AC_DEFUN(AC_PROG_LD_GNU,
|
||||
[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], ac_cv_prog_gnu_ld,
|
||||
[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
|
||||
if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
|
||||
ac_cv_prog_gnu_ld=yes
|
||||
else
|
||||
ac_cv_prog_gnu_ld=no
|
||||
fi])
|
||||
])
|
||||
|
||||
dnl Removes -I/usr/include/? from given variable
|
||||
AC_DEFUN(CFLAGS_REMOVE_USR_INCLUDE,[
|
||||
ac_new_flags=""
|
||||
for i in [$]$1; do
|
||||
case [$]i in
|
||||
-I/usr/include|-I/usr/include/) ;;
|
||||
*) ac_new_flags="[$]ac_new_flags [$]i" ;;
|
||||
esac
|
||||
done
|
||||
$1=[$]ac_new_flags
|
||||
])
|
||||
|
||||
dnl Removes '-L/usr/lib[/]', '-Wl,-rpath,/usr/lib[/]'
|
||||
dnl and '-Wl,-rpath -Wl,/usr/lib[/]' from given variable
|
||||
AC_DEFUN(LIB_REMOVE_USR_LIB,[
|
||||
ac_new_flags=""
|
||||
l=""
|
||||
for i in [$]$1; do
|
||||
case [$]l[$]i in
|
||||
-L/usr/lib) ;;
|
||||
-L/usr/lib/) ;;
|
||||
-Wl,-rpath,/usr/lib) ;;
|
||||
-Wl,-rpath,/usr/lib/) ;;
|
||||
-Wl,-rpath) l=[$]i;;
|
||||
-Wl,-rpath-Wl,/usr/lib) l="";;
|
||||
-Wl,-rpath-Wl,/usr/lib/) l="";;
|
||||
*)
|
||||
s=" "
|
||||
if test x"[$]ac_new_flags" = x""; then
|
||||
s="";
|
||||
fi
|
||||
if test x"[$]l" = x""; then
|
||||
ac_new_flags="[$]ac_new_flags[$]s[$]i";
|
||||
else
|
||||
ac_new_flags="[$]ac_new_flags[$]s[$]l [$]i";
|
||||
fi
|
||||
l=""
|
||||
;;
|
||||
esac
|
||||
done
|
||||
$1=[$]ac_new_flags
|
||||
])
|
||||
|
||||
m4_include(lib/replace/libreplace.m4)
|
||||
m4_include(build/m4/ax_cflags_gcc_option.m4)
|
||||
m4_include(build/m4/ax_cflags_irix_option.m4)
|
||||
m4_include(build/m4/public.m4)
|
||||
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Bartlett 2001-2002
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "auth/auth.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "build.h"
|
||||
|
||||
/***************************************************************************
|
||||
Set a fixed challenge
|
||||
***************************************************************************/
|
||||
NTSTATUS auth_context_set_challenge(struct auth_context *auth_ctx, const uint8_t chal[8], const char *set_by)
|
||||
{
|
||||
auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
|
||||
NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
|
||||
|
||||
auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
|
||||
NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
Set a fixed challenge
|
||||
***************************************************************************/
|
||||
BOOL auth_challenge_may_be_modified(struct auth_context *auth_ctx)
|
||||
{
|
||||
return auth_ctx->challenge.may_be_modified;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Try to get a challenge out of the various authentication modules.
|
||||
Returns a const char of length 8 bytes.
|
||||
****************************************************************************/
|
||||
_PUBLIC_ NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_t **_chal)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_method_context *method;
|
||||
|
||||
if (auth_ctx->challenge.data.length) {
|
||||
DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
|
||||
auth_ctx->challenge.set_by));
|
||||
*_chal = auth_ctx->challenge.data.data;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
for (method = auth_ctx->methods; method; method = method->next) {
|
||||
DATA_BLOB challenge = data_blob(NULL,0);
|
||||
|
||||
nt_status = method->ops->get_challenge(method, auth_ctx, &challenge);
|
||||
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
if (challenge.length != 8) {
|
||||
DEBUG(0, ("auth_get_challenge: invalid challenge (length %u) by mothod [%s]\n",
|
||||
(unsigned)challenge.length, method->ops->name));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
auth_ctx->challenge.data = challenge;
|
||||
auth_ctx->challenge.set_by = method->ops->name;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!auth_ctx->challenge.set_by) {
|
||||
uint8_t chal[8];
|
||||
generate_random_buffer(chal, 8);
|
||||
|
||||
auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
|
||||
NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
|
||||
auth_ctx->challenge.set_by = "random";
|
||||
|
||||
auth_ctx->challenge.may_be_modified = True;
|
||||
}
|
||||
|
||||
DEBUG(10,("auth_get_challenge: challenge set by %s\n",
|
||||
auth_ctx->challenge.set_by));
|
||||
|
||||
*_chal = auth_ctx->challenge.data.data;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
struct auth_check_password_sync_state {
|
||||
BOOL finished;
|
||||
NTSTATUS status;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
};
|
||||
|
||||
static void auth_check_password_sync_callback(struct auth_check_password_request *req,
|
||||
void *private_data)
|
||||
{
|
||||
struct auth_check_password_sync_state *s = talloc_get_type(private_data,
|
||||
struct auth_check_password_sync_state);
|
||||
|
||||
s->finished = True;
|
||||
s->status = auth_check_password_recv(req, s, &s->server_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a user's Plaintext, LM or NTLM password.
|
||||
* (sync version)
|
||||
*
|
||||
* Check a user's password, as given in the user_info struct and return various
|
||||
* interesting details in the server_info struct.
|
||||
*
|
||||
* The return value takes precedence over the contents of the server_info
|
||||
* struct. When the return is other than NT_STATUS_OK the contents
|
||||
* of that structure is undefined.
|
||||
*
|
||||
* @param auth_ctx Supplies the challenges and some other data.
|
||||
* Must be created with auth_context_create(), and the challenges should be
|
||||
* filled in, either at creation or by calling the challenge geneation
|
||||
* function auth_get_challenge().
|
||||
*
|
||||
* @param user_info Contains the user supplied components, including the passwords.
|
||||
*
|
||||
* @param mem_ctx The parent memory context for the server_info structure
|
||||
*
|
||||
* @param server_info If successful, contains information about the authentication,
|
||||
* including a SAM_ACCOUNT struct describing the user.
|
||||
*
|
||||
* @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
|
||||
*
|
||||
**/
|
||||
|
||||
NTSTATUS auth_check_password(struct auth_context *auth_ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
struct auth_check_password_sync_state *sync_state;
|
||||
NTSTATUS status;
|
||||
|
||||
sync_state = talloc_zero(auth_ctx, struct auth_check_password_sync_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(sync_state);
|
||||
|
||||
auth_check_password_send(auth_ctx, user_info, auth_check_password_sync_callback, sync_state);
|
||||
|
||||
while (!sync_state->finished) {
|
||||
event_loop_once(auth_ctx->event_ctx);
|
||||
}
|
||||
|
||||
status = sync_state->status;
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
*server_info = talloc_steal(mem_ctx, sync_state->server_info);
|
||||
}
|
||||
|
||||
talloc_free(sync_state);
|
||||
return status;
|
||||
}
|
||||
|
||||
struct auth_check_password_request {
|
||||
struct auth_context *auth_ctx;
|
||||
const struct auth_usersupplied_info *user_info;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
struct auth_method_context *method;
|
||||
NTSTATUS status;
|
||||
struct {
|
||||
void (*fn)(struct auth_check_password_request *req, void *private_data);
|
||||
void *private_data;
|
||||
} callback;
|
||||
};
|
||||
|
||||
static void auth_check_password_async_timed_handler(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *ptr)
|
||||
{
|
||||
struct auth_check_password_request *req = talloc_get_type(ptr, struct auth_check_password_request);
|
||||
req->status = req->method->ops->check_password(req->method, req, req->user_info, &req->server_info);
|
||||
req->callback.fn(req, req->callback.private_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a user's Plaintext, LM or NTLM password.
|
||||
* async send hook
|
||||
*
|
||||
* Check a user's password, as given in the user_info struct and return various
|
||||
* interesting details in the server_info struct.
|
||||
*
|
||||
* The return value takes precedence over the contents of the server_info
|
||||
* struct. When the return is other than NT_STATUS_OK the contents
|
||||
* of that structure is undefined.
|
||||
*
|
||||
* @param auth_ctx Supplies the challenges and some other data.
|
||||
* Must be created with make_auth_context(), and the challenges should be
|
||||
* filled in, either at creation or by calling the challenge geneation
|
||||
* function auth_get_challenge().
|
||||
*
|
||||
* @param user_info Contains the user supplied components, including the passwords.
|
||||
*
|
||||
* @param callback A callback function which will be called when the operation is finished.
|
||||
* The callback function needs to call auth_check_password_recv() to get the return values
|
||||
*
|
||||
* @param private_data A private pointer which will ba passed to the callback function
|
||||
*
|
||||
**/
|
||||
|
||||
void auth_check_password_send(struct auth_context *auth_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
void (*callback)(struct auth_check_password_request *req, void *private_data),
|
||||
void *private_data)
|
||||
{
|
||||
/* if all the modules say 'not for me' this is reasonable */
|
||||
NTSTATUS nt_status;
|
||||
struct auth_method_context *method;
|
||||
const uint8_t *challenge;
|
||||
struct auth_usersupplied_info *user_info_tmp;
|
||||
struct auth_check_password_request *req = NULL;
|
||||
|
||||
DEBUG(3, ("auth_check_password_send: Checking password for unmapped user [%s]\\[%s]@[%s]\n",
|
||||
user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
|
||||
|
||||
req = talloc_zero(auth_ctx, struct auth_check_password_request);
|
||||
if (!req) {
|
||||
callback(NULL, private_data);
|
||||
return;
|
||||
}
|
||||
req->auth_ctx = auth_ctx;
|
||||
req->user_info = user_info;
|
||||
req->callback.fn = callback;
|
||||
req->callback.private_data = private_data;
|
||||
|
||||
if (!user_info->mapped_state) {
|
||||
nt_status = map_user_info(req, user_info, &user_info_tmp);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) goto failed;
|
||||
user_info = user_info_tmp;
|
||||
req->user_info = user_info_tmp;
|
||||
}
|
||||
|
||||
DEBUGADD(3,("auth_check_password_send: mapped user is: [%s]\\[%s]@[%s]\n",
|
||||
user_info->mapped.domain_name, user_info->mapped.account_name, user_info->workstation_name));
|
||||
|
||||
nt_status = auth_get_challenge(auth_ctx, &challenge);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(0, ("auth_check_password_send: Invalid challenge (length %u) stored for this auth context set_by %s - cannot continue: %s\n",
|
||||
(unsigned)auth_ctx->challenge.data.length, auth_ctx->challenge.set_by, nt_errstr(nt_status)));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (auth_ctx->challenge.set_by) {
|
||||
DEBUG(10, ("auth_check_password_send: auth_context challenge created by %s\n",
|
||||
auth_ctx->challenge.set_by));
|
||||
}
|
||||
|
||||
DEBUG(10, ("auth_check_password_send: challenge is: \n"));
|
||||
dump_data(5, auth_ctx->challenge.data.data, auth_ctx->challenge.data.length);
|
||||
|
||||
nt_status = NT_STATUS_NO_SUCH_USER; /* If all the modules say 'not for me', then this is reasonable */
|
||||
for (method = auth_ctx->methods; method; method = method->next) {
|
||||
NTSTATUS result;
|
||||
struct timed_event *te = NULL;
|
||||
|
||||
/* check if the module wants to chek the password */
|
||||
result = method->ops->want_check(method, req, user_info);
|
||||
if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
|
||||
DEBUG(11,("auth_check_password_send: %s had nothing to say\n", method->ops->name));
|
||||
continue;
|
||||
}
|
||||
|
||||
nt_status = result;
|
||||
req->method = method;
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) break;
|
||||
|
||||
te = event_add_timed(auth_ctx->event_ctx, req,
|
||||
timeval_zero(),
|
||||
auth_check_password_async_timed_handler, req);
|
||||
if (!te) {
|
||||
nt_status = NT_STATUS_NO_MEMORY;
|
||||
goto failed;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
failed:
|
||||
req->status = nt_status;
|
||||
req->callback.fn(req, req->callback.private_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a user's Plaintext, LM or NTLM password.
|
||||
* async receive function
|
||||
*
|
||||
* The return value takes precedence over the contents of the server_info
|
||||
* struct. When the return is other than NT_STATUS_OK the contents
|
||||
* of that structure is undefined.
|
||||
*
|
||||
*
|
||||
* @param req The async auth_check_password state, passes to the callers callback function
|
||||
*
|
||||
* @param mem_ctx The parent memory context for the server_info structure
|
||||
*
|
||||
* @param server_info If successful, contains information about the authentication,
|
||||
* including a SAM_ACCOUNT struct describing the user.
|
||||
*
|
||||
* @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
|
||||
*
|
||||
**/
|
||||
|
||||
NTSTATUS auth_check_password_recv(struct auth_check_password_request *req,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
if (NT_STATUS_IS_OK(req->status)) {
|
||||
DEBUG(5,("auth_check_password_recv: %s authentication for user [%s\\%s] succeeded\n",
|
||||
req->method->ops->name, req->server_info->domain_name, req->server_info->account_name));
|
||||
|
||||
*server_info = talloc_steal(mem_ctx, req->server_info);
|
||||
} else {
|
||||
DEBUG(2,("auth_check_password_recv: %s authentication for user [%s\\%s] FAILED with error %s\n",
|
||||
(req->method ? req->method->ops->name : "NO_METHOD"),
|
||||
req->user_info->mapped.domain_name,
|
||||
req->user_info->mapped.account_name,
|
||||
nt_errstr(req->status)));
|
||||
}
|
||||
|
||||
status = req->status;
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
Make a auth_info struct for the auth subsystem
|
||||
***************************************************************************/
|
||||
NTSTATUS auth_context_create(TALLOC_CTX *mem_ctx, const char **methods,
|
||||
struct event_context *ev,
|
||||
struct messaging_context *msg,
|
||||
struct auth_context **auth_ctx)
|
||||
{
|
||||
int i;
|
||||
struct auth_context *ctx;
|
||||
|
||||
if (!methods) {
|
||||
DEBUG(0,("auth_context_create: No auth method list!?\n"));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!ev) {
|
||||
DEBUG(0,("auth_context_create: called with out event context\n"));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!msg) {
|
||||
DEBUG(0,("auth_context_create: called with out messaging context\n"));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ctx = talloc(mem_ctx, struct auth_context);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ctx);
|
||||
ctx->challenge.set_by = NULL;
|
||||
ctx->challenge.may_be_modified = False;
|
||||
ctx->challenge.data = data_blob(NULL, 0);
|
||||
ctx->methods = NULL;
|
||||
ctx->event_ctx = ev;
|
||||
ctx->msg_ctx = msg;
|
||||
|
||||
for (i=0; methods[i] ; i++) {
|
||||
struct auth_method_context *method;
|
||||
|
||||
method = talloc(ctx, struct auth_method_context);
|
||||
NT_STATUS_HAVE_NO_MEMORY(method);
|
||||
|
||||
method->ops = auth_backend_byname(methods[i]);
|
||||
if (!method->ops) {
|
||||
DEBUG(1,("auth_context_create: failed to find method=%s\n",
|
||||
methods[i]));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
method->auth_ctx = ctx;
|
||||
method->depth = i;
|
||||
DLIST_ADD_END(ctx->methods, method, struct auth_method_context *);
|
||||
}
|
||||
|
||||
if (!ctx->methods) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
*auth_ctx = ctx;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* the list of currently registered AUTH backends */
|
||||
static struct auth_backend {
|
||||
const struct auth_operations *ops;
|
||||
} *backends = NULL;
|
||||
static int num_backends;
|
||||
|
||||
/*
|
||||
register a AUTH backend.
|
||||
|
||||
The 'name' can be later used by other backends to find the operations
|
||||
structure for this backend.
|
||||
*/
|
||||
NTSTATUS auth_register(const void *_ops)
|
||||
{
|
||||
const struct auth_operations *ops = _ops;
|
||||
struct auth_operations *new_ops;
|
||||
|
||||
if (auth_backend_byname(ops->name) != NULL) {
|
||||
/* its already registered! */
|
||||
DEBUG(0,("AUTH backend '%s' already registered\n",
|
||||
ops->name));
|
||||
return NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
|
||||
backends = realloc_p(backends, struct auth_backend, num_backends+1);
|
||||
if (!backends) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
new_ops = smb_xmemdup(ops, sizeof(*ops));
|
||||
new_ops->name = smb_xstrdup(ops->name);
|
||||
|
||||
backends[num_backends].ops = new_ops;
|
||||
|
||||
num_backends++;
|
||||
|
||||
DEBUG(3,("AUTH backend '%s' registered\n",
|
||||
ops->name));
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
return the operations structure for a named backend of the specified type
|
||||
*/
|
||||
const struct auth_operations *auth_backend_byname(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0;i<num_backends;i++) {
|
||||
if (strcmp(backends[i].ops->name, name) == 0) {
|
||||
return backends[i].ops;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
return the AUTH interface version, and the size of some critical types
|
||||
This can be used by backends to either detect compilation errors, or provide
|
||||
multiple implementations for different smbd compilation options in one module
|
||||
*/
|
||||
const struct auth_critical_sizes *auth_interface_version(void)
|
||||
{
|
||||
static const struct auth_critical_sizes critical_sizes = {
|
||||
AUTH_INTERFACE_VERSION,
|
||||
sizeof(struct auth_operations),
|
||||
sizeof(struct auth_method_context),
|
||||
sizeof(struct auth_context),
|
||||
sizeof(struct auth_usersupplied_info),
|
||||
sizeof(struct auth_serversupplied_info)
|
||||
};
|
||||
|
||||
return &critical_sizes;
|
||||
}
|
||||
|
||||
NTSTATUS auth_init(void)
|
||||
{
|
||||
static BOOL initialized = False;
|
||||
|
||||
init_module_fn static_init[] = STATIC_auth_MODULES;
|
||||
init_module_fn *shared_init;
|
||||
|
||||
if (initialized) return NT_STATUS_OK;
|
||||
initialized = True;
|
||||
|
||||
shared_init = load_samba_modules(NULL, "auth");
|
||||
|
||||
run_init_functions(static_init);
|
||||
run_init_functions(shared_init);
|
||||
|
||||
talloc_free(shared_init);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS server_service_auth_init(void)
|
||||
{
|
||||
return auth_init();
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Standardised Authentication types
|
||||
Copyright (C) Andrew Bartlett 2001
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _SAMBA_AUTH_H
|
||||
#define _SAMBA_AUTH_H
|
||||
|
||||
union netr_Validation;
|
||||
struct netr_SamBaseInfo;
|
||||
struct netr_SamInfo3;
|
||||
|
||||
/* modules can use the following to determine if the interface has changed
|
||||
* please increment the version number after each interface change
|
||||
* with a comment and maybe update struct auth_critical_sizes.
|
||||
*/
|
||||
/* version 1 - version from samba 3.0 - metze */
|
||||
/* version 2 - initial samba4 version - metze */
|
||||
/* version 3 - subsequent samba4 version - abartlet */
|
||||
/* version 4 - subsequent samba4 version - metze */
|
||||
/* version 0 - till samba4 is stable - metze */
|
||||
#define AUTH_INTERFACE_VERSION 0
|
||||
|
||||
#define USER_INFO_CASE_INSENSITIVE_USERNAME 0x01 /* username may be in any case */
|
||||
#define USER_INFO_CASE_INSENSITIVE_PASSWORD 0x02 /* password may be in any case */
|
||||
#define USER_INFO_DONT_CHECK_UNIX_ACCOUNT 0x04 /* dont check unix account status */
|
||||
#define USER_INFO_INTERACTIVE_LOGON 0x08 /* dont check unix account status */
|
||||
|
||||
enum auth_password_state {
|
||||
AUTH_PASSWORD_RESPONSE,
|
||||
AUTH_PASSWORD_HASH,
|
||||
AUTH_PASSWORD_PLAIN
|
||||
};
|
||||
|
||||
struct auth_usersupplied_info
|
||||
{
|
||||
const char *workstation_name;
|
||||
struct socket_address *remote_host;
|
||||
|
||||
uint32_t logon_parameters;
|
||||
|
||||
BOOL mapped_state;
|
||||
/* the values the client gives us */
|
||||
struct {
|
||||
const char *account_name;
|
||||
const char *domain_name;
|
||||
} client, mapped;
|
||||
|
||||
enum auth_password_state password_state;
|
||||
|
||||
union {
|
||||
struct {
|
||||
DATA_BLOB lanman;
|
||||
DATA_BLOB nt;
|
||||
} response;
|
||||
struct {
|
||||
struct samr_Password *lanman;
|
||||
struct samr_Password *nt;
|
||||
} hash;
|
||||
|
||||
char *plaintext;
|
||||
} password;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct auth_serversupplied_info
|
||||
{
|
||||
struct dom_sid *account_sid;
|
||||
struct dom_sid *primary_group_sid;
|
||||
|
||||
size_t n_domain_groups;
|
||||
struct dom_sid **domain_groups;
|
||||
|
||||
DATA_BLOB user_session_key;
|
||||
DATA_BLOB lm_session_key;
|
||||
|
||||
const char *account_name;
|
||||
const char *domain_name;
|
||||
|
||||
const char *full_name;
|
||||
const char *logon_script;
|
||||
const char *profile_path;
|
||||
const char *home_directory;
|
||||
const char *home_drive;
|
||||
const char *logon_server;
|
||||
|
||||
NTTIME last_logon;
|
||||
NTTIME last_logoff;
|
||||
NTTIME acct_expiry;
|
||||
NTTIME last_password_change;
|
||||
NTTIME allow_password_change;
|
||||
NTTIME force_password_change;
|
||||
|
||||
uint16_t logon_count;
|
||||
uint16_t bad_password_count;
|
||||
|
||||
uint32_t acct_flags;
|
||||
|
||||
BOOL authenticated;
|
||||
};
|
||||
|
||||
struct auth_session_info {
|
||||
struct security_token *security_token;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
DATA_BLOB session_key;
|
||||
struct cli_credentials *credentials;
|
||||
};
|
||||
|
||||
struct auth_method_context;
|
||||
struct auth_check_password_request;
|
||||
|
||||
struct auth_operations {
|
||||
const char *name;
|
||||
|
||||
/* If you are using this interface, then you are probably
|
||||
* getting something wrong. This interface is only for
|
||||
* security=server, and makes a number of compromises to allow
|
||||
* that. It is not compatible with being a PDC. */
|
||||
|
||||
NTSTATUS (*get_challenge)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge);
|
||||
|
||||
/* Given the user supplied info, check if this backend want to handle the password checking */
|
||||
|
||||
NTSTATUS (*want_check)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info);
|
||||
|
||||
/* Given the user supplied info, check a password */
|
||||
|
||||
NTSTATUS (*check_password)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info);
|
||||
};
|
||||
|
||||
struct auth_method_context {
|
||||
struct auth_method_context *prev, *next;
|
||||
struct auth_context *auth_ctx;
|
||||
const struct auth_operations *ops;
|
||||
int depth;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
struct auth_context {
|
||||
struct {
|
||||
/* Who set this up in the first place? */
|
||||
const char *set_by;
|
||||
|
||||
BOOL may_be_modified;
|
||||
|
||||
DATA_BLOB data;
|
||||
} challenge;
|
||||
|
||||
/* methods, in the order they should be called */
|
||||
struct auth_method_context *methods;
|
||||
|
||||
/* the event context to use for calls that can block */
|
||||
struct event_context *event_ctx;
|
||||
|
||||
/* the messaging context which can be used by backends */
|
||||
struct messaging_context *msg_ctx;
|
||||
};
|
||||
|
||||
/* this structure is used by backends to determine the size of some critical types */
|
||||
struct auth_critical_sizes {
|
||||
int interface_version;
|
||||
int sizeof_auth_operations;
|
||||
int sizeof_auth_methods;
|
||||
int sizeof_auth_context;
|
||||
int sizeof_auth_usersupplied_info;
|
||||
int sizeof_auth_serversupplied_info;
|
||||
};
|
||||
|
||||
NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
|
||||
enum auth_password_state to_state,
|
||||
const struct auth_usersupplied_info *user_info_in,
|
||||
const struct auth_usersupplied_info **user_info_encrypted);
|
||||
|
||||
#include "auth/auth_proto.h"
|
||||
|
||||
#endif /* _SMBAUTH_H_ */
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Anonymous Authentification
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
/**
|
||||
* Return a anonymous logon for anonymous users (username = "")
|
||||
*
|
||||
* Typically used as the first module in the auth chain, this allows
|
||||
* anonymou logons to be dealt with in one place. Non-anonymou logons 'fail'
|
||||
* and pass onto the next module.
|
||||
**/
|
||||
static NTSTATUS anonymous_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
if (user_info->client.account_name && *user_info->client.account_name) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a anonymous logon for anonymous users (username = "")
|
||||
*
|
||||
* Typically used as the first module in the auth chain, this allows
|
||||
* anonymou logons to be dealt with in one place. Non-anonymou logons 'fail'
|
||||
* and pass onto the next module.
|
||||
**/
|
||||
static NTSTATUS anonymous_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
return auth_anonymous_server_info(mem_ctx, _server_info);
|
||||
}
|
||||
|
||||
static struct auth_operations anonymous_auth_ops = {
|
||||
.name = "anonymous",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = anonymous_want_check,
|
||||
.check_password = anonymous_check_password
|
||||
};
|
||||
|
||||
NTSTATUS auth_anonymous_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
ret = auth_register(&anonymous_auth_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'anonymous' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Generic authentication types
|
||||
Copyright (C) Andrew Bartlett 2001-2002
|
||||
Copyright (C) Jelmer Vernooij 2002
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "librpc/gen_ndr/ndr_samr.h"
|
||||
|
||||
static NTSTATUS name_to_ntstatus_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an error based on username
|
||||
*
|
||||
* This function allows the testing of obsure errors, as well as the generation
|
||||
* of NT_STATUS -> DOS error mapping tables.
|
||||
*
|
||||
* This module is of no value to end-users.
|
||||
*
|
||||
* The password is ignored.
|
||||
*
|
||||
* @return An NTSTATUS value based on the username
|
||||
**/
|
||||
|
||||
static NTSTATUS name_to_ntstatus_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
uint32_t error_num;
|
||||
const char *user;
|
||||
|
||||
user = user_info->client.account_name;
|
||||
|
||||
if (strncasecmp("NT_STATUS", user, strlen("NT_STATUS")) == 0) {
|
||||
nt_status = nt_status_string_to_code(user);
|
||||
} else {
|
||||
error_num = strtoul(user, NULL, 16);
|
||||
DEBUG(5,("name_to_ntstatus_check_password: Error for user %s was 0x%08X\n", user, error_num));
|
||||
nt_status = NT_STATUS(error_num);
|
||||
}
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
|
||||
|
||||
/* is this correct? */
|
||||
server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
|
||||
|
||||
server_info->n_domain_groups = 0;
|
||||
server_info->domain_groups = NULL;
|
||||
|
||||
/* annoying, but the Anonymous really does have a session key,
|
||||
and it is all zeros! */
|
||||
server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
|
||||
|
||||
server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
|
||||
|
||||
data_blob_clear(&server_info->user_session_key);
|
||||
data_blob_clear(&server_info->lm_session_key);
|
||||
|
||||
server_info->account_name = talloc_asprintf(server_info, "NAME TO NTSTATUS %s ANONYMOUS LOGON", user);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
|
||||
server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
|
||||
|
||||
server_info->full_name = talloc_asprintf(server_info, "NAME TO NTSTATUS %s Anonymous Logon", user);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
|
||||
|
||||
server_info->logon_script = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
|
||||
|
||||
server_info->profile_path = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
|
||||
|
||||
server_info->home_directory = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
|
||||
|
||||
server_info->home_drive = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
|
||||
|
||||
server_info->last_logon = 0;
|
||||
server_info->last_logoff = 0;
|
||||
server_info->acct_expiry = 0;
|
||||
server_info->last_password_change = 0;
|
||||
server_info->allow_password_change = 0;
|
||||
server_info->force_password_change = 0;
|
||||
|
||||
server_info->logon_count = 0;
|
||||
server_info->bad_password_count = 0;
|
||||
|
||||
server_info->acct_flags = ACB_NORMAL;
|
||||
|
||||
server_info->authenticated = False;
|
||||
|
||||
*_server_info = server_info;
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
static struct auth_operations name_to_ntstatus_auth_ops = {
|
||||
.name = "name_to_ntstatus",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = name_to_ntstatus_want_check,
|
||||
.check_password = name_to_ntstatus_check_password
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a 'fixed' challenge instead of a variable one.
|
||||
*
|
||||
* The idea of this function is to make packet snifs consistant
|
||||
* with a fixed challenge, so as to aid debugging.
|
||||
*
|
||||
* This module is of no value to end-users.
|
||||
*
|
||||
* This module does not actually authenticate the user, but
|
||||
* just pretenteds to need a specified challenge.
|
||||
* This module removes *all* security from the challenge-response system
|
||||
*
|
||||
* @return NT_STATUS_UNSUCCESSFUL
|
||||
**/
|
||||
static NTSTATUS fixed_challenge_get_challenge(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
const char *challenge = "I am a teapot";
|
||||
|
||||
blob = data_blob_talloc(mem_ctx, challenge, 8);
|
||||
NT_STATUS_HAVE_NO_MEMORY(blob.data);
|
||||
|
||||
*_blob = blob;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS fixed_challenge_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
/* don't handle any users */
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static NTSTATUS fixed_challenge_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
/* don't handle any users */
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
static struct auth_operations fixed_challenge_auth_ops = {
|
||||
.name = "fixed_challenge",
|
||||
.get_challenge = fixed_challenge_get_challenge,
|
||||
.want_check = fixed_challenge_want_check,
|
||||
.check_password = fixed_challenge_check_password
|
||||
};
|
||||
|
||||
NTSTATUS auth_developer_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
ret = auth_register(&name_to_ntstatus_auth_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'name_to_ntstatus' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = auth_register(&fixed_challenge_auth_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'fixed_challenge' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
|
||||
Copyright (C) Gerald Carter 2003
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon.h"
|
||||
#include "system/time.h"
|
||||
#include "db_wrap.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/auth_sam.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
|
||||
extern const char *user_attrs[];
|
||||
extern const char *domain_ref_attrs[];
|
||||
|
||||
/****************************************************************************
|
||||
Look for the specified user in the sam, return ldb result structures
|
||||
****************************************************************************/
|
||||
|
||||
static NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
|
||||
const char *account_name,
|
||||
const char *domain_name,
|
||||
struct ldb_message ***ret_msgs,
|
||||
struct ldb_message ***ret_msgs_domain_ref)
|
||||
{
|
||||
struct ldb_message **msgs_tmp;
|
||||
struct ldb_message **msgs;
|
||||
struct ldb_message **msgs_domain_ref;
|
||||
struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
|
||||
|
||||
int ret;
|
||||
int ret_domain;
|
||||
|
||||
struct ldb_dn *domain_dn = NULL;
|
||||
|
||||
if (domain_name) {
|
||||
char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name);
|
||||
/* find the domain's DN */
|
||||
ret_domain = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &msgs_domain_ref, domain_ref_attrs,
|
||||
"(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
|
||||
escaped_domain, escaped_domain);
|
||||
if (ret_domain == -1) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (ret_domain == 0) {
|
||||
DEBUG(3,("sam_search_user: Couldn't find domain [%s] in samdb.\n",
|
||||
domain_name));
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
if (ret_domain > 1) {
|
||||
DEBUG(0,("Found %d records matching domain [%s]\n",
|
||||
ret_domain, domain_name));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msgs_domain_ref[0], "nCName", NULL);
|
||||
}
|
||||
|
||||
/* pull the user attributes */
|
||||
ret = gendb_search(sam_ctx, mem_ctx, domain_dn, &msgs, user_attrs,
|
||||
"(&(sAMAccountName=%s)(objectclass=user))",
|
||||
ldb_binary_encode_string(mem_ctx, account_name));
|
||||
if (ret == -1) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
DEBUG(3,("sam_search_user: Couldn't find user [%s\\%s] in samdb, under %s\n",
|
||||
domain_name, account_name, ldb_dn_get_linearized(domain_dn)));
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
if (ret > 1) {
|
||||
DEBUG(0,("Found %d records matching user [%s]\n", ret, account_name));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (!domain_name) {
|
||||
struct dom_sid *domain_sid;
|
||||
|
||||
domain_sid = samdb_result_sid_prefix(mem_ctx, msgs[0], "objectSid");
|
||||
if (!domain_sid) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
/* find the domain's DN */
|
||||
ret = gendb_search(sam_ctx, mem_ctx, NULL, &msgs_tmp, NULL,
|
||||
"(&(objectSid=%s)(objectClass=domain))",
|
||||
ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
|
||||
if (ret == -1) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
DEBUG(3,("check_sam_security: Couldn't find domain_sid [%s] in passdb file.\n",
|
||||
dom_sid_string(mem_ctx, domain_sid)));
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
if (ret > 1) {
|
||||
DEBUG(0,("Found %d records matching domain_sid [%s]\n",
|
||||
ret, dom_sid_string(mem_ctx, domain_sid)));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
ret_domain = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &msgs_domain_ref, domain_ref_attrs,
|
||||
"(nCName=%s)", ldb_dn_alloc_linearized(msgs_tmp, msgs_tmp[0]->dn));
|
||||
|
||||
if (ret_domain == -1) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (ret_domain == 0) {
|
||||
DEBUG(3,("check_sam_security: Couldn't find domain [%s] in passdb file.\n",
|
||||
ldb_dn_get_linearized(msgs_tmp[0]->dn)));
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
if (ret_domain > 1) {
|
||||
DEBUG(0,("Found %d records matching domain [%s]\n",
|
||||
ret_domain, ldb_dn_get_linearized(msgs_tmp[0]->dn)));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*ret_msgs = msgs;
|
||||
*ret_msgs_domain_ref = msgs_domain_ref;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Do a specific test for an smb password being correct, given a smb_password and
|
||||
the lanman and NT responses.
|
||||
****************************************************************************/
|
||||
static NTSTATUS authsam_password_ok(struct auth_context *auth_context,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
uint16_t acct_flags,
|
||||
const struct samr_Password *lm_pwd,
|
||||
const struct samr_Password *nt_pwd,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
DATA_BLOB *user_sess_key,
|
||||
DATA_BLOB *lm_sess_key)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (acct_flags & ACB_PWNOTREQ) {
|
||||
if (lp_null_passwords()) {
|
||||
DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n",
|
||||
user_info->mapped.account_name));
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n",
|
||||
user_info->mapped.account_name));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
switch (user_info->password_state) {
|
||||
case AUTH_PASSWORD_PLAIN:
|
||||
{
|
||||
const struct auth_usersupplied_info *user_info_temp;
|
||||
status = encrypt_user_info(mem_ctx, auth_context,
|
||||
AUTH_PASSWORD_HASH,
|
||||
user_info, &user_info_temp);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("Failed to convert plaintext password to password HASH: %s\n", nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
user_info = user_info_temp;
|
||||
|
||||
/*fall through*/
|
||||
}
|
||||
case AUTH_PASSWORD_HASH:
|
||||
*lm_sess_key = data_blob(NULL, 0);
|
||||
*user_sess_key = data_blob(NULL, 0);
|
||||
status = hash_password_check(mem_ctx,
|
||||
user_info->password.hash.lanman,
|
||||
user_info->password.hash.nt,
|
||||
user_info->mapped.account_name,
|
||||
lm_pwd, nt_pwd);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
break;
|
||||
|
||||
case AUTH_PASSWORD_RESPONSE:
|
||||
status = ntlm_password_check(mem_ctx, user_info->logon_parameters,
|
||||
&auth_context->challenge.data,
|
||||
&user_info->password.response.lanman,
|
||||
&user_info->password.response.nt,
|
||||
user_info->mapped.account_name,
|
||||
user_info->client.account_name,
|
||||
user_info->client.domain_name,
|
||||
lm_pwd, nt_pwd,
|
||||
user_sess_key, lm_sess_key);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
break;
|
||||
}
|
||||
|
||||
if (user_sess_key && user_sess_key->data) {
|
||||
talloc_steal(auth_context, user_sess_key->data);
|
||||
}
|
||||
if (lm_sess_key && lm_sess_key->data) {
|
||||
talloc_steal(auth_context, lm_sess_key->data);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static NTSTATUS authsam_authenticate(struct auth_context *auth_context,
|
||||
TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
|
||||
struct ldb_message **msgs,
|
||||
struct ldb_message **msgs_domain_ref,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key)
|
||||
{
|
||||
struct samr_Password *lm_pwd, *nt_pwd;
|
||||
NTSTATUS nt_status;
|
||||
uint16_t acct_flags = samdb_result_acct_flags(msgs[0], "userAccountControl");
|
||||
|
||||
/* Quit if the account was locked out. */
|
||||
if (acct_flags & ACB_AUTOLOCK) {
|
||||
DEBUG(3,("check_sam_security: Account for user %s was locked out.\n",
|
||||
user_info->mapped.account_name));
|
||||
return NT_STATUS_ACCOUNT_LOCKED_OUT;
|
||||
}
|
||||
|
||||
/* You can only do an interactive login to normal accounts */
|
||||
if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
|
||||
if (!(acct_flags & ACB_NORMAL)) {
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
}
|
||||
|
||||
nt_status = samdb_result_passwords(mem_ctx, msgs[0], &lm_pwd, &nt_pwd);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
nt_status = authsam_password_ok(auth_context, mem_ctx,
|
||||
acct_flags, lm_pwd, nt_pwd,
|
||||
user_info, user_sess_key, lm_sess_key);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
nt_status = authsam_account_ok(mem_ctx, sam_ctx,
|
||||
user_info->logon_parameters,
|
||||
msgs[0],
|
||||
msgs_domain_ref[0],
|
||||
user_info->workstation_name,
|
||||
user_info->mapped.account_name);
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static NTSTATUS authsam_check_password_internals(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char *domain,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
const char *account_name = user_info->mapped.account_name;
|
||||
struct ldb_message **msgs;
|
||||
struct ldb_message **domain_ref_msgs;
|
||||
struct ldb_context *sam_ctx;
|
||||
DATA_BLOB user_sess_key, lm_sess_key;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
|
||||
if (!account_name || !*account_name) {
|
||||
/* 'not for me' */
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
tmp_ctx = talloc_new(mem_ctx);
|
||||
if (!tmp_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
sam_ctx = samdb_connect(tmp_ctx, system_session(mem_ctx));
|
||||
if (sam_ctx == NULL) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INVALID_SYSTEM_SERVICE;
|
||||
}
|
||||
|
||||
nt_status = authsam_search_account(tmp_ctx, sam_ctx, account_name, domain, &msgs, &domain_ref_msgs);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = authsam_authenticate(ctx->auth_ctx, tmp_ctx, sam_ctx, msgs, domain_ref_msgs, user_info,
|
||||
&user_sess_key, &lm_sess_key);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, msgs[0], domain_ref_msgs[0],
|
||||
user_sess_key, lm_sess_key,
|
||||
server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
talloc_steal(mem_ctx, *server_info);
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS authsam_ignoredomain_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS authsam_ignoredomain_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
return authsam_check_password_internals(ctx, mem_ctx, NULL, user_info, server_info);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Check SAM security (above) but with a few extra checks.
|
||||
****************************************************************************/
|
||||
static NTSTATUS authsam_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
BOOL is_local_name, is_my_domain;
|
||||
|
||||
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
is_local_name = is_myname(user_info->mapped.domain_name);
|
||||
is_my_domain = strequal(user_info->mapped.domain_name, lp_workgroup());
|
||||
|
||||
/* check whether or not we service this domain/workgroup name */
|
||||
switch (lp_server_role()) {
|
||||
case ROLE_STANDALONE:
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case ROLE_DOMAIN_MEMBER:
|
||||
if (!is_local_name) {
|
||||
DEBUG(6,("authsam_check_password: %s is not one of my local names (DOMAIN_MEMBER)\n",
|
||||
user_info->mapped.domain_name));
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case ROLE_DOMAIN_PDC:
|
||||
case ROLE_DOMAIN_BDC:
|
||||
if (!is_local_name && !is_my_domain) {
|
||||
DEBUG(6,("authsam_check_password: %s is not one of my local names or domain name (DC)\n",
|
||||
user_info->mapped.domain_name));
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(6,("authsam_check_password: lp_server_role() has an undefined value\n"));
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Check SAM security (above) but with a few extra checks.
|
||||
****************************************************************************/
|
||||
static NTSTATUS authsam_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
const char *domain;
|
||||
|
||||
/* check whether or not we service this domain/workgroup name */
|
||||
switch (lp_server_role()) {
|
||||
case ROLE_STANDALONE:
|
||||
case ROLE_DOMAIN_MEMBER:
|
||||
domain = lp_netbios_name();
|
||||
break;
|
||||
|
||||
case ROLE_DOMAIN_PDC:
|
||||
case ROLE_DOMAIN_BDC:
|
||||
domain = lp_workgroup();
|
||||
break;
|
||||
|
||||
default:
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
return authsam_check_password_internals(ctx, mem_ctx, domain, user_info, server_info);
|
||||
}
|
||||
|
||||
static const struct auth_operations sam_ignoredomain_ops = {
|
||||
.name = "sam_ignoredomain",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = authsam_ignoredomain_want_check,
|
||||
.check_password = authsam_ignoredomain_check_password
|
||||
};
|
||||
|
||||
static const struct auth_operations sam_ops = {
|
||||
.name = "sam",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = authsam_want_check,
|
||||
.check_password = authsam_check_password
|
||||
};
|
||||
|
||||
NTSTATUS auth_sam_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
ret = auth_register(&sam_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'sam' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = auth_register(&sam_ignoredomain_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'sam_ignoredomain' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Convert a server info struct into the form for PAC and NETLOGON replies
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
|
||||
Copyright (C) Stefan Metzmacher <metze@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon.h"
|
||||
|
||||
NTSTATUS auth_convert_server_info_sambaseinfo(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
struct netr_SamBaseInfo **_sam)
|
||||
{
|
||||
struct netr_SamBaseInfo *sam = talloc_zero(mem_ctx, struct netr_SamBaseInfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(sam);
|
||||
|
||||
sam->domain_sid = dom_sid_dup(mem_ctx, server_info->account_sid);
|
||||
NT_STATUS_HAVE_NO_MEMORY(sam->domain_sid);
|
||||
sam->domain_sid->num_auths--;
|
||||
|
||||
sam->last_logon = server_info->last_logon;
|
||||
sam->last_logoff = server_info->last_logoff;
|
||||
sam->acct_expiry = server_info->acct_expiry;
|
||||
sam->last_password_change = server_info->last_password_change;
|
||||
sam->allow_password_change = server_info->allow_password_change;
|
||||
sam->force_password_change = server_info->force_password_change;
|
||||
|
||||
sam->account_name.string = server_info->account_name;
|
||||
sam->full_name.string = server_info->full_name;
|
||||
sam->logon_script.string = server_info->logon_script;
|
||||
sam->profile_path.string = server_info->profile_path;
|
||||
sam->home_directory.string = server_info->home_directory;
|
||||
sam->home_drive.string = server_info->home_drive;
|
||||
|
||||
sam->logon_count = server_info->logon_count;
|
||||
sam->bad_password_count = sam->bad_password_count;
|
||||
sam->rid = server_info->account_sid->sub_auths[server_info->account_sid->num_auths-1];
|
||||
sam->primary_gid = server_info->primary_group_sid->sub_auths[server_info->primary_group_sid->num_auths-1];
|
||||
|
||||
sam->groups.count = 0;
|
||||
sam->groups.rids = NULL;
|
||||
|
||||
if (server_info->n_domain_groups > 0) {
|
||||
int i;
|
||||
sam->groups.rids = talloc_array(sam, struct samr_RidWithAttribute,
|
||||
server_info->n_domain_groups);
|
||||
|
||||
if (sam->groups.rids == NULL)
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
|
||||
for (i=0; i<server_info->n_domain_groups; i++) {
|
||||
struct dom_sid *group_sid = server_info->domain_groups[i];
|
||||
if (!dom_sid_in_domain(sam->domain_sid, group_sid)) {
|
||||
/* We handle this elsewhere */
|
||||
continue;
|
||||
}
|
||||
sam->groups.rids[sam->groups.count].rid =
|
||||
group_sid->sub_auths[group_sid->num_auths-1];
|
||||
|
||||
sam->groups.rids[sam->groups.count].attributes =
|
||||
SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
|
||||
sam->groups.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
sam->user_flags = 0; /* TODO: w2k3 uses 0x120. We know 0x20
|
||||
* as extra sids (PAC doc) but what is
|
||||
* 0x100? */
|
||||
sam->acct_flags = server_info->acct_flags;
|
||||
sam->logon_server.string = server_info->logon_server;
|
||||
sam->domain.string = server_info->domain_name;
|
||||
|
||||
ZERO_STRUCT(sam->unknown);
|
||||
|
||||
ZERO_STRUCT(sam->key);
|
||||
if (server_info->user_session_key.length == sizeof(sam->key.key)) {
|
||||
memcpy(sam->key.key, server_info->user_session_key.data, sizeof(sam->key.key));
|
||||
}
|
||||
|
||||
ZERO_STRUCT(sam->LMSessKey);
|
||||
if (server_info->lm_session_key.length == sizeof(sam->LMSessKey.key)) {
|
||||
memcpy(sam->LMSessKey.key, server_info->lm_session_key.data,
|
||||
sizeof(sam->LMSessKey.key));
|
||||
}
|
||||
|
||||
*_sam = sam;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS auth_convert_server_info_saminfo3(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
struct netr_SamInfo3 **_sam3)
|
||||
{
|
||||
struct netr_SamBaseInfo *sam;
|
||||
struct netr_SamInfo3 *sam3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
|
||||
NTSTATUS status;
|
||||
int i;
|
||||
NT_STATUS_HAVE_NO_MEMORY(sam3);
|
||||
|
||||
status = auth_convert_server_info_sambaseinfo(mem_ctx, server_info, &sam);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
sam3->base = *sam;
|
||||
sam3->sidcount = 0;
|
||||
sam3->sids = NULL;
|
||||
|
||||
|
||||
sam3->sids = talloc_array(sam, struct netr_SidAttr,
|
||||
server_info->n_domain_groups);
|
||||
NT_STATUS_HAVE_NO_MEMORY(sam3->sids);
|
||||
|
||||
for (i=0; i<server_info->n_domain_groups; i++) {
|
||||
if (dom_sid_in_domain(sam->domain_sid, server_info->domain_groups[i])) {
|
||||
continue;
|
||||
}
|
||||
sam3->sids[sam3->sidcount].sid = talloc_reference(sam3->sids,server_info->domain_groups[i]);
|
||||
sam3->sids[sam3->sidcount].attribute =
|
||||
SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
|
||||
sam3->sidcount += 1;
|
||||
}
|
||||
if (sam3->sidcount) {
|
||||
sam3->base.user_flags |= NETLOGON_EXTRA_SIDS;
|
||||
} else {
|
||||
sam3->sids = NULL;
|
||||
}
|
||||
*_sam3 = sam3;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Authenticate to a remote server
|
||||
Copyright (C) Andrew Tridgell 1992-1998
|
||||
Copyright (C) Andrew Bartlett 2001
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/****************************************************************************
|
||||
Support for server level security.
|
||||
****************************************************************************/
|
||||
|
||||
static struct smbcli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct smbcli_state *cli = NULL;
|
||||
fstring desthost;
|
||||
struct ipv4_addr dest_ip;
|
||||
const char *p;
|
||||
char *pserver;
|
||||
BOOL connected_ok = False;
|
||||
|
||||
if (!(cli = smbcli_initialise(cli)))
|
||||
return NULL;
|
||||
|
||||
/* security = server just can't function with spnego */
|
||||
cli->use_spnego = False;
|
||||
|
||||
pserver = talloc_strdup(mem_ctx, lp_passwordserver());
|
||||
p = pserver;
|
||||
|
||||
while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
|
||||
strupper(desthost);
|
||||
|
||||
if(!resolve_name( desthost, &dest_ip, 0x20)) {
|
||||
DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ismyip(dest_ip)) {
|
||||
DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we use a mutex to prevent two connections at once - when a
|
||||
Win2k PDC get two connections where one hasn't completed a
|
||||
session setup yet it will send a TCP reset to the first
|
||||
connection (tridge) */
|
||||
|
||||
if (!grab_server_mutex(desthost)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (smbcli_connect(cli, desthost, &dest_ip)) {
|
||||
DEBUG(3,("connected to password server %s\n",desthost));
|
||||
connected_ok = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!connected_ok) {
|
||||
release_server_mutex();
|
||||
DEBUG(0,("password server not available\n"));
|
||||
talloc_free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!attempt_netbios_session_request(cli, lp_netbios_name(),
|
||||
desthost, &dest_ip)) {
|
||||
release_server_mutex();
|
||||
DEBUG(1,("password server fails session request\n"));
|
||||
talloc_free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strequal(desthost,myhostname(mem_ctx))) {
|
||||
exit_server("Password server loop!");
|
||||
}
|
||||
|
||||
DEBUG(3,("got session\n"));
|
||||
|
||||
if (!smbcli_negprot(cli)) {
|
||||
DEBUG(1,("%s rejected the negprot\n",desthost));
|
||||
release_server_mutex();
|
||||
talloc_free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cli->protocol < PROTOCOL_LANMAN2 ||
|
||||
!(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
|
||||
DEBUG(1,("%s isn't in user level security mode\n",desthost));
|
||||
release_server_mutex();
|
||||
talloc_free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the first session setup done quickly, to avoid silly
|
||||
Win2k bugs. (The next connection to the server will kill
|
||||
this one...
|
||||
*/
|
||||
|
||||
if (!smbcli_session_setup(cli, "", "", 0, "", 0,
|
||||
"")) {
|
||||
DEBUG(0,("%s rejected the initial session setup (%s)\n",
|
||||
desthost, smbcli_errstr(cli)));
|
||||
release_server_mutex();
|
||||
talloc_free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
release_server_mutex();
|
||||
|
||||
DEBUG(3,("password server OK\n"));
|
||||
|
||||
return cli;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Clean up our allocated cli.
|
||||
****************************************************************************/
|
||||
|
||||
static void free_server_private_data(void **private_data_pointer)
|
||||
{
|
||||
struct smbcli_state **cli = (struct smbcli_state **)private_data_pointer;
|
||||
if (*cli && (*cli)->initialised) {
|
||||
talloc_free(*cli);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Get the challenge out of a password server.
|
||||
****************************************************************************/
|
||||
|
||||
static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_context,
|
||||
void **my_private_data,
|
||||
TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct smbcli_state *cli = server_cryptkey(mem_ctx);
|
||||
|
||||
if (cli) {
|
||||
DEBUG(3,("using password server validation\n"));
|
||||
|
||||
if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
|
||||
/* We can't work with unencrypted password servers
|
||||
unless 'encrypt passwords = no' */
|
||||
DEBUG(5,("make_auth_info_server: Server is unencrypted, no challenge available..\n"));
|
||||
|
||||
/* However, it is still a perfectly fine connection
|
||||
to pass that unencrypted password over */
|
||||
*my_private_data = (void *)cli;
|
||||
return data_blob(NULL, 0);
|
||||
|
||||
} else if (cli->secblob.length < 8) {
|
||||
/* We can't do much if we don't get a full challenge */
|
||||
DEBUG(2,("make_auth_info_server: Didn't receive a full challenge from server\n"));
|
||||
talloc_free(cli);
|
||||
return data_blob(NULL, 0);
|
||||
}
|
||||
|
||||
*my_private_data = (void *)cli;
|
||||
|
||||
/* The return must be allocated on the caller's mem_ctx, as our own will be
|
||||
destoyed just after the call. */
|
||||
return data_blob_talloc(auth_context->mem_ctx, cli->secblob.data,8);
|
||||
} else {
|
||||
return data_blob(NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Check for a valid username and password in security=server mode.
|
||||
- Validate a password with the password server.
|
||||
****************************************************************************/
|
||||
|
||||
static NTSTATUS check_smbserver_security(const struct auth_context *auth_context,
|
||||
void *my_private_data,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const auth_usersupplied_info *user_info,
|
||||
auth_serversupplied_info **server_info)
|
||||
{
|
||||
struct smbcli_state *cli;
|
||||
static uint8_t badpass[24];
|
||||
static fstring baduser;
|
||||
static BOOL tested_password_server = False;
|
||||
static BOOL bad_password_server = False;
|
||||
NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
|
||||
BOOL locally_made_cli = False;
|
||||
|
||||
/*
|
||||
* Check that the requested domain is not our own machine name.
|
||||
* If it is, we should never check the PDC here, we use our own local
|
||||
* password file.
|
||||
*/
|
||||
|
||||
if(is_myname(user_info->domain.str)) {
|
||||
DEBUG(3,("check_smbserver_security: Requested domain was for this machine.\n"));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
|
||||
cli = my_private_data;
|
||||
|
||||
if (cli) {
|
||||
} else {
|
||||
cli = server_cryptkey(mem_ctx);
|
||||
locally_made_cli = True;
|
||||
}
|
||||
|
||||
if (!cli || !cli->initialised) {
|
||||
DEBUG(1,("password server is not connected (cli not initilised)\n"));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
|
||||
if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
|
||||
if (user_info->encrypted) {
|
||||
DEBUG(1,("password server %s is plaintext, but we are encrypted. This just can't work :-(\n", cli->desthost));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
} else {
|
||||
if (memcmp(cli->secblob.data, auth_context->challenge.data, 8) != 0) {
|
||||
DEBUG(1,("the challenge that the password server (%s) supplied us is not the one we gave our client. This just can't work :-(\n", cli->desthost));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if(badpass[0] == 0)
|
||||
memset(badpass, 0x1f, sizeof(badpass));
|
||||
|
||||
if((user_info->nt_resp.length == sizeof(badpass)) &&
|
||||
!memcmp(badpass, user_info->nt_resp.data, sizeof(badpass))) {
|
||||
/*
|
||||
* Very unlikely, our random bad password is the same as the users
|
||||
* password.
|
||||
*/
|
||||
memset(badpass, badpass[0]+1, sizeof(badpass));
|
||||
}
|
||||
|
||||
if(baduser[0] == 0) {
|
||||
fstrcpy(baduser, INVALID_USER_PREFIX);
|
||||
fstrcat(baduser, lp_netbios_name());
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt a session setup with a totally incorrect password.
|
||||
* If this succeeds with the guest bit *NOT* set then the password
|
||||
* server is broken and is not correctly setting the guest bit. We
|
||||
* need to detect this as some versions of NT4.x are broken. JRA.
|
||||
*/
|
||||
|
||||
/* I sure as hell hope that there aren't servers out there that take
|
||||
* NTLMv2 and have this bug, as we don't test for that...
|
||||
* - abartlet@samba.org
|
||||
*/
|
||||
|
||||
if ((!tested_password_server) && (lp_paranoid_server_security())) {
|
||||
if (smbcli_session_setup(cli, baduser, (char *)badpass, sizeof(badpass),
|
||||
(char *)badpass, sizeof(badpass), user_info->domain.str)) {
|
||||
|
||||
/*
|
||||
* We connected to the password server so we
|
||||
* can say we've tested it.
|
||||
*/
|
||||
tested_password_server = True;
|
||||
|
||||
if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) {
|
||||
DEBUG(0,("server_validate: password server %s allows users as non-guest \
|
||||
with a bad password.\n", cli->desthost));
|
||||
DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \
|
||||
use this machine as the password server.\n"));
|
||||
smbcli_ulogoff(cli);
|
||||
|
||||
/*
|
||||
* Password server has the bug.
|
||||
*/
|
||||
bad_password_server = True;
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
smbcli_ulogoff(cli);
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
* We have already tested the password server.
|
||||
* Fail immediately if it has the bug.
|
||||
*/
|
||||
|
||||
if(bad_password_server) {
|
||||
DEBUG(0,("server_validate: [1] password server %s allows users as non-guest \
|
||||
with a bad password.\n", cli->desthost));
|
||||
DEBUG(0,("server_validate: [1] This is broken (and insecure) behaviour. Please do not \
|
||||
use this machine as the password server.\n"));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we know the password server will correctly set the guest bit, or is
|
||||
* not guest enabled, we can try with the real password.
|
||||
*/
|
||||
|
||||
if (!user_info->encrypted) {
|
||||
/* Plaintext available */
|
||||
if (!smbcli_session_setup(cli, user_info->smb_name.str,
|
||||
(char *)user_info->plaintext_password.data,
|
||||
user_info->plaintext_password.length,
|
||||
NULL, 0,
|
||||
user_info->domain.str)) {
|
||||
DEBUG(1,("password server %s rejected the password\n", cli->desthost));
|
||||
/* Make this smbcli_nt_error() when the conversion is in */
|
||||
nt_status = smbcli_nt_error(cli);
|
||||
} else {
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
} else {
|
||||
if (!smbcli_session_setup(cli, user_info->smb_name.str,
|
||||
(char *)user_info->lm_resp.data,
|
||||
user_info->lm_resp.length,
|
||||
(char *)user_info->nt_resp.data,
|
||||
user_info->nt_resp.length,
|
||||
user_info->domain.str)) {
|
||||
DEBUG(1,("password server %s rejected the password\n", cli->desthost));
|
||||
/* Make this smbcli_nt_error() when the conversion is in */
|
||||
nt_status = smbcli_nt_error(cli);
|
||||
} else {
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* if logged in as guest then reject */
|
||||
if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) {
|
||||
DEBUG(1,("password server %s gave us guest only\n", cli->desthost));
|
||||
nt_status = NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
|
||||
smbcli_ulogoff(cli);
|
||||
|
||||
if NT_STATUS_IS_OK(nt_status) {
|
||||
struct passwd *pass = Get_Pwnam(user_info->internal_username.str);
|
||||
if (pass) {
|
||||
nt_status = make_server_info_pw(auth_context, server_info, pass);
|
||||
} else {
|
||||
nt_status = NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
}
|
||||
|
||||
if (locally_made_cli) {
|
||||
talloc_free(cli);
|
||||
}
|
||||
|
||||
return(nt_status);
|
||||
}
|
||||
|
||||
NTSTATUS auth_init_smbserver(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
|
||||
{
|
||||
if (!make_auth_methods(auth_context, auth_method)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
(*auth_method)->name = "smbserver";
|
||||
(*auth_method)->auth = check_smbserver_security;
|
||||
(*auth_method)->get_chal = auth_get_challenge_server;
|
||||
(*auth_method)->send_keepalive = send_server_keepalive;
|
||||
(*auth_method)->free_private_data = free_server_private_data;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
auth functions
|
||||
|
||||
Copyright (C) Simo Sorce 2005
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Andrew Bartlett 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "lib/events/events.h"
|
||||
|
||||
_PUBLIC_ NTSTATUS authenticate_username_pw(TALLOC_CTX *mem_ctx,
|
||||
struct event_context *ev,
|
||||
struct messaging_context *msg,
|
||||
const char *nt4_domain,
|
||||
const char *nt4_username,
|
||||
const char *password,
|
||||
struct auth_session_info **session_info)
|
||||
{
|
||||
struct auth_context *auth_context;
|
||||
struct auth_usersupplied_info *user_info;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
NTSTATUS nt_status;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
|
||||
|
||||
if (!tmp_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
nt_status = auth_context_create(tmp_ctx, lp_auth_methods(),
|
||||
ev, msg,
|
||||
&auth_context);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
user_info = talloc(tmp_ctx, struct auth_usersupplied_info);
|
||||
if (!user_info) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
user_info->mapped_state = True;
|
||||
user_info->client.account_name = nt4_username;
|
||||
user_info->mapped.account_name = nt4_username;
|
||||
user_info->client.domain_name = nt4_domain;
|
||||
user_info->mapped.domain_name = nt4_domain;
|
||||
|
||||
user_info->workstation_name = NULL;
|
||||
|
||||
user_info->remote_host = NULL;
|
||||
|
||||
user_info->password_state = AUTH_PASSWORD_PLAIN;
|
||||
user_info->password.plaintext = talloc_strdup(user_info, password);
|
||||
|
||||
user_info->flags = USER_INFO_CASE_INSENSITIVE_USERNAME |
|
||||
USER_INFO_DONT_CHECK_UNIX_ACCOUNT;
|
||||
|
||||
user_info->logon_parameters = 0;
|
||||
|
||||
nt_status = auth_check_password(auth_context, tmp_ctx, user_info, &server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = auth_generate_session_info(tmp_ctx, server_info, session_info);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_steal(mem_ctx, *session_info);
|
||||
}
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,839 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Bartlett 2001
|
||||
Copyright (C) Jeremy Allison 2001
|
||||
Copyright (C) Simo Sorce 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "system/passwd.h" /* needed by some systems for struct passwd */
|
||||
#include "lib/socket/socket.h"
|
||||
#include "auth/pam_errors.h"
|
||||
|
||||
/* TODO: look at how to best fill in parms retrieveing a struct passwd info
|
||||
* except in case USER_INFO_DONT_CHECK_UNIX_ACCOUNT is set
|
||||
*/
|
||||
static NTSTATUS authunix_make_server_info(TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct passwd *pwd,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
struct auth_serversupplied_info *server_info;
|
||||
NTSTATUS status;
|
||||
|
||||
/* This is a real, real hack */
|
||||
if (pwd->pw_uid == 0) {
|
||||
status = auth_system_server_info(mem_ctx, &server_info);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
server_info->account_name = talloc_steal(server_info, pwd->pw_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
|
||||
server_info->domain_name = talloc_strdup(server_info, "unix");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
|
||||
} else {
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
server_info->authenticated = True;
|
||||
|
||||
server_info->account_name = talloc_steal(server_info, pwd->pw_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
|
||||
server_info->domain_name = talloc_strdup(server_info, "unix");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
|
||||
|
||||
/* This isn't in any way correct.. */
|
||||
server_info->account_sid = NULL;
|
||||
server_info->primary_group_sid = NULL;
|
||||
server_info->n_domain_groups = 0;
|
||||
server_info->domain_groups = NULL;
|
||||
}
|
||||
server_info->user_session_key = data_blob(NULL,0);
|
||||
server_info->lm_session_key = data_blob(NULL,0);
|
||||
|
||||
server_info->full_name = talloc_steal(server_info, pwd->pw_gecos);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
|
||||
server_info->logon_script = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
|
||||
server_info->profile_path = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
|
||||
server_info->home_directory = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
|
||||
server_info->home_drive = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
|
||||
|
||||
server_info->last_logon = 0;
|
||||
server_info->last_logoff = 0;
|
||||
server_info->acct_expiry = 0;
|
||||
server_info->last_password_change = 0;
|
||||
server_info->allow_password_change = 0;
|
||||
server_info->force_password_change = 0;
|
||||
server_info->logon_count = 0;
|
||||
server_info->bad_password_count = 0;
|
||||
server_info->acct_flags = 0;
|
||||
|
||||
*_server_info = server_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS talloc_getpwnam(TALLOC_CTX *ctx, const char *username, struct passwd **pws)
|
||||
{
|
||||
struct passwd *ret;
|
||||
struct passwd *from;
|
||||
|
||||
*pws = NULL;
|
||||
|
||||
ret = talloc(ctx, struct passwd);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret);
|
||||
|
||||
from = getpwnam(username);
|
||||
if (!from) {
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
ret->pw_name = talloc_strdup(ctx, from->pw_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret->pw_name);
|
||||
|
||||
ret->pw_passwd = talloc_strdup(ctx, from->pw_passwd);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret->pw_passwd);
|
||||
|
||||
ret->pw_uid = from->pw_uid;
|
||||
ret->pw_gid = from->pw_gid;
|
||||
ret->pw_gecos = talloc_strdup(ctx, from->pw_gecos);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret->pw_gecos);
|
||||
|
||||
ret->pw_dir = talloc_strdup(ctx, from->pw_dir);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret->pw_dir);
|
||||
|
||||
ret->pw_shell = talloc_strdup(ctx, from->pw_shell);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret->pw_shell);
|
||||
|
||||
*pws = ret;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_SECURITY_PAM_APPL_H
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
struct smb_pam_user_info {
|
||||
const char *account_name;
|
||||
const char *plaintext_password;
|
||||
};
|
||||
|
||||
#define COPY_STRING(s) (s) ? strdup(s) : NULL
|
||||
|
||||
/*
|
||||
* Check user password
|
||||
* Currently it uses PAM only and fails on systems without PAM
|
||||
* Samba3 code located in pass_check.c is to ugly to be used directly it will
|
||||
* need major rework that's why pass_check.c is still there.
|
||||
*/
|
||||
|
||||
static int smb_pam_conv(int num_msg, const struct pam_message **msg,
|
||||
struct pam_response **reply, void *appdata_ptr)
|
||||
{
|
||||
struct smb_pam_user_info *info = (struct smb_pam_user_info *)appdata_ptr;
|
||||
int num;
|
||||
|
||||
if (num_msg <= 0) {
|
||||
*reply = NULL;
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apparantly HPUX has a buggy PAM that doesn't support the
|
||||
* data pointer. Fail if this is the case. JRA.
|
||||
*/
|
||||
|
||||
if (info == NULL) {
|
||||
*reply = NULL;
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM frees memory in reply messages by itself
|
||||
* so use malloc instead of talloc here.
|
||||
*/
|
||||
*reply = malloc_array_p(struct pam_response, num_msg);
|
||||
if (*reply == NULL) {
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
|
||||
for (num = 0; num < num_msg; num++) {
|
||||
switch (msg[num]->msg_style) {
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
(*reply)[num].resp_retcode = PAM_SUCCESS;
|
||||
(*reply)[num].resp = COPY_STRING(info->account_name);
|
||||
break;
|
||||
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
(*reply)[num].resp_retcode = PAM_SUCCESS;
|
||||
(*reply)[num].resp = COPY_STRING(info->plaintext_password);
|
||||
break;
|
||||
|
||||
case PAM_TEXT_INFO:
|
||||
(*reply)[num].resp_retcode = PAM_SUCCESS;
|
||||
(*reply)[num].resp = NULL;
|
||||
DEBUG(4,("PAM Info message in conversation function: %s\n", (msg[num]->msg)));
|
||||
break;
|
||||
|
||||
case PAM_ERROR_MSG:
|
||||
(*reply)[num].resp_retcode = PAM_SUCCESS;
|
||||
(*reply)[num].resp = NULL;
|
||||
DEBUG(4,("PAM Error message in conversation function: %s\n", (msg[num]->msg)));
|
||||
break;
|
||||
|
||||
default:
|
||||
while (num > 0) {
|
||||
SAFE_FREE((*reply)[num-1].resp);
|
||||
num--;
|
||||
}
|
||||
SAFE_FREE(*reply);
|
||||
*reply = NULL;
|
||||
DEBUG(1,("Error: PAM subsystme sent an UNKNOWN message type to the conversation function!\n"));
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start PAM authentication for specified account
|
||||
*/
|
||||
|
||||
static NTSTATUS smb_pam_start(pam_handle_t **pamh, const char *account_name, const char *remote_host, struct pam_conv *pconv)
|
||||
{
|
||||
int pam_error;
|
||||
|
||||
if (account_name == NULL || remote_host == NULL) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", account_name));
|
||||
|
||||
pam_error = pam_start("samba", account_name, pconv, pamh);
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
/* no vaild pamh here, can we reliably call pam_strerror ? */
|
||||
DEBUG(4,("smb_pam_start: pam_start failed!\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
#ifdef PAM_RHOST
|
||||
DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", remote_host));
|
||||
pam_error = pam_set_item(*pamh, PAM_RHOST, remote_host);
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
NTSTATUS nt_status;
|
||||
|
||||
DEBUG(4,("smb_pam_start: setting rhost failed with error: %s\n",
|
||||
pam_strerror(*pamh, pam_error)));
|
||||
nt_status = pam_to_nt_status(pam_error);
|
||||
|
||||
pam_error = pam_end(*pamh, 0);
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
/* no vaild pamh here, can we reliably call pam_strerror ? */
|
||||
DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
|
||||
pam_error));
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
#endif
|
||||
#ifdef PAM_TTY
|
||||
DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
|
||||
pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
NTSTATUS nt_status;
|
||||
|
||||
DEBUG(4,("smb_pam_start: setting tty failed with error: %s\n",
|
||||
pam_strerror(*pamh, pam_error)));
|
||||
nt_status = pam_to_nt_status(pam_error);
|
||||
|
||||
pam_error = pam_end(*pamh, 0);
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
/* no vaild pamh here, can we reliably call pam_strerror ? */
|
||||
DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
|
||||
pam_error));
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
#endif
|
||||
DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", account_name));
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS smb_pam_end(pam_handle_t *pamh)
|
||||
{
|
||||
int pam_error;
|
||||
|
||||
if (pamh != NULL) {
|
||||
pam_error = pam_end(pamh, 0);
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
/* no vaild pamh here, can we reliably call pam_strerror ? */
|
||||
DEBUG(4,("smb_pam_end: clean up failed, pam_end gave error %d.\n",
|
||||
pam_error));
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(2,("smb_pam_end: pamh is NULL, PAM not initialized ?\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM Authentication Handler
|
||||
*/
|
||||
static NTSTATUS smb_pam_auth(pam_handle_t *pamh, const char *user)
|
||||
{
|
||||
int pam_error;
|
||||
|
||||
/*
|
||||
* To enable debugging set in /etc/pam.d/samba:
|
||||
* auth required /lib/security/pam_pwdb.so nullok shadow audit
|
||||
*/
|
||||
|
||||
DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user));
|
||||
|
||||
pam_error = pam_authenticate(pamh, PAM_SILENT | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK);
|
||||
switch( pam_error ){
|
||||
case PAM_AUTH_ERR:
|
||||
DEBUG(2, ("smb_pam_auth: PAM: Authentication Error for user %s\n", user));
|
||||
break;
|
||||
case PAM_CRED_INSUFFICIENT:
|
||||
DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
|
||||
break;
|
||||
case PAM_AUTHINFO_UNAVAIL:
|
||||
DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
|
||||
break;
|
||||
case PAM_USER_UNKNOWN:
|
||||
DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
|
||||
break;
|
||||
case PAM_MAXTRIES:
|
||||
DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
|
||||
break;
|
||||
case PAM_ABORT:
|
||||
DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
|
||||
break;
|
||||
case PAM_SUCCESS:
|
||||
DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
|
||||
break;
|
||||
default:
|
||||
DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
|
||||
break;
|
||||
}
|
||||
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM Account Handler
|
||||
*/
|
||||
static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
|
||||
{
|
||||
int pam_error;
|
||||
|
||||
DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user));
|
||||
|
||||
pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
|
||||
switch( pam_error ) {
|
||||
case PAM_AUTHTOK_EXPIRED:
|
||||
DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user));
|
||||
break;
|
||||
case PAM_ACCT_EXPIRED:
|
||||
DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user));
|
||||
break;
|
||||
case PAM_AUTH_ERR:
|
||||
DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user));
|
||||
break;
|
||||
case PAM_PERM_DENIED:
|
||||
DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user));
|
||||
break;
|
||||
case PAM_USER_UNKNOWN:
|
||||
DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user));
|
||||
break;
|
||||
case PAM_SUCCESS:
|
||||
DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user));
|
||||
break;
|
||||
default:
|
||||
DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user));
|
||||
break;
|
||||
}
|
||||
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM Credential Setting
|
||||
*/
|
||||
|
||||
static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
|
||||
{
|
||||
int pam_error;
|
||||
|
||||
/*
|
||||
* This will allow samba to aquire a kerberos token. And, when
|
||||
* exporting an AFS cell, be able to /write/ to this cell.
|
||||
*/
|
||||
|
||||
DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
|
||||
|
||||
pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT));
|
||||
switch( pam_error ) {
|
||||
case PAM_CRED_UNAVAIL:
|
||||
DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user ));
|
||||
break;
|
||||
case PAM_CRED_EXPIRED:
|
||||
DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user ));
|
||||
break;
|
||||
case PAM_USER_UNKNOWN:
|
||||
DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user ));
|
||||
break;
|
||||
case PAM_CRED_ERR:
|
||||
DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user ));
|
||||
break;
|
||||
case PAM_SUCCESS:
|
||||
DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user));
|
||||
break;
|
||||
default:
|
||||
DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user));
|
||||
break;
|
||||
}
|
||||
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
|
||||
static NTSTATUS check_unix_password(TALLOC_CTX *ctx, const struct auth_usersupplied_info *user_info, struct passwd **pws)
|
||||
{
|
||||
struct smb_pam_user_info *info;
|
||||
struct pam_conv *pamconv;
|
||||
pam_handle_t *pamh;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
info = talloc(ctx, struct smb_pam_user_info);
|
||||
if (info == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
info->account_name = user_info->mapped.account_name;
|
||||
info->plaintext_password = user_info->password.plaintext;
|
||||
|
||||
pamconv = talloc(ctx, struct pam_conv);
|
||||
if (pamconv == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
pamconv->conv = smb_pam_conv;
|
||||
pamconv->appdata_ptr = (void *)info;
|
||||
|
||||
/* TODO:
|
||||
* check for user_info->flags & USER_INFO_CASE_INSENSITIVE_USERNAME
|
||||
* if true set up a crack name routine.
|
||||
*/
|
||||
|
||||
nt_status = smb_pam_start(&pamh, user_info->mapped.account_name, user_info->remote_host ? user_info->remote_host->addr : NULL, pamconv);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = smb_pam_auth(pamh, user_info->mapped.account_name);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
smb_pam_end(pamh);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if ( ! (user_info->flags & USER_INFO_DONT_CHECK_UNIX_ACCOUNT)) {
|
||||
|
||||
nt_status = smb_pam_account(pamh, user_info->mapped.account_name);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
smb_pam_end(pamh);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = smb_pam_setcred(pamh, user_info->mapped.account_name);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
smb_pam_end(pamh);
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
smb_pam_end(pamh);
|
||||
|
||||
nt_status = talloc_getpwnam(ctx, user_info->mapped.account_name, pws);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/****************************************************************************
|
||||
core of password checking routine
|
||||
****************************************************************************/
|
||||
static NTSTATUS password_check(const char *username, const char *password,
|
||||
const char *crypted, const char *salt)
|
||||
{
|
||||
BOOL ret;
|
||||
|
||||
#ifdef WITH_AFS
|
||||
if (afs_auth(username, password))
|
||||
return NT_STATUS_OK;
|
||||
#endif /* WITH_AFS */
|
||||
|
||||
#ifdef WITH_DFS
|
||||
if (dfs_auth(username, password))
|
||||
return NT_STATUS_OK;
|
||||
#endif /* WITH_DFS */
|
||||
|
||||
#ifdef OSF1_ENH_SEC
|
||||
|
||||
ret = (strcmp(osf1_bigcrypt(password, salt), crypted) == 0);
|
||||
|
||||
if (!ret) {
|
||||
DEBUG(2,
|
||||
("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
|
||||
ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
|
||||
}
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
#endif /* OSF1_ENH_SEC */
|
||||
|
||||
#ifdef ULTRIX_AUTH
|
||||
ret = (strcmp((char *)crypt16(password, salt), crypted) == 0);
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
#endif /* ULTRIX_AUTH */
|
||||
|
||||
#ifdef LINUX_BIGCRYPT
|
||||
ret = (linux_bigcrypt(password, salt, crypted));
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
#endif /* LINUX_BIGCRYPT */
|
||||
|
||||
#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
|
||||
|
||||
/*
|
||||
* Some systems have bigcrypt in the C library but might not
|
||||
* actually use it for the password hashes (HPUX 10.20) is
|
||||
* a noteable example. So we try bigcrypt first, followed
|
||||
* by crypt.
|
||||
*/
|
||||
|
||||
if (strcmp(bigcrypt(password, salt), crypted) == 0)
|
||||
return NT_STATUS_OK;
|
||||
else
|
||||
ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
|
||||
|
||||
#ifdef HAVE_BIGCRYPT
|
||||
ret = (strcmp(bigcrypt(password, salt), crypted) == 0);
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
#endif /* HAVE_BIGCRYPT */
|
||||
|
||||
#ifndef HAVE_CRYPT
|
||||
DEBUG(1, ("Warning - no crypt available\n"));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
#else /* HAVE_CRYPT */
|
||||
ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
#endif /* HAVE_CRYPT */
|
||||
#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
|
||||
}
|
||||
|
||||
static NTSTATUS check_unix_password(TALLOC_CTX *ctx, const struct auth_usersupplied_info *user_info, struct passwd **ret_passwd)
|
||||
{
|
||||
char *username;
|
||||
char *password;
|
||||
char *pwcopy;
|
||||
char *salt;
|
||||
char *crypted;
|
||||
struct passwd *pws;
|
||||
NTSTATUS nt_status;
|
||||
int level = lp_passwordlevel();
|
||||
|
||||
*ret_passwd = NULL;
|
||||
|
||||
username = talloc_strdup(ctx, user_info->mapped.account_name);
|
||||
password = talloc_strdup(ctx, user_info->password.plaintext);
|
||||
|
||||
nt_status = talloc_getpwnam(ctx, username, &pws);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
crypted = pws->pw_passwd;
|
||||
salt = pws->pw_passwd;
|
||||
|
||||
#ifdef HAVE_GETSPNAM
|
||||
{
|
||||
struct spwd *spass;
|
||||
|
||||
/* many shadow systems require you to be root to get
|
||||
the password, in most cases this should already be
|
||||
the case when this function is called, except
|
||||
perhaps for IPC password changing requests */
|
||||
|
||||
spass = getspnam(pws->pw_name);
|
||||
if (spass && spass->sp_pwdp) {
|
||||
crypted = talloc_strdup(ctx, spass->sp_pwdp);
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
salt = talloc_strdup(ctx, spass->sp_pwdp);
|
||||
NT_STATUS_HAVE_NO_MEMORY(salt);
|
||||
}
|
||||
}
|
||||
#elif defined(IA_UINFO)
|
||||
{
|
||||
char *ia_password;
|
||||
/* Need to get password with SVR4.2's ia_ functions
|
||||
instead of get{sp,pw}ent functions. Required by
|
||||
UnixWare 2.x, tested on version
|
||||
2.1. (tangent@cyberport.com) */
|
||||
uinfo_t uinfo;
|
||||
if (ia_openinfo(pws->pw_name, &uinfo) != -1) {
|
||||
ia_get_logpwd(uinfo, &ia_password);
|
||||
crypted = talloc_strdup(ctx, ia_password);
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETPRPWNAM
|
||||
{
|
||||
struct pr_passwd *pr_pw = getprpwnam(pws->pw_name);
|
||||
if (pr_pw && pr_pw->ufld.fd_encrypt) {
|
||||
crypted = talloc_strdup(ctx, pr_pw->ufld.fd_encrypt);
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETPWANAM
|
||||
{
|
||||
struct passwd_adjunct *pwret;
|
||||
pwret = getpwanam(s);
|
||||
if (pwret && pwret->pwa_passwd) {
|
||||
crypted = talloc_strdup(ctx, pwret->pwa_passwd);
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OSF1_ENH_SEC
|
||||
{
|
||||
struct pr_passwd *mypasswd;
|
||||
DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n", username));
|
||||
mypasswd = getprpwnam(username);
|
||||
if (mypasswd) {
|
||||
username = talloc_strdup(ctx, mypasswd->ufld.fd_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(username);
|
||||
crypted = talloc_strdup(ctx, mypasswd->ufld.fd_encrypt);
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
} else {
|
||||
DEBUG(5,("OSF1_ENH_SEC: No entry for user %s in protected database !\n", username));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ULTRIX_AUTH
|
||||
{
|
||||
AUTHORIZATION *ap = getauthuid(pws->pw_uid);
|
||||
if (ap) {
|
||||
crypted = talloc_strdup(ctx, ap->a_password);
|
||||
endauthent();
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_TRUNCATED_SALT)
|
||||
/* crypt on some platforms (HPUX in particular)
|
||||
won't work with more than 2 salt characters. */
|
||||
salt[2] = 0;
|
||||
#endif
|
||||
|
||||
if (crypted[0] == '\0') {
|
||||
if (!lp_null_passwords()) {
|
||||
DEBUG(2, ("Disallowing %s with null password\n", username));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
if (password == NULL) {
|
||||
DEBUG(3, ("Allowing access to %s with null password\n", username));
|
||||
*ret_passwd = pws;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* try it as it came to us */
|
||||
nt_status = password_check(username, password, crypted, salt);
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
*ret_passwd = pws;
|
||||
return nt_status;
|
||||
}
|
||||
else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
|
||||
/* No point continuing if its not the password thats to blame (ie PAM disabled). */
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if ( user_info->flags | USER_INFO_CASE_INSENSITIVE_PASSWORD) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* if the password was given to us with mixed case then we don't
|
||||
* need to proceed as we know it hasn't been case modified by the
|
||||
* client */
|
||||
if (strhasupper(password) && strhaslower(password)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* make a copy of it */
|
||||
pwcopy = talloc_strdup(ctx, password);
|
||||
if (!pwcopy)
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
|
||||
/* try all lowercase if it's currently all uppercase */
|
||||
if (strhasupper(pwcopy)) {
|
||||
strlower(pwcopy);
|
||||
nt_status = password_check(username, pwcopy, crypted, salt);
|
||||
if NT_STATUS_IS_OK(nt_status) {
|
||||
*ret_passwd = pws;
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
/* give up? */
|
||||
if (level < 1) {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
/* last chance - all combinations of up to level chars upper! */
|
||||
strlower(pwcopy);
|
||||
|
||||
#if 0
|
||||
if (NT_STATUS_IS_OK(nt_status = string_combinations(pwcopy, password_check, level))) {
|
||||
*ret_passwd = pws;
|
||||
return nt_status;
|
||||
}
|
||||
#endif
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** Check a plaintext username/password
|
||||
*
|
||||
**/
|
||||
|
||||
static NTSTATUS authunix_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS authunix_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
TALLOC_CTX *check_ctx;
|
||||
NTSTATUS nt_status;
|
||||
struct passwd *pwd;
|
||||
|
||||
if (user_info->password_state != AUTH_PASSWORD_PLAIN) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
check_ctx = talloc_named_const(mem_ctx, 0, "check_unix_password");
|
||||
if (check_ctx == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
nt_status = check_unix_password(check_ctx, user_info, &pwd);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(check_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = authunix_make_server_info(mem_ctx, user_info, pwd, server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(check_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
talloc_free(check_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static const struct auth_operations unix_ops = {
|
||||
.name = "unix",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = authunix_want_check,
|
||||
.check_password = authunix_check_password
|
||||
};
|
||||
|
||||
NTSTATUS auth_unix_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
ret = auth_register(&unix_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register unix auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,669 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Authentication utility functions
|
||||
Copyright (C) Andrew Tridgell 1992-1998
|
||||
Copyright (C) Andrew Bartlett 2001
|
||||
Copyright (C) Jeremy Allison 2000-2001
|
||||
Copyright (C) Rafal Szczesniak 2002
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
/* this default function can be used by mostly all backends
|
||||
* which don't want to set a challenge
|
||||
*/
|
||||
NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge)
|
||||
{
|
||||
/* we don't want to set a challenge */
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Create an auth_usersupplied_data structure after appropriate mapping.
|
||||
****************************************************************************/
|
||||
|
||||
NTSTATUS map_user_info(TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_usersupplied_info **user_info_mapped)
|
||||
{
|
||||
const char *domain;
|
||||
char *account_name;
|
||||
char *d;
|
||||
DEBUG(5,("map_user_info: Mapping user [%s]\\[%s] from workstation [%s]\n",
|
||||
user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
|
||||
|
||||
account_name = talloc_strdup(mem_ctx, user_info->client.account_name);
|
||||
if (!account_name) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* don't allow "" as a domain, fixes a Win9X bug
|
||||
where it doens't supply a domain for logon script
|
||||
'net use' commands. */
|
||||
|
||||
/* Split user@realm names into user and realm components. This is TODO to fix with proper userprincipalname support */
|
||||
if (user_info->client.domain_name && *user_info->client.domain_name) {
|
||||
domain = user_info->client.domain_name;
|
||||
} else if (strchr_m(user_info->client.account_name, '@')) {
|
||||
d = strchr_m(account_name, '@');
|
||||
if (!d) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
d[0] = '\0';
|
||||
d++;
|
||||
domain = d;
|
||||
} else {
|
||||
domain = lp_workgroup();
|
||||
}
|
||||
|
||||
*user_info_mapped = talloc(mem_ctx, struct auth_usersupplied_info);
|
||||
if (!*user_info_mapped) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
talloc_reference(*user_info_mapped, user_info);
|
||||
**user_info_mapped = *user_info;
|
||||
(*user_info_mapped)->mapped_state = True;
|
||||
(*user_info_mapped)->mapped.domain_name = talloc_strdup(*user_info_mapped, domain);
|
||||
(*user_info_mapped)->mapped.account_name = talloc_strdup(*user_info_mapped, account_name);
|
||||
talloc_free(account_name);
|
||||
if (!(*user_info_mapped)->mapped.domain_name
|
||||
|| !(*user_info_mapped)->mapped.account_name) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Create an auth_usersupplied_data structure after appropriate mapping.
|
||||
****************************************************************************/
|
||||
|
||||
NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
|
||||
enum auth_password_state to_state,
|
||||
const struct auth_usersupplied_info *user_info_in,
|
||||
const struct auth_usersupplied_info **user_info_encrypted)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_usersupplied_info *user_info_temp;
|
||||
switch (to_state) {
|
||||
case AUTH_PASSWORD_RESPONSE:
|
||||
switch (user_info_in->password_state) {
|
||||
case AUTH_PASSWORD_PLAIN:
|
||||
{
|
||||
const struct auth_usersupplied_info *user_info_temp2;
|
||||
nt_status = encrypt_user_info(mem_ctx, auth_context,
|
||||
AUTH_PASSWORD_HASH,
|
||||
user_info_in, &user_info_temp2);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
user_info_in = user_info_temp2;
|
||||
/* fall through */
|
||||
}
|
||||
case AUTH_PASSWORD_HASH:
|
||||
{
|
||||
const uint8_t *challenge;
|
||||
DATA_BLOB chall_blob;
|
||||
user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
|
||||
if (!user_info_temp) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
talloc_reference(user_info_temp, user_info_in);
|
||||
*user_info_temp = *user_info_in;
|
||||
user_info_temp->mapped_state = to_state;
|
||||
|
||||
nt_status = auth_get_challenge(auth_context, &challenge);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
chall_blob = data_blob_talloc(mem_ctx, challenge, 8);
|
||||
if (lp_client_ntlmv2_auth()) {
|
||||
DATA_BLOB names_blob = NTLMv2_generate_names_blob(mem_ctx, lp_netbios_name(), lp_workgroup());
|
||||
DATA_BLOB lmv2_response, ntlmv2_response, lmv2_session_key, ntlmv2_session_key;
|
||||
|
||||
if (!SMBNTLMv2encrypt_hash(user_info_temp,
|
||||
user_info_in->client.account_name,
|
||||
user_info_in->client.domain_name,
|
||||
user_info_in->password.hash.nt->hash, &chall_blob,
|
||||
&names_blob,
|
||||
&lmv2_response, &ntlmv2_response,
|
||||
&lmv2_session_key, &ntlmv2_session_key)) {
|
||||
data_blob_free(&names_blob);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
data_blob_free(&names_blob);
|
||||
user_info_temp->password.response.lanman = lmv2_response;
|
||||
user_info_temp->password.response.nt = ntlmv2_response;
|
||||
|
||||
data_blob_free(&lmv2_session_key);
|
||||
data_blob_free(&ntlmv2_session_key);
|
||||
} else {
|
||||
DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
SMBOWFencrypt(user_info_in->password.hash.nt->hash, challenge, blob.data);
|
||||
|
||||
user_info_temp->password.response.nt = blob;
|
||||
if (lp_client_lanman_auth() && user_info_in->password.hash.lanman) {
|
||||
DATA_BLOB lm_blob = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
SMBOWFencrypt(user_info_in->password.hash.lanman->hash, challenge, blob.data);
|
||||
user_info_temp->password.response.lanman = lm_blob;
|
||||
} else {
|
||||
/* if not sending the LM password, send the NT password twice */
|
||||
user_info_temp->password.response.lanman = user_info_temp->password.response.nt;
|
||||
}
|
||||
}
|
||||
|
||||
user_info_in = user_info_temp;
|
||||
/* fall through */
|
||||
}
|
||||
case AUTH_PASSWORD_RESPONSE:
|
||||
*user_info_encrypted = user_info_in;
|
||||
}
|
||||
break;
|
||||
case AUTH_PASSWORD_HASH:
|
||||
{
|
||||
switch (user_info_in->password_state) {
|
||||
case AUTH_PASSWORD_PLAIN:
|
||||
{
|
||||
struct samr_Password lanman;
|
||||
struct samr_Password nt;
|
||||
|
||||
user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
|
||||
if (!user_info_temp) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
talloc_reference(user_info_temp, user_info_in);
|
||||
*user_info_temp = *user_info_in;
|
||||
user_info_temp->mapped_state = to_state;
|
||||
|
||||
if (E_deshash(user_info_in->password.plaintext, lanman.hash)) {
|
||||
user_info_temp->password.hash.lanman = talloc(user_info_temp,
|
||||
struct samr_Password);
|
||||
*user_info_temp->password.hash.lanman = lanman;
|
||||
} else {
|
||||
user_info_temp->password.hash.lanman = NULL;
|
||||
}
|
||||
|
||||
E_md4hash(user_info_in->password.plaintext, nt.hash);
|
||||
user_info_temp->password.hash.nt = talloc(user_info_temp,
|
||||
struct samr_Password);
|
||||
*user_info_temp->password.hash.nt = nt;
|
||||
|
||||
user_info_in = user_info_temp;
|
||||
/* fall through */
|
||||
}
|
||||
case AUTH_PASSWORD_HASH:
|
||||
*user_info_encrypted = user_info_in;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
Make a server_info struct from the info3 returned by a domain logon
|
||||
***************************************************************************/
|
||||
NTSTATUS make_server_info_netlogon_validation(TALLOC_CTX *mem_ctx,
|
||||
const char *account_name,
|
||||
uint16_t validation_level,
|
||||
union netr_Validation *validation,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
struct auth_serversupplied_info *server_info;
|
||||
struct netr_SamBaseInfo *base = NULL;
|
||||
int i;
|
||||
|
||||
switch (validation_level) {
|
||||
case 2:
|
||||
if (!validation || !validation->sam2) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
base = &validation->sam2->base;
|
||||
break;
|
||||
case 3:
|
||||
if (!validation || !validation->sam3) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
base = &validation->sam3->base;
|
||||
break;
|
||||
case 6:
|
||||
if (!validation || !validation->sam6) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
base = &validation->sam6->base;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
/*
|
||||
Here is where we should check the list of
|
||||
trusted domains, and verify that the SID
|
||||
matches.
|
||||
*/
|
||||
server_info->account_sid = dom_sid_add_rid(server_info, base->domain_sid, base->rid);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
|
||||
|
||||
|
||||
server_info->primary_group_sid = dom_sid_add_rid(server_info, base->domain_sid, base->primary_gid);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
|
||||
|
||||
server_info->n_domain_groups = base->groups.count;
|
||||
if (base->groups.count) {
|
||||
server_info->domain_groups = talloc_array(server_info, struct dom_sid*, base->groups.count);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_groups);
|
||||
} else {
|
||||
server_info->domain_groups = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < base->groups.count; i++) {
|
||||
server_info->domain_groups[i] = dom_sid_add_rid(server_info, base->domain_sid, base->groups.rids[i].rid);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_groups[i]);
|
||||
}
|
||||
|
||||
/* Copy 'other' sids. We need to do sid filtering here to
|
||||
prevent possible elevation of privileges. See:
|
||||
|
||||
http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
|
||||
*/
|
||||
|
||||
if (validation_level == 3) {
|
||||
struct dom_sid **dgrps = server_info->domain_groups;
|
||||
size_t sidcount = server_info->n_domain_groups + validation->sam3->sidcount;
|
||||
size_t n_dgrps = server_info->n_domain_groups;
|
||||
|
||||
if (validation->sam3->sidcount > 0) {
|
||||
dgrps = talloc_realloc(server_info, dgrps, struct dom_sid*, sidcount);
|
||||
NT_STATUS_HAVE_NO_MEMORY(dgrps);
|
||||
|
||||
for (i = 0; i < validation->sam3->sidcount; i++) {
|
||||
dgrps[n_dgrps + i] = talloc_reference(dgrps, validation->sam3->sids[i].sid);
|
||||
}
|
||||
}
|
||||
|
||||
server_info->n_domain_groups = sidcount;
|
||||
server_info->domain_groups = dgrps;
|
||||
|
||||
/* Where are the 'global' sids?... */
|
||||
}
|
||||
|
||||
if (base->account_name.string) {
|
||||
server_info->account_name = talloc_reference(server_info, base->account_name.string);
|
||||
} else {
|
||||
server_info->account_name = talloc_strdup(server_info, account_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
}
|
||||
|
||||
server_info->domain_name = talloc_reference(server_info, base->domain.string);
|
||||
server_info->full_name = talloc_reference(server_info, base->full_name.string);
|
||||
server_info->logon_script = talloc_reference(server_info, base->logon_script.string);
|
||||
server_info->profile_path = talloc_reference(server_info, base->profile_path.string);
|
||||
server_info->home_directory = talloc_reference(server_info, base->home_directory.string);
|
||||
server_info->home_drive = talloc_reference(server_info, base->home_drive.string);
|
||||
server_info->logon_server = talloc_reference(server_info, base->logon_server.string);
|
||||
server_info->last_logon = base->last_logon;
|
||||
server_info->last_logoff = base->last_logoff;
|
||||
server_info->acct_expiry = base->acct_expiry;
|
||||
server_info->last_password_change = base->last_password_change;
|
||||
server_info->allow_password_change = base->allow_password_change;
|
||||
server_info->force_password_change = base->force_password_change;
|
||||
server_info->logon_count = base->logon_count;
|
||||
server_info->bad_password_count = base->bad_password_count;
|
||||
server_info->acct_flags = base->acct_flags;
|
||||
|
||||
server_info->authenticated = True;
|
||||
|
||||
/* ensure we are never given NULL session keys */
|
||||
|
||||
if (all_zero(base->key.key, sizeof(base->key.key))) {
|
||||
server_info->user_session_key = data_blob(NULL, 0);
|
||||
} else {
|
||||
server_info->user_session_key = data_blob_talloc(server_info, base->key.key, sizeof(base->key.key));
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
|
||||
}
|
||||
|
||||
if (all_zero(base->LMSessKey.key, sizeof(base->LMSessKey.key))) {
|
||||
server_info->lm_session_key = data_blob(NULL, 0);
|
||||
} else {
|
||||
server_info->lm_session_key = data_blob_talloc(server_info, base->LMSessKey.key, sizeof(base->LMSessKey.key));
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
|
||||
}
|
||||
|
||||
*_server_info = server_info;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS auth_anonymous_server_info(TALLOC_CTX *mem_ctx, struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
struct auth_serversupplied_info *server_info;
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
|
||||
|
||||
/* is this correct? */
|
||||
server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
|
||||
|
||||
server_info->n_domain_groups = 0;
|
||||
server_info->domain_groups = NULL;
|
||||
|
||||
/* annoying, but the Anonymous really does have a session key,
|
||||
and it is all zeros! */
|
||||
server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
|
||||
|
||||
server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
|
||||
|
||||
data_blob_clear(&server_info->user_session_key);
|
||||
data_blob_clear(&server_info->lm_session_key);
|
||||
|
||||
server_info->account_name = talloc_strdup(server_info, "ANONYMOUS LOGON");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
|
||||
server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
|
||||
|
||||
server_info->full_name = talloc_strdup(server_info, "Anonymous Logon");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
|
||||
|
||||
server_info->logon_script = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
|
||||
|
||||
server_info->profile_path = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
|
||||
|
||||
server_info->home_directory = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
|
||||
|
||||
server_info->home_drive = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
|
||||
|
||||
server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
|
||||
|
||||
server_info->last_logon = 0;
|
||||
server_info->last_logoff = 0;
|
||||
server_info->acct_expiry = 0;
|
||||
server_info->last_password_change = 0;
|
||||
server_info->allow_password_change = 0;
|
||||
server_info->force_password_change = 0;
|
||||
|
||||
server_info->logon_count = 0;
|
||||
server_info->bad_password_count = 0;
|
||||
|
||||
server_info->acct_flags = ACB_NORMAL;
|
||||
|
||||
server_info->authenticated = False;
|
||||
|
||||
*_server_info = server_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS auth_system_server_info(TALLOC_CTX *mem_ctx, struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
struct auth_serversupplied_info *server_info;
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_SYSTEM);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
|
||||
|
||||
/* is this correct? */
|
||||
server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_ADMINISTRATORS);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
|
||||
|
||||
server_info->n_domain_groups = 0;
|
||||
server_info->domain_groups = NULL;
|
||||
|
||||
/* annoying, but the Anonymous really does have a session key,
|
||||
and it is all zeros! */
|
||||
server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
|
||||
|
||||
server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
|
||||
|
||||
data_blob_clear(&server_info->user_session_key);
|
||||
data_blob_clear(&server_info->lm_session_key);
|
||||
|
||||
server_info->account_name = talloc_strdup(server_info, "SYSTEM");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
|
||||
server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
|
||||
|
||||
server_info->full_name = talloc_strdup(server_info, "System");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
|
||||
|
||||
server_info->logon_script = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
|
||||
|
||||
server_info->profile_path = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
|
||||
|
||||
server_info->home_directory = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
|
||||
|
||||
server_info->home_drive = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
|
||||
|
||||
server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
|
||||
|
||||
server_info->last_logon = 0;
|
||||
server_info->last_logoff = 0;
|
||||
server_info->acct_expiry = 0;
|
||||
server_info->last_password_change = 0;
|
||||
server_info->allow_password_change = 0;
|
||||
server_info->force_password_change = 0;
|
||||
|
||||
server_info->logon_count = 0;
|
||||
server_info->bad_password_count = 0;
|
||||
|
||||
server_info->acct_flags = ACB_NORMAL;
|
||||
|
||||
server_info->authenticated = True;
|
||||
|
||||
*_server_info = server_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
struct auth_session_info **_session_info)
|
||||
{
|
||||
struct auth_session_info *session_info;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
session_info = talloc(mem_ctx, struct auth_session_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(session_info);
|
||||
|
||||
session_info->server_info = talloc_reference(session_info, server_info);
|
||||
|
||||
/* unless set otherwise, the session key is the user session
|
||||
* key from the auth subsystem */
|
||||
session_info->session_key = server_info->user_session_key;
|
||||
|
||||
nt_status = security_token_create(session_info,
|
||||
server_info->account_sid,
|
||||
server_info->primary_group_sid,
|
||||
server_info->n_domain_groups,
|
||||
server_info->domain_groups,
|
||||
server_info->authenticated,
|
||||
&session_info->security_token);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
session_info->credentials = NULL;
|
||||
|
||||
*_session_info = session_info;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS auth_anonymous_session_info(TALLOC_CTX *parent_ctx,
|
||||
struct auth_session_info **_session_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_serversupplied_info *server_info = NULL;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
|
||||
nt_status = auth_anonymous_server_info(mem_ctx,
|
||||
&server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* references the server_info into the session_info */
|
||||
nt_status = auth_generate_session_info(parent_ctx, server_info, &session_info);
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
session_info->credentials = cli_credentials_init(session_info);
|
||||
if (!session_info->credentials) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
cli_credentials_set_conf(session_info->credentials);
|
||||
cli_credentials_set_anonymous(session_info->credentials);
|
||||
|
||||
*_session_info = session_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
struct auth_session_info *anonymous_session(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
nt_status = auth_anonymous_session_info(mem_ctx, &session_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return NULL;
|
||||
}
|
||||
return session_info;
|
||||
}
|
||||
|
||||
NTSTATUS auth_system_session_info(TALLOC_CTX *parent_ctx,
|
||||
struct auth_session_info **_session_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_serversupplied_info *server_info = NULL;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
|
||||
nt_status = auth_system_server_info(mem_ctx,
|
||||
&server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* references the server_info into the session_info */
|
||||
nt_status = auth_generate_session_info(parent_ctx, server_info, &session_info);
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
session_info->credentials = cli_credentials_init(session_info);
|
||||
if (!session_info->credentials) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
cli_credentials_set_conf(session_info->credentials);
|
||||
|
||||
if (lp_parm_bool(-1,"system","anonymous", False)) {
|
||||
cli_credentials_set_anonymous(session_info->credentials);
|
||||
} else {
|
||||
cli_credentials_set_machine_account_pending(session_info->credentials);
|
||||
}
|
||||
*_session_info = session_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ struct auth_session_info *system_session(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
nt_status = auth_system_session_info(mem_ctx, &session_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return NULL;
|
||||
}
|
||||
return session_info;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
prints a struct auth_session_info security token to debug output.
|
||||
****************************************************************************/
|
||||
void auth_session_info_debug(int dbg_lev,
|
||||
const struct auth_session_info *session_info)
|
||||
{
|
||||
if (!session_info) {
|
||||
DEBUG(dbg_lev, ("Session Info: (NULL)\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
security_token_debug(dbg_lev, session_info->security_token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Squash an NT_STATUS in line with security requirements.
|
||||
* In an attempt to avoid giving the whole game away when users
|
||||
* are authenticating, NT replaces both NT_STATUS_NO_SUCH_USER and
|
||||
* NT_STATUS_WRONG_PASSWORD with NT_STATUS_LOGON_FAILURE in certain situations
|
||||
* (session setups in particular).
|
||||
*
|
||||
* @param nt_status NTSTATUS input for squashing.
|
||||
* @return the 'squashed' nt_status
|
||||
**/
|
||||
NTSTATUS auth_nt_status_squash(NTSTATUS nt_status)
|
||||
{
|
||||
if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) {
|
||||
/* Match WinXP and don't give the game away */
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
} else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) {
|
||||
/* Match WinXP and don't give the game away */
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Winbind authentication mechnism
|
||||
|
||||
Copyright (C) Tim Potter 2000
|
||||
Copyright (C) Andrew Bartlett 2001 - 2002
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "nsswitch/winbind_client.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon.h"
|
||||
#include "librpc/gen_ndr/ndr_winbind.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
|
||||
static NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct winbindd_response *response, struct netr_SamInfo3 *info3)
|
||||
{
|
||||
size_t len = response->length - sizeof(struct winbindd_response);
|
||||
if (len > 4) {
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
blob.length = len - 4;
|
||||
blob.data = (uint8_t *)(((char *)response->extra_data) + 4);
|
||||
|
||||
status = ndr_pull_struct_blob(&blob, mem_ctx, info3,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_netr_SamInfo3);
|
||||
|
||||
return status;
|
||||
} else {
|
||||
DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS winbind_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/* TODO: maybe limit the user scope to remote users only */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Authenticate a user with a challenge/response
|
||||
using the samba3 winbind protocol
|
||||
*/
|
||||
static NTSTATUS winbind_check_password_samba3(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
struct winbindd_request request;
|
||||
struct winbindd_response response;
|
||||
NSS_STATUS result;
|
||||
NTSTATUS nt_status;
|
||||
struct netr_SamInfo3 info3;
|
||||
|
||||
/* Send off request */
|
||||
const struct auth_usersupplied_info *user_info_temp;
|
||||
nt_status = encrypt_user_info(mem_ctx, ctx->auth_ctx,
|
||||
AUTH_PASSWORD_RESPONSE,
|
||||
user_info, &user_info_temp);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
user_info = user_info_temp;
|
||||
|
||||
ZERO_STRUCT(request);
|
||||
ZERO_STRUCT(response);
|
||||
request.flags = WBFLAG_PAM_INFO3_NDR;
|
||||
|
||||
request.data.auth_crap.logon_parameters = user_info->logon_parameters;
|
||||
|
||||
winbind_strcpy(request.data.auth_crap.user,
|
||||
user_info->client.account_name);
|
||||
winbind_strcpy(request.data.auth_crap.domain,
|
||||
user_info->client.domain_name);
|
||||
winbind_strcpy(request.data.auth_crap.workstation,
|
||||
user_info->workstation_name);
|
||||
|
||||
memcpy(request.data.auth_crap.chal, ctx->auth_ctx->challenge.data.data, sizeof(request.data.auth_crap.chal));
|
||||
|
||||
request.data.auth_crap.lm_resp_len = MIN(user_info->password.response.lanman.length,
|
||||
sizeof(request.data.auth_crap.lm_resp));
|
||||
request.data.auth_crap.nt_resp_len = MIN(user_info->password.response.nt.length,
|
||||
sizeof(request.data.auth_crap.nt_resp));
|
||||
|
||||
memcpy(request.data.auth_crap.lm_resp, user_info->password.response.lanman.data,
|
||||
request.data.auth_crap.lm_resp_len);
|
||||
memcpy(request.data.auth_crap.nt_resp, user_info->password.response.nt.data,
|
||||
request.data.auth_crap.nt_resp_len);
|
||||
|
||||
result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
|
||||
|
||||
nt_status = NT_STATUS(response.data.auth.nt_status);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
if (result == NSS_STATUS_SUCCESS && response.extra_data) {
|
||||
union netr_Validation validation;
|
||||
|
||||
nt_status = get_info3_from_ndr(mem_ctx, &response, &info3);
|
||||
SAFE_FREE(response.extra_data);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
validation.sam3 = &info3;
|
||||
nt_status = make_server_info_netlogon_validation(mem_ctx,
|
||||
user_info->client.account_name,
|
||||
3, &validation,
|
||||
server_info);
|
||||
return nt_status;
|
||||
} else if (result == NSS_STATUS_SUCCESS && !response.extra_data) {
|
||||
DEBUG(0, ("Winbindd authenticated the user [%s]\\[%s], "
|
||||
"but did not include the required info3 reply!\n",
|
||||
user_info->client.domain_name, user_info->client.account_name));
|
||||
return NT_STATUS_INSUFFICIENT_LOGON_INFO;
|
||||
} else if (NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("Winbindd authentication for [%s]\\[%s] failed, "
|
||||
"but no error code is available!\n",
|
||||
user_info->client.domain_name, user_info->client.account_name));
|
||||
return NT_STATUS_NO_LOGON_SERVERS;
|
||||
}
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
struct winbind_check_password_state {
|
||||
struct winbind_SamLogon req;
|
||||
};
|
||||
|
||||
/*
|
||||
Authenticate a user with a challenge/response
|
||||
using IRPC to the winbind task
|
||||
*/
|
||||
static NTSTATUS winbind_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
uint32_t *winbind_servers;
|
||||
struct winbind_check_password_state *s;
|
||||
const struct auth_usersupplied_info *user_info_new;
|
||||
struct netr_IdentityInfo *identity_info;
|
||||
|
||||
winbind_servers = irpc_servers_byname(ctx->auth_ctx->msg_ctx, "winbind_server");
|
||||
if ((winbind_servers == NULL) || (winbind_servers[0] == 0)) {
|
||||
DEBUG(0, ("Winbind authentication for [%s]\\[%s] failed, "
|
||||
"no winbind_server running!\n",
|
||||
user_info->client.domain_name, user_info->client.account_name));
|
||||
return NT_STATUS_NO_LOGON_SERVERS;
|
||||
}
|
||||
|
||||
s = talloc(mem_ctx, struct winbind_check_password_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(s);
|
||||
|
||||
if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
|
||||
struct netr_PasswordInfo *password_info;
|
||||
|
||||
status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_HASH,
|
||||
user_info, &user_info_new);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
user_info = user_info_new;
|
||||
|
||||
password_info = talloc(s, struct netr_PasswordInfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(password_info);
|
||||
|
||||
password_info->lmpassword = *user_info->password.hash.lanman;
|
||||
password_info->ntpassword = *user_info->password.hash.nt;
|
||||
|
||||
identity_info = &password_info->identity_info;
|
||||
s->req.in.logon_level = 1;
|
||||
s->req.in.logon.password= password_info;
|
||||
} else {
|
||||
struct netr_NetworkInfo *network_info;
|
||||
const uint8_t *challenge;
|
||||
|
||||
status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_RESPONSE,
|
||||
user_info, &user_info_new);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
user_info = user_info_new;
|
||||
|
||||
network_info = talloc(s, struct netr_NetworkInfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(network_info);
|
||||
|
||||
status = auth_get_challenge(ctx->auth_ctx, &challenge);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
memcpy(network_info->challenge, challenge, sizeof(network_info->challenge));
|
||||
|
||||
network_info->nt.length = user_info->password.response.nt.length;
|
||||
network_info->nt.data = user_info->password.response.nt.data;
|
||||
|
||||
network_info->nt.length = user_info->password.response.lanman.length;
|
||||
network_info->nt.data = user_info->password.response.lanman.data;
|
||||
|
||||
identity_info = &network_info->identity_info;
|
||||
s->req.in.logon_level = 2;
|
||||
s->req.in.logon.network = network_info;
|
||||
}
|
||||
|
||||
identity_info->domain_name.string = user_info->client.domain_name;
|
||||
identity_info->parameter_control = user_info->logon_parameters; /* see MSV1_0_* */
|
||||
identity_info->logon_id_low = 0;
|
||||
identity_info->logon_id_high = 0;
|
||||
identity_info->account_name.string = user_info->client.account_name;
|
||||
identity_info->workstation.string = user_info->workstation_name;
|
||||
|
||||
s->req.in.validation_level = 3;
|
||||
status = IRPC_CALL(ctx->auth_ctx->msg_ctx, winbind_servers[0],
|
||||
winbind, WINBIND_SAMLOGON,
|
||||
&s->req, s);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = make_server_info_netlogon_validation(mem_ctx,
|
||||
user_info->client.account_name,
|
||||
s->req.in.validation_level,
|
||||
&s->req.out.validation,
|
||||
server_info);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static const struct auth_operations winbind_samba3_ops = {
|
||||
.name = "winbind_samba3",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = winbind_want_check,
|
||||
.check_password = winbind_check_password_samba3
|
||||
};
|
||||
|
||||
static const struct auth_operations winbind_ops = {
|
||||
.name = "winbind",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = winbind_want_check,
|
||||
.check_password = winbind_check_password
|
||||
};
|
||||
|
||||
NTSTATUS auth_winbind_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
ret = auth_register(&winbind_samba3_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'winbind_samba3' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = auth_register(&winbind_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'winbind' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
###############################
|
||||
# start SMB_EXT_LIB_PAM
|
||||
# check for security/pam_appl.h and -lpam
|
||||
AC_CHECK_HEADERS(security/pam_appl.h)
|
||||
AC_CHECK_LIB_EXT(pam, PAM_LIBS, pam_start)
|
||||
if test x"$ac_cv_header_security_pam_appl_h" = x"yes" -a x"$ac_cv_lib_ext_pam_pam_start" = x"yes";then
|
||||
SMB_ENABLE(PAM,YES)
|
||||
fi
|
||||
SMB_EXT_LIB(PAM, $PAM_LIBS)
|
||||
# end SMB_EXT_LIB_PAM
|
||||
###############################
|
||||
|
||||
################################################
|
||||
# test for where we get crypt() from
|
||||
AC_CHECK_LIB_EXT(crypt, CRYPT_LIBS, crypt)
|
||||
SMB_ENABLE(CRYPT,YES)
|
||||
SMB_EXT_LIB(CRYPT, $CRYPT_LIBS)
|
||||
|
||||
AC_CHECK_FUNCS(crypt16 getauthuid getpwanam)
|
||||
|
||||
AC_CHECK_HEADERS(sasl/sasl.h)
|
||||
AC_CHECK_LIB_EXT(sasl2, SASL_LIBS, sasl_client_init)
|
||||
SMB_EXT_LIB(SASL, $SASL_LIBS)
|
||||
|
||||
if test x"$ac_cv_header_sasl_sasl_h" = x"yes" -a x"$ac_cv_lib_ext_sasl2_sasl_client_init" = x"yes";then
|
||||
SMB_ENABLE(SASL,YES)
|
||||
SMB_ENABLE(cyrus_sasl,YES)
|
||||
else
|
||||
SMB_ENABLE(cyrus_sasl,NO)
|
||||
fi
|
||||
@@ -0,0 +1,79 @@
|
||||
# auth server subsystem
|
||||
include gensec/config.mk
|
||||
include kerberos/config.mk
|
||||
include ntlmssp/config.mk
|
||||
include credentials/config.mk
|
||||
|
||||
[SUBSYSTEM::auth_sam]
|
||||
PRIVATE_PROTO_HEADER = auth_sam.h
|
||||
OBJ_FILES = sam.o auth_sam_reply.o ntlm_check.o
|
||||
PUBLIC_DEPENDENCIES = SAMDB
|
||||
|
||||
#######################
|
||||
# Start MODULE auth_sam
|
||||
[MODULE::auth_sam_module]
|
||||
# gensec_krb5 and gensec_gssapi depend on it
|
||||
INIT_FUNCTION = auth_sam_init
|
||||
SUBSYSTEM = auth
|
||||
OBJ_FILES = auth_sam.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
SAMDB auth_sam
|
||||
# End MODULE auth_sam
|
||||
#######################
|
||||
|
||||
#######################
|
||||
# Start MODULE auth_anonymous
|
||||
[MODULE::auth_anonymous]
|
||||
INIT_FUNCTION = auth_anonymous_init
|
||||
SUBSYSTEM = auth
|
||||
OBJ_FILES = auth_anonymous.o
|
||||
# End MODULE auth_anonymous
|
||||
#######################
|
||||
|
||||
#######################
|
||||
# Start MODULE auth_winbind
|
||||
[MODULE::auth_winbind]
|
||||
INIT_FUNCTION = auth_winbind_init
|
||||
SUBSYSTEM = auth
|
||||
OBJ_FILES = auth_winbind.o
|
||||
PUBLIC_DEPENDENCIES = NDR_WINBIND MESSAGING LIBWINBIND-CLIENT
|
||||
# End MODULE auth_winbind
|
||||
#######################
|
||||
|
||||
#######################
|
||||
# Start MODULE auth_developer
|
||||
[MODULE::auth_developer]
|
||||
INIT_FUNCTION = auth_developer_init
|
||||
SUBSYSTEM = auth
|
||||
OBJ_FILES = auth_developer.o
|
||||
# End MODULE auth_developer
|
||||
#######################
|
||||
|
||||
#######################
|
||||
# Start MODULE auth_unix
|
||||
[MODULE::auth_unix]
|
||||
INIT_FUNCTION = auth_unix_init
|
||||
SUBSYSTEM = auth
|
||||
OBJ_FILES = auth_unix.o
|
||||
PUBLIC_DEPENDENCIES = CRYPT PAM PAM_ERRORS
|
||||
# End MODULE auth_unix
|
||||
#######################
|
||||
|
||||
[SUBSYSTEM::PAM_ERRORS]
|
||||
PRIVATE_PROTO_HEADER = pam_errors.h
|
||||
OBJ_FILES = pam_errors.o
|
||||
|
||||
#######################
|
||||
# Start SUBSYSTEM auth
|
||||
[SUBSYSTEM::auth]
|
||||
#VERSION = 0.0.1
|
||||
#SO_VERSION = 0
|
||||
PUBLIC_HEADERS = auth.h
|
||||
PUBLIC_PROTO_HEADER = auth_proto.h
|
||||
OBJ_FILES = \
|
||||
auth.o \
|
||||
auth_util.o \
|
||||
auth_simple.o
|
||||
PUBLIC_DEPENDENCIES = LIBSECURITY SAMDB CREDENTIALS
|
||||
# End SUBSYSTEM auth
|
||||
#######################
|
||||
@@ -0,0 +1,24 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM CREDENTIALS
|
||||
[SUBSYSTEM::CREDENTIALS]
|
||||
PUBLIC_PROTO_HEADER = credentials_proto.h
|
||||
PUBLIC_HEADERS = credentials.h
|
||||
OBJ_FILES = credentials.o \
|
||||
credentials_files.o \
|
||||
credentials_ntlm.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
LIBCLI_AUTH SECRETS LIBCRYPTO KERBEROS
|
||||
PRIVATE_DEPENDENCIES = CREDENTIALS_KRB5
|
||||
# End SUBSYSTEM CREDENTIALS
|
||||
#################################
|
||||
|
||||
#################################
|
||||
# Start SUBSYSTEM CREDENTIALS
|
||||
[SUBSYSTEM::CREDENTIALS_KRB5]
|
||||
PUBLIC_PROTO_HEADER = credentials_krb5_proto.h
|
||||
PUBLIC_HEADERS = credentials_krb5.h
|
||||
OBJ_FILES = credentials_krb5.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
HEIMDAL_GSSAPI
|
||||
# End SUBSYSTEM CREDENTIALS
|
||||
#################################
|
||||
@@ -0,0 +1,717 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
User credentials handling
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Tim Potter 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
|
||||
/**
|
||||
* Create a new credentials structure
|
||||
* @param mem_ctx TALLOC_CTX parent for credentials structure
|
||||
*/
|
||||
struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials);
|
||||
if (!cred) {
|
||||
return cred;
|
||||
}
|
||||
|
||||
cred->netlogon_creds = NULL;
|
||||
cred->machine_account_pending = False;
|
||||
cred->workstation_obtained = CRED_UNINITIALISED;
|
||||
cred->username_obtained = CRED_UNINITIALISED;
|
||||
cred->password_obtained = CRED_UNINITIALISED;
|
||||
cred->domain_obtained = CRED_UNINITIALISED;
|
||||
cred->realm_obtained = CRED_UNINITIALISED;
|
||||
cred->ccache_obtained = CRED_UNINITIALISED;
|
||||
cred->client_gss_creds_obtained = CRED_UNINITIALISED;
|
||||
cred->server_gss_creds_obtained = CRED_UNINITIALISED;
|
||||
cred->keytab_obtained = CRED_UNINITIALISED;
|
||||
cred->principal_obtained = CRED_UNINITIALISED;
|
||||
|
||||
cred->old_password = NULL;
|
||||
cred->smb_krb5_context = NULL;
|
||||
cred->salt_principal = NULL;
|
||||
cred->machine_account = False;
|
||||
|
||||
cred->bind_dn = NULL;
|
||||
|
||||
cred->tries = 3;
|
||||
cred->callback_running = False;
|
||||
|
||||
cli_credentials_set_kerberos_state(cred, CRED_AUTO_USE_KERBEROS);
|
||||
|
||||
return cred;
|
||||
}
|
||||
|
||||
void cli_credentials_set_kerberos_state(struct cli_credentials *creds,
|
||||
enum credentials_use_kerberos use_kerberos)
|
||||
{
|
||||
creds->use_kerberos = use_kerberos;
|
||||
}
|
||||
|
||||
enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds)
|
||||
{
|
||||
return creds->use_kerberos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the username for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The username set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_username(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->username_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->username = cred->username_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->username_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->username;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_username(struct cli_credentials *cred,
|
||||
const char *val, enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->username_obtained) {
|
||||
cred->username = talloc_strdup(cred, val);
|
||||
cred->username_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_username_callback(struct cli_credentials *cred,
|
||||
const char *(*username_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->username_obtained < CRED_CALLBACK) {
|
||||
cred->username_cb = username_cb;
|
||||
cred->username_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_bind_dn(struct cli_credentials *cred,
|
||||
const char *bind_dn)
|
||||
{
|
||||
cred->bind_dn = talloc_strdup(cred, bind_dn);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the BIND DN for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The username set on this context.
|
||||
* @note Return value will be NULL if not specified explictly
|
||||
*/
|
||||
const char *cli_credentials_get_bind_dn(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->bind_dn;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the client principal for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The username set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->principal_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->principal = cred->principal_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->principal_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
if (cred->principal_obtained < cred->username_obtained) {
|
||||
if (cred->domain_obtained > cred->realm_obtained) {
|
||||
return talloc_asprintf(mem_ctx, "%s@%s",
|
||||
cli_credentials_get_username(cred),
|
||||
cli_credentials_get_domain(cred));
|
||||
} else {
|
||||
return talloc_asprintf(mem_ctx, "%s@%s",
|
||||
cli_credentials_get_username(cred),
|
||||
cli_credentials_get_realm(cred));
|
||||
}
|
||||
}
|
||||
return talloc_reference(mem_ctx, cred->principal);
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_principal(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->principal_obtained) {
|
||||
cred->principal = talloc_strdup(cred, val);
|
||||
cred->principal_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Set a callback to get the principal. This could be a popup dialog,
|
||||
* a terminal prompt or similar. */
|
||||
|
||||
BOOL cli_credentials_set_principal_callback(struct cli_credentials *cred,
|
||||
const char *(*principal_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->principal_obtained < CRED_CALLBACK) {
|
||||
cred->principal_cb = principal_cb;
|
||||
cred->principal_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Some of our tools are 'anonymous by default'. This is a single
|
||||
* function to determine if authentication has been explicitly
|
||||
* requested */
|
||||
|
||||
BOOL cli_credentials_authentication_requested(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->bind_dn) {
|
||||
return True;
|
||||
}
|
||||
|
||||
if (cli_credentials_is_anonymous(cred)){
|
||||
return False;
|
||||
}
|
||||
|
||||
if (cred->principal_obtained >= CRED_SPECIFIED) {
|
||||
return True;
|
||||
}
|
||||
if (cred->username_obtained >= CRED_SPECIFIED) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the password for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval If set, the cleartext password, otherwise NULL
|
||||
*/
|
||||
const char *cli_credentials_get_password(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->password_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->password = cred->password_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->password_obtained = CRED_CALLBACK_RESULT;
|
||||
}
|
||||
|
||||
return cred->password;
|
||||
}
|
||||
|
||||
/* Set a password on the credentials context, including an indication
|
||||
* of 'how' the password was obtained */
|
||||
|
||||
BOOL cli_credentials_set_password(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->password_obtained) {
|
||||
cred->password = talloc_strdup(cred, val);
|
||||
cred->password_obtained = obtained;
|
||||
|
||||
cred->nt_hash = NULL;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_password_callback(struct cli_credentials *cred,
|
||||
const char *(*password_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->password_obtained < CRED_CALLBACK) {
|
||||
cred->password_cb = password_cb;
|
||||
cred->password_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the 'old' password for this credentials context (used for join accounts).
|
||||
* @param cred credentials context
|
||||
* @retval If set, the cleartext password, otherwise NULL
|
||||
*/
|
||||
const char *cli_credentials_get_old_password(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
return cred->old_password;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_old_password(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
cred->old_password = talloc_strdup(cred, val);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the password, in the form MD4(unicode(password)) for this credentials context.
|
||||
*
|
||||
* Sometimes we only have this much of the password, while the rest of
|
||||
* the time this call avoids calling E_md4hash themselves.
|
||||
*
|
||||
* @param cred credentials context
|
||||
* @retval If set, the cleartext password, otherwise NULL
|
||||
*/
|
||||
const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred,
|
||||
TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
const char *password = cli_credentials_get_password(cred);
|
||||
|
||||
if (password) {
|
||||
struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
|
||||
if (!nt_hash) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
E_md4hash(password, nt_hash->hash);
|
||||
|
||||
return nt_hash;
|
||||
} else {
|
||||
return cred->nt_hash;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_nt_hash(struct cli_credentials *cred,
|
||||
const struct samr_Password *nt_hash,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->password_obtained) {
|
||||
cli_credentials_set_password(cred, NULL, obtained);
|
||||
cred->nt_hash = talloc(cred, struct samr_Password);
|
||||
*cred->nt_hash = *nt_hash;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the 'short' or 'NetBIOS' domain for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The domain set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_domain(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->domain_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->domain = cred->domain_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->domain_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->domain;
|
||||
}
|
||||
|
||||
|
||||
BOOL cli_credentials_set_domain(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->domain_obtained) {
|
||||
/* it is important that the domain be in upper case,
|
||||
* particularly for the sensitive NTLMv2
|
||||
* calculations */
|
||||
cred->domain = strupper_talloc(cred, val);
|
||||
cred->domain_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_domain_callback(struct cli_credentials *cred,
|
||||
const char *(*domain_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->domain_obtained < CRED_CALLBACK) {
|
||||
cred->domain_cb = domain_cb;
|
||||
cred->domain_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the Kerberos realm for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The realm set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_realm(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->realm_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->realm = cred->realm_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->realm_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the realm for this credentials context, and force it to
|
||||
* uppercase for the sainity of our local kerberos libraries
|
||||
*/
|
||||
BOOL cli_credentials_set_realm(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->realm_obtained) {
|
||||
cred->realm = strupper_talloc(cred, val);
|
||||
cred->realm_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_realm_callback(struct cli_credentials *cred,
|
||||
const char *(*realm_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->realm_obtained < CRED_CALLBACK) {
|
||||
cred->realm_cb = realm_cb;
|
||||
cred->realm_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
|
||||
*
|
||||
* @param cred credentials context
|
||||
* @retval The workstation name set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_workstation(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->workstation_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->workstation = cred->workstation_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->workstation_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->workstation;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_workstation(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->workstation_obtained) {
|
||||
cred->workstation = talloc_strdup(cred, val);
|
||||
cred->workstation_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_workstation_callback(struct cli_credentials *cred,
|
||||
const char *(*workstation_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->workstation_obtained < CRED_CALLBACK) {
|
||||
cred->workstation_cb = workstation_cb;
|
||||
cred->workstation_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
|
||||
*
|
||||
* The format accepted is [domain\\]user[%password] or user[@realm][%password]
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param data the string containing the username, password etc
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
|
||||
{
|
||||
char *uname, *p;
|
||||
|
||||
if (strcmp("%",data) == 0) {
|
||||
cli_credentials_set_anonymous(credentials);
|
||||
return;
|
||||
}
|
||||
|
||||
uname = talloc_strdup(credentials, data);
|
||||
if ((p = strchr_m(uname,'%'))) {
|
||||
*p = 0;
|
||||
cli_credentials_set_password(credentials, p+1, obtained);
|
||||
}
|
||||
|
||||
if ((p = strchr_m(uname,'@'))) {
|
||||
cli_credentials_set_principal(credentials, uname, obtained);
|
||||
*p = 0;
|
||||
cli_credentials_set_realm(credentials, p+1, obtained);
|
||||
return;
|
||||
} else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
|
||||
*p = 0;
|
||||
cli_credentials_set_domain(credentials, uname, obtained);
|
||||
uname = p+1;
|
||||
}
|
||||
cli_credentials_set_username(credentials, uname, obtained);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a a credentials structure, print it as a string
|
||||
*
|
||||
* The format output is [domain\\]user[%password] or user[@realm][%password]
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param mem_ctx The memory context to place the result on
|
||||
*/
|
||||
|
||||
const char *cli_credentials_get_unparsed_name(struct cli_credentials *credentials, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
const char *bind_dn = cli_credentials_get_bind_dn(credentials);
|
||||
const char *domain;
|
||||
const char *username;
|
||||
const char *name;
|
||||
|
||||
if (bind_dn) {
|
||||
name = talloc_reference(mem_ctx, bind_dn);
|
||||
} else {
|
||||
cli_credentials_get_ntlm_username_domain(credentials, mem_ctx, &username, &domain);
|
||||
if (domain && domain[0]) {
|
||||
name = talloc_asprintf(mem_ctx, "%s\\%s",
|
||||
domain, username);
|
||||
} else {
|
||||
name = talloc_asprintf(mem_ctx, "%s",
|
||||
username);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies default values for domain, workstation and realm
|
||||
* from the smb.conf configuration file
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
*/
|
||||
void cli_credentials_set_conf(struct cli_credentials *cred)
|
||||
{
|
||||
cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
|
||||
cli_credentials_set_domain(cred, lp_workgroup(), CRED_UNINITIALISED);
|
||||
cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_UNINITIALISED);
|
||||
cli_credentials_set_realm(cred, lp_realm(), CRED_UNINITIALISED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess defaults for credentials from environment variables,
|
||||
* and from the configuration file
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
*/
|
||||
void cli_credentials_guess(struct cli_credentials *cred)
|
||||
{
|
||||
char *p;
|
||||
|
||||
cli_credentials_set_conf(cred);
|
||||
|
||||
if (getenv("LOGNAME")) {
|
||||
cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESS_ENV);
|
||||
}
|
||||
|
||||
if (getenv("USER")) {
|
||||
cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESS_ENV);
|
||||
if ((p = strchr_m(getenv("USER"),'%'))) {
|
||||
memset(p,0,strlen(cred->password));
|
||||
}
|
||||
}
|
||||
|
||||
if (getenv("DOMAIN")) {
|
||||
cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESS_ENV);
|
||||
}
|
||||
|
||||
if (getenv("PASSWD")) {
|
||||
cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESS_ENV);
|
||||
}
|
||||
|
||||
if (getenv("PASSWD_FD")) {
|
||||
cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESS_FILE);
|
||||
}
|
||||
|
||||
if (getenv("PASSWD_FILE")) {
|
||||
cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESS_FILE);
|
||||
}
|
||||
|
||||
if (cli_credentials_get_kerberos_state(cred) != CRED_DONT_USE_KERBEROS) {
|
||||
cli_credentials_set_ccache(cred, NULL, CRED_GUESS_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach NETLOGON credentials for use with SCHANNEL
|
||||
*/
|
||||
|
||||
void cli_credentials_set_netlogon_creds(struct cli_credentials *cred,
|
||||
struct creds_CredentialState *netlogon_creds)
|
||||
{
|
||||
cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return attached NETLOGON credentials
|
||||
*/
|
||||
|
||||
struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->netlogon_creds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set NETLOGON secure channel type
|
||||
*/
|
||||
|
||||
void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
|
||||
enum netr_SchannelType secure_channel_type)
|
||||
{
|
||||
cred->secure_channel_type = secure_channel_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return NETLOGON secure chanel type
|
||||
*/
|
||||
|
||||
enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->secure_channel_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in a credentials structure as the anonymous user
|
||||
*/
|
||||
void cli_credentials_set_anonymous(struct cli_credentials *cred)
|
||||
{
|
||||
cli_credentials_set_username(cred, "", CRED_SPECIFIED);
|
||||
cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
|
||||
cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe a credentials context as anonymous or authenticated
|
||||
* @retval True if anonymous, False if a username is specified
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_is_anonymous(struct cli_credentials *cred)
|
||||
{
|
||||
const char *username;
|
||||
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
username = cli_credentials_get_username(cred);
|
||||
|
||||
/* Yes, it is deliberate that we die if we have a NULL pointer
|
||||
* here - anonymous is "", not NULL, which is 'never specified,
|
||||
* never guessed', ie programmer bug */
|
||||
if (!username[0]) {
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the current password for a credentials struct as wrong. This will
|
||||
* cause the password to be prompted again (if a callback is set).
|
||||
*
|
||||
* This will decrement the number of times the password can be tried.
|
||||
*
|
||||
* @retval whether the credentials struct is finished
|
||||
*/
|
||||
BOOL cli_credentials_wrong_password(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->password_obtained != CRED_CALLBACK_RESULT) {
|
||||
return False;
|
||||
}
|
||||
|
||||
cred->password_obtained = CRED_CALLBACK;
|
||||
|
||||
cred->tries--;
|
||||
|
||||
return (cred->tries > 0);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
samba -- Unix SMB/CIFS implementation.
|
||||
|
||||
Client credentials structure
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2004-2006
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef __CREDENTIALS_H__
|
||||
#define __CREDENTIALS_H__
|
||||
|
||||
#include "librpc/gen_ndr/misc.h"
|
||||
|
||||
struct ccache_container;
|
||||
|
||||
/* In order of priority */
|
||||
enum credentials_obtained {
|
||||
CRED_UNINITIALISED = 0, /* We don't even have a guess yet */
|
||||
CRED_GUESS_ENV, /* Current value should be used, which was guessed */
|
||||
CRED_CALLBACK, /* Callback should be used to obtain value */
|
||||
CRED_GUESS_FILE, /* A guess from a file (or file pointed at in env variable) */
|
||||
CRED_CALLBACK_RESULT, /* Value was obtained from a callback */
|
||||
CRED_SPECIFIED /* Was explicitly specified on the command-line */
|
||||
};
|
||||
|
||||
enum credentials_use_kerberos {
|
||||
CRED_AUTO_USE_KERBEROS = 0, /* Default, we try kerberos if available */
|
||||
CRED_DONT_USE_KERBEROS, /* Sometimes trying kerberos just does 'bad things', so don't */
|
||||
CRED_MUST_USE_KERBEROS /* Sometimes administrators are parinoid, so always do kerberos */
|
||||
};
|
||||
|
||||
#define CLI_CRED_NTLM2 0x01
|
||||
#define CLI_CRED_NTLMv2_AUTH 0x02
|
||||
#define CLI_CRED_LANMAN_AUTH 0x04
|
||||
#define CLI_CRED_NTLM_AUTH 0x08
|
||||
#define CLI_CRED_CLEAR_AUTH 0x10 /* TODO: Push cleartext auth with this flag */
|
||||
|
||||
struct cli_credentials {
|
||||
enum credentials_obtained workstation_obtained;
|
||||
enum credentials_obtained username_obtained;
|
||||
enum credentials_obtained password_obtained;
|
||||
enum credentials_obtained domain_obtained;
|
||||
enum credentials_obtained realm_obtained;
|
||||
enum credentials_obtained ccache_obtained;
|
||||
enum credentials_obtained client_gss_creds_obtained;
|
||||
enum credentials_obtained principal_obtained;
|
||||
enum credentials_obtained keytab_obtained;
|
||||
enum credentials_obtained server_gss_creds_obtained;
|
||||
|
||||
const char *workstation;
|
||||
const char *username;
|
||||
const char *password;
|
||||
const char *old_password;
|
||||
const char *domain;
|
||||
const char *realm;
|
||||
const char *principal;
|
||||
const char *salt_principal;
|
||||
|
||||
const char *bind_dn;
|
||||
|
||||
struct samr_Password *nt_hash;
|
||||
|
||||
struct ccache_container *ccache;
|
||||
struct gssapi_creds_container *client_gss_creds;
|
||||
struct keytab_container *keytab;
|
||||
struct gssapi_creds_container *server_gss_creds;
|
||||
|
||||
const char *(*workstation_cb) (struct cli_credentials *);
|
||||
const char *(*password_cb) (struct cli_credentials *);
|
||||
const char *(*username_cb) (struct cli_credentials *);
|
||||
const char *(*domain_cb) (struct cli_credentials *);
|
||||
const char *(*realm_cb) (struct cli_credentials *);
|
||||
const char *(*principal_cb) (struct cli_credentials *);
|
||||
|
||||
/* Private handle for the callback routines to use */
|
||||
void *priv_data;
|
||||
|
||||
struct creds_CredentialState *netlogon_creds;
|
||||
enum netr_SchannelType secure_channel_type;
|
||||
int kvno;
|
||||
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
|
||||
/* We are flagged to get machine account details from the
|
||||
* secrets.ldb when we are asked for a username or password */
|
||||
|
||||
BOOL machine_account_pending;
|
||||
|
||||
/* Is this a machine account? */
|
||||
BOOL machine_account;
|
||||
|
||||
/* Should we be trying to use kerberos? */
|
||||
enum credentials_use_kerberos use_kerberos;
|
||||
|
||||
/* Number of retries left before bailing out */
|
||||
int tries;
|
||||
|
||||
/* Whether any callback is currently running */
|
||||
BOOL callback_running;
|
||||
};
|
||||
|
||||
#include "auth/credentials/credentials_proto.h"
|
||||
|
||||
#endif /* __CREDENTIALS_H__ */
|
||||
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
User credentials handling (as regards on-disk files)
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Tim Potter 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
|
||||
#include "param/secrets.h"
|
||||
#include "system/filesys.h"
|
||||
#include "db_wrap.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
/**
|
||||
* Read a file descriptor, and parse it for a password (eg from a file or stdin)
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param fd open file descriptor to read the password from
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_parse_password_fd(struct cli_credentials *credentials,
|
||||
int fd, enum credentials_obtained obtained)
|
||||
{
|
||||
char *p;
|
||||
char pass[128];
|
||||
|
||||
for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
|
||||
p && p - pass < sizeof(pass);) {
|
||||
switch (read(fd, p, 1)) {
|
||||
case 1:
|
||||
if (*p != '\n' && *p != '\0') {
|
||||
*++p = '\0'; /* advance p, and null-terminate pass */
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case 0:
|
||||
if (p - pass) {
|
||||
*p = '\0'; /* null-terminate it, just in case... */
|
||||
p = NULL; /* then force the loop condition to become false */
|
||||
break;
|
||||
} else {
|
||||
fprintf(stderr, "Error reading password from file descriptor %d: %s\n", fd, "empty password\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Error reading password from file descriptor %d: %s\n",
|
||||
fd, strerror(errno));
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
cli_credentials_set_password(credentials, pass, obtained);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a named file, and parse it for a password
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param file a named file to read the password from
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained)
|
||||
{
|
||||
int fd = open(file, O_RDONLY, 0);
|
||||
BOOL ret;
|
||||
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
|
||||
file, strerror(errno));
|
||||
return False;
|
||||
}
|
||||
|
||||
ret = cli_credentials_parse_password_fd(credentials, fd, obtained);
|
||||
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a named file, and parse it for username, domain, realm and password
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param file a named file to read the details from
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained)
|
||||
{
|
||||
uint16_t len = 0;
|
||||
char *ptr, *val, *param;
|
||||
char **lines;
|
||||
int i, numlines;
|
||||
|
||||
lines = file_lines_load(file, &numlines, NULL);
|
||||
|
||||
if (lines == NULL)
|
||||
{
|
||||
/* fail if we can't open the credentials file */
|
||||
d_printf("ERROR: Unable to open credentials file!\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
for (i = 0; i < numlines; i++) {
|
||||
len = strlen(lines[i]);
|
||||
|
||||
if (len == 0)
|
||||
continue;
|
||||
|
||||
/* break up the line into parameter & value.
|
||||
* will need to eat a little whitespace possibly */
|
||||
param = lines[i];
|
||||
if (!(ptr = strchr_m (lines[i], '=')))
|
||||
continue;
|
||||
|
||||
val = ptr+1;
|
||||
*ptr = '\0';
|
||||
|
||||
/* eat leading white space */
|
||||
while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
|
||||
val++;
|
||||
|
||||
if (strwicmp("password", param) == 0) {
|
||||
cli_credentials_set_password(cred, val, obtained);
|
||||
} else if (strwicmp("username", param) == 0) {
|
||||
cli_credentials_set_username(cred, val, obtained);
|
||||
} else if (strwicmp("domain", param) == 0) {
|
||||
cli_credentials_set_domain(cred, val, obtained);
|
||||
} else if (strwicmp("realm", param) == 0) {
|
||||
cli_credentials_set_realm(cred, val, obtained);
|
||||
}
|
||||
memset(lines[i], 0, len);
|
||||
}
|
||||
|
||||
talloc_free(lines);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred,
|
||||
const char *base,
|
||||
const char *filter)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
struct ldb_context *ldb;
|
||||
int ldb_ret;
|
||||
struct ldb_message **msgs;
|
||||
const char *attrs[] = {
|
||||
"secret",
|
||||
"priorSecret",
|
||||
"samAccountName",
|
||||
"flatname",
|
||||
"realm",
|
||||
"secureChannelType",
|
||||
"ntPwdHash",
|
||||
"msDS-KeyVersionNumber",
|
||||
"saltPrincipal",
|
||||
"privateKeytab",
|
||||
"krb5Keytab",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *machine_account;
|
||||
const char *password;
|
||||
const char *old_password;
|
||||
const char *domain;
|
||||
const char *realm;
|
||||
enum netr_SchannelType sct;
|
||||
const char *salt_principal;
|
||||
const char *keytab;
|
||||
|
||||
/* ok, we are going to get it now, don't recurse back here */
|
||||
cred->machine_account_pending = False;
|
||||
|
||||
/* some other parts of the system will key off this */
|
||||
cred->machine_account = True;
|
||||
|
||||
mem_ctx = talloc_named(cred, 0, "cli_credentials fetch machine password");
|
||||
|
||||
/* Local secrets are stored in secrets.ldb */
|
||||
ldb = secrets_db_connect(mem_ctx);
|
||||
if (!ldb) {
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
DEBUG(1, ("Could not open secrets.ldb\n"));
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
/* search for the secret record */
|
||||
ldb_ret = gendb_search(ldb,
|
||||
mem_ctx, ldb_dn_new(mem_ctx, ldb, base),
|
||||
&msgs, attrs,
|
||||
"%s", filter);
|
||||
if (ldb_ret == 0) {
|
||||
DEBUG(1, ("Could not find entry to match filter: %s\n",
|
||||
filter));
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
} else if (ldb_ret != 1) {
|
||||
DEBUG(1, ("Found more than one (%d) entry to match filter: %s\n",
|
||||
ldb_ret, filter));
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
password = ldb_msg_find_attr_as_string(msgs[0], "secret", NULL);
|
||||
old_password = ldb_msg_find_attr_as_string(msgs[0], "priorSecret", NULL);
|
||||
|
||||
machine_account = ldb_msg_find_attr_as_string(msgs[0], "samAccountName", NULL);
|
||||
|
||||
if (!machine_account) {
|
||||
DEBUG(1, ("Could not find 'samAccountName' in join record to domain: %s\n",
|
||||
cli_credentials_get_domain(cred)));
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
salt_principal = ldb_msg_find_attr_as_string(msgs[0], "saltPrincipal", NULL);
|
||||
cli_credentials_set_salt_principal(cred, salt_principal);
|
||||
|
||||
sct = ldb_msg_find_attr_as_int(msgs[0], "secureChannelType", 0);
|
||||
if (sct) {
|
||||
cli_credentials_set_secure_channel_type(cred, sct);
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
const struct ldb_val *nt_password_hash = ldb_msg_find_ldb_val(msgs[0], "ntPwdHash");
|
||||
struct samr_Password hash;
|
||||
ZERO_STRUCT(hash);
|
||||
if (nt_password_hash) {
|
||||
memcpy(hash.hash, nt_password_hash->data,
|
||||
MIN(nt_password_hash->length, sizeof(hash.hash)));
|
||||
|
||||
cli_credentials_set_nt_hash(cred, &hash, CRED_SPECIFIED);
|
||||
} else {
|
||||
cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
|
||||
}
|
||||
} else {
|
||||
cli_credentials_set_password(cred, password, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
|
||||
domain = ldb_msg_find_attr_as_string(msgs[0], "flatname", NULL);
|
||||
if (domain) {
|
||||
cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
realm = ldb_msg_find_attr_as_string(msgs[0], "realm", NULL);
|
||||
if (realm) {
|
||||
cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
|
||||
|
||||
cli_credentials_set_kvno(cred, ldb_msg_find_attr_as_int(msgs[0], "msDS-KeyVersionNumber", 0));
|
||||
|
||||
/* If there was an external keytab specified by reference in
|
||||
* the LDB, then use this. Otherwise we will make one up
|
||||
* (chewing CPU time) from the password */
|
||||
keytab = ldb_msg_find_attr_as_string(msgs[0], "krb5Keytab", NULL);
|
||||
if (keytab) {
|
||||
cli_credentials_set_keytab_name(cred, keytab, CRED_SPECIFIED);
|
||||
} else {
|
||||
keytab = ldb_msg_find_attr_as_string(msgs[0], "privateKeytab", NULL);
|
||||
if (keytab) {
|
||||
keytab = talloc_asprintf(mem_ctx, "FILE:%s", private_path(mem_ctx, keytab));
|
||||
if (keytab) {
|
||||
cli_credentials_set_keytab_name(cred, keytab, CRED_SPECIFIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred)
|
||||
{
|
||||
char *filter;
|
||||
/* Bleh, nasty recursion issues: We are setting a machine
|
||||
* account here, so we don't want the 'pending' flag around
|
||||
* any more */
|
||||
cred->machine_account_pending = False;
|
||||
filter = talloc_asprintf(cred, SECRETS_PRIMARY_DOMAIN_FILTER,
|
||||
cli_credentials_get_domain(cred));
|
||||
return cli_credentials_set_secrets(cred, SECRETS_PRIMARY_DOMAIN_DN,
|
||||
filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_krbtgt(struct cli_credentials *cred)
|
||||
{
|
||||
char *filter;
|
||||
/* Bleh, nasty recursion issues: We are setting a machine
|
||||
* account here, so we don't want the 'pending' flag around
|
||||
* any more */
|
||||
cred->machine_account_pending = False;
|
||||
filter = talloc_asprintf(cred, SECRETS_KRBTGT_SEARCH,
|
||||
cli_credentials_get_realm(cred),
|
||||
cli_credentials_get_domain(cred));
|
||||
return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN,
|
||||
filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred,
|
||||
const char *serviceprincipal)
|
||||
{
|
||||
char *filter;
|
||||
/* Bleh, nasty recursion issues: We are setting a machine
|
||||
* account here, so we don't want the 'pending' flag around
|
||||
* any more */
|
||||
cred->machine_account_pending = False;
|
||||
filter = talloc_asprintf(cred, SECRETS_PRINCIPAL_SEARCH,
|
||||
cli_credentials_get_realm(cred),
|
||||
cli_credentials_get_domain(cred),
|
||||
serviceprincipal);
|
||||
return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN,
|
||||
filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask that when required, the credentials system will be filled with
|
||||
* machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @note This function is used to call the above function after, rather
|
||||
* than during, popt processing.
|
||||
*
|
||||
*/
|
||||
void cli_credentials_set_machine_account_pending(struct cli_credentials *cred)
|
||||
{
|
||||
cred->machine_account_pending = True;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS cli_credentials_update_all_keytabs(TALLOC_CTX *parent_ctx)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
int ldb_ret;
|
||||
struct ldb_context *ldb;
|
||||
struct ldb_message **msgs;
|
||||
const char *attrs[] = { NULL };
|
||||
struct cli_credentials *creds;
|
||||
const char *filter;
|
||||
NTSTATUS status;
|
||||
int i, ret;
|
||||
|
||||
mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* Local secrets are stored in secrets.ldb */
|
||||
ldb = secrets_db_connect(mem_ctx);
|
||||
if (!ldb) {
|
||||
DEBUG(1, ("Could not open secrets.ldb\n"));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* search for the secret record, but only of things we can
|
||||
* actually update */
|
||||
ldb_ret = gendb_search(ldb,
|
||||
mem_ctx, NULL,
|
||||
&msgs, attrs,
|
||||
"(&(objectClass=kerberosSecret)(|(secret=*)(ntPwdHash=*)))");
|
||||
if (ldb_ret == -1) {
|
||||
DEBUG(1, ("Error looking for kerberos type secrets to push into a keytab:: %s", ldb_errstring(ldb)));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
for (i=0; i < ldb_ret; i++) {
|
||||
/* Make a credentials structure from it */
|
||||
creds = cli_credentials_init(mem_ctx);
|
||||
if (!creds) {
|
||||
DEBUG(1, ("cli_credentials_init failed!"));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
cli_credentials_set_conf(creds);
|
||||
filter = talloc_asprintf(mem_ctx, "dn=%s", ldb_dn_get_linearized(msgs[i]->dn));
|
||||
status = cli_credentials_set_secrets(creds, NULL, filter);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("Failed to read secrets for keytab update for %s\n",
|
||||
filter));
|
||||
continue;
|
||||
}
|
||||
ret = cli_credentials_update_keytab(creds);
|
||||
if (ret != 0) {
|
||||
DEBUG(1, ("Failed to update keytab for %s\n",
|
||||
filter));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Handle user credentials (as regards krb5)
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Tim Potter 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
int cli_credentials_get_krb5_context(struct cli_credentials *cred,
|
||||
struct smb_krb5_context **smb_krb5_context)
|
||||
{
|
||||
int ret;
|
||||
if (cred->smb_krb5_context) {
|
||||
*smb_krb5_context = cred->smb_krb5_context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = smb_krb5_init_context(cred, &cred->smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
*smb_krb5_context = cred->smb_krb5_context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This needs to be called directly after the cli_credentials_init(),
|
||||
* otherwise we might have problems with the krb5 context already
|
||||
* being here.
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
|
||||
struct smb_krb5_context *smb_krb5_context)
|
||||
{
|
||||
if (!talloc_reference(cred, smb_krb5_context)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
cred->smb_krb5_context = smb_krb5_context;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
int cli_credentials_set_from_ccache(struct cli_credentials *cred,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
|
||||
krb5_principal princ;
|
||||
krb5_error_code ret;
|
||||
char *name;
|
||||
char **realm;
|
||||
|
||||
if (cred->ccache_obtained > obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context,
|
||||
cred->ccache->ccache, &princ);
|
||||
|
||||
if (ret) {
|
||||
char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
|
||||
DEBUG(1,("failed to get principal from ccache: %s\n",
|
||||
err_mess));
|
||||
talloc_free(err_mess);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = krb5_unparse_name(cred->ccache->smb_krb5_context->krb5_context, princ, &name);
|
||||
if (ret) {
|
||||
char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
|
||||
DEBUG(1,("failed to unparse principal from ccache: %s\n",
|
||||
err_mess));
|
||||
talloc_free(err_mess);
|
||||
return ret;
|
||||
}
|
||||
|
||||
realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ);
|
||||
|
||||
cli_credentials_set_principal(cred, name, obtained);
|
||||
|
||||
free(name);
|
||||
|
||||
krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ);
|
||||
|
||||
cred->ccache_obtained = obtained;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free a memory ccache */
|
||||
static int free_mccache(struct ccache_container *ccc)
|
||||
{
|
||||
krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free a disk-based ccache */
|
||||
static int free_dccache(struct ccache_container *ccc) {
|
||||
krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cli_credentials_set_ccache(struct cli_credentials *cred,
|
||||
const char *name,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal princ;
|
||||
struct ccache_container *ccc;
|
||||
if (cred->ccache_obtained > obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ccc = talloc(cred, struct ccache_container);
|
||||
if (!ccc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
|
||||
if (ret) {
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
talloc_reference(ccc, ccc->smb_krb5_context);
|
||||
|
||||
if (name) {
|
||||
ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
|
||||
name,
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
|
||||
if (ret) {
|
||||
DEBUG(3,("failed to read default krb5 ccache: %s\n",
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_set_destructor(ccc, free_dccache);
|
||||
|
||||
ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
|
||||
|
||||
if (ret) {
|
||||
DEBUG(3,("failed to get principal from default ccache: %s\n",
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
|
||||
|
||||
cred->ccache = ccc;
|
||||
talloc_steal(cred, ccc);
|
||||
|
||||
ret = cli_credentials_set_from_ccache(cred, obtained);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *rand_string;
|
||||
struct ccache_container *ccc = talloc(cred, struct ccache_container);
|
||||
char *ccache_name;
|
||||
if (!ccc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
rand_string = generate_random_str(NULL, 16);
|
||||
if (!rand_string) {
|
||||
talloc_free(ccc);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ccache_name = talloc_asprintf(ccc, "MEMORY:%s",
|
||||
rand_string);
|
||||
talloc_free(rand_string);
|
||||
|
||||
if (!ccache_name) {
|
||||
talloc_free(ccc);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
|
||||
if (ret) {
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
talloc_reference(ccc, ccc->smb_krb5_context);
|
||||
|
||||
ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
|
||||
ccache_name,
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccache_name);
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_set_destructor(ccc, free_mccache);
|
||||
|
||||
cred->ccache = ccc;
|
||||
talloc_steal(cred, ccc);
|
||||
talloc_free(ccache_name);
|
||||
|
||||
if (_ccc) {
|
||||
*_ccc = ccc;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cli_credentials_get_ccache(struct cli_credentials *cred,
|
||||
struct ccache_container **ccc)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
if (cred->ccache_obtained >= (MAX(cred->principal_obtained,
|
||||
cred->username_obtained))) {
|
||||
*ccc = cred->ccache;
|
||||
return 0;
|
||||
}
|
||||
if (cli_credentials_is_anonymous(cred)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
ret = cli_credentials_new_ccache(cred, NULL);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
*ccc = cred->ccache;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int free_gssapi_creds(struct gssapi_creds_container *gcc)
|
||||
{
|
||||
OM_uint32 min_stat, maj_stat;
|
||||
maj_stat = gss_release_cred(&min_stat, &gcc->creds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
|
||||
struct gssapi_creds_container **_gcc)
|
||||
{
|
||||
int ret = 0;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
struct gssapi_creds_container *gcc;
|
||||
struct ccache_container *ccache;
|
||||
if (cred->client_gss_creds_obtained >= (MAX(cred->ccache_obtained,
|
||||
MAX(cred->principal_obtained,
|
||||
cred->username_obtained)))) {
|
||||
*_gcc = cred->client_gss_creds;
|
||||
return 0;
|
||||
}
|
||||
ret = cli_credentials_get_ccache(cred,
|
||||
&ccache);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
gcc = talloc(cred, struct gssapi_creds_container);
|
||||
if (!gcc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
|
||||
&gcc->creds);
|
||||
if (maj_stat) {
|
||||
if (min_stat) {
|
||||
ret = min_stat;
|
||||
} else {
|
||||
ret = EINVAL;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
cred->client_gss_creds_obtained = cred->ccache_obtained;
|
||||
talloc_set_destructor(gcc, free_gssapi_creds);
|
||||
cred->client_gss_creds = gcc;
|
||||
*_gcc = gcc;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
Set a gssapi cred_id_t into the credentails system. (Client case)
|
||||
|
||||
This grabs the credentials both 'intact' and getting the krb5
|
||||
ccache out of it. This routine can be generalised in future for
|
||||
the case where we deal with GSSAPI mechs other than krb5.
|
||||
|
||||
On sucess, the caller must not free gssapi_cred, as it now belongs
|
||||
to the credentials system.
|
||||
*/
|
||||
|
||||
int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
|
||||
gss_cred_id_t gssapi_cred,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
int ret;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
struct ccache_container *ccc;
|
||||
struct gssapi_creds_container *gcc;
|
||||
if (cred->client_gss_creds_obtained > obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
gcc = talloc(cred, struct gssapi_creds_container);
|
||||
if (!gcc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_new_ccache(cred, &ccc);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
maj_stat = gss_krb5_copy_ccache(&min_stat,
|
||||
gssapi_cred, ccc->ccache);
|
||||
if (maj_stat) {
|
||||
if (min_stat) {
|
||||
ret = min_stat;
|
||||
} else {
|
||||
ret = EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ret = cli_credentials_set_from_ccache(cred, obtained);
|
||||
}
|
||||
if (ret == 0) {
|
||||
gcc->creds = gssapi_cred;
|
||||
talloc_set_destructor(gcc, free_gssapi_creds);
|
||||
|
||||
cred->client_gss_creds_obtained = obtained;
|
||||
cred->client_gss_creds = gcc;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the keytab (actually, a container containing the krb5_keytab)
|
||||
* attached to this context. If this hasn't been done or set before,
|
||||
* it will be generated from the password.
|
||||
*/
|
||||
int cli_credentials_get_keytab(struct cli_credentials *cred,
|
||||
struct keytab_container **_ktc)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
|
||||
cred->username_obtained))) {
|
||||
*_ktc = cred->keytab;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cli_credentials_is_anonymous(cred)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = smb_krb5_create_memory_keytab(mem_ctx, cred, smb_krb5_context, &ktc);
|
||||
if (ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cred->keytab_obtained = (MAX(cred->principal_obtained,
|
||||
cred->username_obtained));
|
||||
|
||||
talloc_steal(cred, ktc);
|
||||
cred->keytab = ktc;
|
||||
*_ktc = cred->keytab;
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Given the name of a keytab (presumably in the format
|
||||
* FILE:/etc/krb5.keytab), open it and attach it */
|
||||
|
||||
int cli_credentials_set_keytab_name(struct cli_credentials *cred,
|
||||
const char *keytab_name,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if (cred->keytab_obtained >= obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
|
||||
keytab_name, &ktc);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
cred->keytab_obtained = obtained;
|
||||
|
||||
talloc_steal(cred, ktc);
|
||||
cred->keytab = ktc;
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cli_credentials_update_keytab(struct cli_credentials *cred)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_keytab(cred, &ktc);
|
||||
if (ret != 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, ktc);
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get server gss credentials (in gsskrb5, this means the keytab) */
|
||||
|
||||
int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
|
||||
struct gssapi_creds_container **_gcc)
|
||||
{
|
||||
int ret = 0;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
struct gssapi_creds_container *gcc;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
krb5_principal princ;
|
||||
|
||||
if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
|
||||
MAX(cred->principal_obtained,
|
||||
cred->username_obtained)))) {
|
||||
*_gcc = cred->server_gss_creds;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_keytab(cred,
|
||||
&ktc);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gcc = talloc(cred, struct gssapi_creds_container);
|
||||
if (!gcc) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* This creates a GSSAPI cred_id_t with the principal and keytab set */
|
||||
maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
|
||||
&gcc->creds);
|
||||
if (maj_stat) {
|
||||
if (min_stat) {
|
||||
ret = min_stat;
|
||||
} else {
|
||||
ret = EINVAL;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
cred->server_gss_creds_obtained = cred->keytab_obtained;
|
||||
talloc_set_destructor(gcc, free_gssapi_creds);
|
||||
cred->server_gss_creds = gcc;
|
||||
*_gcc = gcc;
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Kerberos KVNO
|
||||
*/
|
||||
|
||||
void cli_credentials_set_kvno(struct cli_credentials *cred,
|
||||
int kvno)
|
||||
{
|
||||
cred->kvno = kvno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Kerberos KVNO
|
||||
*/
|
||||
|
||||
int cli_credentials_get_kvno(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->kvno;
|
||||
}
|
||||
|
||||
const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->salt_principal;
|
||||
}
|
||||
|
||||
void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
|
||||
{
|
||||
cred->salt_principal = talloc_strdup(cred, principal);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
samba -- Unix SMB/CIFS implementation.
|
||||
|
||||
Client credentials structure
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2004-2006
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "heimdal/lib/gssapi/gssapi/gssapi.h"
|
||||
|
||||
struct ccache_container;
|
||||
|
||||
struct gssapi_creds_container {
|
||||
gss_cred_id_t creds;
|
||||
};
|
||||
|
||||
#include "auth/credentials/credentials_krb5_proto.h"
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
User credentials handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
|
||||
void cli_credentials_get_ntlm_username_domain(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
|
||||
const char **username,
|
||||
const char **domain)
|
||||
{
|
||||
if (cred->principal_obtained > cred->username_obtained) {
|
||||
*domain = talloc_strdup(mem_ctx, "");
|
||||
*username = cli_credentials_get_principal(cred, mem_ctx);
|
||||
} else {
|
||||
*domain = cli_credentials_get_domain(cred);
|
||||
*username = cli_credentials_get_username(cred);
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
|
||||
int *flags,
|
||||
DATA_BLOB challenge, DATA_BLOB target_info,
|
||||
DATA_BLOB *_lm_response, DATA_BLOB *_nt_response,
|
||||
DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key)
|
||||
{
|
||||
const char *user, *domain;
|
||||
DATA_BLOB lm_response, nt_response;
|
||||
DATA_BLOB lm_session_key, session_key;
|
||||
const struct samr_Password *nt_hash;
|
||||
lm_session_key = data_blob(NULL, 0);
|
||||
|
||||
nt_hash = cli_credentials_get_nt_hash(cred, mem_ctx);
|
||||
|
||||
cli_credentials_get_ntlm_username_domain(cred, mem_ctx, &user, &domain);
|
||||
|
||||
/* If we are sending a username@realm login (see function
|
||||
* above), then we will not send LM, it will not be
|
||||
* accepted */
|
||||
if (cred->principal_obtained > cred->username_obtained) {
|
||||
*flags = *flags & ~CLI_CRED_LANMAN_AUTH;
|
||||
}
|
||||
|
||||
/* Likewise if we are a machine account (avoid protocol downgrade attacks) */
|
||||
if (cred->machine_account) {
|
||||
*flags = *flags & ~CLI_CRED_LANMAN_AUTH;
|
||||
}
|
||||
|
||||
if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (!nt_hash) {
|
||||
static const uint8_t zeros[16];
|
||||
/* do nothing - blobs are zero length */
|
||||
|
||||
/* session key is all zeros */
|
||||
session_key = data_blob_talloc(mem_ctx, zeros, 16);
|
||||
lm_session_key = data_blob_talloc(mem_ctx, zeros, 16);
|
||||
|
||||
lm_response = data_blob(NULL, 0);
|
||||
nt_response = data_blob(NULL, 0);
|
||||
|
||||
/* not doing NTLM2 without a password */
|
||||
*flags &= ~CLI_CRED_NTLM2;
|
||||
} else if (*flags & CLI_CRED_NTLMv2_AUTH) {
|
||||
|
||||
if (!target_info.length) {
|
||||
/* be lazy, match win2k - we can't do NTLMv2 without it */
|
||||
DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* TODO: if the remote server is standalone, then we should replace 'domain'
|
||||
with the server name as supplied above */
|
||||
|
||||
if (!SMBNTLMv2encrypt_hash(mem_ctx,
|
||||
user,
|
||||
domain,
|
||||
nt_hash->hash, &challenge,
|
||||
&target_info,
|
||||
&lm_response, &nt_response,
|
||||
NULL, &session_key)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
} else if (*flags & CLI_CRED_NTLM2) {
|
||||
struct MD5Context md5_session_nonce_ctx;
|
||||
uint8_t session_nonce[16];
|
||||
uint8_t session_nonce_hash[16];
|
||||
uint8_t user_session_key[16];
|
||||
|
||||
lm_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
generate_random_buffer(lm_response.data, 8);
|
||||
memset(lm_response.data+8, 0, 16);
|
||||
|
||||
memcpy(session_nonce, challenge.data, 8);
|
||||
memcpy(&session_nonce[8], lm_response.data, 8);
|
||||
|
||||
MD5Init(&md5_session_nonce_ctx);
|
||||
MD5Update(&md5_session_nonce_ctx, challenge.data, 8);
|
||||
MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
|
||||
MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
|
||||
|
||||
DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
|
||||
DEBUG(5, ("challenge is: \n"));
|
||||
dump_data(5, session_nonce_hash, 8);
|
||||
|
||||
nt_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
SMBOWFencrypt(nt_hash->hash,
|
||||
session_nonce_hash,
|
||||
nt_response.data);
|
||||
|
||||
session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
|
||||
SMBsesskeygen_ntv1(nt_hash->hash, user_session_key);
|
||||
hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data);
|
||||
dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
} else {
|
||||
uint8_t lm_hash[16];
|
||||
nt_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
SMBOWFencrypt(nt_hash->hash, challenge.data,
|
||||
nt_response.data);
|
||||
|
||||
session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
SMBsesskeygen_ntv1(nt_hash->hash, session_key.data);
|
||||
dump_data_pw("NT session key:\n", session_key.data, session_key.length);
|
||||
|
||||
/* lanman auth is insecure, it may be disabled.
|
||||
We may also not have a password */
|
||||
if (*flags & CLI_CRED_LANMAN_AUTH) {
|
||||
const char *password;
|
||||
password = cli_credentials_get_password(cred);
|
||||
if (!password) {
|
||||
lm_response = nt_response;
|
||||
} else {
|
||||
lm_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
if (!SMBencrypt(password,challenge.data,
|
||||
lm_response.data)) {
|
||||
/* If the LM password was too long (and therefore the LM hash being
|
||||
of the first 14 chars only), don't send it.
|
||||
|
||||
We don't have any better options but to send the NT response
|
||||
*/
|
||||
data_blob_free(&lm_response);
|
||||
lm_response = nt_response;
|
||||
/* LM Key is incompatible with 'long' passwords */
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
} else {
|
||||
E_deshash(password, lm_hash);
|
||||
lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
memcpy(lm_session_key.data, lm_hash, 8);
|
||||
memset(&lm_session_key.data[8], '\0', 8);
|
||||
|
||||
if (!(*flags & CLI_CRED_NTLM_AUTH)) {
|
||||
session_key = lm_session_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char *password;
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
lm_response = nt_response;
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
|
||||
password = cli_credentials_get_password(cred);
|
||||
if (password) {
|
||||
E_deshash(password, lm_hash);
|
||||
lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
memcpy(lm_session_key.data, lm_hash, 8);
|
||||
memset(&lm_session_key.data[8], '\0', 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_lm_response) {
|
||||
*_lm_response = lm_response;
|
||||
}
|
||||
if (_nt_response) {
|
||||
*_nt_response = nt_response;
|
||||
}
|
||||
if (_lm_session_key) {
|
||||
*_lm_session_key = lm_session_key;
|
||||
}
|
||||
if (_session_key) {
|
||||
*_session_key = session_key;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
SMB_ENABLE(gensec_krb5, $HAVE_KRB5)
|
||||
SMB_ENABLE(gensec_gssapi, $HAVE_KRB5)
|
||||
@@ -0,0 +1,91 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM gensec
|
||||
[LIBRARY::gensec]
|
||||
VERSION = 0.0.1
|
||||
SO_VERSION = 0
|
||||
DESCRIPTION = Generic Security Library
|
||||
PUBLIC_HEADERS = gensec.h spnego.h
|
||||
PUBLIC_PROTO_HEADER = gensec_proto.h
|
||||
OBJ_FILES = gensec.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
CREDENTIALS LIBSAMBA-UTIL LIBCRYPTO ASN1_UTIL
|
||||
# End SUBSYSTEM gensec
|
||||
#################################
|
||||
|
||||
################################################
|
||||
# Start MODULE gensec_krb5
|
||||
[MODULE::gensec_krb5]
|
||||
SUBSYSTEM = gensec
|
||||
INIT_FUNCTION = gensec_krb5_init
|
||||
OBJ_FILES = gensec_krb5.o
|
||||
PUBLIC_DEPENDENCIES = CREDENTIALS_KRB5 KERBEROS auth auth_sam
|
||||
# End MODULE gensec_krb5
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE gensec_gssapi
|
||||
[MODULE::gensec_gssapi]
|
||||
SUBSYSTEM = gensec
|
||||
INIT_FUNCTION = gensec_gssapi_init
|
||||
OBJ_FILES = gensec_gssapi.o
|
||||
PUBLIC_DEPENDENCIES = CREDENTIALS_KRB5 KERBEROS auth HEIMDAL_GSSAPI
|
||||
# End MODULE gensec_gssapi
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE cyrus_sasl
|
||||
[MODULE::cyrus_sasl]
|
||||
SUBSYSTEM = gensec
|
||||
INIT_FUNCTION = gensec_sasl_init
|
||||
OBJ_FILES = cyrus_sasl.o
|
||||
PUBLIC_DEPENDENCIES = CREDENTIALS SASL auth
|
||||
# End MODULE cyrus_sasl
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE gensec_spnego
|
||||
[MODULE::gensec_spnego]
|
||||
SUBSYSTEM = gensec
|
||||
INIT_FUNCTION = gensec_spnego_init
|
||||
PRIVATE_PROTO_HEADER = spnego_proto.h
|
||||
PRIVATE_DEPENDENCIES = ASN1_UTIL GENSEC_SOCKET
|
||||
PUBLIC_DEPENDENCIES = CREDENTIALS
|
||||
OBJ_FILES = spnego.o \
|
||||
spnego_parse.o
|
||||
# End MODULE gensec_spnego
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE gensec_schannel
|
||||
[MODULE::gensec_schannel]
|
||||
SUBSYSTEM = gensec
|
||||
PRIVATE_PROTO_HEADER = schannel_proto.h
|
||||
INIT_FUNCTION = gensec_schannel_init
|
||||
OBJ_FILES = schannel.o \
|
||||
schannel_sign.o
|
||||
PUBLIC_DEPENDENCIES = auth SCHANNELDB NDR_SCHANNEL CREDENTIALS
|
||||
OUTPUT_TYPE = INTEGRATED
|
||||
# End MODULE gensec_schannel
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM SCHANNELDB
|
||||
[SUBSYSTEM::SCHANNELDB]
|
||||
PRIVATE_PROTO_HEADER = schannel_state.h
|
||||
OBJ_FILES = \
|
||||
schannel_state.o
|
||||
#
|
||||
# End SUBSYSTEM SCHANNELDB
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM GENSEC_SOCKET
|
||||
[SUBSYSTEM::GENSEC_SOCKET]
|
||||
OBJ_FILES = \
|
||||
socket.o
|
||||
PUBLIC_DEPENDENCIES = samba-socket LIBPACKET
|
||||
#PUBLIC_DEPENDENCIES = gensec
|
||||
#
|
||||
# End SUBSYSTEM GENSEC_SOCKET
|
||||
################################################
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Connect GENSEC to an external SASL lib
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include <sasl/sasl.h>
|
||||
|
||||
struct gensec_sasl_state {
|
||||
sasl_conn_t *conn;
|
||||
int step;
|
||||
};
|
||||
|
||||
static NTSTATUS sasl_nt_status(int sasl_ret)
|
||||
{
|
||||
switch (sasl_ret) {
|
||||
case SASL_CONTINUE:
|
||||
return NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
case SASL_NOMEM:
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
case SASL_BADPARAM:
|
||||
case SASL_NOMECH:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
case SASL_BADMAC:
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
case SASL_OK:
|
||||
return NT_STATUS_OK;
|
||||
default:
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
}
|
||||
|
||||
static int gensec_sasl_get_user(void *context, int id,
|
||||
const char **result, unsigned *len)
|
||||
{
|
||||
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
|
||||
const char *username = cli_credentials_get_username(gensec_get_credentials(gensec_security));
|
||||
if (id != SASL_CB_USER && id != SASL_CB_AUTHNAME) {
|
||||
return SASL_FAIL;
|
||||
}
|
||||
|
||||
*result = username;
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
static int gensec_sasl_get_realm(void *context, int id,
|
||||
const char **availrealms,
|
||||
const char **result)
|
||||
{
|
||||
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
|
||||
const char *realm = cli_credentials_get_realm(gensec_get_credentials(gensec_security));
|
||||
int i;
|
||||
if (id != SASL_CB_GETREALM) {
|
||||
return SASL_FAIL;
|
||||
}
|
||||
|
||||
for (i=0; availrealms && availrealms[i]; i++) {
|
||||
if (strcasecmp_m(realm, availrealms[i]) == 0) {
|
||||
result[i] = availrealms[i];
|
||||
return SASL_OK;
|
||||
}
|
||||
}
|
||||
/* None of the realms match, so lets not specify one */
|
||||
*result = "";
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
static int gensec_sasl_get_password(sasl_conn_t *conn, void *context, int id,
|
||||
sasl_secret_t **psecret)
|
||||
{
|
||||
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
|
||||
const char *password = cli_credentials_get_password(gensec_get_credentials(gensec_security));
|
||||
|
||||
sasl_secret_t *secret;
|
||||
if (!password) {
|
||||
*psecret = NULL;
|
||||
return SASL_OK;
|
||||
}
|
||||
secret = talloc_size(gensec_security, sizeof(sasl_secret_t)+strlen(password));
|
||||
if (!secret) {
|
||||
return SASL_NOMEM;
|
||||
}
|
||||
secret->len = strlen(password);
|
||||
safe_strcpy(secret->data, password, secret->len+1);
|
||||
*psecret = secret;
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
static int gensec_sasl_dispose(struct gensec_sasl_state *gensec_sasl_state)
|
||||
{
|
||||
sasl_dispose(&gensec_sasl_state->conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_sasl_client_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct gensec_sasl_state *gensec_sasl_state;
|
||||
const char *service = gensec_get_target_service(gensec_security);
|
||||
const char *target_name = gensec_get_target_hostname(gensec_security);
|
||||
struct socket_address *local_socket_addr = gensec_get_my_addr(gensec_security);
|
||||
struct socket_address *remote_socket_addr = gensec_get_peer_addr(gensec_security);
|
||||
char *local_addr = NULL;
|
||||
char *remote_addr = NULL;
|
||||
int sasl_ret;
|
||||
|
||||
sasl_callback_t *callbacks;
|
||||
|
||||
gensec_sasl_state = talloc(gensec_security, struct gensec_sasl_state);
|
||||
if (!gensec_sasl_state) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
callbacks = talloc_array(gensec_sasl_state, sasl_callback_t, 5);
|
||||
callbacks[0].id = SASL_CB_USER;
|
||||
callbacks[0].proc = gensec_sasl_get_user;
|
||||
callbacks[0].context = gensec_security;
|
||||
|
||||
callbacks[1].id = SASL_CB_AUTHNAME;
|
||||
callbacks[1].proc = gensec_sasl_get_user;
|
||||
callbacks[1].context = gensec_security;
|
||||
|
||||
callbacks[2].id = SASL_CB_GETREALM;
|
||||
callbacks[2].proc = gensec_sasl_get_realm;
|
||||
callbacks[2].context = gensec_security;
|
||||
|
||||
callbacks[3].id = SASL_CB_PASS;
|
||||
callbacks[3].proc = gensec_sasl_get_password;
|
||||
callbacks[3].context = gensec_security;
|
||||
|
||||
callbacks[4].id = SASL_CB_LIST_END;
|
||||
callbacks[4].proc = NULL;
|
||||
callbacks[4].context = NULL;
|
||||
|
||||
gensec_security->private_data = gensec_sasl_state;
|
||||
|
||||
if (local_socket_addr) {
|
||||
local_addr = talloc_asprintf(gensec_sasl_state,
|
||||
"%s;%d",
|
||||
local_socket_addr->addr,
|
||||
local_socket_addr->port);
|
||||
}
|
||||
|
||||
if (remote_socket_addr) {
|
||||
remote_addr = talloc_asprintf(gensec_sasl_state,
|
||||
"%s;%d",
|
||||
remote_socket_addr->addr,
|
||||
remote_socket_addr->port);
|
||||
}
|
||||
gensec_sasl_state->step = 0;
|
||||
|
||||
sasl_ret = sasl_client_new(service,
|
||||
target_name,
|
||||
local_addr, remote_addr, callbacks, 0,
|
||||
&gensec_sasl_state->conn);
|
||||
|
||||
if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
|
||||
sasl_security_properties_t props;
|
||||
talloc_set_destructor(gensec_sasl_state, gensec_sasl_dispose);
|
||||
|
||||
ZERO_STRUCT(props);
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
|
||||
props.min_ssf = 1;
|
||||
}
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
|
||||
props.min_ssf = 40;
|
||||
}
|
||||
|
||||
props.max_ssf = UINT_MAX;
|
||||
props.maxbufsize = 65536;
|
||||
sasl_ret = sasl_setprop(gensec_sasl_state->conn, SASL_SEC_PROPS, &props);
|
||||
if (sasl_ret != SASL_OK) {
|
||||
return sasl_nt_status(sasl_ret);
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG(1, ("GENSEC SASL: client_new failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
|
||||
}
|
||||
return sasl_nt_status(sasl_ret);
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_sasl_update(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
|
||||
struct gensec_sasl_state);
|
||||
int sasl_ret;
|
||||
const char *out_data;
|
||||
unsigned int out_len;
|
||||
|
||||
if (gensec_sasl_state->step == 0) {
|
||||
const char *mech;
|
||||
sasl_ret = sasl_client_start(gensec_sasl_state->conn, gensec_security->ops->sasl_name,
|
||||
NULL, &out_data, &out_len, &mech);
|
||||
} else {
|
||||
sasl_ret = sasl_client_step(gensec_sasl_state->conn,
|
||||
in.data, in.length, NULL, &out_data, &out_len);
|
||||
}
|
||||
if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
|
||||
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
|
||||
} else {
|
||||
DEBUG(1, ("GENSEC SASL: step %d update failed: %s\n", gensec_sasl_state->step,
|
||||
sasl_errdetail(gensec_sasl_state->conn)));
|
||||
}
|
||||
gensec_sasl_state->step++;
|
||||
return sasl_nt_status(sasl_ret);
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_sasl_unwrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed)
|
||||
{
|
||||
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
|
||||
struct gensec_sasl_state);
|
||||
const char *out_data;
|
||||
unsigned int out_len;
|
||||
|
||||
int sasl_ret = sasl_decode(gensec_sasl_state->conn,
|
||||
in->data, in->length, &out_data, &out_len);
|
||||
if (sasl_ret == SASL_OK) {
|
||||
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
|
||||
*len_processed = in->length;
|
||||
} else {
|
||||
DEBUG(1, ("GENSEC SASL: unwrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
|
||||
}
|
||||
return sasl_nt_status(sasl_ret);
|
||||
|
||||
}
|
||||
static NTSTATUS gensec_sasl_wrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed)
|
||||
{
|
||||
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
|
||||
struct gensec_sasl_state);
|
||||
const char *out_data;
|
||||
unsigned int out_len;
|
||||
|
||||
int sasl_ret = sasl_encode(gensec_sasl_state->conn,
|
||||
in->data, in->length, &out_data, &out_len);
|
||||
if (sasl_ret == SASL_OK) {
|
||||
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
|
||||
*len_processed = in->length;
|
||||
} else {
|
||||
DEBUG(1, ("GENSEC SASL: wrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
|
||||
}
|
||||
return sasl_nt_status(sasl_ret);
|
||||
}
|
||||
|
||||
/* Try to figure out what features we actually got on the connection */
|
||||
static BOOL gensec_sasl_have_feature(struct gensec_security *gensec_security,
|
||||
uint32_t feature)
|
||||
{
|
||||
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
|
||||
struct gensec_sasl_state);
|
||||
sasl_ssf_t ssf;
|
||||
int sasl_ret = sasl_getprop(gensec_sasl_state->conn, SASL_SSF, &ssf);
|
||||
if (sasl_ret != SASL_OK) {
|
||||
return False;
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_SIGN) {
|
||||
if (ssf == 0) {
|
||||
return False;
|
||||
}
|
||||
if (ssf >= 1) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_SEAL) {
|
||||
if (ssf <= 1) {
|
||||
return False;
|
||||
}
|
||||
if (ssf > 1) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/* This could in theory work with any SASL mech */
|
||||
static const struct gensec_security_ops gensec_sasl_security_ops = {
|
||||
.name = "sasl-DIGEST-MD5",
|
||||
.sasl_name = "DIGEST-MD5",
|
||||
.client_start = gensec_sasl_client_start,
|
||||
.update = gensec_sasl_update,
|
||||
.wrap_packets = gensec_sasl_wrap_packets,
|
||||
.unwrap_packets = gensec_sasl_unwrap_packets,
|
||||
.have_feature = gensec_sasl_have_feature,
|
||||
.enabled = True,
|
||||
.priority = GENSEC_SASL
|
||||
};
|
||||
|
||||
int gensec_sasl_log(void *context,
|
||||
int sasl_log_level,
|
||||
const char *message)
|
||||
{
|
||||
int debug_level;
|
||||
switch (sasl_log_level) {
|
||||
case SASL_LOG_NONE:
|
||||
debug_level = 0;
|
||||
break;
|
||||
case SASL_LOG_ERR:
|
||||
debug_level = 1;
|
||||
break;
|
||||
case SASL_LOG_FAIL:
|
||||
debug_level = 2;
|
||||
break;
|
||||
case SASL_LOG_WARN:
|
||||
debug_level = 3;
|
||||
break;
|
||||
case SASL_LOG_NOTE:
|
||||
debug_level = 5;
|
||||
break;
|
||||
case SASL_LOG_DEBUG:
|
||||
debug_level = 10;
|
||||
break;
|
||||
case SASL_LOG_TRACE:
|
||||
debug_level = 11;
|
||||
break;
|
||||
#if DEBUG_PASSWORD
|
||||
case SASL_LOG_PASS:
|
||||
debug_level = 100;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
debug_level = 0;
|
||||
break;
|
||||
}
|
||||
DEBUG(debug_level, ("gensec_sasl: %s\n", message));
|
||||
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
NTSTATUS gensec_sasl_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
int sasl_ret, i;
|
||||
const char **sasl_mechs;
|
||||
|
||||
static const sasl_callback_t callbacks[] = {
|
||||
{
|
||||
.id = SASL_CB_LOG,
|
||||
.proc = gensec_sasl_log,
|
||||
.context = NULL,
|
||||
},
|
||||
{
|
||||
.id = SASL_CB_LIST_END,
|
||||
.proc = gensec_sasl_log,
|
||||
.context = NULL,
|
||||
}
|
||||
};
|
||||
sasl_ret = sasl_client_init(callbacks);
|
||||
|
||||
if (sasl_ret == SASL_NOMECH) {
|
||||
/* Nothing to do here */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (sasl_ret != SASL_OK) {
|
||||
return sasl_nt_status(sasl_ret);
|
||||
}
|
||||
|
||||
/* For now, we just register DIGEST-MD5 */
|
||||
#if 1
|
||||
ret = gensec_register(&gensec_sasl_security_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_sasl_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
sasl_mechs = sasl_global_listmech();
|
||||
for (i = 0; sasl_mechs && sasl_mechs[i]; i++) {
|
||||
const struct gensec_security_ops *oldmech;
|
||||
struct gensec_security_ops *newmech;
|
||||
oldmech = gensec_security_by_sasl_name(NULL, sasl_mechs[i]);
|
||||
if (oldmech) {
|
||||
continue;
|
||||
}
|
||||
newmech = talloc(talloc_autofree_context(), struct gensec_security_ops);
|
||||
if (!newmech) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
*newmech = gensec_sasl_security_ops;
|
||||
newmech->sasl_name = talloc_strdup(newmech, sasl_mechs[i]);
|
||||
newmech->name = talloc_asprintf(newmech, "sasl-%s", sasl_mechs[i]);
|
||||
if (!newmech->sasl_name || !newmech->name) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = gensec_register(newmech);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_sasl_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Generic Authentication Interface
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GENSEC_H__
|
||||
#define __GENSEC_H__
|
||||
|
||||
#include "core.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
|
||||
#define GENSEC_OID_NTLMSSP "1 3 6 1 4 1 311 2 2 10"
|
||||
#define GENSEC_OID_SPNEGO "1 3 6 1 5 5 2"
|
||||
#define GENSEC_OID_KERBEROS5 "1 2 840 113554 1 2 2"
|
||||
#define GENSEC_OID_KERBEROS5_OLD "1 2 840 48018 1 2 2"
|
||||
#define GENSEC_OID_KERBEROS5_USER2USER "1 2 840 113554 1 2 2 3"
|
||||
|
||||
enum gensec_priority {
|
||||
GENSEC_SPNEGO = 90,
|
||||
GENSEC_GSSAPI = 80,
|
||||
GENSEC_KRB5 = 70,
|
||||
GENSEC_SCHANNEL = 60,
|
||||
GENSEC_NTLMSSP = 50,
|
||||
GENSEC_SASL = 20,
|
||||
GENSEC_OTHER = 0
|
||||
};
|
||||
|
||||
enum credentials_use_kerberos;
|
||||
|
||||
struct gensec_security;
|
||||
struct gensec_target {
|
||||
const char *principal;
|
||||
const char *hostname;
|
||||
const char *service;
|
||||
};
|
||||
|
||||
#define GENSEC_FEATURE_SESSION_KEY 0x00000001
|
||||
#define GENSEC_FEATURE_SIGN 0x00000002
|
||||
#define GENSEC_FEATURE_SEAL 0x00000004
|
||||
#define GENSEC_FEATURE_DCE_STYLE 0x00000008
|
||||
#define GENSEC_FEATURE_ASYNC_REPLIES 0x00000010
|
||||
#define GENSEC_FEATURE_DATAGRAM_MODE 0x00000020
|
||||
|
||||
/* GENSEC mode */
|
||||
enum gensec_role
|
||||
{
|
||||
GENSEC_SERVER,
|
||||
GENSEC_CLIENT
|
||||
};
|
||||
|
||||
struct auth_session_info;
|
||||
|
||||
struct gensec_update_request {
|
||||
struct gensec_security *gensec_security;
|
||||
void *private_data;
|
||||
DATA_BLOB in;
|
||||
DATA_BLOB out;
|
||||
NTSTATUS status;
|
||||
struct {
|
||||
void (*fn)(struct gensec_update_request *req, void *private_data);
|
||||
void *private_data;
|
||||
} callback;
|
||||
};
|
||||
|
||||
struct gensec_security_ops {
|
||||
const char *name;
|
||||
const char *sasl_name;
|
||||
uint8_t auth_type; /* 0 if not offered on DCE-RPC */
|
||||
const char **oid; /* NULL if not offered by SPNEGO */
|
||||
NTSTATUS (*client_start)(struct gensec_security *gensec_security);
|
||||
NTSTATUS (*server_start)(struct gensec_security *gensec_security);
|
||||
/**
|
||||
Determine if a packet has the right 'magic' for this mechanism
|
||||
*/
|
||||
NTSTATUS (*magic)(struct gensec_security *gensec_security,
|
||||
const DATA_BLOB *first_packet);
|
||||
NTSTATUS (*update)(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out);
|
||||
NTSTATUS (*seal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig);
|
||||
NTSTATUS (*sign_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig);
|
||||
size_t (*sig_size)(struct gensec_security *gensec_security, size_t data_size);
|
||||
size_t (*max_input_size)(struct gensec_security *gensec_security);
|
||||
size_t (*max_wrapped_size)(struct gensec_security *gensec_security);
|
||||
NTSTATUS (*check_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig);
|
||||
NTSTATUS (*unseal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig);
|
||||
NTSTATUS (*wrap)(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out);
|
||||
NTSTATUS (*unwrap)(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out);
|
||||
NTSTATUS (*wrap_packets)(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed);
|
||||
NTSTATUS (*unwrap_packets)(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed);
|
||||
NTSTATUS (*packet_full_request)(struct gensec_security *gensec_security,
|
||||
DATA_BLOB blob, size_t *size);
|
||||
NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key);
|
||||
NTSTATUS (*session_info)(struct gensec_security *gensec_security,
|
||||
struct auth_session_info **session_info);
|
||||
BOOL (*have_feature)(struct gensec_security *gensec_security,
|
||||
uint32_t feature);
|
||||
BOOL enabled;
|
||||
BOOL kerberos;
|
||||
enum gensec_priority priority;
|
||||
};
|
||||
|
||||
struct gensec_security_ops_wrapper {
|
||||
const struct gensec_security_ops *op;
|
||||
const char *oid;
|
||||
};
|
||||
|
||||
#define GENSEC_INTERFACE_VERSION 0
|
||||
|
||||
struct gensec_security {
|
||||
const struct gensec_security_ops *ops;
|
||||
void *private_data;
|
||||
struct cli_credentials *credentials;
|
||||
struct gensec_target target;
|
||||
enum gensec_role gensec_role;
|
||||
BOOL subcontext;
|
||||
uint32_t want_features;
|
||||
struct event_context *event_ctx;
|
||||
struct messaging_context *msg_ctx; /* only valid as server */
|
||||
struct socket_address *my_addr, *peer_addr;
|
||||
};
|
||||
|
||||
/* this structure is used by backends to determine the size of some critical types */
|
||||
struct gensec_critical_sizes {
|
||||
int interface_version;
|
||||
int sizeof_gensec_security_ops;
|
||||
int sizeof_gensec_security;
|
||||
};
|
||||
|
||||
#include "auth/gensec/gensec_proto.h"
|
||||
|
||||
#endif /* __GENSEC_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,792 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Kerberos backend for GENSEC
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
Copyright (C) Stefan Metzmacher 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "librpc/gen_ndr/krb5pac.h"
|
||||
#include "auth/auth.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "auth/auth_sam.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "librpc/rpc/dcerpc.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
enum GENSEC_KRB5_STATE {
|
||||
GENSEC_KRB5_SERVER_START,
|
||||
GENSEC_KRB5_CLIENT_START,
|
||||
GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
|
||||
GENSEC_KRB5_DONE
|
||||
};
|
||||
|
||||
struct gensec_krb5_state {
|
||||
DATA_BLOB session_key;
|
||||
DATA_BLOB pac;
|
||||
enum GENSEC_KRB5_STATE state_position;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_auth_context auth_context;
|
||||
krb5_data enc_ticket;
|
||||
krb5_keyblock *keyblock;
|
||||
krb5_ticket *ticket;
|
||||
BOOL gssapi;
|
||||
};
|
||||
|
||||
static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
|
||||
{
|
||||
if (!gensec_krb5_state->smb_krb5_context) {
|
||||
/* We can't clean anything else up unless we started up this far */
|
||||
return 0;
|
||||
}
|
||||
if (gensec_krb5_state->enc_ticket.length) {
|
||||
kerberos_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
&gensec_krb5_state->enc_ticket);
|
||||
}
|
||||
|
||||
if (gensec_krb5_state->ticket) {
|
||||
krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->ticket);
|
||||
}
|
||||
|
||||
/* ccache freed in a child destructor */
|
||||
|
||||
krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->keyblock);
|
||||
|
||||
if (gensec_krb5_state->auth_context) {
|
||||
krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->auth_context);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct gensec_krb5_state *gensec_krb5_state;
|
||||
struct cli_credentials *creds;
|
||||
const struct socket_address *my_addr, *peer_addr;
|
||||
krb5_address my_krb5_addr, peer_krb5_addr;
|
||||
|
||||
creds = gensec_get_credentials(gensec_security);
|
||||
if (!creds) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state);
|
||||
if (!gensec_krb5_state) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
gensec_security->private_data = gensec_krb5_state;
|
||||
gensec_krb5_state->smb_krb5_context = NULL;
|
||||
gensec_krb5_state->auth_context = NULL;
|
||||
gensec_krb5_state->ticket = NULL;
|
||||
ZERO_STRUCT(gensec_krb5_state->enc_ticket);
|
||||
gensec_krb5_state->keyblock = NULL;
|
||||
gensec_krb5_state->session_key = data_blob(NULL, 0);
|
||||
gensec_krb5_state->pac = data_blob(NULL, 0);
|
||||
gensec_krb5_state->gssapi = False;
|
||||
|
||||
talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy);
|
||||
|
||||
if (cli_credentials_get_krb5_context(creds, &gensec_krb5_state->smb_krb5_context)) {
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, gensec_krb5_state)));
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->auth_context,
|
||||
KRB5_AUTH_CONTEXT_DO_SEQUENCE);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, gensec_krb5_state)));
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
my_addr = gensec_get_my_addr(gensec_security);
|
||||
if (my_addr && my_addr->sockaddr) {
|
||||
ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
my_addr->sockaddr, &my_krb5_addr);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, gensec_krb5_state)));
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
peer_addr = gensec_get_peer_addr(gensec_security);
|
||||
if (peer_addr && peer_addr->sockaddr) {
|
||||
ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
peer_addr->sockaddr, &peer_krb5_addr);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, gensec_krb5_state)));
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->auth_context,
|
||||
my_addr ? &my_krb5_addr : NULL,
|
||||
peer_addr ? &peer_krb5_addr : NULL);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, gensec_krb5_state)));
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct gensec_krb5_state *gensec_krb5_state;
|
||||
|
||||
nt_status = gensec_krb5_start(gensec_security);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
gensec_krb5_state = gensec_security->private_data;
|
||||
gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS nt_status = gensec_krb5_server_start(gensec_security);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
struct gensec_krb5_state *gensec_krb5_state;
|
||||
gensec_krb5_state = gensec_security->private_data;
|
||||
gensec_krb5_state->gssapi = True;
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state;
|
||||
krb5_error_code ret;
|
||||
NTSTATUS nt_status;
|
||||
struct ccache_container *ccache_container;
|
||||
const char *hostname;
|
||||
krb5_flags ap_req_options = AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED;
|
||||
|
||||
const char *principal;
|
||||
krb5_data in_data;
|
||||
|
||||
hostname = gensec_get_target_hostname(gensec_security);
|
||||
if (!hostname) {
|
||||
DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (is_ipaddress(hostname)) {
|
||||
DEBUG(2, ("Cannot do krb5 to an IP address"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (strcmp(hostname, "localhost") == 0) {
|
||||
DEBUG(2, ("krb5 to 'localhost' does not make sense"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
nt_status = gensec_krb5_start(gensec_security);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
gensec_krb5_state = gensec_security->private_data;
|
||||
gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
|
||||
|
||||
ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), &ccache_container);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: cli_credentials_get_ccache failed: %s\n",
|
||||
error_message(ret)));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
in_data.length = 0;
|
||||
|
||||
principal = gensec_get_target_principal(gensec_security);
|
||||
if (principal && lp_client_use_spnego_principal()) {
|
||||
krb5_principal target_principal;
|
||||
ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
|
||||
&target_principal);
|
||||
if (ret == 0) {
|
||||
ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
&gensec_krb5_state->auth_context,
|
||||
ap_req_options,
|
||||
target_principal,
|
||||
&in_data, ccache_container->ccache,
|
||||
&gensec_krb5_state->enc_ticket);
|
||||
krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
target_principal);
|
||||
}
|
||||
} else {
|
||||
ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
&gensec_krb5_state->auth_context,
|
||||
ap_req_options,
|
||||
gensec_get_target_service(gensec_security),
|
||||
hostname,
|
||||
&in_data, ccache_container->ccache,
|
||||
&gensec_krb5_state->enc_ticket);
|
||||
}
|
||||
switch (ret) {
|
||||
case 0:
|
||||
return NT_STATUS_OK;
|
||||
case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
|
||||
DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n",
|
||||
hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
|
||||
return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
|
||||
case KRB5_KDC_UNREACH:
|
||||
DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
|
||||
hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
|
||||
return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
|
||||
case KRB5KDC_ERR_PREAUTH_FAILED:
|
||||
case KRB5KRB_AP_ERR_TKT_EXPIRED:
|
||||
case KRB5_CC_END:
|
||||
/* Too much clock skew - we will need to kinit to re-skew the clock */
|
||||
case KRB5KRB_AP_ERR_SKEW:
|
||||
case KRB5_KDCREP_SKEW:
|
||||
{
|
||||
DEBUG(3, ("kerberos (mk_req) failed: %s\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
|
||||
/*fall through*/
|
||||
}
|
||||
|
||||
/* just don't print a message for these really ordinary messages */
|
||||
case KRB5_FCC_NOFILE:
|
||||
case KRB5_CC_NOTFOUND:
|
||||
case ENOENT:
|
||||
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG(0, ("kerberos: %s\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS nt_status = gensec_krb5_client_start(gensec_security);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
struct gensec_krb5_state *gensec_krb5_state;
|
||||
gensec_krb5_state = gensec_security->private_data;
|
||||
gensec_krb5_state->gssapi = True;
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the packet is one for this mechansim
|
||||
*
|
||||
* @param gensec_security GENSEC state
|
||||
* @param in The request, as a DATA_BLOB
|
||||
* @return Error, INVALID_PARAMETER if it's not a packet for us
|
||||
* or NT_STATUS_OK if the packet is ok.
|
||||
*/
|
||||
|
||||
static NTSTATUS gensec_fake_gssapi_krb5_magic(struct gensec_security *gensec_security,
|
||||
const DATA_BLOB *in)
|
||||
{
|
||||
if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Next state function for the Krb5 GENSEC mechanism
|
||||
*
|
||||
* @param gensec_krb5_state KRB5 State
|
||||
* @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
|
||||
* @param in The request, as a DATA_BLOB
|
||||
* @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
|
||||
* @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
|
||||
* or NT_STATUS_OK if the user is authenticated.
|
||||
*/
|
||||
|
||||
static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
krb5_error_code ret = 0;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
switch (gensec_krb5_state->state_position) {
|
||||
case GENSEC_KRB5_CLIENT_START:
|
||||
{
|
||||
DATA_BLOB unwrapped_out;
|
||||
|
||||
if (gensec_krb5_state->gssapi) {
|
||||
unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
|
||||
|
||||
/* wrap that up in a nice GSS-API wrapping */
|
||||
*out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
|
||||
} else {
|
||||
*out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
|
||||
}
|
||||
gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
|
||||
nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
|
||||
{
|
||||
DATA_BLOB unwrapped_in;
|
||||
krb5_data inbuf;
|
||||
krb5_ap_rep_enc_part *repl = NULL;
|
||||
uint8_t tok_id[2];
|
||||
|
||||
if (gensec_krb5_state->gssapi) {
|
||||
if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
|
||||
DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
|
||||
dump_data_pw("Mutual authentication message:\n", in.data, in.length);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
} else {
|
||||
unwrapped_in = in;
|
||||
}
|
||||
/* TODO: check the tok_id */
|
||||
|
||||
inbuf.data = unwrapped_in.data;
|
||||
inbuf.length = unwrapped_in.length;
|
||||
ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->auth_context,
|
||||
&inbuf, &repl);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
|
||||
dump_data_pw("Mutual authentication message:\n", inbuf.data, inbuf.length);
|
||||
nt_status = NT_STATUS_ACCESS_DENIED;
|
||||
} else {
|
||||
*out = data_blob(NULL, 0);
|
||||
nt_status = NT_STATUS_OK;
|
||||
gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
|
||||
}
|
||||
if (repl) {
|
||||
krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
case GENSEC_KRB5_SERVER_START:
|
||||
{
|
||||
DATA_BLOB unwrapped_in;
|
||||
DATA_BLOB unwrapped_out = data_blob(NULL, 0);
|
||||
krb5_data inbuf, outbuf;
|
||||
uint8_t tok_id[2];
|
||||
struct keytab_container *keytab;
|
||||
krb5_principal server_in_keytab;
|
||||
|
||||
if (!in.data) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Grab the keytab, however generated */
|
||||
ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security), &keytab);
|
||||
if (ret) {
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
/* This ensures we lookup the correct entry in that keytab */
|
||||
ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security),
|
||||
gensec_krb5_state->smb_krb5_context,
|
||||
&server_in_keytab);
|
||||
|
||||
if (ret) {
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
/* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
|
||||
if (gensec_krb5_state->gssapi
|
||||
&& gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
|
||||
inbuf.data = unwrapped_in.data;
|
||||
inbuf.length = unwrapped_in.length;
|
||||
} else {
|
||||
inbuf.data = in.data;
|
||||
inbuf.length = in.length;
|
||||
}
|
||||
|
||||
ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
&gensec_krb5_state->auth_context,
|
||||
&inbuf, keytab->keytab, server_in_keytab,
|
||||
&outbuf,
|
||||
&gensec_krb5_state->ticket,
|
||||
&gensec_krb5_state->keyblock);
|
||||
|
||||
if (ret) {
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
unwrapped_out.data = outbuf.data;
|
||||
unwrapped_out.length = outbuf.length;
|
||||
gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
|
||||
/* wrap that up in a nice GSS-API wrapping */
|
||||
if (gensec_krb5_state->gssapi) {
|
||||
*out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
|
||||
} else {
|
||||
*out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
|
||||
}
|
||||
krb5_data_free(&outbuf);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
case GENSEC_KRB5_DONE:
|
||||
default:
|
||||
/* Asking too many times... */
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security,
|
||||
DATA_BLOB *session_key)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
|
||||
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
|
||||
krb5_keyblock *skey;
|
||||
krb5_error_code err = -1;
|
||||
|
||||
if (gensec_krb5_state->session_key.data) {
|
||||
*session_key = gensec_krb5_state->session_key;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
switch (gensec_security->gensec_role) {
|
||||
case GENSEC_CLIENT:
|
||||
err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
|
||||
break;
|
||||
case GENSEC_SERVER:
|
||||
err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
|
||||
break;
|
||||
}
|
||||
if (err == 0 && skey != NULL) {
|
||||
DEBUG(10, ("Got KRB5 session key of length %d\n",
|
||||
(int)KRB5_KEY_LENGTH(skey)));
|
||||
gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state,
|
||||
KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
|
||||
*session_key = gensec_krb5_state->session_key;
|
||||
dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
|
||||
|
||||
krb5_free_keyblock(context, skey);
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(10, ("KRB5 error getting session key %d\n", err));
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
|
||||
struct auth_session_info **_session_info)
|
||||
{
|
||||
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
|
||||
struct auth_serversupplied_info *server_info = NULL;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
struct PAC_LOGON_INFO *logon_info;
|
||||
|
||||
krb5_principal client_principal;
|
||||
char *principal_string;
|
||||
|
||||
DATA_BLOB pac;
|
||||
krb5_data pac_data;
|
||||
|
||||
krb5_error_code ret;
|
||||
|
||||
TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
|
||||
if (ret) {
|
||||
DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n",
|
||||
smb_get_krb5_error_message(context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
client_principal, &principal_string);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Unable to parse client principal: %s\n",
|
||||
smb_get_krb5_error_message(context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket,
|
||||
KRB5_AUTHDATA_WIN2K_PAC,
|
||||
&pac_data);
|
||||
|
||||
if (ret && lp_parm_bool(-1, "gensec", "require_pac", False)) {
|
||||
DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access: %s \n",
|
||||
principal_string,
|
||||
smb_get_krb5_error_message(context,
|
||||
ret, mem_ctx)));
|
||||
krb5_free_principal(context, client_principal);
|
||||
free(principal_string);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
} else if (ret) {
|
||||
/* NO pac */
|
||||
DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n",
|
||||
smb_get_krb5_error_message(context,
|
||||
ret, mem_ctx)));
|
||||
nt_status = sam_get_server_info_principal(mem_ctx, principal_string,
|
||||
&server_info);
|
||||
krb5_free_principal(context, client_principal);
|
||||
free(principal_string);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
} else {
|
||||
/* Found pac */
|
||||
union netr_Validation validation;
|
||||
free(principal_string);
|
||||
|
||||
pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length);
|
||||
if (!pac.data) {
|
||||
krb5_free_principal(context, client_principal);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* decode and verify the pac */
|
||||
nt_status = kerberos_pac_logon_info(gensec_krb5_state, &logon_info, pac,
|
||||
gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
NULL, gensec_krb5_state->keyblock,
|
||||
client_principal,
|
||||
gensec_krb5_state->ticket->ticket.authtime, NULL);
|
||||
krb5_free_principal(context, client_principal);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
validation.sam3 = &logon_info->info3;
|
||||
nt_status = make_server_info_netlogon_validation(mem_ctx,
|
||||
NULL,
|
||||
3, &validation,
|
||||
&server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
/* references the server_info into the session_info */
|
||||
nt_status = auth_generate_session_info(mem_ctx, server_info, &session_info);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = gensec_krb5_session_key(gensec_security, &session_info->session_key);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
*_session_info = session_info;
|
||||
|
||||
talloc_steal(gensec_krb5_state, session_info);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
|
||||
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
|
||||
krb5_error_code ret;
|
||||
krb5_data input, output;
|
||||
input.length = in->length;
|
||||
input.data = in->data;
|
||||
|
||||
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
|
||||
ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
|
||||
if (ret) {
|
||||
DEBUG(1, ("krb5_mk_priv failed: %s\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
*out = data_blob_talloc(mem_ctx, output.data, output.length);
|
||||
|
||||
krb5_data_free(&output);
|
||||
} else {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
|
||||
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
|
||||
krb5_error_code ret;
|
||||
krb5_data input, output;
|
||||
krb5_replay_data replay;
|
||||
input.length = in->length;
|
||||
input.data = in->data;
|
||||
|
||||
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
|
||||
ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
|
||||
if (ret) {
|
||||
DEBUG(1, ("krb5_rd_priv failed: %s\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
*out = data_blob_talloc(mem_ctx, output.data, output.length);
|
||||
|
||||
krb5_data_free(&output);
|
||||
} else {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static BOOL gensec_krb5_have_feature(struct gensec_security *gensec_security,
|
||||
uint32_t feature)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
if (feature & GENSEC_FEATURE_SESSION_KEY) {
|
||||
return True;
|
||||
}
|
||||
if (!gensec_krb5_state->gssapi &&
|
||||
(feature & GENSEC_FEATURE_SEAL)) {
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
static const char *gensec_krb5_oids[] = {
|
||||
GENSEC_OID_KERBEROS5,
|
||||
GENSEC_OID_KERBEROS5_OLD,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
|
||||
.name = "fake_gssapi_krb5",
|
||||
.auth_type = DCERPC_AUTH_TYPE_KRB5,
|
||||
.oid = gensec_krb5_oids,
|
||||
.client_start = gensec_fake_gssapi_krb5_client_start,
|
||||
.server_start = gensec_fake_gssapi_krb5_server_start,
|
||||
.update = gensec_krb5_update,
|
||||
.magic = gensec_fake_gssapi_krb5_magic,
|
||||
.session_key = gensec_krb5_session_key,
|
||||
.session_info = gensec_krb5_session_info,
|
||||
.have_feature = gensec_krb5_have_feature,
|
||||
.enabled = False,
|
||||
.kerberos = True,
|
||||
.priority = GENSEC_KRB5
|
||||
};
|
||||
|
||||
static const struct gensec_security_ops gensec_krb5_security_ops = {
|
||||
.name = "krb5",
|
||||
.client_start = gensec_krb5_client_start,
|
||||
.server_start = gensec_krb5_server_start,
|
||||
.update = gensec_krb5_update,
|
||||
.session_key = gensec_krb5_session_key,
|
||||
.session_info = gensec_krb5_session_info,
|
||||
.have_feature = gensec_krb5_have_feature,
|
||||
.wrap = gensec_krb5_wrap,
|
||||
.unwrap = gensec_krb5_unwrap,
|
||||
.enabled = True,
|
||||
.kerberos = True,
|
||||
.priority = GENSEC_KRB5
|
||||
};
|
||||
|
||||
NTSTATUS gensec_krb5_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
auth_init();
|
||||
|
||||
ret = gensec_register(&gensec_krb5_security_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_krb5_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gensec_register(&gensec_fake_gssapi_krb5_security_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_krb5_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
dcerpc schannel operations
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/ndr_schannel.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/gensec/schannel.h"
|
||||
#include "auth/gensec/schannel_state.h"
|
||||
#include "auth/gensec/schannel_proto.h"
|
||||
#include "librpc/rpc/dcerpc.h"
|
||||
|
||||
static size_t schannel_sig_size(struct gensec_security *gensec_security, size_t data_size)
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
static NTSTATUS schannel_session_key(struct gensec_security *gensec_security,
|
||||
DATA_BLOB *session_key)
|
||||
{
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct schannel_state *state = gensec_security->private_data;
|
||||
NTSTATUS status;
|
||||
struct schannel_bind bind_schannel;
|
||||
struct schannel_bind_ack bind_schannel_ack;
|
||||
struct creds_CredentialState *creds;
|
||||
|
||||
const char *workstation;
|
||||
const char *domain;
|
||||
*out = data_blob(NULL, 0);
|
||||
|
||||
switch (gensec_security->gensec_role) {
|
||||
case GENSEC_CLIENT:
|
||||
if (state->state != SCHANNEL_STATE_START) {
|
||||
/* we could parse the bind ack, but we don't know what it is yet */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
state->creds = talloc_reference(state, cli_credentials_get_netlogon_creds(gensec_security->credentials));
|
||||
|
||||
bind_schannel.unknown1 = 0;
|
||||
#if 0
|
||||
/* to support this we'd need to have access to the full domain name */
|
||||
bind_schannel.bind_type = 23;
|
||||
bind_schannel.u.info23.domain = cli_credentials_get_domain(gensec_security->credentials);
|
||||
bind_schannel.u.info23.workstation = cli_credentials_get_workstation(gensec_security->credentials);
|
||||
bind_schannel.u.info23.dnsdomain = cli_credentials_get_realm(gensec_security->credentials);
|
||||
/* w2k3 refuses us if we use the full DNS workstation?
|
||||
why? perhaps because we don't fill in the dNSHostName
|
||||
attribute in the machine account? */
|
||||
bind_schannel.u.info23.dnsworkstation = cli_credentials_get_workstation(gensec_security->credentials);
|
||||
#else
|
||||
bind_schannel.bind_type = 3;
|
||||
bind_schannel.u.info3.domain = cli_credentials_get_domain(gensec_security->credentials);
|
||||
bind_schannel.u.info3.workstation = cli_credentials_get_workstation(gensec_security->credentials);
|
||||
#endif
|
||||
|
||||
status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
|
||||
(ndr_push_flags_fn_t)ndr_push_schannel_bind);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(3, ("Could not create schannel bind: %s\n",
|
||||
nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
|
||||
state->state = SCHANNEL_STATE_UPDATE_1;
|
||||
|
||||
return NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
case GENSEC_SERVER:
|
||||
|
||||
if (state->state != SCHANNEL_STATE_START) {
|
||||
/* no third leg on this protocol */
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* parse the schannel startup blob */
|
||||
status = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_schannel_bind);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (bind_schannel.bind_type == 23) {
|
||||
workstation = bind_schannel.u.info23.workstation;
|
||||
domain = bind_schannel.u.info23.domain;
|
||||
} else {
|
||||
workstation = bind_schannel.u.info3.workstation;
|
||||
domain = bind_schannel.u.info3.domain;
|
||||
}
|
||||
|
||||
/* pull the session key for this client */
|
||||
status = schannel_fetch_session_key(out_mem_ctx, workstation,
|
||||
domain, &creds);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n",
|
||||
workstation, nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
|
||||
state->creds = talloc_reference(state, creds);
|
||||
|
||||
bind_schannel_ack.unknown1 = 1;
|
||||
bind_schannel_ack.unknown2 = 0;
|
||||
bind_schannel_ack.unknown3 = 0x6c0000;
|
||||
|
||||
status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack,
|
||||
(ndr_push_flags_fn_t)ndr_push_schannel_bind_ack);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n",
|
||||
workstation, nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
|
||||
state->state = SCHANNEL_STATE_UPDATE_1;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the struct creds_CredentialState.
|
||||
*
|
||||
* Make sure not to call this unless gensec is using schannel...
|
||||
*/
|
||||
|
||||
/* TODO: make this non-public */
|
||||
_PUBLIC_ NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct creds_CredentialState **creds)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
|
||||
*creds = talloc_reference(mem_ctx, state->creds);
|
||||
if (!*creds) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns anonymous credentials for schannel, matching Win2k3.
|
||||
*
|
||||
*/
|
||||
|
||||
static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
|
||||
struct auth_session_info **_session_info)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
return auth_anonymous_session_info(state, _session_info);
|
||||
}
|
||||
|
||||
static NTSTATUS schannel_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct schannel_state *state;
|
||||
|
||||
state = talloc(gensec_security, struct schannel_state);
|
||||
if (!state) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
state->state = SCHANNEL_STATE_START;
|
||||
state->seq_num = 0;
|
||||
gensec_security->private_data = state;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS schannel_server_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct schannel_state *state;
|
||||
|
||||
status = schannel_start(gensec_security);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
state = gensec_security->private_data;
|
||||
state->initiator = False;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS schannel_client_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct schannel_state *state;
|
||||
|
||||
status = schannel_start(gensec_security);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
state = gensec_security->private_data;
|
||||
state->initiator = True;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static BOOL schannel_have_feature(struct gensec_security *gensec_security,
|
||||
uint32_t feature)
|
||||
{
|
||||
if (feature & (GENSEC_FEATURE_SIGN |
|
||||
GENSEC_FEATURE_SEAL)) {
|
||||
return True;
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_DCE_STYLE) {
|
||||
return True;
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
static const struct gensec_security_ops gensec_schannel_security_ops = {
|
||||
.name = "schannel",
|
||||
.auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
|
||||
.client_start = schannel_client_start,
|
||||
.server_start = schannel_server_start,
|
||||
.update = schannel_update,
|
||||
.seal_packet = schannel_seal_packet,
|
||||
.sign_packet = schannel_sign_packet,
|
||||
.check_packet = schannel_check_packet,
|
||||
.unseal_packet = schannel_unseal_packet,
|
||||
.session_key = schannel_session_key,
|
||||
.session_info = schannel_session_info,
|
||||
.sig_size = schannel_sig_size,
|
||||
.have_feature = schannel_have_feature,
|
||||
.enabled = True,
|
||||
.priority = GENSEC_SCHANNEL
|
||||
};
|
||||
|
||||
NTSTATUS gensec_schannel_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
ret = gensec_register(&gensec_schannel_security_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_schannel_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
dcerpc schannel operations
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "libcli/auth/credentials.h"
|
||||
|
||||
enum schannel_position {
|
||||
SCHANNEL_STATE_START = 0,
|
||||
SCHANNEL_STATE_UPDATE_1
|
||||
};
|
||||
|
||||
struct schannel_state {
|
||||
enum schannel_position state;
|
||||
uint32_t seq_num;
|
||||
BOOL initiator;
|
||||
struct creds_CredentialState *creds;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
schannel library code
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/gensec/schannel.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 }
|
||||
#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 }
|
||||
|
||||
/*******************************************************************
|
||||
Encode or Decode the sequence number (which is symmetric)
|
||||
********************************************************************/
|
||||
static void netsec_deal_with_seq_num(struct schannel_state *state,
|
||||
const uint8_t packet_digest[8],
|
||||
uint8_t seq_num[8])
|
||||
{
|
||||
static const uint8_t zeros[4];
|
||||
uint8_t sequence_key[16];
|
||||
uint8_t digest1[16];
|
||||
|
||||
hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1);
|
||||
hmac_md5(digest1, packet_digest, 8, sequence_key);
|
||||
arcfour_crypt(seq_num, sequence_key, 8);
|
||||
|
||||
state->seq_num++;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
Calculate the key with which to encode the data payload
|
||||
********************************************************************/
|
||||
static void netsec_get_sealing_key(const uint8_t session_key[16],
|
||||
const uint8_t seq_num[8],
|
||||
uint8_t sealing_key[16])
|
||||
{
|
||||
static const uint8_t zeros[4];
|
||||
uint8_t digest2[16];
|
||||
uint8_t sess_kf0[16];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
sess_kf0[i] = session_key[i] ^ 0xf0;
|
||||
}
|
||||
|
||||
hmac_md5(sess_kf0, zeros, 4, digest2);
|
||||
hmac_md5(digest2, seq_num, 8, sealing_key);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
Create a digest over the entire packet (including the data), and
|
||||
MD5 it with the session key.
|
||||
********************************************************************/
|
||||
static void schannel_digest(const uint8_t sess_key[16],
|
||||
const uint8_t netsec_sig[8],
|
||||
const uint8_t *confounder,
|
||||
const uint8_t *data, size_t data_len,
|
||||
uint8_t digest_final[16])
|
||||
{
|
||||
uint8_t packet_digest[16];
|
||||
static const uint8_t zeros[4];
|
||||
struct MD5Context ctx;
|
||||
|
||||
MD5Init(&ctx);
|
||||
MD5Update(&ctx, zeros, 4);
|
||||
MD5Update(&ctx, netsec_sig, 8);
|
||||
if (confounder) {
|
||||
MD5Update(&ctx, confounder, 8);
|
||||
}
|
||||
MD5Update(&ctx, data, data_len);
|
||||
MD5Final(packet_digest, &ctx);
|
||||
|
||||
hmac_md5(sess_key, packet_digest, sizeof(packet_digest), digest_final);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
unseal a packet
|
||||
*/
|
||||
NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
|
||||
uint8_t digest_final[16];
|
||||
uint8_t confounder[8];
|
||||
uint8_t seq_num[8];
|
||||
uint8_t sealing_key[16];
|
||||
static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
|
||||
|
||||
if (sig->length != 32) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
memcpy(confounder, sig->data+24, 8);
|
||||
|
||||
RSIVAL(seq_num, 0, state->seq_num);
|
||||
SIVAL(seq_num, 4, state->initiator?0:0x80);
|
||||
|
||||
netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
|
||||
arcfour_crypt(confounder, sealing_key, 8);
|
||||
arcfour_crypt(data, sealing_key, length);
|
||||
|
||||
schannel_digest(state->creds->session_key,
|
||||
netsec_sig, confounder,
|
||||
data, length, digest_final);
|
||||
|
||||
if (memcmp(digest_final, sig->data+16, 8) != 0) {
|
||||
dump_data_pw("calc digest:", digest_final, 8);
|
||||
dump_data_pw("wire digest:", sig->data+16, 8);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
netsec_deal_with_seq_num(state, digest_final, seq_num);
|
||||
|
||||
if (memcmp(seq_num, sig->data+8, 8) != 0) {
|
||||
dump_data_pw("calc seq num:", seq_num, 8);
|
||||
dump_data_pw("wire seq num:", sig->data+8, 8);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
check the signature on a packet
|
||||
*/
|
||||
NTSTATUS schannel_check_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
|
||||
uint8_t digest_final[16];
|
||||
uint8_t seq_num[8];
|
||||
static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
|
||||
|
||||
/* w2k sends just 24 bytes and skip the confounder */
|
||||
if (sig->length != 32 && sig->length != 24) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
RSIVAL(seq_num, 0, state->seq_num);
|
||||
SIVAL(seq_num, 4, state->initiator?0:0x80);
|
||||
|
||||
dump_data_pw("seq_num:\n", seq_num, 8);
|
||||
dump_data_pw("sess_key:\n", state->creds->session_key, 16);
|
||||
|
||||
schannel_digest(state->creds->session_key,
|
||||
netsec_sig, NULL,
|
||||
data, length, digest_final);
|
||||
|
||||
netsec_deal_with_seq_num(state, digest_final, seq_num);
|
||||
|
||||
if (memcmp(seq_num, sig->data+8, 8) != 0) {
|
||||
dump_data_pw("calc seq num:", seq_num, 8);
|
||||
dump_data_pw("wire seq num:", sig->data+8, 8);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (memcmp(digest_final, sig->data+16, 8) != 0) {
|
||||
dump_data_pw("calc digest:", digest_final, 8);
|
||||
dump_data_pw("wire digest:", sig->data+16, 8);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
seal a packet
|
||||
*/
|
||||
NTSTATUS schannel_seal_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
|
||||
uint8_t digest_final[16];
|
||||
uint8_t confounder[8];
|
||||
uint8_t seq_num[8];
|
||||
uint8_t sealing_key[16];
|
||||
static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
|
||||
|
||||
generate_random_buffer(confounder, 8);
|
||||
|
||||
RSIVAL(seq_num, 0, state->seq_num);
|
||||
SIVAL(seq_num, 4, state->initiator?0x80:0);
|
||||
|
||||
schannel_digest(state->creds->session_key,
|
||||
netsec_sig, confounder,
|
||||
data, length, digest_final);
|
||||
|
||||
netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
|
||||
arcfour_crypt(confounder, sealing_key, 8);
|
||||
arcfour_crypt(data, sealing_key, length);
|
||||
|
||||
netsec_deal_with_seq_num(state, digest_final, seq_num);
|
||||
|
||||
(*sig) = data_blob_talloc(mem_ctx, NULL, 32);
|
||||
|
||||
memcpy(sig->data, netsec_sig, 8);
|
||||
memcpy(sig->data+8, seq_num, 8);
|
||||
memcpy(sig->data+16, digest_final, 8);
|
||||
memcpy(sig->data+24, confounder, 8);
|
||||
|
||||
dump_data_pw("signature:", sig->data+ 0, 8);
|
||||
dump_data_pw("seq_num :", sig->data+ 8, 8);
|
||||
dump_data_pw("digest :", sig->data+16, 8);
|
||||
dump_data_pw("confound :", sig->data+24, 8);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
sign a packet
|
||||
*/
|
||||
NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
|
||||
uint8_t digest_final[16];
|
||||
uint8_t seq_num[8];
|
||||
static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
|
||||
|
||||
RSIVAL(seq_num, 0, state->seq_num);
|
||||
SIVAL(seq_num, 4, state->initiator?0x80:0);
|
||||
|
||||
schannel_digest(state->creds->session_key,
|
||||
netsec_sig, NULL,
|
||||
data, length, digest_final);
|
||||
|
||||
netsec_deal_with_seq_num(state, digest_final, seq_num);
|
||||
|
||||
(*sig) = data_blob_talloc(mem_ctx, NULL, 32);
|
||||
|
||||
memcpy(sig->data, netsec_sig, 8);
|
||||
memcpy(sig->data+8, seq_num, 8);
|
||||
memcpy(sig->data+16, digest_final, 8);
|
||||
memset(sig->data+24, 0, 8);
|
||||
|
||||
dump_data_pw("signature:", sig->data+ 0, 8);
|
||||
dump_data_pw("seq_num :", sig->data+ 8, 8);
|
||||
dump_data_pw("digest :", sig->data+16, 8);
|
||||
dump_data_pw("confound :", sig->data+24, 8);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
module to store/fetch session keys for the schannel server
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "lib/ldb/include/ldb_errors.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "db_wrap.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
/**
|
||||
connect to the schannel ldb
|
||||
*/
|
||||
struct ldb_context *schannel_db_connect(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
char *path;
|
||||
struct ldb_context *ldb;
|
||||
BOOL existed;
|
||||
const char *init_ldif =
|
||||
"dn: @ATTRIBUTES\n" \
|
||||
"computerName: CASE_INSENSITIVE\n" \
|
||||
"flatname: CASE_INSENSITIVE\n";
|
||||
|
||||
path = smbd_tmp_path(mem_ctx, "schannel.ldb");
|
||||
if (!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
existed = file_exist(path);
|
||||
|
||||
ldb = ldb_wrap_connect(mem_ctx, path, system_session(mem_ctx),
|
||||
NULL, LDB_FLG_NOSYNC, NULL);
|
||||
talloc_free(path);
|
||||
if (!ldb) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!existed) {
|
||||
gendb_add_ldif(ldb, init_ldif);
|
||||
}
|
||||
|
||||
return ldb;
|
||||
}
|
||||
|
||||
/*
|
||||
remember an established session key for a netr server authentication
|
||||
use a simple ldb structure
|
||||
*/
|
||||
NTSTATUS schannel_store_session_key_ldb(TALLOC_CTX *mem_ctx,
|
||||
struct ldb_context *ldb,
|
||||
struct creds_CredentialState *creds)
|
||||
{
|
||||
struct ldb_message *msg;
|
||||
struct ldb_val val, seed, client_state, server_state;
|
||||
char *f;
|
||||
char *sct;
|
||||
int ret;
|
||||
|
||||
f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags);
|
||||
|
||||
if (f == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type);
|
||||
|
||||
if (sct == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
msg = ldb_msg_new(ldb);
|
||||
if (msg == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
msg->dn = ldb_dn_new_fmt(msg, ldb, "computerName=%s", creds->computer_name);
|
||||
if ( ! msg->dn) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
val.data = creds->session_key;
|
||||
val.length = sizeof(creds->session_key);
|
||||
|
||||
seed.data = creds->seed.data;
|
||||
seed.length = sizeof(creds->seed.data);
|
||||
|
||||
client_state.data = creds->client.data;
|
||||
client_state.length = sizeof(creds->client.data);
|
||||
server_state.data = creds->server.data;
|
||||
server_state.length = sizeof(creds->server.data);
|
||||
|
||||
ldb_msg_add_string(msg, "objectClass", "schannelState");
|
||||
ldb_msg_add_value(msg, "sessionKey", &val, NULL);
|
||||
ldb_msg_add_value(msg, "seed", &seed, NULL);
|
||||
ldb_msg_add_value(msg, "clientState", &client_state, NULL);
|
||||
ldb_msg_add_value(msg, "serverState", &server_state, NULL);
|
||||
ldb_msg_add_string(msg, "negotiateFlags", f);
|
||||
ldb_msg_add_string(msg, "secureChannelType", sct);
|
||||
ldb_msg_add_string(msg, "accountName", creds->account_name);
|
||||
ldb_msg_add_string(msg, "computerName", creds->computer_name);
|
||||
ldb_msg_add_string(msg, "flatname", creds->domain);
|
||||
samdb_msg_add_dom_sid(ldb, mem_ctx, msg, "objectSid", creds->sid);
|
||||
|
||||
ldb_delete(ldb, msg->dn);
|
||||
|
||||
ret = ldb_add(ldb, msg);
|
||||
|
||||
if (ret != 0) {
|
||||
DEBUG(0,("Unable to add %s to session key db - %s\n",
|
||||
ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb)));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS schannel_store_session_key(TALLOC_CTX *mem_ctx,
|
||||
struct creds_CredentialState *creds)
|
||||
{
|
||||
struct ldb_context *ldb;
|
||||
NTSTATUS nt_status;
|
||||
int ret;
|
||||
|
||||
ldb = schannel_db_connect(mem_ctx);
|
||||
if (!ldb) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
ret = ldb_transaction_start(ldb);
|
||||
if (ret != 0) {
|
||||
talloc_free(ldb);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
nt_status = schannel_store_session_key_ldb(mem_ctx, ldb, creds);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
ret = ldb_transaction_commit(ldb);
|
||||
} else {
|
||||
ret = ldb_transaction_cancel(ldb);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
DEBUG(0,("Unable to commit adding credentials for %s to schannel key db - %s\n",
|
||||
creds->computer_name, ldb_errstring(ldb)));
|
||||
talloc_free(ldb);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
talloc_free(ldb);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/*
|
||||
read back a credentials back for a computer
|
||||
*/
|
||||
NTSTATUS schannel_fetch_session_key_ldb(TALLOC_CTX *mem_ctx,
|
||||
struct ldb_context *ldb,
|
||||
const char *computer_name,
|
||||
const char *domain,
|
||||
struct creds_CredentialState **creds)
|
||||
{
|
||||
struct ldb_result *res;
|
||||
int ret;
|
||||
const struct ldb_val *val;
|
||||
|
||||
*creds = talloc_zero(mem_ctx, struct creds_CredentialState);
|
||||
if (!*creds) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = ldb_search_exp_fmt(ldb, mem_ctx, &res,
|
||||
NULL, LDB_SCOPE_SUBTREE, NULL,
|
||||
"(&(computerName=%s)(flatname=%s))", computer_name, domain);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
DEBUG(3,("schannel: Failed to find a record for client %s: %s\n", computer_name, ldb_errstring(ldb)));
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
if (res->count != 1) {
|
||||
DEBUG(3,("schannel: Failed to find a record for client: %s (found %d records)\n", computer_name, res->count));
|
||||
talloc_free(res);
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
val = ldb_msg_find_ldb_val(res->msgs[0], "sessionKey");
|
||||
if (val == NULL || val->length != 16) {
|
||||
DEBUG(1,("schannel: record in schannel DB must contain a sessionKey of length 16, when searching for client: %s\n", computer_name));
|
||||
talloc_free(res);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
memcpy((*creds)->session_key, val->data, 16);
|
||||
|
||||
val = ldb_msg_find_ldb_val(res->msgs[0], "seed");
|
||||
if (val == NULL || val->length != 8) {
|
||||
DEBUG(1,("schannel: record in schannel DB must contain a vaid seed of length 8, when searching for client: %s\n", computer_name));
|
||||
talloc_free(res);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
memcpy((*creds)->seed.data, val->data, 8);
|
||||
|
||||
val = ldb_msg_find_ldb_val(res->msgs[0], "clientState");
|
||||
if (val == NULL || val->length != 8) {
|
||||
DEBUG(1,("schannel: record in schannel DB must contain a vaid clientState of length 8, when searching for client: %s\n", computer_name));
|
||||
talloc_free(res);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
memcpy((*creds)->client.data, val->data, 8);
|
||||
|
||||
val = ldb_msg_find_ldb_val(res->msgs[0], "serverState");
|
||||
if (val == NULL || val->length != 8) {
|
||||
DEBUG(1,("schannel: record in schannel DB must contain a vaid serverState of length 8, when searching for client: %s\n", computer_name));
|
||||
talloc_free(res);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
memcpy((*creds)->server.data, val->data, 8);
|
||||
|
||||
(*creds)->negotiate_flags = ldb_msg_find_attr_as_int(res->msgs[0], "negotiateFlags", 0);
|
||||
|
||||
(*creds)->secure_channel_type = ldb_msg_find_attr_as_int(res->msgs[0], "secureChannelType", 0);
|
||||
|
||||
(*creds)->account_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "accountName", NULL));
|
||||
if ((*creds)->account_name == NULL) {
|
||||
talloc_free(res);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*creds)->computer_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "computerName", NULL));
|
||||
if ((*creds)->computer_name == NULL) {
|
||||
talloc_free(res);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*creds)->domain = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "flatname", NULL));
|
||||
if ((*creds)->domain == NULL) {
|
||||
talloc_free(res);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*creds)->sid = samdb_result_dom_sid(*creds, res->msgs[0], "objectSid");
|
||||
|
||||
talloc_free(res);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS schannel_fetch_session_key(TALLOC_CTX *mem_ctx,
|
||||
const char *computer_name,
|
||||
const char *domain,
|
||||
struct creds_CredentialState **creds)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct ldb_context *ldb;
|
||||
|
||||
ldb = schannel_db_connect(mem_ctx);
|
||||
if (!ldb) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
nt_status = schannel_fetch_session_key_ldb(mem_ctx, ldb,
|
||||
computer_name, domain,
|
||||
creds);
|
||||
talloc_free(ldb);
|
||||
return nt_status;
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
GENSEC socket interface
|
||||
|
||||
Copyright (C) Andrew Bartlett 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/stream/packet.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
static const struct socket_ops gensec_socket_ops;
|
||||
|
||||
struct gensec_socket {
|
||||
struct gensec_security *gensec_security;
|
||||
struct socket_context *socket;
|
||||
struct event_context *ev;
|
||||
struct packet_context *packet;
|
||||
DATA_BLOB read_buffer; /* SASL packets are turned into liniarlised data here, for reading */
|
||||
size_t orig_send_len;
|
||||
BOOL eof;
|
||||
NTSTATUS error;
|
||||
BOOL interrupted;
|
||||
void (*recv_handler)(void *, uint16_t);
|
||||
void *recv_private;
|
||||
int in_extra_read;
|
||||
BOOL wrap; /* Should we be wrapping on this socket at all? */
|
||||
};
|
||||
|
||||
static NTSTATUS gensec_socket_init_fn(struct socket_context *sock)
|
||||
{
|
||||
switch (sock->type) {
|
||||
case SOCKET_TYPE_STREAM:
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
sock->backend_name = "gensec";
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
_PUBLIC_ NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed)
|
||||
{
|
||||
if (!gensec_security->ops->wrap_packets) {
|
||||
NTSTATUS nt_status;
|
||||
size_t max_input_size;
|
||||
DATA_BLOB unwrapped, wrapped;
|
||||
max_input_size = gensec_max_input_size(gensec_security);
|
||||
unwrapped = data_blob_const(in->data, MIN(max_input_size, (size_t)in->length));
|
||||
|
||||
nt_status = gensec_wrap(gensec_security,
|
||||
mem_ctx,
|
||||
&unwrapped, &wrapped);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
*out = data_blob_talloc(mem_ctx, NULL, 4);
|
||||
if (!out->data) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
RSIVAL(out->data, 0, wrapped.length);
|
||||
|
||||
nt_status = data_blob_append(mem_ctx, out, wrapped.data, wrapped.length);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
*len_processed = unwrapped.length;
|
||||
return nt_status;
|
||||
}
|
||||
return gensec_security->ops->wrap_packets(gensec_security, mem_ctx, in, out,
|
||||
len_processed);
|
||||
}
|
||||
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed)
|
||||
{
|
||||
if (!gensec_security->ops->unwrap_packets) {
|
||||
DATA_BLOB wrapped;
|
||||
NTSTATUS nt_status;
|
||||
size_t packet_size;
|
||||
if (in->length < 4) {
|
||||
/* Missing the header we already had! */
|
||||
DEBUG(0, ("Asked to unwrap packet of bogus length! How did we get the short packet?!\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
packet_size = RIVAL(in->data, 0);
|
||||
|
||||
wrapped = data_blob_const(in->data + 4, packet_size);
|
||||
|
||||
if (wrapped.length > (in->length - 4)) {
|
||||
DEBUG(0, ("Asked to unwrap packed of bogus length %d > %d! How did we get this?!\n",
|
||||
(int)wrapped.length, (int)(in->length - 4)));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
nt_status = gensec_unwrap(gensec_security,
|
||||
mem_ctx,
|
||||
&wrapped, out);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
*len_processed = packet_size + 4;
|
||||
return nt_status;
|
||||
}
|
||||
return gensec_security->ops->unwrap_packets(gensec_security, mem_ctx, in, out,
|
||||
len_processed);
|
||||
}
|
||||
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
|
||||
DATA_BLOB blob, size_t *size)
|
||||
{
|
||||
if (gensec_security->ops->packet_full_request) {
|
||||
return gensec_security->ops->packet_full_request(gensec_security,
|
||||
blob, size);
|
||||
}
|
||||
if (gensec_security->ops->unwrap_packets) {
|
||||
if (blob.length) {
|
||||
*size = blob.length;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return STATUS_MORE_ENTRIES;
|
||||
}
|
||||
return packet_full_request_u32(NULL, blob, size);
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_socket_full_request(void *private, DATA_BLOB blob, size_t *size)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
struct gensec_security *gensec_security = gensec_socket->gensec_security;
|
||||
return gensec_packet_full_request(gensec_security, blob, size);
|
||||
}
|
||||
|
||||
/* Try to figure out how much data is waiting to be read */
|
||||
static NTSTATUS gensec_socket_pending(struct socket_context *sock, size_t *npending)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
if (!gensec_socket->wrap) {
|
||||
return socket_pending(gensec_socket->socket, npending);
|
||||
}
|
||||
|
||||
if (gensec_socket->read_buffer.length > 0) {
|
||||
*npending = gensec_socket->read_buffer.length;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* This is a lie. We hope the decrypted data will always be
|
||||
* less than this value, so the application just gets a short
|
||||
* read. Without reading and decrypting it, we can't tell.
|
||||
* If the SASL mech does compression, then we just need to
|
||||
* manually trigger read events */
|
||||
return socket_pending(gensec_socket->socket, npending);
|
||||
}
|
||||
|
||||
/* Note if an error occours, so we can return it up the stack */
|
||||
static void gensec_socket_error_handler(void *private, NTSTATUS status)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
|
||||
gensec_socket->eof = True;
|
||||
} else {
|
||||
gensec_socket->error = status;
|
||||
}
|
||||
}
|
||||
|
||||
static void gensec_socket_trigger_read(struct event_context *ev,
|
||||
struct timed_event *te,
|
||||
struct timeval t, void *private)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
|
||||
gensec_socket->in_extra_read++;
|
||||
gensec_socket->recv_handler(gensec_socket->recv_private, EVENT_FD_READ);
|
||||
gensec_socket->in_extra_read--;
|
||||
|
||||
/* It may well be that, having run the recv handler, we still
|
||||
* have even more data waiting for us!
|
||||
*/
|
||||
if (gensec_socket->read_buffer.length && gensec_socket->recv_handler) {
|
||||
/* Schedule this funcion to run again */
|
||||
event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
|
||||
gensec_socket_trigger_read, gensec_socket);
|
||||
}
|
||||
}
|
||||
|
||||
/* These two routines could be changed to use a circular buffer of
|
||||
* some kind, or linked lists, or ... */
|
||||
static NTSTATUS gensec_socket_recv(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
|
||||
if (!gensec_socket->wrap) {
|
||||
return socket_recv(gensec_socket->socket, buf, wantlen, nread);
|
||||
}
|
||||
|
||||
gensec_socket->error = NT_STATUS_OK;
|
||||
|
||||
if (gensec_socket->read_buffer.length == 0) {
|
||||
/* Process any data on the socket, into the read buffer. At
|
||||
* this point, the socket is not available for read any
|
||||
* longer */
|
||||
packet_recv(gensec_socket->packet);
|
||||
|
||||
if (gensec_socket->eof) {
|
||||
*nread = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
|
||||
return gensec_socket->error;
|
||||
}
|
||||
|
||||
if (gensec_socket->read_buffer.length == 0) {
|
||||
/* Clearly we don't have the entire SASL packet yet,
|
||||
* so it has not been written into the buffer */
|
||||
*nread = 0;
|
||||
return STATUS_MORE_ENTRIES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*nread = MIN(wantlen, gensec_socket->read_buffer.length);
|
||||
memcpy(buf, gensec_socket->read_buffer.data, *nread);
|
||||
|
||||
if (gensec_socket->read_buffer.length > *nread) {
|
||||
memmove(gensec_socket->read_buffer.data,
|
||||
gensec_socket->read_buffer.data + *nread,
|
||||
gensec_socket->read_buffer.length - *nread);
|
||||
}
|
||||
|
||||
gensec_socket->read_buffer.length -= *nread;
|
||||
gensec_socket->read_buffer.data = talloc_realloc(gensec_socket,
|
||||
gensec_socket->read_buffer.data,
|
||||
uint8_t,
|
||||
gensec_socket->read_buffer.length);
|
||||
|
||||
if (gensec_socket->read_buffer.length &&
|
||||
gensec_socket->in_extra_read == 0 &&
|
||||
gensec_socket->recv_handler) {
|
||||
/* Manually call a read event, to get this moving
|
||||
* again (as the socket should be dry, so the normal
|
||||
* event handler won't trigger) */
|
||||
event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
|
||||
gensec_socket_trigger_read, gensec_socket);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Completed SASL packet callback. When we have a 'whole' SASL
|
||||
* packet, decrypt it, and add it to the read buffer
|
||||
*
|
||||
* This function (and anything under it) MUST NOT call the event system
|
||||
*/
|
||||
static NTSTATUS gensec_socket_unwrap(void *private, DATA_BLOB blob)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
DATA_BLOB unwrapped;
|
||||
NTSTATUS nt_status;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
size_t packet_size;
|
||||
|
||||
mem_ctx = talloc_new(gensec_socket);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
nt_status = gensec_unwrap_packets(gensec_socket->gensec_security,
|
||||
mem_ctx,
|
||||
&blob, &unwrapped,
|
||||
&packet_size);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (packet_size != blob.length) {
|
||||
DEBUG(0, ("gensec_socket_unwrap: Did not consume entire packet!\n"));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/* We could change this into a linked list, and have
|
||||
* gensec_socket_recv() and gensec_socket_pending() walk the
|
||||
* linked list */
|
||||
|
||||
nt_status = data_blob_append(gensec_socket, &gensec_socket->read_buffer,
|
||||
unwrapped.data, unwrapped.length);
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* when the data is sent, we know we have not been interrupted */
|
||||
static void send_callback(void *private)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
gensec_socket->interrupted = False;
|
||||
}
|
||||
|
||||
/*
|
||||
send data, but only as much as we allow in one packet.
|
||||
|
||||
If this returns STATUS_MORE_ENTRIES, the caller must retry with
|
||||
exactly the same data, or a NULL blob.
|
||||
*/
|
||||
static NTSTATUS gensec_socket_send(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
DATA_BLOB wrapped;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if (!gensec_socket->wrap) {
|
||||
return socket_send(gensec_socket->socket, blob, sendlen);
|
||||
}
|
||||
|
||||
*sendlen = 0;
|
||||
|
||||
/* We have have been interupted, so the caller should be
|
||||
* giving us the same data again. */
|
||||
if (gensec_socket->interrupted) {
|
||||
packet_queue_run(gensec_socket->packet);
|
||||
|
||||
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
|
||||
return gensec_socket->error;
|
||||
} else if (gensec_socket->interrupted) {
|
||||
return STATUS_MORE_ENTRIES;
|
||||
} else {
|
||||
*sendlen = gensec_socket->orig_send_len;
|
||||
gensec_socket->orig_send_len = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(gensec_socket);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
nt_status = gensec_wrap_packets(gensec_socket->gensec_security,
|
||||
mem_ctx,
|
||||
blob, &wrapped,
|
||||
&gensec_socket->orig_send_len);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
gensec_socket->interrupted = True;
|
||||
gensec_socket->error = NT_STATUS_OK;
|
||||
|
||||
nt_status = packet_send_callback(gensec_socket->packet,
|
||||
wrapped,
|
||||
send_callback, gensec_socket);
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
packet_queue_run(gensec_socket->packet);
|
||||
|
||||
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
|
||||
return gensec_socket->error;
|
||||
} else if (gensec_socket->interrupted) {
|
||||
return STATUS_MORE_ENTRIES;
|
||||
} else {
|
||||
*sendlen = gensec_socket->orig_send_len;
|
||||
gensec_socket->orig_send_len = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Turn a normal socket into a potentially GENSEC wrapped socket */
|
||||
|
||||
NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
|
||||
struct socket_context *current_socket,
|
||||
struct event_context *ev,
|
||||
void (*recv_handler)(void *, uint16_t),
|
||||
void *recv_private,
|
||||
struct socket_context **new_socket)
|
||||
{
|
||||
struct gensec_socket *gensec_socket;
|
||||
struct socket_context *new_sock;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
nt_status = socket_create_with_ops(current_socket, &gensec_socket_ops, &new_sock,
|
||||
SOCKET_TYPE_STREAM, current_socket->flags | SOCKET_FLAG_ENCRYPT);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
*new_socket = NULL;
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
new_sock->state = current_socket->state;
|
||||
|
||||
gensec_socket = talloc(new_sock, struct gensec_socket);
|
||||
if (gensec_socket == NULL) {
|
||||
*new_socket = NULL;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
new_sock->private_data = gensec_socket;
|
||||
gensec_socket->socket = current_socket;
|
||||
|
||||
if (talloc_reference(gensec_socket, current_socket) == NULL) {
|
||||
*new_socket = NULL;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* Nothing to do here, if we are not actually wrapping on this socket */
|
||||
if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) &&
|
||||
!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
|
||||
|
||||
gensec_socket->wrap = False;
|
||||
*new_socket = new_sock;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
gensec_socket->gensec_security = gensec_security;
|
||||
|
||||
gensec_socket->wrap = True;
|
||||
gensec_socket->eof = False;
|
||||
gensec_socket->error = NT_STATUS_OK;
|
||||
gensec_socket->interrupted = False;
|
||||
gensec_socket->in_extra_read = 0;
|
||||
|
||||
gensec_socket->read_buffer = data_blob(NULL, 0);
|
||||
|
||||
gensec_socket->recv_handler = recv_handler;
|
||||
gensec_socket->recv_private = recv_private;
|
||||
gensec_socket->ev = ev;
|
||||
|
||||
gensec_socket->packet = packet_init(gensec_socket);
|
||||
if (gensec_socket->packet == NULL) {
|
||||
*new_socket = NULL;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
packet_set_private(gensec_socket->packet, gensec_socket);
|
||||
packet_set_socket(gensec_socket->packet, gensec_socket->socket);
|
||||
packet_set_callback(gensec_socket->packet, gensec_socket_unwrap);
|
||||
packet_set_full_request(gensec_socket->packet, gensec_socket_full_request);
|
||||
packet_set_error_handler(gensec_socket->packet, gensec_socket_error_handler);
|
||||
packet_set_serialise(gensec_socket->packet);
|
||||
|
||||
/* TODO: full-request that knows about maximum packet size */
|
||||
|
||||
*new_socket = new_sock;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS gensec_socket_set_option(struct socket_context *sock, const char *option, const char *val)
|
||||
{
|
||||
set_socket_options(socket_get_fd(sock), option);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static char *gensec_socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_peer_name(gensec->socket, mem_ctx);
|
||||
}
|
||||
|
||||
static struct socket_address *gensec_socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_peer_addr(gensec->socket, mem_ctx);
|
||||
}
|
||||
|
||||
static struct socket_address *gensec_socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_my_addr(gensec->socket, mem_ctx);
|
||||
}
|
||||
|
||||
static int gensec_socket_get_fd(struct socket_context *sock)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_fd(gensec->socket);
|
||||
}
|
||||
|
||||
static const struct socket_ops gensec_socket_ops = {
|
||||
.name = "gensec",
|
||||
.fn_init = gensec_socket_init_fn,
|
||||
.fn_recv = gensec_socket_recv,
|
||||
.fn_send = gensec_socket_send,
|
||||
.fn_pending = gensec_socket_pending,
|
||||
|
||||
.fn_set_option = gensec_socket_set_option,
|
||||
|
||||
.fn_get_peer_name = gensec_socket_get_peer_name,
|
||||
.fn_get_peer_addr = gensec_socket_get_peer_addr,
|
||||
.fn_get_my_addr = gensec_socket_get_my_addr,
|
||||
.fn_get_fd = gensec_socket_get_fd
|
||||
};
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Generic Authentication Interface (socket wrapper)
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
struct gensec_security;
|
||||
struct socket_context;
|
||||
|
||||
NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
|
||||
struct socket_context *current_socket,
|
||||
struct event_context *ev,
|
||||
void (*recv_handler)(void *, uint16_t),
|
||||
void *recv_private,
|
||||
struct socket_context **new_socket);
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed);
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed);
|
||||
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
|
||||
DATA_BLOB blob, size_t *size);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
RFC2478 Compliant SPNEGO implementation
|
||||
|
||||
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#define SPNEGO_DELEG_FLAG 0x01
|
||||
#define SPNEGO_MUTUAL_FLAG 0x02
|
||||
#define SPNEGO_REPLAY_FLAG 0x04
|
||||
#define SPNEGO_SEQUENCE_FLAG 0x08
|
||||
#define SPNEGO_ANON_FLAG 0x10
|
||||
#define SPNEGO_CONF_FLAG 0x20
|
||||
#define SPNEGO_INTEG_FLAG 0x40
|
||||
#define SPNEGO_REQ_FLAG 0x80
|
||||
|
||||
enum spnego_negResult {
|
||||
SPNEGO_ACCEPT_COMPLETED = 0,
|
||||
SPNEGO_ACCEPT_INCOMPLETE = 1,
|
||||
SPNEGO_REJECT = 2,
|
||||
SPNEGO_NONE_RESULT = 3
|
||||
};
|
||||
|
||||
struct spnego_negTokenInit {
|
||||
const char **mechTypes;
|
||||
int reqFlags;
|
||||
DATA_BLOB mechToken;
|
||||
DATA_BLOB mechListMIC;
|
||||
char *targetPrincipal;
|
||||
};
|
||||
|
||||
struct spnego_negTokenTarg {
|
||||
uint8_t negResult;
|
||||
const char *supportedMech;
|
||||
DATA_BLOB responseToken;
|
||||
DATA_BLOB mechListMIC;
|
||||
};
|
||||
|
||||
struct spnego_data {
|
||||
int type;
|
||||
struct spnego_negTokenInit negTokenInit;
|
||||
struct spnego_negTokenTarg negTokenTarg;
|
||||
};
|
||||
|
||||
enum spnego_message_type {
|
||||
SPNEGO_NEG_TOKEN_INIT = 0,
|
||||
SPNEGO_NEG_TOKEN_TARG = 1,
|
||||
};
|
||||
|
||||
#include "auth/gensec/spnego_proto.h"
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
RFC2478 Compliant SPNEGO implementation
|
||||
|
||||
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/gensec/spnego.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "libcli/util/asn_1.h"
|
||||
|
||||
static BOOL read_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
|
||||
{
|
||||
ZERO_STRUCTP(token);
|
||||
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
|
||||
|
||||
while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
|
||||
int i;
|
||||
uint8_t context;
|
||||
if (!asn1_peek_uint8(asn1, &context)) {
|
||||
asn1->has_error = True;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (context) {
|
||||
/* Read mechTypes */
|
||||
case ASN1_CONTEXT(0):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
|
||||
|
||||
token->mechTypes = talloc(NULL, const char *);
|
||||
for (i = 0; !asn1->has_error &&
|
||||
0 < asn1_tag_remaining(asn1); i++) {
|
||||
token->mechTypes = talloc_realloc(NULL,
|
||||
token->mechTypes,
|
||||
const char *, i+2);
|
||||
asn1_read_OID(asn1, token->mechTypes + i);
|
||||
if (token->mechTypes[i]) {
|
||||
talloc_steal(token->mechTypes,
|
||||
token->mechTypes[i]);
|
||||
}
|
||||
}
|
||||
token->mechTypes[i] = NULL;
|
||||
|
||||
asn1_end_tag(asn1);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
/* Read reqFlags */
|
||||
case ASN1_CONTEXT(1):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_read_Integer(asn1, &token->reqFlags);
|
||||
token->reqFlags |= SPNEGO_REQ_FLAG;
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
/* Read mechToken */
|
||||
case ASN1_CONTEXT(2):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(2));
|
||||
asn1_read_OctetString(asn1, &token->mechToken);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
/* Read mecListMIC */
|
||||
case ASN1_CONTEXT(3):
|
||||
{
|
||||
uint8_t type_peek;
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(3));
|
||||
if (!asn1_peek_uint8(asn1, &type_peek)) {
|
||||
asn1->has_error = True;
|
||||
break;
|
||||
}
|
||||
if (type_peek == ASN1_OCTET_STRING) {
|
||||
asn1_read_OctetString(asn1,
|
||||
&token->mechListMIC);
|
||||
} else {
|
||||
/* RFC 2478 says we have an Octet String here,
|
||||
but W2k sends something different... */
|
||||
char *mechListMIC;
|
||||
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_read_GeneralString(asn1, &mechListMIC);
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
|
||||
token->targetPrincipal = mechListMIC;
|
||||
}
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
asn1->has_error = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
asn1_end_tag(asn1);
|
||||
asn1_end_tag(asn1);
|
||||
|
||||
return !asn1->has_error;
|
||||
}
|
||||
|
||||
static BOOL write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
|
||||
{
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
|
||||
|
||||
/* Write mechTypes */
|
||||
if (token->mechTypes && *token->mechTypes) {
|
||||
int i;
|
||||
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
|
||||
for (i = 0; token->mechTypes[i]; i++) {
|
||||
asn1_write_OID(asn1, token->mechTypes[i]);
|
||||
}
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
/* write reqFlags */
|
||||
if (token->reqFlags & SPNEGO_REQ_FLAG) {
|
||||
int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
|
||||
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_write_Integer(asn1, flags);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
/* write mechToken */
|
||||
if (token->mechToken.data) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(2));
|
||||
asn1_write_OctetString(asn1, token->mechToken.data,
|
||||
token->mechToken.length);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
/* write mechListMIC */
|
||||
if (token->mechListMIC.data) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(3));
|
||||
#if 0
|
||||
/* This is what RFC 2478 says ... */
|
||||
asn1_write_OctetString(asn1, token->mechListMIC.data,
|
||||
token->mechListMIC.length);
|
||||
#else
|
||||
/* ... but unfortunately this is what Windows
|
||||
sends/expects */
|
||||
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_push_tag(asn1, ASN1_GENERAL_STRING);
|
||||
asn1_write(asn1, token->mechListMIC.data,
|
||||
token->mechListMIC.length);
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
#endif
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
|
||||
return !asn1->has_error;
|
||||
}
|
||||
|
||||
static BOOL read_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
|
||||
{
|
||||
ZERO_STRUCTP(token);
|
||||
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
|
||||
|
||||
while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
|
||||
uint8_t context;
|
||||
if (!asn1_peek_uint8(asn1, &context)) {
|
||||
asn1->has_error = True;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (context) {
|
||||
case ASN1_CONTEXT(0):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_start_tag(asn1, ASN1_ENUMERATED);
|
||||
asn1_read_uint8(asn1, &token->negResult);
|
||||
asn1_end_tag(asn1);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
case ASN1_CONTEXT(1):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_read_OID(asn1, &token->supportedMech);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
case ASN1_CONTEXT(2):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(2));
|
||||
asn1_read_OctetString(asn1, &token->responseToken);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
case ASN1_CONTEXT(3):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(3));
|
||||
asn1_read_OctetString(asn1, &token->mechListMIC);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
default:
|
||||
asn1->has_error = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
asn1_end_tag(asn1);
|
||||
asn1_end_tag(asn1);
|
||||
|
||||
return !asn1->has_error;
|
||||
}
|
||||
|
||||
static BOOL write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
|
||||
{
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
|
||||
|
||||
if (token->negResult != SPNEGO_NONE_RESULT) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_write_enumerated(asn1, token->negResult);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
if (token->supportedMech) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_write_OID(asn1, token->supportedMech);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
if (token->responseToken.data) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(2));
|
||||
asn1_write_OctetString(asn1, token->responseToken.data,
|
||||
token->responseToken.length);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
if (token->mechListMIC.data) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(3));
|
||||
asn1_write_OctetString(asn1, token->mechListMIC.data,
|
||||
token->mechListMIC.length);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
|
||||
return !asn1->has_error;
|
||||
}
|
||||
|
||||
ssize_t spnego_read_data(DATA_BLOB data, struct spnego_data *token)
|
||||
{
|
||||
struct asn1_data asn1;
|
||||
ssize_t ret = -1;
|
||||
uint8_t context;
|
||||
|
||||
ZERO_STRUCTP(token);
|
||||
ZERO_STRUCT(asn1);
|
||||
|
||||
if (data.length == 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
asn1_load(&asn1, data);
|
||||
|
||||
if (!asn1_peek_uint8(&asn1, &context)) {
|
||||
asn1.has_error = True;
|
||||
} else {
|
||||
switch (context) {
|
||||
case ASN1_APPLICATION(0):
|
||||
asn1_start_tag(&asn1, ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&asn1, GENSEC_OID_SPNEGO);
|
||||
if (read_negTokenInit(&asn1, &token->negTokenInit)) {
|
||||
token->type = SPNEGO_NEG_TOKEN_INIT;
|
||||
}
|
||||
asn1_end_tag(&asn1);
|
||||
break;
|
||||
case ASN1_CONTEXT(1):
|
||||
if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
|
||||
token->type = SPNEGO_NEG_TOKEN_TARG;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
asn1.has_error = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!asn1.has_error) ret = asn1.ofs;
|
||||
asn1_free(&asn1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
|
||||
{
|
||||
struct asn1_data asn1;
|
||||
ssize_t ret = -1;
|
||||
|
||||
ZERO_STRUCT(asn1);
|
||||
|
||||
switch (spnego->type) {
|
||||
case SPNEGO_NEG_TOKEN_INIT:
|
||||
asn1_push_tag(&asn1, ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&asn1, GENSEC_OID_SPNEGO);
|
||||
write_negTokenInit(&asn1, &spnego->negTokenInit);
|
||||
asn1_pop_tag(&asn1);
|
||||
break;
|
||||
case SPNEGO_NEG_TOKEN_TARG:
|
||||
write_negTokenTarg(&asn1, &spnego->negTokenTarg);
|
||||
break;
|
||||
default:
|
||||
asn1.has_error = True;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!asn1.has_error) {
|
||||
*blob = data_blob_talloc(mem_ctx, asn1.data, asn1.length);
|
||||
ret = asn1.ofs;
|
||||
}
|
||||
asn1_free(&asn1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL spnego_free_data(struct spnego_data *spnego)
|
||||
{
|
||||
BOOL ret = True;
|
||||
|
||||
if (!spnego) goto out;
|
||||
|
||||
switch(spnego->type) {
|
||||
case SPNEGO_NEG_TOKEN_INIT:
|
||||
if (spnego->negTokenInit.mechTypes) {
|
||||
talloc_free(spnego->negTokenInit.mechTypes);
|
||||
}
|
||||
data_blob_free(&spnego->negTokenInit.mechToken);
|
||||
data_blob_free(&spnego->negTokenInit.mechListMIC);
|
||||
talloc_free(spnego->negTokenInit.targetPrincipal);
|
||||
break;
|
||||
case SPNEGO_NEG_TOKEN_TARG:
|
||||
if (spnego->negTokenTarg.supportedMech) {
|
||||
talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
|
||||
}
|
||||
data_blob_free(&spnego->negTokenTarg.responseToken);
|
||||
data_blob_free(&spnego->negTokenTarg.mechListMIC);
|
||||
break;
|
||||
default:
|
||||
ret = False;
|
||||
break;
|
||||
}
|
||||
ZERO_STRUCTP(spnego);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5 routines for active directory
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/network.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "system/time.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
|
||||
#if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
|
||||
krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
|
||||
{
|
||||
return krb5_set_default_in_tkt_etypes(ctx, enc);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
|
||||
/* HEIMDAL */
|
||||
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
|
||||
{
|
||||
pkaddr->addr_type = KRB5_ADDRESS_INET;
|
||||
pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
}
|
||||
#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
|
||||
/* MIT */
|
||||
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
|
||||
{
|
||||
pkaddr->addrtype = ADDRTYPE_INET;
|
||||
pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
}
|
||||
#else
|
||||
#error UNKNOWN_ADDRTYPE
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK)
|
||||
int create_kerberos_key_from_string(krb5_context context,
|
||||
krb5_principal host_princ,
|
||||
krb5_data *password,
|
||||
krb5_keyblock *key,
|
||||
krb5_enctype enctype)
|
||||
{
|
||||
int ret;
|
||||
krb5_data salt;
|
||||
krb5_encrypt_block eblock;
|
||||
|
||||
ret = krb5_principal2salt(context, host_princ, &salt);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
krb5_use_enctype(context, &eblock, enctype);
|
||||
ret = krb5_string_to_key(context, &eblock, key, password, &salt);
|
||||
SAFE_FREE(salt.data);
|
||||
return ret;
|
||||
}
|
||||
#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
|
||||
int create_kerberos_key_from_string(krb5_context context,
|
||||
krb5_principal host_princ,
|
||||
krb5_data *password,
|
||||
krb5_keyblock *key,
|
||||
krb5_enctype enctype)
|
||||
{
|
||||
int ret;
|
||||
krb5_salt salt;
|
||||
|
||||
ret = krb5_get_pw_salt(context, host_princ, &salt);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
ret = krb5_string_to_key_salt(context, enctype, password->data,
|
||||
salt, key);
|
||||
krb5_free_salt(context, salt);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#error UNKNOWN_CREATE_KEY_FUNCTIONS
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
|
||||
krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
|
||||
krb5_enctype **enctypes)
|
||||
{
|
||||
return krb5_get_permitted_enctypes(context, enctypes);
|
||||
}
|
||||
#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
|
||||
krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
|
||||
krb5_enctype **enctypes)
|
||||
{
|
||||
return krb5_get_default_in_tkt_etypes(context, enctypes);
|
||||
}
|
||||
#else
|
||||
#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
|
||||
#endif
|
||||
|
||||
void free_kerberos_etypes(krb5_context context,
|
||||
krb5_enctype *enctypes)
|
||||
{
|
||||
#if defined(HAVE_KRB5_FREE_KTYPES)
|
||||
krb5_free_ktypes(context, enctypes);
|
||||
return;
|
||||
#else
|
||||
SAFE_FREE(enctypes);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
|
||||
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
|
||||
krb5_auth_context auth_context,
|
||||
krb5_keyblock *keyblock)
|
||||
{
|
||||
return krb5_auth_con_setkey(context, auth_context, keyblock);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
|
||||
void krb5_free_unparsed_name(krb5_context context, char *val)
|
||||
{
|
||||
SAFE_FREE(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
void kerberos_free_data_contents(krb5_context context, krb5_data *pdata)
|
||||
{
|
||||
#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
|
||||
if (pdata->data) {
|
||||
krb5_free_data_contents(context, pdata);
|
||||
}
|
||||
#else
|
||||
SAFE_FREE(pdata->data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype)
|
||||
{
|
||||
#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS)
|
||||
KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype;
|
||||
#elif defined(HAVE_KRB5_SESSION_IN_CREDS)
|
||||
KRB5_KEY_TYPE((&pcreds->session)) = enctype;
|
||||
#else
|
||||
#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL kerberos_compatible_enctypes(krb5_context context,
|
||||
krb5_enctype enctype1,
|
||||
krb5_enctype enctype2)
|
||||
{
|
||||
#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE)
|
||||
krb5_boolean similar = 0;
|
||||
|
||||
krb5_c_enctype_compare(context, enctype1, enctype2, &similar);
|
||||
return similar ? True : False;
|
||||
#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS)
|
||||
return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False;
|
||||
#endif
|
||||
}
|
||||
|
||||
krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry)
|
||||
{
|
||||
#if defined(HAVE_KRB5_KT_FREE_ENTRY)
|
||||
return krb5_kt_free_entry(context, kt_entry);
|
||||
#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
|
||||
return krb5_free_keytab_entry_contents(context, kt_entry);
|
||||
#else
|
||||
#error UNKNOWN_KT_FREE_FUNCTION
|
||||
#endif
|
||||
}
|
||||
|
||||
char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
#if defined(HAVE_KRB5_GET_ERROR_STRING) && defined(HAVE_KRB5_FREE_ERROR_STRING)
|
||||
char *context_error = krb5_get_error_string(context);
|
||||
if (context_error) {
|
||||
ret = talloc_asprintf(mem_ctx, "%s: %s", error_message(code), context_error);
|
||||
krb5_free_error_string(context, context_error);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
ret = talloc_strdup(mem_ctx, error_message(code));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,540 @@
|
||||
# NOTE! this whole m4 file is disabled in configure.in for now
|
||||
|
||||
#################################################
|
||||
# KRB5 support
|
||||
KRB5_CFLAGS=""
|
||||
KRB5_CPPFLAGS=""
|
||||
KRB5_LDFLAGS=""
|
||||
KRB5_LIBS=""
|
||||
with_krb5_support=auto
|
||||
krb5_withval=auto
|
||||
AC_MSG_CHECKING([for KRB5 support])
|
||||
|
||||
# Do no harm to the values of CFLAGS and LIBS while testing for
|
||||
# Kerberos support.
|
||||
AC_ARG_WITH(krb5,
|
||||
[ --with-krb5=base-dir Locate Kerberos 5 support (default=auto)],
|
||||
[ case "$withval" in
|
||||
no)
|
||||
with_krb5_support=no
|
||||
AC_MSG_RESULT(no)
|
||||
krb5_withval=no
|
||||
;;
|
||||
yes)
|
||||
with_krb5_support=yes
|
||||
AC_MSG_RESULT(yes)
|
||||
krb5_withval=yes
|
||||
;;
|
||||
auto)
|
||||
with_krb5_support=auto
|
||||
AC_MSG_RESULT(auto)
|
||||
krb5_withval=auto
|
||||
;;
|
||||
*)
|
||||
with_krb5_support=yes
|
||||
AC_MSG_RESULT(yes)
|
||||
krb5_withval=$withval
|
||||
KRB5CONFIG="$krb5_withval/bin/krb5-config"
|
||||
;;
|
||||
esac ],
|
||||
AC_MSG_RESULT($with_krb5_support)
|
||||
)
|
||||
|
||||
if test x$with_krb5_support != x"no"; then
|
||||
FOUND_KRB5=no
|
||||
FOUND_KRB5_VIA_CONFIG=no
|
||||
|
||||
#################################################
|
||||
# check for krb5-config from recent MIT and Heimdal kerberos 5
|
||||
AC_MSG_CHECKING(for working specified location for krb5-config)
|
||||
if test x$KRB5CONFIG != "x"; then
|
||||
if test -x "$KRB5CONFIG"; then
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS="";export CFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="";export LDFLAGS
|
||||
KRB5_LIBS="`$KRB5CONFIG --libs gssapi`"
|
||||
KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
CFLAGS=$ac_save_CFLAGS;export CFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
|
||||
FOUND_KRB5=yes
|
||||
FOUND_KRB5_VIA_CONFIG=yes
|
||||
AC_MSG_RESULT(yes. Found $KRB5CONFIG)
|
||||
else
|
||||
AC_MSG_RESULT(no. Fallback to specified directory)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no. Fallback to finding krb5-config in path)
|
||||
#################################################
|
||||
# check for krb5-config from recent MIT and Heimdal kerberos 5
|
||||
AC_PATH_PROG(KRB5CONFIG, krb5-config)
|
||||
AC_MSG_CHECKING(for working krb5-config in path)
|
||||
if test -x "$KRB5CONFIG"; then
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS="";export CFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="";export LDFLAGS
|
||||
KRB5_LIBS="`$KRB5CONFIG --libs gssapi`"
|
||||
KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
CFLAGS=$ac_save_CFLAGS;export CFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
|
||||
FOUND_KRB5=yes
|
||||
FOUND_KRB5_VIA_CONFIG=yes
|
||||
AC_MSG_RESULT(yes. Found $KRB5CONFIG)
|
||||
else
|
||||
AC_MSG_RESULT(no. Fallback to previous krb5 detection strategy)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$FOUND_KRB5 != x"yes"; then
|
||||
#################################################
|
||||
# check for location of Kerberos 5 install
|
||||
AC_MSG_CHECKING(for kerberos 5 install path)
|
||||
case "$krb5_withval" in
|
||||
no)
|
||||
AC_MSG_RESULT(no krb5-path given)
|
||||
;;
|
||||
yes)
|
||||
AC_MSG_RESULT(/usr)
|
||||
FOUND_KRB5=yes
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT($krb5_withval)
|
||||
KRB5_CFLAGS="-I$krb5_withval/include"
|
||||
KRB5_CPPFLAGS="-I$krb5_withval/include"
|
||||
KRB5_LDFLAGS="-L$krb5_withval/lib"
|
||||
FOUND_KRB5=yes
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x$FOUND_KRB5 != x"yes"; then
|
||||
#################################################
|
||||
# see if this box has the SuSE location for the heimdal krb implementation
|
||||
AC_MSG_CHECKING(for /usr/include/heimdal)
|
||||
if test -d /usr/include/heimdal; then
|
||||
if test -f /usr/lib/heimdal/lib/libkrb5.a; then
|
||||
KRB5_CFLAGS="-I/usr/include/heimdal"
|
||||
KRB5_CPPFLAGS="-I/usr/include/heimdal"
|
||||
KRB5_LDFLAGS="-L/usr/lib/heimdal/lib"
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
KRB5_CFLAGS="-I/usr/include/heimdal"
|
||||
KRB5_CPPFLAGS="-I/usr/include/heimdal"
|
||||
AC_MSG_RESULT(yes)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$FOUND_KRB5 != x"yes"; then
|
||||
#################################################
|
||||
# see if this box has the RedHat location for kerberos
|
||||
AC_MSG_CHECKING(for /usr/kerberos)
|
||||
if test -d /usr/kerberos -a -f /usr/kerberos/lib/libkrb5.a; then
|
||||
KRB5_LDFLAGS="-L/usr/kerberos/lib"
|
||||
KRB5_CFLAGS="-I/usr/kerberos/include"
|
||||
KRB5_CPPFLAGS="-I/usr/kerberos/include"
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
fi
|
||||
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
ac_save_CPPFLAGS=$CPPFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
|
||||
#MIT needs this, to let us see 'internal' parts of the headers we use
|
||||
KRB5_CFLAGS="${KRB5_CFLAGS} -DKRB5_PRIVATE -DKRB5_DEPRECATED"
|
||||
|
||||
#Heimdal needs this
|
||||
#TODO: we need to parse KRB5_LIBS for -L path
|
||||
# and set -Wl,-rpath -Wl,path
|
||||
|
||||
CFLAGS="$CFLAGS $KRB5_CFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
|
||||
|
||||
KRB5_LIBS="$KRB5_LDFLAGS $KRB5_LIBS"
|
||||
|
||||
# now check for krb5.h. Some systems have the libraries without the headers!
|
||||
# note that this check is done here to allow for different kerberos
|
||||
# include paths
|
||||
AC_CHECK_HEADERS(krb5.h)
|
||||
|
||||
if test x"$ac_cv_header_krb5_h" = x"no"; then
|
||||
# Give a warning if KRB5 support was not explicitly requested,
|
||||
# i.e with_krb5_support = auto, otherwise die with an error.
|
||||
if test x"$with_krb5_support" = x"yes"; then
|
||||
AC_MSG_ERROR([KRB5 cannot be supported without krb5.h])
|
||||
else
|
||||
AC_MSG_WARN([KRB5 cannot be supported without krb5.h])
|
||||
fi
|
||||
# Turn off AD support and restore CFLAGS and LIBS variables
|
||||
with_krb5_support="no"
|
||||
fi
|
||||
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
CPPFLAGS=$ac_save_CPPFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS
|
||||
fi
|
||||
|
||||
# Now we have determined whether we really want KRB5 support
|
||||
|
||||
if test x"$with_krb5_support" != x"no"; then
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
ac_save_CPPFLAGS=$CPPFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
ac_save_LIBS=$LIBS
|
||||
|
||||
CFLAGS="$CFLAGS $KRB5_CFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
|
||||
|
||||
# now check for gssapi headers. This is also done here to allow for
|
||||
# different kerberos include paths
|
||||
AC_CHECK_HEADERS(gssapi.h gssapi_krb5.h gssapi/gssapi.h gssapi/gssapi_generic.h gssapi/gssapi_krb5.h com_err.h)
|
||||
|
||||
|
||||
# Heimdal checks.
|
||||
# But only if we didn't have a krb5-config to tell us this already
|
||||
if test x"$FOUND_KRB5_VIA_CONFIG" != x"yes"; then
|
||||
##################################################################
|
||||
# we might need the k5crypto and com_err libraries on some systems
|
||||
AC_CHECK_LIB_EXT(com_err, KRB5_LIBS, _et_list)
|
||||
AC_CHECK_LIB_EXT(k5crypto, KRB5_LIBS, krb5_encrypt_data)
|
||||
|
||||
AC_CHECK_LIB_EXT(crypto, KRB5_LIBS, des_set_key)
|
||||
AC_CHECK_LIB_EXT(asn1, KRB5_LIBS, copy_Authenticator)
|
||||
AC_CHECK_LIB_EXT(roken, KRB5_LIBS, roken_getaddrinfo_hostspec)
|
||||
fi
|
||||
|
||||
# Heimdal checks. On static Heimdal gssapi must be linked before krb5.
|
||||
AC_CHECK_LIB_EXT(gssapi, KRB5_LIBS, gss_display_status,[],[],
|
||||
AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
|
||||
|
||||
########################################################
|
||||
# now see if we can find the krb5 libs in standard paths
|
||||
# or as specified above
|
||||
AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_mk_req_extended)
|
||||
AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_kt_compare)
|
||||
|
||||
########################################################
|
||||
# now see if we can find the gssapi libs in standard paths
|
||||
if test x"$ac_cv_lib_ext_gssapi_gss_display_status" != x"yes"; then
|
||||
AC_CHECK_LIB_EXT(gssapi_krb5, KRB5_LIBS,gss_display_status,[],[],
|
||||
AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNC_EXT(krb5_set_real_time, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_set_default_in_tkt_etypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_set_default_tgs_ktypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_principal2salt, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_use_enctype, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_string_to_key, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_pw_salt, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_string_to_key_salt, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_auth_con_setkey, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_auth_con_setuseruserkey, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_locate_kdc, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_permitted_enctypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_default_in_tkt_etypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_ktypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_data_contents, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_principal_get_comp_string, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_unparsed_name, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_keytab_entry_contents, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_kt_free_entry, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_verify_checksum, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_c_verify_checksum, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_ticket_get_authorization_data_type, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_c_enctype_compare, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_enctypes_compatible_keys, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_error_string, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_error_string, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_initlog, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_addlog_func, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_set_warn_dest, $KRB5_LIBS)
|
||||
|
||||
LIBS="$LIBS $KRB5_LIBS"
|
||||
|
||||
AC_CACHE_CHECK([for krb5_log_facility type],
|
||||
samba_cv_HAVE_KRB5_LOG_FACILITY,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_log_facility block;],
|
||||
samba_cv_HAVE_KRB5_LOG_FACILITY=yes,
|
||||
samba_cv_HAVE_KRB5_LOG_FACILITY=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_LOG_FACILITY" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_LOG_FACILITY,1,
|
||||
[Whether the type krb5_log_facility exists])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for krb5_encrypt_block type],
|
||||
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_encrypt_block block;],
|
||||
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=yes,
|
||||
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_ENCRYPT_BLOCK" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_ENCRYPT_BLOCK,1,
|
||||
[Whether the type krb5_encrypt_block exists])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for addrtype in krb5_address],
|
||||
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_address kaddr; kaddr.addrtype = ADDRTYPE_INET;],
|
||||
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=yes,
|
||||
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=no)])
|
||||
if test x"$samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_ADDRTYPE_IN_KRB5_ADDRESS,1,
|
||||
[Whether the krb5_address struct has a addrtype property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for addr_type in krb5_address],
|
||||
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_address kaddr; kaddr.addr_type = KRB5_ADDRESS_INET;],
|
||||
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=yes,
|
||||
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=no)])
|
||||
if test x"$samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,1,
|
||||
[Whether the krb5_address struct has a addr_type property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for enc_part2 in krb5_ticket],
|
||||
samba_cv_HAVE_KRB5_TKT_ENC_PART2,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_ticket tkt; tkt.enc_part2->authorization_data[0]->contents = NULL;],
|
||||
samba_cv_HAVE_KRB5_TKT_ENC_PART2=yes,
|
||||
samba_cv_HAVE_KRB5_TKT_ENC_PART2=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_TKT_ENC_PART2" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_TKT_ENC_PART2,1,
|
||||
[Whether the krb5_ticket struct has a enc_part2 property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for keyblock in krb5_creds],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_creds creds; krb5_keyblock kb; creds.keyblock = kb;],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=yes,
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYBLOCK_IN_CREDS,1,
|
||||
[Whether the krb5_creds struct has a keyblock property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for session in krb5_creds],
|
||||
samba_cv_HAVE_KRB5_SESSION_IN_CREDS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_creds creds; krb5_keyblock kb; creds.session = kb;],
|
||||
samba_cv_HAVE_KRB5_SESSION_IN_CREDS=yes,
|
||||
samba_cv_HAVE_KRB5_SESSION_IN_CREDS=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_SESSION_IN_CREDS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_SESSION_IN_CREDS,1,
|
||||
[Whether the krb5_creds struct has a session property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for keyvalue in krb5_keyblock],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keyblock key; key.keyvalue.data = NULL;],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=yes,
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYBLOCK_KEYVALUE,1,
|
||||
[Whether the krb5_keyblock struct has a keyvalue property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5],
|
||||
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;],
|
||||
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes,
|
||||
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)])
|
||||
AC_CACHE_CHECK([for KEYTYPE_ARCFOUR_56],
|
||||
samba_cv_HAVE_KEYTYPE_ARCFOUR_56,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytype keytype; keytype = KEYTYPE_ARCFOUR_56;],
|
||||
samba_cv_HAVE_KEYTYPE_ARCFOUR_56=yes,
|
||||
samba_cv_HAVE_KEYTYPE_ARCFOUR_56=no)])
|
||||
# Heimdals with KEYTYPE_ARCFOUR but not KEYTYPE_ARCFOUR_56 are broken
|
||||
# w.r.t. arcfour and windows, so we must not enable it here
|
||||
if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes" -a\
|
||||
x"$samba_cv_HAVE_KEYTYPE_ARCFOUR_56" = x"yes"; then
|
||||
AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1,
|
||||
[Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for AP_OPTS_USE_SUBKEY],
|
||||
samba_cv_HAVE_AP_OPTS_USE_SUBKEY,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_flags ap_options; ap_options = AP_OPTS_USE_SUBKEY;],
|
||||
samba_cv_HAVE_AP_OPTS_USE_SUBKEY=yes,
|
||||
samba_cv_HAVE_AP_OPTS_USE_SUBKEY=no)])
|
||||
if test x"$samba_cv_HAVE_AP_OPTS_USE_SUBKEY" = x"yes"; then
|
||||
AC_DEFINE(HAVE_AP_OPTS_USE_SUBKEY,1,
|
||||
[Whether the AP_OPTS_USE_SUBKEY ap option is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for KV5M_KEYTAB],
|
||||
samba_cv_HAVE_KV5M_KEYTAB,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytab_entry entry; entry.magic = KV5M_KEYTAB;],
|
||||
samba_cv_HAVE_KV5M_KEYTAB=yes,
|
||||
samba_cv_HAVE_KV5M_KEYTAB=no)])
|
||||
if test x"$samba_cv_HAVE_KV5M_KEYTAB" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KV5M_KEYTAB,1,
|
||||
[Whether the KV5M_KEYTAB option is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for the krb5_princ_component macro],
|
||||
samba_cv_HAVE_KRB5_PRINC_COMPONENT,[
|
||||
AC_TRY_LINK([#include <krb5.h>],
|
||||
[const krb5_data *pkdata; krb5_context context; krb5_principal principal;
|
||||
pkdata = krb5_princ_component(context, principal, 0);],
|
||||
samba_cv_HAVE_KRB5_PRINC_COMPONENT=yes,
|
||||
samba_cv_HAVE_KRB5_PRINC_COMPONENT=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_PRINC_COMPONENT" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_PRINC_COMPONENT,1,
|
||||
[Whether krb5_princ_component is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for key in krb5_keytab_entry],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytab_entry entry; krb5_keyblock e; entry.key = e;],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=yes,
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEY,1,
|
||||
[Whether krb5_keytab_entry has key member])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for keyblock in krb5_keytab_entry],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytab_entry entry; entry.keyblock.keytype = 0;],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=yes,
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,1,
|
||||
[Whether krb5_keytab_entry has keyblock member])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for WRFILE: keytab support],
|
||||
samba_cv_HAVE_WRFILE_KEYTAB,[
|
||||
AC_TRY_RUN([
|
||||
#include<krb5.h>
|
||||
main()
|
||||
{
|
||||
krb5_context context;
|
||||
krb5_keytab keytab;
|
||||
krb5_init_context(&context);
|
||||
return krb5_kt_resolve(context, "WRFILE:api", &keytab);
|
||||
}],
|
||||
samba_cv_HAVE_WRFILE_KEYTAB=yes,
|
||||
samba_cv_HAVE_WRFILE_KEYTAB=no)])
|
||||
if test x"$samba_cv_HAVE_WRFILE_KEYTAB" = x"yes"; then
|
||||
AC_DEFINE(HAVE_WRFILE_KEYTAB,1,
|
||||
[Whether the WRFILE:-keytab is supported])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for krb5_princ_realm returns krb5_realm or krb5_data],
|
||||
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_context context;krb5_principal principal;krb5_realm realm;
|
||||
realm = *krb5_princ_realm(context, principal);],
|
||||
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=yes,
|
||||
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=no)])
|
||||
if test x"$samba_cv_KRB5_PRINC_REALM_RETURNS_REALM" = x"yes"; then
|
||||
AC_DEFINE(KRB5_PRINC_REALM_RETURNS_REALM,1,
|
||||
[Whether krb5_princ_realm returns krb5_realm or krb5_data])
|
||||
fi
|
||||
|
||||
# TODO: check all gssapi headers for this
|
||||
AC_CACHE_CHECK([for GSS_C_DCE_STYLE in gssapi.h],
|
||||
samba_cv_GSS_C_DCE_STYLE,[
|
||||
AC_TRY_COMPILE([#include <gssapi.h>],
|
||||
[int flags = GSS_C_DCE_STYLE;],
|
||||
samba_cv_GSS_C_DCE_STYLE=yes,
|
||||
samba_cv_GSS_C_DCE_STYLE=no)])
|
||||
|
||||
AC_CHECK_FUNC_EXT(gsskrb5_get_initiator_subkey, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(gsskrb5_extract_authz_data_from_sec_context, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(gsskrb5_register_acceptor_identity, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(gss_krb5_ccache_name, $KRB5_LIBS)
|
||||
if test x"$ac_cv_lib_ext_krb5_krb5_mk_req_extended" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5,1,[Whether to have KRB5 support])
|
||||
AC_MSG_CHECKING(whether KRB5 support is used)
|
||||
SMB_ENABLE(KRB5,YES)
|
||||
AC_MSG_RESULT(yes)
|
||||
echo "KRB5_CFLAGS: ${KRB5_CFLAGS}"
|
||||
echo "KRB5_CPPFLAGS: ${KRB5_CPPFLAGS}"
|
||||
echo "KRB5_LDFLAGS: ${KRB5_LDFLAGS}"
|
||||
echo "KRB5_LIBS: ${KRB5_LIBS}"
|
||||
else
|
||||
if test x"$with_krb5_support" = x"yes"; then
|
||||
AC_MSG_ERROR(a working krb5 library is needed for KRB5 support)
|
||||
else
|
||||
AC_MSG_WARN(a working krb5 library is needed for KRB5 support)
|
||||
fi
|
||||
KRB5_CFLAGS=""
|
||||
KRB5_CPPFLAGS=""
|
||||
KRB5_LDFLAGS=""
|
||||
KRB5_LIBS=""
|
||||
with_krb5_support=no
|
||||
fi
|
||||
|
||||
# checks if we have access to a libkdc
|
||||
# and can use it for our builtin kdc server_service
|
||||
KDC_CFLAGS=""
|
||||
KDC_CPPFLAGS=""
|
||||
KDC_DLFLAGS=""
|
||||
KDC_LIBS=""
|
||||
AC_CHECK_HEADERS(kdc.h)
|
||||
AC_CHECK_LIB_EXT(kdc, KDC_LIBS, krb5_kdc_default_config)
|
||||
AC_CHECK_LIB_EXT(hdb, KDC_LIBS, hdb_generate_key_set_password)
|
||||
|
||||
AC_MSG_CHECKING(whether libkdc is used)
|
||||
if test x"$ac_cv_header_kdc_h" = x"yes"; then
|
||||
if test x"$ac_cv_lib_ext_kdc_krb5_kdc_default_config" = x"yes"; then
|
||||
if test x"$ac_cv_lib_ext_hdb_hdb_generate_key_set_password" = x"yes"; then
|
||||
SMB_ENABLE(KDC,YES)
|
||||
AC_MSG_RESULT(yes)
|
||||
echo "KDC_LIBS: ${KDC_LIBS}"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
CPPFLAGS=$ac_save_CPPFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS
|
||||
LIBS="$ac_save_LIBS"
|
||||
|
||||
# as a nasty hack add the krb5 stuff to the global vars,
|
||||
# at some point this should not be needed anymore when the build system
|
||||
# can handle that alone
|
||||
CFLAGS="$CFLAGS $KRB5_CFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
|
||||
fi
|
||||
|
||||
SMB_EXT_LIB(KRB5,[${KRB5_LIBS}],[${KRB5_CFLAGS}],[${KRB5_CPPFLAGS}],[${KRB5_LDFLAGS}])
|
||||
SMB_EXT_LIB(KDC,[${KDC_LIBS}],[${KDC_CFLAGS}],[${KDC_CPPFLAGS}],[${KDC_LDFLAGS}])
|
||||
@@ -0,0 +1,15 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM KERBEROS
|
||||
[SUBSYSTEM::KERBEROS]
|
||||
PRIVATE_PROTO_HEADER = proto.h
|
||||
OBJ_FILES = kerberos.o \
|
||||
clikrb5.o \
|
||||
kerberos_heimdal.o \
|
||||
kerberos_util.o \
|
||||
kerberos_pac.o \
|
||||
gssapi_parse.o \
|
||||
krb5_init_context.o
|
||||
PUBLIC_DEPENDENCIES = HEIMDAL_KRB5 NDR_KRB5PAC samba-socket LIBCLI_RESOLVE
|
||||
PRIVATE_DEPENDENCIES = ASN1_UTIL HEIMDAL_ROKEN_ADDRINFO auth_sam CREDENTIALS_KRB5
|
||||
# End SUBSYSTEM KERBEROS
|
||||
#################################
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
simple GSSAPI wrappers
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
|
||||
Copyright (C) Luke Howard 2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/util/asn_1.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
/*
|
||||
generate a krb5 GSS-API wrapper packet given a ticket
|
||||
*/
|
||||
DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2])
|
||||
{
|
||||
struct asn1_data data;
|
||||
DATA_BLOB ret = data_blob(NULL,0);
|
||||
|
||||
if (!ticket->data) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(data);
|
||||
|
||||
asn1_push_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&data, GENSEC_OID_KERBEROS5);
|
||||
|
||||
asn1_write(&data, tok_id, 2);
|
||||
asn1_write(&data, ticket->data, ticket->length);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
if (data.has_error) {
|
||||
DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
|
||||
asn1_free(&data);
|
||||
}
|
||||
|
||||
ret = data_blob_talloc(mem_ctx, data.data, data.length);
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
parse a krb5 GSS-API wrapper packet giving a ticket
|
||||
*/
|
||||
BOOL gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2])
|
||||
{
|
||||
BOOL ret;
|
||||
struct asn1_data data;
|
||||
int data_remaining;
|
||||
|
||||
asn1_load(&data, *blob);
|
||||
asn1_start_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&data, GENSEC_OID_KERBEROS5);
|
||||
|
||||
data_remaining = asn1_tag_remaining(&data);
|
||||
|
||||
if (data_remaining < 3) {
|
||||
data.has_error = True;
|
||||
} else {
|
||||
asn1_read(&data, tok_id, 2);
|
||||
data_remaining -= 2;
|
||||
*ticket = data_blob_talloc(mem_ctx, NULL, data_remaining);
|
||||
asn1_read(&data, ticket->data, ticket->length);
|
||||
}
|
||||
|
||||
asn1_end_tag(&data);
|
||||
|
||||
ret = !data.has_error;
|
||||
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
check a GSS-API wrapper packet givin an expected OID
|
||||
*/
|
||||
BOOL gensec_gssapi_check_oid(const DATA_BLOB *blob, const char *oid)
|
||||
{
|
||||
BOOL ret;
|
||||
struct asn1_data data;
|
||||
|
||||
asn1_load(&data, *blob);
|
||||
asn1_start_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&data, GENSEC_OID_KERBEROS5);
|
||||
|
||||
ret = !data.has_error;
|
||||
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
AllowedWorkstationNames and Krb5
|
||||
--------------------------------
|
||||
|
||||
Microsoft uses the clientAddresses *multiple value* field in the krb5
|
||||
protocol (particularly the AS_REQ) to communicate it's netbios name.
|
||||
This is (my guess) to permit the userWorkstations field to work.
|
||||
|
||||
The KDC I imagine checks the netbios address against this value, in
|
||||
the same way that the Samba server does this.
|
||||
|
||||
The checking of this implies a little of the next question:
|
||||
|
||||
Is a DAL the layer we need?
|
||||
---------------------------
|
||||
|
||||
Looking at what we need to pass around, I start to seriously wonder if
|
||||
the DAL is even the right layer - we seem to want to create an account
|
||||
authorization abstraction layer - is this account permitted to login to
|
||||
this computer, at this time?
|
||||
|
||||
This information in AD is much richer than the Heimdal HDB, and it
|
||||
seems to make sense to do AD-specific access control checks in an
|
||||
AD-specific layer, not in the back-end agnostic server.
|
||||
|
||||
Because the DAL only reads in the principalName as the key, it has
|
||||
trouble performing access control decisions on things other than the
|
||||
name.
|
||||
|
||||
I'll be very interested if the DAL really works for eDirectory too.
|
||||
Perhaps all we need to do is add in the same kludges as we have in
|
||||
Samba 3.0 for eDirectory. Hmm...
|
||||
|
||||
That said, the current layer provides us with a very good start, and
|
||||
any redefinition would occour from that basis.
|
||||
|
||||
|
||||
GSSAPI layer requirements
|
||||
-------------------------
|
||||
|
||||
Welcome to the wonderful world of canonicalisation
|
||||
|
||||
The MIT GSSAPI libs do not support kinit returning a different
|
||||
realm to what the client asked for, even just in case differences.
|
||||
|
||||
Heimdal has the same problem, and this applies to the krb5 layer, not
|
||||
just gssapi.
|
||||
|
||||
We need to test if the canonicalisation is controlled by the KDCOption
|
||||
flags, windows always sends the Canonicalize flags
|
||||
|
||||
Old Clients (samba3 and HPUX clients) uses 'selfmade' gssapi/krb5
|
||||
for using it in the CIFS session setup. Because they use krb5_mk_req()
|
||||
they get a chksum field depending on the encryption type, but that's wrong
|
||||
for GSSAPI (see rfc 1964 section 1.1.1). The Cheksum type 8003
|
||||
should be used in the Authenticator of the AP-REQ! That allows the channel bindings,
|
||||
the GCC_C_* req_flags and optional delegation tickets to be passed from the client to the server.
|
||||
Hower windows doesn't seems to care about if the checksum is of the wrong type,
|
||||
for CIFS SessionSetups, it seems that the req_flags are just set to 0.
|
||||
So this can't work for LDAP connections with sign or seal, or for any DCERPC
|
||||
connection.
|
||||
|
||||
So we need to also support old clients!
|
||||
|
||||
Principal Names, long and short names
|
||||
-------------------------------------
|
||||
|
||||
As far as servicePrincipalNames are concerned, these are not
|
||||
canonicalised, except as regards the realm in the reply. That is, the
|
||||
client gets back the principal it asked for, with the realm portion
|
||||
'fixed' to uppercase, long form.
|
||||
|
||||
The short name of the realm seems to be accepted for at least AS_REQ
|
||||
operations, but because the server performs canonicalisation, this
|
||||
causes pain for current client libraries.
|
||||
|
||||
The canonicalisation of names matters not only for the KDC, but also
|
||||
for code that has to deal with keytabs.
|
||||
|
||||
We also need to handle type 10 names (NT-ENTERPRISE), which are a full
|
||||
principal name in the principal field, unrelated to the realm.
|
||||
|
||||
HOST/ Aliases
|
||||
-------------
|
||||
|
||||
There is another post somewhere (ref lost for the moment) that details
|
||||
where in active directory the list of stored aliases for HOST/ is.
|
||||
This should be read, parsed and used to allow any of these requests to
|
||||
use the HOST/ key.
|
||||
|
||||
For example, this is how HTTP/, DNS/ and CIFS/ can use HOST/ without
|
||||
any explicit entry.
|
||||
|
||||
|
||||
Jean-Baptiste.Marchand@hsc.fr reminds me:
|
||||
|
||||
> This is the SPNMappings attribute in Active Directory:
|
||||
|
||||
> http://msdn.microsoft.com/library/en-us/adschema/adschema/a_spnmappings.asp
|
||||
|
||||
We implement this in hdb-ldb.
|
||||
|
||||
Implicit names for Win2000 Accounts
|
||||
-----------------------------------
|
||||
|
||||
Despite not having a DNS name, nor a servicePrincipalName on accounts
|
||||
created by computers running win2000, it appears we are expected to
|
||||
have an implicit mapping from host/computer.full.name and
|
||||
host/computer to it's entry.
|
||||
|
||||
Returned Salt for PreAuthentication
|
||||
-----------------------------------
|
||||
|
||||
When the server replies for pre-authentication, it returns the Salt,
|
||||
which may be in the form of a principalName that is in no way
|
||||
connected with the current names. (ie, even if the userPrincipalName
|
||||
and samAccountName are renamed, the old salt is returned).
|
||||
|
||||
This is probably the kerberos standard salt, kept in the 'Key'. The
|
||||
standard generation rules are found in a Mail from Luke Howard dated
|
||||
10 Nov 2004:
|
||||
|
||||
|
||||
From: Luke Howard <lukeh@padl.com>
|
||||
Message-Id: <200411100231.iAA2VLUW006101@au.padl.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=US-ASCII
|
||||
Organization: PADL Software Pty Ltd
|
||||
To: lukeh@padl.com
|
||||
Date: Wed, 10 Nov 2004 13:31:21 +1100
|
||||
Versions: dmail (bsd44) 2.6d/makemail 2.10
|
||||
Cc: huaraz@moeller.plus.com, samba-technical@lists.samba.org
|
||||
Subject: Re: Samba-3.0.7-1.3E Active Directory Issues
|
||||
X-BeenThere: samba-technical@lists.samba.org
|
||||
X-Mailman-Version: 2.1.4
|
||||
Precedence: list
|
||||
Reply-To: lukeh@padl.com
|
||||
|
||||
Did some more testing, it appears the behaviour has another
|
||||
explanation. It appears that the standard Kerberos password salt
|
||||
algorithm is applied in Windows 2003, just that the source principal
|
||||
name is different.
|
||||
|
||||
Here is what I've been able to deduce from creating a bunch of
|
||||
different accounts:
|
||||
|
||||
Type of account Principal for Salting
|
||||
========================================================================
|
||||
Computer Account host/<SAM-Name-Without-$>.realm@REALM
|
||||
User Account Without UPN <SAM-Name>@REALM
|
||||
User Account With UPN <LHS-Of-UPN>@REALM
|
||||
|
||||
Note that if the computer account's SAM account name does not include
|
||||
the trailing '$', then the entire SAM account name is used as input to
|
||||
the salting principal. Setting a UPN for a computer account has no
|
||||
effect.
|
||||
|
||||
It seems to me odd that the RHS of the UPN is not used in the salting
|
||||
principal. For example, a user with UPN foo@mydomain.com in the realm
|
||||
MYREALM.COM would have a salt of MYREALM.COMfoo. Perhaps this is to
|
||||
allow a user's UPN suffix to be changed without changing the salt. And
|
||||
perhaps using the UPN for salting signifies a move away SAM names and
|
||||
their associated constraints.
|
||||
|
||||
For more information on how UPNs relate to the Kerberos protocol,
|
||||
see:
|
||||
|
||||
http://www.ietf.org/proceedings/01dec/I-D/draft-ietf-krb-wg-kerberos-referrals-02.txt
|
||||
|
||||
-- Luke
|
||||
|
||||
--
|
||||
|
||||
|
||||
|
||||
|
||||
Heimdal oddities
|
||||
----------------
|
||||
|
||||
Heimdal is built such that it should be able to serve multiple realms
|
||||
at the same time. This isn't relevant for Samba's use, but it shows
|
||||
up in a lot of generalisations throughout the code.
|
||||
|
||||
Other odd things:
|
||||
- Support for multiple passwords on a client account: we seem to
|
||||
call hdb_next_enctype2key() in the pre-authentication routines to
|
||||
allow multiple passwords per account in krb5. (I think this was
|
||||
intened to allow multiple salts)
|
||||
|
||||
State Machine safety
|
||||
--------------------
|
||||
|
||||
Samba is a giant state machine, and as such have very different
|
||||
requirements to those traditionally expressed for kerberos and GSSAPI
|
||||
libraries.
|
||||
|
||||
Samba requires all of the libraries it uses to be state machine safe in
|
||||
their use of internal data. This does not mean thread safe, and an
|
||||
application could be thread safe, but not state machine safe (if it
|
||||
instead used thread-local variables).
|
||||
|
||||
So, what does it mean for a library to be state machine safe? This is
|
||||
mostly a question of context, and how the library manages whatever
|
||||
internal state machines it has. If the library uses a context
|
||||
variable, passed in by the caller, which contains all the information
|
||||
about the current state of the library, then it is safe. An example
|
||||
of this state is the sequence number and session keys for an ongoing
|
||||
encrypted session).
|
||||
|
||||
The other issue affecting state machines is 'blocking' (waiting for a
|
||||
read on a network socket).
|
||||
|
||||
Heimdal has this 'state machine safety' in parts, and we have modified
|
||||
the lorikeet branch to improve this behviour, when using a new,
|
||||
non-standard API.
|
||||
|
||||
Heimdal uses a per-context variable for the 'krb5_auth_context', which
|
||||
controls the ongoing encrypted connection, but does use global
|
||||
variables for the ubiquitous krb5_context parameter.
|
||||
|
||||
The modification that has added most to 'state machine safety' of
|
||||
GSSAPI is the addition of the gsskrb5_acquire_creds function. This
|
||||
allows the caller to specify a keytab and ccache, for use by the
|
||||
GSSAPI code. Therefore there is no need to use global variables to
|
||||
communicate this information.
|
||||
|
||||
At a more theoritical level (simply counting static and global
|
||||
variables) Heimdal is not state machine safe for the GSSAPI layer.
|
||||
The Krb5 layer alone is much closer, as far as I can tell, blocking
|
||||
excepted. .
|
||||
|
||||
To deal with blocking, we could have a fork()ed child per context,
|
||||
using the 'GSSAPI export context' function to transfer
|
||||
the GSSAPI state back into the main code for the wrap()/unwrap() part
|
||||
of the operation. This will still hit issues of static storage (one
|
||||
gss_krb5_context per process, and multiple GSSAPI encrypted sessions
|
||||
at a time) but these may not matter in practice.
|
||||
|
||||
In the short-term, we deal with blocking by taking over the network
|
||||
send() and recv() functions, therefore making them 'semi-async'. This
|
||||
doens't apply to DNS yet.
|
||||
|
||||
GSSAPI and Kerberos extensions
|
||||
------------------------------
|
||||
|
||||
This is a general list of the other extensions we have made to / need from
|
||||
the kerberos libraries
|
||||
|
||||
- DCE_STYLE
|
||||
|
||||
- gsskrb5_get_initiator_subkey() (return the exact key that Samba3
|
||||
has always asked for. gsskrb5_get_subkey() might do what we need
|
||||
anyway)
|
||||
|
||||
- gsskrb5_acquire_creds() (takes keytab and/or ccache as input
|
||||
parameters, see keytab and state machine discussion)
|
||||
|
||||
- gss_krb5_copy_service_keyblock() (get the key used to actually
|
||||
encrypt the ticket to the server, because the same key is used for
|
||||
the PAC validation).
|
||||
- gsskrb5_extract_authtime_from_sec_context (get authtime from
|
||||
kerberos ticket)
|
||||
- gsskrb5_extract_authz_data_from_sec_context (get authdata from
|
||||
ticket, ie the PAC. Must unwrap the data if in an AD-IFRELEVENT)
|
||||
- gsskrb5_wrap_size (find out how big the wrapped packet will be,
|
||||
given input length).
|
||||
|
||||
Keytab requirements
|
||||
-------------------
|
||||
|
||||
Because windows machine account handling is very different to the
|
||||
tranditional 'MIT' keytab operation. This starts when we look at the
|
||||
basis of the secrets handling:
|
||||
|
||||
Traditional 'MIT' behaviour is to use a keytab, continaing salted key
|
||||
data, extracted from the KDC. (In this modal, there is no 'service
|
||||
password', instead the keys are often simply application of random
|
||||
bytes). Heimdal also implements this behaviour.
|
||||
|
||||
The windows modal is very different - instead of sharing a keytab with
|
||||
each member server, a password is stored for the whole machine. The
|
||||
password is set with non-kerberos mechanisms (particularly SAMR, a
|
||||
DCE-RPC service) and when interacting on a kerberos basis, the
|
||||
password is salted by the client. (That is, no salt infromation
|
||||
appears to be convayed from the KDC to the member).
|
||||
|
||||
In dealing with this modal, we leverage both the traditional file
|
||||
keytab and in-MEMORY keytabs.
|
||||
|
||||
When dealing with a windows KDC, the behaviour regarding case
|
||||
sensitivity and canonacolisation must be accomidated. This means that
|
||||
an incoming request to a member server may have a wide variety of
|
||||
service principal names. These include:
|
||||
|
||||
machine$@REALM (samba clients)
|
||||
HOST/foo.bar@realm (win2k clients)
|
||||
HOST/foo@realm (win2k clients, using netbios)
|
||||
cifs/foo.bar@realm (winxp clients)
|
||||
cifs/foo@realm (winxp clients, using netbios)
|
||||
|
||||
as well as all case variations on the above.
|
||||
|
||||
Because that all got 'too hard' to put into a keytab in the
|
||||
traditional way (with the client to specify the name), we either
|
||||
pre-compute the keys into a traditional keytab or make an in-MEMORY
|
||||
keytab at run time. In both cases we specifiy the principal name to
|
||||
GSSAPI, which avoids the need to store duplicate principals.
|
||||
|
||||
We use a 'private' keytab in our private dir, referenced from the
|
||||
secrets.ldb by default.
|
||||
|
||||
Extra Heimdal functions used
|
||||
----------------------------
|
||||
(an attempt to list some of the Heimdal-specific functions I know we use)
|
||||
|
||||
krb5_free_keyblock_contents()
|
||||
|
||||
also a raft of prinicpal manipulation functions:
|
||||
|
||||
Prncipal Manipulation
|
||||
---------------------
|
||||
|
||||
Samba makes extensive use of the principal manipulation functions in
|
||||
Heimdal, including the known structure behind krb_principal and
|
||||
krb5_realm (a char *).
|
||||
|
||||
Authz data extraction
|
||||
---------------------
|
||||
|
||||
We use krb5_ticket_get_authorization_data_type(), and expect it to
|
||||
return the correct authz data, even if wrapped in an AD-IFRELEVENT container.
|
||||
|
||||
|
||||
KDC/hdb Extensions
|
||||
--------------
|
||||
|
||||
We have modified Heimdal's 'hdb' interface to specify the 'type' of
|
||||
Principal being requested. This allows us to correctly behave with
|
||||
the different 'classes' of Principal name.
|
||||
|
||||
We currently define 2 classes:
|
||||
- client (kinit)
|
||||
- server (tgt)
|
||||
|
||||
I also now specify the kerberos principal as an explict parameter, not
|
||||
an in/out value on the entry itself.
|
||||
|
||||
Inside hdb-ldb, we add krbtgt as a special class of principal, because
|
||||
of particular special-case backend requirements.
|
||||
|
||||
Callbacks:
|
||||
In addition, I have added a new interface hdb_fetch_ex(), which
|
||||
returns a structure including callbacks, which provide the hook for
|
||||
the PAC, as well as a callback into the main access control routines.
|
||||
|
||||
A new callback should be added to increment the bad password counter
|
||||
on failure.
|
||||
|
||||
Another possability for a callback is to obtain the keys. This would
|
||||
allow the plaintext password to only be hashed into the encryption
|
||||
types we need. This idea from the eDirectory/MIT DAL work.
|
||||
|
||||
This probably should be combined with storing the hashed passwords in
|
||||
the supplementalCredentials attribute. If combined with a kvno
|
||||
parameter, this could also allow changing of the krbtgt password
|
||||
(valuable for security).
|
||||
|
||||
libkdc
|
||||
------
|
||||
|
||||
Samba4 needs to be built as a single binary (design requirement), and
|
||||
this should include the KDC. Samba also (and perhaps more
|
||||
importantly) needs to control the configuration environment of the
|
||||
KDC.
|
||||
|
||||
The interface we have defined for libkdc allow for packet injection
|
||||
into the post-socket layer, with a defined krb5_context and
|
||||
kdb5_kdc_configuration structure. These effectively redirect the
|
||||
kerberos warnings, logging and database calls as we require.
|
||||
|
||||
Using our socket lib
|
||||
--------------------
|
||||
|
||||
An important detail in the use of libkdc is that we use our own socket
|
||||
lib. This allows the KDC code to be as portable as the rest of samba
|
||||
(this cuts both ways), but far more importantly it ensures a
|
||||
consistancy in the handling of requests, binding to sockets etc.
|
||||
|
||||
To handle TCP, we use of our socket layer in much the same way as
|
||||
we deal with TCP for CIFS. Tridge created a generic packet handling
|
||||
layer for this.
|
||||
|
||||
For the client, we likewise must take over the socket functions, so
|
||||
that our single thread smbd will not lock up talking to itself. (We
|
||||
allow processing while waiting for packets in our socket routines).
|
||||
|
||||
Kerberos logging support
|
||||
------------------------
|
||||
|
||||
Samba now (optionally in the main code, required for the KDC) uses the
|
||||
krb5_log_facility from Heimdal. This allows us to redirect the
|
||||
warnings and status from the KDC (and client/server kerberos code) to
|
||||
Samba's DEBUG() system.
|
||||
|
||||
Similarly important is the Heimdal-specific krb5_get_error_string()
|
||||
function, which does a lot to reduce the 'administrator pain' level,
|
||||
by providing specific, english text-string error messages instead of
|
||||
just error code translations.
|
||||
|
||||
|
||||
Short name rules
|
||||
----------------
|
||||
|
||||
Samba is highly likely to be misconfigured, in many weird and
|
||||
interesting ways. As such, we have a patch for Heimdal that avoids
|
||||
DNS lookups on names without a . in them. This should avoid some
|
||||
delay and root server load.
|
||||
|
||||
PAC Correctness
|
||||
---------------
|
||||
|
||||
We now put the PAC into the TGT, not just the service ticket.
|
||||
|
||||
Forwarded tickets
|
||||
-----------------
|
||||
|
||||
We extract forwarded tickets from the GSSAPI layer, and put
|
||||
them into the credentials. We can then use them for proxy work.
|
||||
|
||||
|
||||
Kerberos TODO
|
||||
=============
|
||||
|
||||
(Feel free to contribute to any of these tasks, or ask
|
||||
abartlet@samba.org about them).
|
||||
|
||||
Lockout Control
|
||||
--------------
|
||||
|
||||
We need to get (either if PADL publishes their patch, or write our
|
||||
own) access control hooks in the Heimdal KDC. We need to lockout
|
||||
accounts, and perform other controls.
|
||||
|
||||
Gssmonger
|
||||
---------
|
||||
|
||||
Microsoft has released a testsuite called gssmonger, which tests
|
||||
interop. We should compile it against lorikeet-heimdal, MIT and see
|
||||
if we can build a 'Samba4' server for it.
|
||||
|
||||
Kpasswd server
|
||||
--------------
|
||||
|
||||
I have a partial kpasswd server which needs finishing, and a we need a
|
||||
client testsuite written, either via the krb5 API or directly against
|
||||
GENSEC and the ASN.1 routines.
|
||||
|
||||
Currently it only works for Heimdal, not MIT clients. This may be due
|
||||
to call ordering constraints.
|
||||
|
||||
|
||||
Correct TCP support
|
||||
-------------------
|
||||
|
||||
Our current TCP support does not send back 'too large' error messages
|
||||
if the high bit is set. This is needed for a proposed extension
|
||||
mechanism, but is likewise unsupported in both current Heimdal and MIT.
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
kerberos utility library
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Remus Koos 2001
|
||||
Copyright (C) Nalin Dahyabhai 2004.
|
||||
Copyright (C) Jeremy Allison 2004.
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "roken.h"
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
|
||||
/*
|
||||
simulate a kinit, putting the tgt in the given credentials cache.
|
||||
Orignally by remus@snapserver.com
|
||||
|
||||
This version is built to use a keyblock, rather than needing the
|
||||
original password.
|
||||
*/
|
||||
int kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, krb5_keyblock *keyblock,
|
||||
time_t *expire_time, time_t *kdc_time)
|
||||
{
|
||||
krb5_error_code code = 0;
|
||||
krb5_creds my_creds;
|
||||
krb5_get_init_creds_opt options;
|
||||
|
||||
krb5_get_init_creds_opt_init(&options);
|
||||
|
||||
krb5_get_init_creds_opt_set_default_flags(ctx, NULL, NULL, &options);
|
||||
|
||||
if ((code = krb5_get_init_creds_keyblock(ctx, &my_creds, principal, keyblock,
|
||||
0, NULL, &options))) {
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_initialize(ctx, cc, principal))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if (expire_time) {
|
||||
*expire_time = (time_t) my_creds.times.endtime;
|
||||
}
|
||||
|
||||
if (kdc_time) {
|
||||
*kdc_time = (time_t) my_creds.times.starttime;
|
||||
}
|
||||
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
simulate a kinit, putting the tgt in the given credentials cache.
|
||||
Orignally by remus@snapserver.com
|
||||
*/
|
||||
int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, const char *password,
|
||||
time_t *expire_time, time_t *kdc_time)
|
||||
{
|
||||
krb5_error_code code = 0;
|
||||
krb5_creds my_creds;
|
||||
krb5_get_init_creds_opt options;
|
||||
|
||||
krb5_get_init_creds_opt_init(&options);
|
||||
|
||||
krb5_get_init_creds_opt_set_default_flags(ctx, NULL, NULL, &options);
|
||||
|
||||
if ((code = krb5_get_init_creds_password(ctx, &my_creds, principal, password,
|
||||
NULL,
|
||||
NULL, 0, NULL, &options))) {
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_initialize(ctx, cc, principal))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if (expire_time) {
|
||||
*expire_time = (time_t) my_creds.times.endtime;
|
||||
}
|
||||
|
||||
if (kdc_time) {
|
||||
*kdc_time = (time_t) my_creds.times.starttime;
|
||||
}
|
||||
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5 routines for active directory
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if defined(HAVE_KRB5)
|
||||
|
||||
#include "auth/kerberos/krb5_init_context.h"
|
||||
#include "librpc/gen_ndr/krb5pac.h"
|
||||
|
||||
struct auth_serversupplied_info;
|
||||
struct cli_credentials;
|
||||
|
||||
struct ccache_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_ccache ccache;
|
||||
};
|
||||
|
||||
struct keytab_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_keytab keytab;
|
||||
};
|
||||
|
||||
/* not really ASN.1, but RFC 1964 */
|
||||
#define TOK_ID_KRB_AP_REQ ((const uint8_t *)"\x01\x00")
|
||||
#define TOK_ID_KRB_AP_REP ((const uint8_t *)"\x02\x00")
|
||||
#define TOK_ID_KRB_ERROR ((const uint8_t *)"\x03\x00")
|
||||
#define TOK_ID_GSS_GETMIC ((const uint8_t *)"\x01\x01")
|
||||
#define TOK_ID_GSS_WRAP ((const uint8_t *)"\x02\x01")
|
||||
|
||||
#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
|
||||
#define KRB5_KEY_TYPE(k) ((k)->keytype)
|
||||
#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length)
|
||||
#define KRB5_KEY_DATA(k) ((k)->keyvalue.data)
|
||||
#else
|
||||
#define KRB5_KEY_TYPE(k) ((k)->enctype)
|
||||
#define KRB5_KEY_LENGTH(k) ((k)->length)
|
||||
#define KRB5_KEY_DATA(k) ((k)->contents)
|
||||
#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
|
||||
|
||||
#ifndef HAVE_KRB5_SET_REAL_TIME
|
||||
krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES
|
||||
krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
|
||||
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_KRB5_FREE_UNPARSED_NAME
|
||||
void krb5_free_unparsed_name(krb5_context ctx, char *val);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
|
||||
const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
|
||||
#endif
|
||||
|
||||
/* Samba wrapper function for krb5 functionality. */
|
||||
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
|
||||
int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
|
||||
int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
|
||||
krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
|
||||
krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
|
||||
void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes);
|
||||
BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote);
|
||||
krb5_error_code ads_krb5_mk_req(krb5_context context,
|
||||
krb5_auth_context *auth_context,
|
||||
const krb5_flags ap_req_options,
|
||||
const char *principal,
|
||||
krb5_ccache ccache,
|
||||
krb5_data *outbuf);
|
||||
BOOL get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt);
|
||||
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_auth_context *auth_context,
|
||||
struct cli_credentials *machine_account,
|
||||
const char *service,
|
||||
const DATA_BLOB *enc_ticket,
|
||||
krb5_ticket **tkt,
|
||||
DATA_BLOB *ap_rep,
|
||||
krb5_keyblock **keyblock);
|
||||
int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, const char *password,
|
||||
time_t *expire_time, time_t *kdc_time);
|
||||
int kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, krb5_keyblock *keyblock,
|
||||
time_t *expire_time, time_t *kdc_time);
|
||||
krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
|
||||
krb5_principal host_princ,
|
||||
int enctype);
|
||||
void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype);
|
||||
BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2);
|
||||
void kerberos_free_data_contents(krb5_context context, krb5_data *pdata);
|
||||
krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry);
|
||||
char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx);
|
||||
krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_ccache ccache);
|
||||
krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *princ);
|
||||
NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA **pac_data_out,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret);
|
||||
NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_LOGON_INFO **logon_info,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret);
|
||||
krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA *pac_data,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
DATA_BLOB *pac);
|
||||
krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
DATA_BLOB *pac);
|
||||
|
||||
#include "auth/kerberos/proto.h"
|
||||
|
||||
#endif /* HAVE_KRB5 */
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This file for code taken from the Heimdal code, to preserve licence */
|
||||
/* Modified by Andrew Bartlett <abartlet@samba.org> */
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
|
||||
/* Taken from accept_sec_context.c,v 1.65 */
|
||||
krb5_error_code smb_rd_req_return_stuff(krb5_context context,
|
||||
krb5_auth_context *auth_context,
|
||||
const krb5_data *inbuf,
|
||||
krb5_keytab keytab,
|
||||
krb5_principal acceptor_principal,
|
||||
krb5_data *outbuf,
|
||||
krb5_ticket **ticket,
|
||||
krb5_keyblock **keyblock)
|
||||
{
|
||||
krb5_rd_req_in_ctx in = NULL;
|
||||
krb5_rd_req_out_ctx out = NULL;
|
||||
krb5_error_code kret;
|
||||
|
||||
*keyblock = NULL;
|
||||
*ticket = NULL;
|
||||
outbuf->length = 0;
|
||||
outbuf->data = NULL;
|
||||
|
||||
kret = krb5_rd_req_in_ctx_alloc(context, &in);
|
||||
if (kret == 0)
|
||||
kret = krb5_rd_req_in_set_keytab(context, in, keytab);
|
||||
if (kret) {
|
||||
if (in)
|
||||
krb5_rd_req_in_ctx_free(context, in);
|
||||
return kret;
|
||||
}
|
||||
|
||||
kret = krb5_rd_req_ctx(context,
|
||||
auth_context,
|
||||
inbuf,
|
||||
acceptor_principal,
|
||||
in, &out);
|
||||
krb5_rd_req_in_ctx_free(context, in);
|
||||
if (kret) {
|
||||
return kret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to remember some data on the context_handle.
|
||||
*/
|
||||
kret = krb5_rd_req_out_get_ticket(context, out,
|
||||
ticket);
|
||||
if (kret == 0) {
|
||||
kret = krb5_rd_req_out_get_keyblock(context, out,
|
||||
keyblock);
|
||||
}
|
||||
krb5_rd_req_out_ctx_free(context, out);
|
||||
|
||||
if (kret == 0) {
|
||||
kret = krb5_mk_rep(context, *auth_context, outbuf);
|
||||
}
|
||||
|
||||
if (kret) {
|
||||
krb5_free_ticket(context, *ticket);
|
||||
krb5_free_keyblock(context, *keyblock);
|
||||
krb5_data_free(outbuf);
|
||||
}
|
||||
|
||||
return kret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Create and parse the krb5 PAC
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
Copyright (C) Stefan Metzmacher 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "librpc/gen_ndr/ndr_krb5pac.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "auth/auth_sam.h"
|
||||
|
||||
static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB pac_data,
|
||||
struct PAC_SIGNATURE_DATA *sig,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *keyblock)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_crypto crypto;
|
||||
Checksum cksum;
|
||||
|
||||
cksum.cksumtype = (CKSUMTYPE)sig->type;
|
||||
cksum.checksum.length = sig->signature.length;
|
||||
cksum.checksum.data = sig->signature.data;
|
||||
|
||||
ret = krb5_crypto_init(context,
|
||||
keyblock,
|
||||
0,
|
||||
&crypto);
|
||||
if (ret) {
|
||||
DEBUG(0,("krb5_crypto_init() failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
return ret;
|
||||
}
|
||||
ret = krb5_verify_checksum(context,
|
||||
crypto,
|
||||
KRB5_KU_OTHER_CKSUM,
|
||||
pac_data.data,
|
||||
pac_data.length,
|
||||
&cksum);
|
||||
krb5_crypto_destroy(context, crypto);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA **pac_data_out,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
NTSTATUS status;
|
||||
struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
|
||||
struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
|
||||
struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
|
||||
struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
|
||||
struct PAC_LOGON_INFO *logon_info = NULL;
|
||||
struct PAC_LOGON_NAME *logon_name = NULL;
|
||||
struct PAC_DATA *pac_data;
|
||||
struct PAC_DATA_RAW *pac_data_raw;
|
||||
|
||||
DATA_BLOB *srv_sig_blob = NULL;
|
||||
DATA_BLOB *kdc_sig_blob = NULL;
|
||||
|
||||
DATA_BLOB modified_pac_blob;
|
||||
NTTIME tgs_authtime_nttime;
|
||||
krb5_principal client_principal_pac;
|
||||
int i;
|
||||
|
||||
krb5_clear_error_string(context);
|
||||
|
||||
if (k5ret) {
|
||||
*k5ret = KRB5_PARSE_MALFORMED;
|
||||
}
|
||||
|
||||
pac_data = talloc(mem_ctx, struct PAC_DATA);
|
||||
pac_data_raw = talloc(mem_ctx, struct PAC_DATA_RAW);
|
||||
kdc_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
|
||||
srv_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
|
||||
if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
|
||||
if (k5ret) {
|
||||
*k5ret = ENOMEM;
|
||||
}
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = ndr_pull_struct_blob(&blob, pac_data, pac_data,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the PAC\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
if (pac_data->num_buffers < 4) {
|
||||
/* we need logon_ingo, service_key and kdc_key */
|
||||
DEBUG(0,("less than 4 PAC buffers\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
status = ndr_pull_struct_blob(&blob, pac_data_raw, pac_data_raw,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the PAC\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
if (pac_data_raw->num_buffers < 4) {
|
||||
/* we need logon_ingo, service_key and kdc_key */
|
||||
DEBUG(0,("less than 4 PAC buffers\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (pac_data->num_buffers != pac_data_raw->num_buffers) {
|
||||
/* we need logon_ingo, service_key and kdc_key */
|
||||
DEBUG(0,("misparse! PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n",
|
||||
pac_data->num_buffers, pac_data_raw->num_buffers));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) {
|
||||
DEBUG(0,("misparse! PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n",
|
||||
i, pac_data->buffers[i].type, pac_data->buffers[i].type));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
switch (pac_data->buffers[i].type) {
|
||||
case PAC_TYPE_LOGON_INFO:
|
||||
if (!pac_data->buffers[i].info) {
|
||||
break;
|
||||
}
|
||||
logon_info = pac_data->buffers[i].info->logon_info.info;
|
||||
break;
|
||||
case PAC_TYPE_SRV_CHECKSUM:
|
||||
if (!pac_data->buffers[i].info) {
|
||||
break;
|
||||
}
|
||||
srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum;
|
||||
srv_sig_blob = &pac_data_raw->buffers[i].info->remaining;
|
||||
break;
|
||||
case PAC_TYPE_KDC_CHECKSUM:
|
||||
if (!pac_data->buffers[i].info) {
|
||||
break;
|
||||
}
|
||||
kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum;
|
||||
kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining;
|
||||
break;
|
||||
case PAC_TYPE_LOGON_NAME:
|
||||
logon_name = &pac_data->buffers[i].info->logon_name;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!logon_info) {
|
||||
DEBUG(0,("PAC no logon_info\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!logon_name) {
|
||||
DEBUG(0,("PAC no logon_name\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!srv_sig_ptr || !srv_sig_blob) {
|
||||
DEBUG(0,("PAC no srv_key\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!kdc_sig_ptr || !kdc_sig_blob) {
|
||||
DEBUG(0,("PAC no kdc_key\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Find and zero out the signatures, as required by the signing algorithm */
|
||||
|
||||
/* We find the data blobs above, now we parse them to get at the exact portion we should zero */
|
||||
status = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe, kdc_sig_wipe,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the KDC signature\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
status = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe, srv_sig_wipe,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the SRV signature\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Now zero the decoded structure */
|
||||
memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length);
|
||||
memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length);
|
||||
|
||||
/* and reencode, back into the same place it came from */
|
||||
status = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw, kdc_sig_wipe,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't repack the KDC signature\n"));
|
||||
return status;
|
||||
}
|
||||
status = ndr_push_struct_blob(srv_sig_blob, pac_data_raw, srv_sig_wipe,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't repack the SRV signature\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* push out the whole structure, but now with zero'ed signatures */
|
||||
status = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw, pac_data_raw,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't repack the RAW PAC\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* verify by service_key */
|
||||
ret = check_pac_checksum(mem_ctx,
|
||||
modified_pac_blob, srv_sig_ptr,
|
||||
context,
|
||||
service_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
if (k5ret) {
|
||||
*k5ret = ret;
|
||||
}
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (krbtgt_keyblock) {
|
||||
ret = check_pac_checksum(mem_ctx,
|
||||
srv_sig_ptr->signature, kdc_sig_ptr,
|
||||
context, krbtgt_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
if (k5ret) {
|
||||
*k5ret = ret;
|
||||
}
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert to NT time, so as not to loose accuracy in comparison */
|
||||
unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
|
||||
|
||||
if (tgs_authtime_nttime != logon_name->logon_time) {
|
||||
DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n"));
|
||||
DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time)));
|
||||
DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime)));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
ret = krb5_parse_name_flags(context, logon_name->account_name, KRB5_PRINCIPAL_PARSE_NO_REALM,
|
||||
&client_principal_pac);
|
||||
if (ret) {
|
||||
DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n",
|
||||
logon_name->account_name,
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
if (k5ret) {
|
||||
*k5ret = ret;
|
||||
}
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) {
|
||||
DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n",
|
||||
logon_name->account_name));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (strcasecmp(logon_info->info3.base.account_name.string,
|
||||
"Administrator")== 0) {
|
||||
file_save("tmp_pac_data-admin.dat",blob.data,blob.length);
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG(3,("Found account name from PAC: %s [%s]\n",
|
||||
logon_info->info3.base.account_name.string,
|
||||
logon_info->info3.base.full_name.string));
|
||||
*pac_data_out = pac_data;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_LOGON_INFO **logon_info,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct PAC_DATA *pac_data;
|
||||
int i;
|
||||
nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
|
||||
blob,
|
||||
context,
|
||||
krbtgt_keyblock,
|
||||
service_keyblock,
|
||||
client_principal,
|
||||
tgs_authtime,
|
||||
k5ret);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
*logon_info = NULL;
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
|
||||
continue;
|
||||
}
|
||||
*logon_info = pac_data->buffers[i].info->logon_info.info;
|
||||
}
|
||||
if (!*logon_info) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *pac_data,
|
||||
struct PAC_SIGNATURE_DATA *sig,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *keyblock)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_crypto crypto;
|
||||
Checksum cksum;
|
||||
|
||||
|
||||
ret = krb5_crypto_init(context,
|
||||
keyblock,
|
||||
0,
|
||||
&crypto);
|
||||
if (ret) {
|
||||
DEBUG(0,("krb5_crypto_init() failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
return ret;
|
||||
}
|
||||
ret = krb5_create_checksum(context,
|
||||
crypto,
|
||||
KRB5_KU_OTHER_CKSUM,
|
||||
0,
|
||||
pac_data->data,
|
||||
pac_data->length,
|
||||
&cksum);
|
||||
if (ret) {
|
||||
DEBUG(2, ("PAC Verification failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
}
|
||||
|
||||
krb5_crypto_destroy(context, crypto);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sig->type = cksum.cksumtype;
|
||||
sig->signature = data_blob_talloc(mem_ctx, cksum.checksum.data, cksum.checksum.length);
|
||||
free_Checksum(&cksum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA *pac_data,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
DATA_BLOB *pac)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
krb5_error_code ret;
|
||||
DATA_BLOB zero_blob = data_blob(NULL, 0);
|
||||
DATA_BLOB tmp_blob = data_blob(NULL, 0);
|
||||
struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
|
||||
struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
|
||||
int i;
|
||||
|
||||
/* First, just get the keytypes filled in (and lengths right, eventually) */
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
|
||||
continue;
|
||||
}
|
||||
kdc_checksum = &pac_data->buffers[i].info->kdc_cksum,
|
||||
ret = make_pac_checksum(mem_ctx, &zero_blob,
|
||||
kdc_checksum,
|
||||
context, krbtgt_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
|
||||
continue;
|
||||
}
|
||||
srv_checksum = &pac_data->buffers[i].info->srv_cksum;
|
||||
ret = make_pac_checksum(mem_ctx, &zero_blob,
|
||||
srv_checksum,
|
||||
context, service_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(2, ("making service PAC checksum failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kdc_checksum) {
|
||||
DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
|
||||
return EINVAL;
|
||||
}
|
||||
if (!srv_checksum) {
|
||||
DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* But wipe out the actual signatures */
|
||||
memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
|
||||
memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
|
||||
|
||||
nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_DATA);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(pac_data);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Then sign the result of the previous push, where the sig was zero'ed out */
|
||||
ret = make_pac_checksum(mem_ctx, &tmp_blob, srv_checksum,
|
||||
context, service_keyblock);
|
||||
|
||||
/* Then sign Server checksum */
|
||||
ret = make_pac_checksum(mem_ctx, &srv_checksum->signature, kdc_checksum, context, krbtgt_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* And push it out again, this time to the world. This relies on determanistic pointer values */
|
||||
nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_DATA);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(pac_data);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
*pac = tmp_blob;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
DATA_BLOB *pac)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
krb5_error_code ret;
|
||||
struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
|
||||
struct netr_SamInfo3 *sam3;
|
||||
union PAC_INFO *u_LOGON_INFO;
|
||||
struct PAC_LOGON_INFO *LOGON_INFO;
|
||||
union PAC_INFO *u_LOGON_NAME;
|
||||
struct PAC_LOGON_NAME *LOGON_NAME;
|
||||
union PAC_INFO *u_KDC_CHECKSUM;
|
||||
union PAC_INFO *u_SRV_CHECKSUM;
|
||||
|
||||
char *name;
|
||||
|
||||
enum {
|
||||
PAC_BUF_LOGON_INFO = 0,
|
||||
PAC_BUF_LOGON_NAME = 1,
|
||||
PAC_BUF_SRV_CHECKSUM = 2,
|
||||
PAC_BUF_KDC_CHECKSUM = 3,
|
||||
PAC_BUF_NUM_BUFFERS = 4
|
||||
};
|
||||
|
||||
if (!pac_data) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
|
||||
pac_data->version = 0;
|
||||
|
||||
pac_data->buffers = talloc_array(pac_data,
|
||||
struct PAC_BUFFER,
|
||||
pac_data->num_buffers);
|
||||
if (!pac_data->buffers) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* LOGON_INFO */
|
||||
u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_LOGON_INFO) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
|
||||
pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
|
||||
|
||||
/* LOGON_NAME */
|
||||
u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_LOGON_NAME) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
|
||||
pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
|
||||
LOGON_NAME = &u_LOGON_NAME->logon_name;
|
||||
|
||||
/* SRV_CHECKSUM */
|
||||
u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_SRV_CHECKSUM) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
|
||||
pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
|
||||
|
||||
/* KDC_CHECKSUM */
|
||||
u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_KDC_CHECKSUM) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
|
||||
pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
|
||||
|
||||
/* now the real work begins... */
|
||||
|
||||
LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
|
||||
if (!LOGON_INFO) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
nt_status = auth_convert_server_info_saminfo3(LOGON_INFO, server_info, &sam3);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(pac_data);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
u_LOGON_INFO->logon_info.info = LOGON_INFO;
|
||||
LOGON_INFO->info3 = *sam3;
|
||||
|
||||
ret = krb5_unparse_name_flags(context, client_principal,
|
||||
KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
LOGON_NAME->account_name = talloc_strdup(LOGON_NAME, name);
|
||||
free(name);
|
||||
/*
|
||||
this logon_time field is absolutely critical. This is what
|
||||
caused all our PAC troubles :-)
|
||||
*/
|
||||
unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
|
||||
|
||||
ret = kerberos_encode_pac(mem_ctx,
|
||||
pac_data,
|
||||
context,
|
||||
krbtgt_keyblock,
|
||||
service_keyblock,
|
||||
pac);
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Kerberos utility functions for GENSEC
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
struct principal_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_principal principal;
|
||||
};
|
||||
|
||||
static int free_principal(struct principal_container *pc)
|
||||
{
|
||||
/* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
|
||||
krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *salt_princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *machine_username;
|
||||
char *salt_body;
|
||||
char *lower_realm;
|
||||
const char *salt_principal;
|
||||
struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
salt_principal = cli_credentials_get_salt_principal(machine_account);
|
||||
if (salt_principal) {
|
||||
ret = krb5_parse_name(smb_krb5_context->krb5_context, salt_principal, salt_princ);
|
||||
} else {
|
||||
machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
|
||||
|
||||
if (!machine_username) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (machine_username[strlen(machine_username)-1] == '$') {
|
||||
machine_username[strlen(machine_username)-1] = '\0';
|
||||
}
|
||||
lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
|
||||
if (!lower_realm) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username,
|
||||
lower_realm);
|
||||
if (!salt_body) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ,
|
||||
cli_credentials_get_realm(machine_account),
|
||||
"host", salt_body, NULL);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
/* This song-and-dance effectivly puts the principal
|
||||
* into talloc, so we can't loose it. */
|
||||
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
|
||||
mem_ctx->principal = *salt_princ;
|
||||
talloc_set_destructor(mem_ctx, free_principal);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Obtain the principal set on this context. Requires a
|
||||
* smb_krb5_context because we are doing krb5 principal parsing with
|
||||
* the library routines. The returned princ is placed in the talloc
|
||||
* system by means of a destructor (do *not* free). */
|
||||
|
||||
krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *princ_string;
|
||||
struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
princ_string = cli_credentials_get_principal(credentials, mem_ctx);
|
||||
|
||||
/* A NULL here has meaning, as the gssapi server case will
|
||||
* then use the principal from the client */
|
||||
if (!princ_string) {
|
||||
talloc_free(mem_ctx);
|
||||
princ = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = krb5_parse_name(smb_krb5_context->krb5_context,
|
||||
princ_string, princ);
|
||||
|
||||
if (ret == 0) {
|
||||
/* This song-and-dance effectivly puts the principal
|
||||
* into talloc, so we can't loose it. */
|
||||
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
|
||||
mem_ctx->principal = *princ;
|
||||
talloc_set_destructor(mem_ctx, free_principal);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a freshly allocated ccache (destroyed by destructor on child
|
||||
* of parent_ctx), for a given set of client credentials
|
||||
*/
|
||||
|
||||
krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_ccache ccache)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *password;
|
||||
time_t kdc_time = 0;
|
||||
krb5_principal princ;
|
||||
int tries;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
password = cli_credentials_get_password(credentials);
|
||||
|
||||
tries = 2;
|
||||
while (tries--) {
|
||||
if (password) {
|
||||
ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache,
|
||||
princ,
|
||||
password, NULL, &kdc_time);
|
||||
} else {
|
||||
/* No password available, try to use a keyblock instead */
|
||||
|
||||
krb5_keyblock keyblock;
|
||||
const struct samr_Password *mach_pwd;
|
||||
mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
|
||||
if (!mach_pwd) {
|
||||
talloc_free(mem_ctx);
|
||||
DEBUG(1, ("kinit_to_ccache: No password available for kinit\n"));
|
||||
return EINVAL;
|
||||
}
|
||||
ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
|
||||
ETYPE_ARCFOUR_HMAC_MD5,
|
||||
mach_pwd->hash, sizeof(mach_pwd->hash),
|
||||
&keyblock);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache,
|
||||
princ,
|
||||
&keyblock, NULL, &kdc_time);
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
|
||||
/* Perhaps we have been given an invalid skew, so try again without it */
|
||||
time_t t = time(NULL);
|
||||
krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
|
||||
} else {
|
||||
/* not a skew problem */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
|
||||
DEBUG(1,("kinit for %s failed (%s)\n",
|
||||
cli_credentials_get_principal(credentials, mem_ctx),
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* cope with ticket being in the future due to clock skew */
|
||||
if ((unsigned)kdc_time > time(NULL)) {
|
||||
time_t t = time(NULL);
|
||||
int time_offset =(unsigned)kdc_time-t;
|
||||
DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
|
||||
krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
|
||||
}
|
||||
|
||||
if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
|
||||
ret = kinit_to_ccache(parent_ctx,
|
||||
credentials,
|
||||
smb_krb5_context,
|
||||
ccache);
|
||||
}
|
||||
if (ret) {
|
||||
DEBUG(1,("kinit for %s failed (%s)\n",
|
||||
cli_credentials_get_principal(credentials, mem_ctx),
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int free_keytab(struct keytab_container *ktc)
|
||||
{
|
||||
krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smb_krb5_open_keytab(TALLOC_CTX *mem_ctx,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
const char *keytab_name, struct keytab_container **ktc)
|
||||
{
|
||||
krb5_keytab keytab;
|
||||
int ret;
|
||||
ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to open krb5 keytab: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
*ktc = talloc(mem_ctx, struct keytab_container);
|
||||
if (!*ktc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
(*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
|
||||
(*ktc)->keytab = keytab;
|
||||
talloc_set_destructor(*ktc, free_keytab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct enctypes_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_enctype *enctypes;
|
||||
};
|
||||
|
||||
static int free_enctypes(struct enctypes_container *etc)
|
||||
{
|
||||
free_kerberos_etypes(etc->smb_krb5_context->krb5_context, etc->enctypes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
|
||||
const char *princ_string,
|
||||
krb5_principal princ,
|
||||
krb5_principal salt_princ,
|
||||
int kvno,
|
||||
const char *password_s,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab keytab)
|
||||
{
|
||||
int i;
|
||||
krb5_error_code ret;
|
||||
krb5_enctype *enctypes;
|
||||
char *enctype_string;
|
||||
struct enctypes_container *etc;
|
||||
krb5_data password;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
etc = talloc(mem_ctx, struct enctypes_container);
|
||||
if (!etc) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context,
|
||||
&enctypes);
|
||||
if (ret != 0) {
|
||||
DEBUG(1,("keytab_add_keys: getting encrption types failed (%s)\n",
|
||||
error_message(ret)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
etc->smb_krb5_context = talloc_reference(etc, smb_krb5_context);
|
||||
etc->enctypes = enctypes;
|
||||
|
||||
talloc_set_destructor(etc, free_enctypes);
|
||||
|
||||
password.data = discard_const_p(char *, password_s);
|
||||
password.length = strlen(password_s);
|
||||
|
||||
for (i=0; enctypes[i]; i++) {
|
||||
krb5_keytab_entry entry;
|
||||
ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context,
|
||||
salt_princ, &password, &entry.keyblock, enctypes[i]);
|
||||
if (ret != 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
entry.principal = princ;
|
||||
entry.vno = kvno;
|
||||
ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
|
||||
enctype_string = NULL;
|
||||
krb5_enctype_to_string(smb_krb5_context->krb5_context, enctypes[i], &enctype_string);
|
||||
if (ret != 0) {
|
||||
DEBUG(1, ("Failed to add %s entry for %s(kvno %d) to keytab: %s\n",
|
||||
enctype_string,
|
||||
princ_string,
|
||||
kvno,
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
free(enctype_string);
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
|
||||
princ_string, kvno,
|
||||
enctype_string));
|
||||
free(enctype_string);
|
||||
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab keytab,
|
||||
BOOL add_old)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *password_s;
|
||||
char *enctype_string;
|
||||
const char *old_secret;
|
||||
int kvno;
|
||||
krb5_principal salt_princ;
|
||||
krb5_principal princ;
|
||||
const char *princ_string;
|
||||
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
|
||||
/* Get the principal we will store the new keytab entries under */
|
||||
ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The salt used to generate these entries may be different however, fetch that */
|
||||
ret = salt_principal_from_credentials(mem_ctx, machine_account,
|
||||
smb_krb5_context,
|
||||
&salt_princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Finally, do the dance to get the password to put in the entry */
|
||||
password_s = cli_credentials_get_password(machine_account);
|
||||
if (!password_s) {
|
||||
/* If we don't have the plaintext password, try for
|
||||
* the MD4 password hash */
|
||||
|
||||
krb5_keytab_entry entry;
|
||||
const struct samr_Password *mach_pwd;
|
||||
mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
|
||||
if (!mach_pwd) {
|
||||
DEBUG(1, ("create_keytab: Domain trust informaton for account %s not available\n",
|
||||
cli_credentials_get_principal(machine_account, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return EINVAL;
|
||||
}
|
||||
ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
|
||||
ETYPE_ARCFOUR_HMAC_MD5,
|
||||
mach_pwd->hash, sizeof(mach_pwd->hash),
|
||||
&entry.keyblock);
|
||||
if (ret) {
|
||||
DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
entry.principal = princ;
|
||||
entry.vno = cli_credentials_get_kvno(machine_account);
|
||||
ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s",
|
||||
cli_credentials_get_principal(machine_account, mem_ctx),
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_enctype_to_string(smb_krb5_context->krb5_context,
|
||||
ETYPE_ARCFOUR_HMAC_MD5,
|
||||
&enctype_string);
|
||||
DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
|
||||
cli_credentials_get_principal(machine_account, mem_ctx),
|
||||
cli_credentials_get_kvno(machine_account),
|
||||
enctype_string));
|
||||
free(enctype_string);
|
||||
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
|
||||
/* Can't go any further, we only have this one key */
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvno = cli_credentials_get_kvno(machine_account);
|
||||
/* good, we actually have the real plaintext */
|
||||
ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
|
||||
kvno, password_s, smb_krb5_context, keytab);
|
||||
if (!ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!add_old || kvno == 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
old_secret = cli_credentials_get_old_password(machine_account);
|
||||
if (!old_secret) {
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
|
||||
kvno - 1, old_secret, smb_krb5_context, keytab);
|
||||
if (!ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Walk the keytab, looking for entries of this principal name, with KVNO other than current kvno -1.
|
||||
*
|
||||
* These entries are now stale, we only keep the current, and previous entries around.
|
||||
*
|
||||
* Inspired by the code in Samba3 for 'use kerberos keytab'.
|
||||
*
|
||||
*/
|
||||
|
||||
static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab keytab, BOOL *found_previous)
|
||||
{
|
||||
krb5_error_code ret, ret2;
|
||||
krb5_kt_cursor cursor;
|
||||
krb5_principal princ;
|
||||
int kvno;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
const char *princ_string;
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
*found_previous = False;
|
||||
princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
|
||||
|
||||
/* Get the principal we will store the new keytab entries under */
|
||||
ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kvno = cli_credentials_get_kvno(machine_account);
|
||||
|
||||
/* for each entry in the keytab */
|
||||
ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case HEIM_ERR_OPNOTSUPP:
|
||||
case ENOENT:
|
||||
case KRB5_KT_END:
|
||||
/* no point enumerating if there isn't anything here */
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
default:
|
||||
DEBUG(1,("failed to open keytab for read of old entries: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (!ret) {
|
||||
krb5_keytab_entry entry;
|
||||
ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
/* if it matches our principal */
|
||||
if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
|
||||
/* Free the entry, it wasn't the one we were looking for anyway */
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* delete it, if it is not kvno -1 */
|
||||
if (entry.vno != (kvno - 1 )) {
|
||||
/* Release the enumeration. We are going to
|
||||
* have to start this from the top again,
|
||||
* because deletes during enumeration may not
|
||||
* always be consistant.
|
||||
*
|
||||
* Also, the enumeration locks a FILE: keytab
|
||||
*/
|
||||
|
||||
krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
|
||||
ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
|
||||
/* Deleted: Restart from the top */
|
||||
ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
if (ret2) {
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
DEBUG(1,("failed to restart enumeration of keytab: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return ret2;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
*found_previous = True;
|
||||
}
|
||||
|
||||
/* Free the entry, we don't need it any more */
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
|
||||
|
||||
}
|
||||
krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case ENOENT:
|
||||
case KRB5_KT_END:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
|
||||
princ_string,
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
struct keytab_container *keytab_container)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
BOOL found_previous;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = remove_old_entries(mem_ctx, machine_account,
|
||||
smb_krb5_context, keytab_container->keytab, &found_previous);
|
||||
if (ret != 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Create a new keytab. If during the cleanout we found
|
||||
* entires for kvno -1, then don't try and duplicate them.
|
||||
* Otherwise, add kvno, and kvno -1 */
|
||||
|
||||
ret = create_keytab(mem_ctx, machine_account, smb_krb5_context,
|
||||
keytab_container->keytab,
|
||||
found_previous ? False : True);
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
_PUBLIC_ int smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
struct keytab_container **keytab_container)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
const char *rand_string;
|
||||
const char *keytab_name;
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
*keytab_container = talloc(mem_ctx, struct keytab_container);
|
||||
|
||||
rand_string = generate_random_str(mem_ctx, 16);
|
||||
if (!rand_string) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s",
|
||||
rand_string);
|
||||
if (!keytab_name) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, keytab_name, keytab_container);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = smb_krb5_update_keytab(mem_ctx, machine_account, smb_krb5_context, *keytab_container);
|
||||
if (ret == 0) {
|
||||
talloc_steal(parent_ctx, *keytab_container);
|
||||
} else {
|
||||
*keytab_container = NULL;
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Wrapper for krb5_init_context
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "heimdal/lib/krb5/krb5_locl.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "roken.h"
|
||||
|
||||
/*
|
||||
context structure for operations on cldap packets
|
||||
*/
|
||||
struct smb_krb5_socket {
|
||||
struct socket_context *sock;
|
||||
|
||||
/* the fd event */
|
||||
struct fd_event *fde;
|
||||
|
||||
BOOL timeout;
|
||||
NTSTATUS status;
|
||||
DATA_BLOB request, reply, partial;
|
||||
|
||||
size_t partial_read;
|
||||
|
||||
krb5_krbhst_info *hi;
|
||||
};
|
||||
|
||||
static int smb_krb5_context_destroy_1(struct smb_krb5_context *ctx)
|
||||
{
|
||||
krb5_free_context(ctx->krb5_context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smb_krb5_context_destroy_2(struct smb_krb5_context *ctx)
|
||||
{
|
||||
/* Otherwise krb5_free_context will try and close what we have already free()ed */
|
||||
krb5_set_warn_dest(ctx->krb5_context, NULL);
|
||||
krb5_closelog(ctx->krb5_context, ctx->logf);
|
||||
smb_krb5_context_destroy_1(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We never close down the DEBUG system, and no need to unreference the use */
|
||||
static void smb_krb5_debug_close(void *private) {
|
||||
return;
|
||||
}
|
||||
|
||||
static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private)
|
||||
{
|
||||
DEBUG(2, ("Kerberos: %s\n", msg));
|
||||
}
|
||||
|
||||
/*
|
||||
handle recv events on a smb_krb5 socket
|
||||
*/
|
||||
static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
|
||||
{
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
|
||||
DATA_BLOB blob;
|
||||
size_t nread, dsize;
|
||||
|
||||
switch (smb_krb5->hi->proto) {
|
||||
case KRB5_KRBHST_UDP:
|
||||
smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
blob = data_blob_talloc(tmp_ctx, NULL, dsize);
|
||||
if (blob.data == NULL && dsize != 0) {
|
||||
smb_krb5->status = NT_STATUS_NO_MEMORY;
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
blob.length = nread;
|
||||
|
||||
if (nread == 0) {
|
||||
smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG(2,("Received smb_krb5 packet of length %d\n",
|
||||
(int)blob.length));
|
||||
|
||||
talloc_steal(smb_krb5, blob.data);
|
||||
smb_krb5->reply = blob;
|
||||
talloc_free(tmp_ctx);
|
||||
break;
|
||||
case KRB5_KRBHST_TCP:
|
||||
if (smb_krb5->partial.length == 0) {
|
||||
smb_krb5->partial = data_blob_talloc(smb_krb5, NULL, 4);
|
||||
if (!smb_krb5->partial.data) {
|
||||
smb_krb5->status = NT_STATUS_NO_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->partial_read = 0;
|
||||
}
|
||||
|
||||
/* read in the packet length */
|
||||
if (smb_krb5->partial_read < 4) {
|
||||
uint32_t packet_length;
|
||||
|
||||
smb_krb5->status = socket_recv(smb_krb5->sock,
|
||||
smb_krb5->partial.data + smb_krb5->partial_read,
|
||||
4 - smb_krb5->partial_read,
|
||||
&nread);
|
||||
/* todo: this should be converted to the packet_*() routines */
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->partial_read += nread;
|
||||
if (smb_krb5->partial_read != 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
packet_length = RIVAL(smb_krb5->partial.data, 0);
|
||||
|
||||
smb_krb5->partial.data = talloc_realloc(smb_krb5, smb_krb5->partial.data,
|
||||
uint8_t, packet_length + 4);
|
||||
if (!smb_krb5->partial.data) {
|
||||
smb_krb5->status = NT_STATUS_NO_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->partial.length = packet_length + 4;
|
||||
}
|
||||
|
||||
/* read in the body */
|
||||
smb_krb5->status = socket_recv(smb_krb5->sock,
|
||||
smb_krb5->partial.data + smb_krb5->partial_read,
|
||||
smb_krb5->partial.length - smb_krb5->partial_read,
|
||||
&nread);
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) return;
|
||||
|
||||
smb_krb5->partial_read += nread;
|
||||
|
||||
if (smb_krb5->partial_read != smb_krb5->partial.length) return;
|
||||
|
||||
smb_krb5->reply = data_blob_talloc(smb_krb5, smb_krb5->partial.data + 4, smb_krb5->partial.length - 4);
|
||||
break;
|
||||
case KRB5_KRBHST_HTTP:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
handle request timeouts
|
||||
*/
|
||||
static void smb_krb5_request_timeout(struct event_context *event_ctx,
|
||||
struct timed_event *te, struct timeval t,
|
||||
void *private)
|
||||
{
|
||||
struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
|
||||
DEBUG(5,("Timed out smb_krb5 packet\n"));
|
||||
smb_krb5->timeout = True;
|
||||
}
|
||||
|
||||
/*
|
||||
handle send events on a smb_krb5 socket
|
||||
*/
|
||||
static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
size_t len;
|
||||
|
||||
len = smb_krb5->request.length;
|
||||
status = socket_send(smb_krb5->sock, &smb_krb5->request, &len);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) return;
|
||||
|
||||
EVENT_FD_READABLE(smb_krb5->fde);
|
||||
|
||||
EVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handle fd events on a smb_krb5_socket
|
||||
*/
|
||||
static void smb_krb5_socket_handler(struct event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private)
|
||||
{
|
||||
struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
|
||||
if (flags & EVENT_FD_WRITE) {
|
||||
smb_krb5_socket_send(smb_krb5);
|
||||
}
|
||||
if (flags & EVENT_FD_READ) {
|
||||
smb_krb5_socket_recv(smb_krb5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
|
||||
void *data,
|
||||
krb5_krbhst_info *hi,
|
||||
const krb5_data *send_buf,
|
||||
krb5_data *recv_buf)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
NTSTATUS status;
|
||||
struct socket_address *remote_addr;
|
||||
const char *name;
|
||||
struct addrinfo *ai, *a;
|
||||
struct smb_krb5_socket *smb_krb5;
|
||||
|
||||
struct event_context *ev = talloc_get_type(data, struct event_context);
|
||||
|
||||
DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length);
|
||||
|
||||
ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (a = ai; a; a = ai->ai_next) {
|
||||
smb_krb5 = talloc(NULL, struct smb_krb5_socket);
|
||||
if (!smb_krb5) {
|
||||
return ENOMEM;
|
||||
}
|
||||
smb_krb5->hi = hi;
|
||||
|
||||
switch (a->ai_family) {
|
||||
case PF_INET:
|
||||
name = "ipv4";
|
||||
break;
|
||||
#ifdef HAVE_SOCKET_IPV6
|
||||
case PF_INET6:
|
||||
name = "ipv6";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
switch (hi->proto) {
|
||||
case KRB5_KRBHST_UDP:
|
||||
if (lp_parm_bool(-1, "krb5", "udp", True)) {
|
||||
status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
|
||||
}
|
||||
break;
|
||||
case KRB5_KRBHST_TCP:
|
||||
if (lp_parm_bool(-1, "krb5", "tcp", True)) {
|
||||
status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
|
||||
}
|
||||
break;
|
||||
case KRB5_KRBHST_HTTP:
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
talloc_steal(smb_krb5, smb_krb5->sock);
|
||||
|
||||
remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
|
||||
if (!remote_addr) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
talloc_free(remote_addr);
|
||||
|
||||
smb_krb5->fde = event_add_fd(ev, smb_krb5,
|
||||
socket_get_fd(smb_krb5->sock), 0,
|
||||
smb_krb5_socket_handler, smb_krb5);
|
||||
|
||||
event_add_timed(ev, smb_krb5,
|
||||
timeval_current_ofs(context->kdc_timeout, 0),
|
||||
smb_krb5_request_timeout, smb_krb5);
|
||||
|
||||
EVENT_FD_WRITEABLE(smb_krb5->fde);
|
||||
|
||||
switch (hi->proto) {
|
||||
case KRB5_KRBHST_UDP:
|
||||
smb_krb5->request = send_blob;
|
||||
break;
|
||||
case KRB5_KRBHST_TCP:
|
||||
smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
|
||||
RSIVAL(smb_krb5->request.data, 0, send_blob.length);
|
||||
memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
|
||||
break;
|
||||
case KRB5_KRBHST_HTTP:
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
smb_krb5->timeout = False;
|
||||
smb_krb5->status = NT_STATUS_OK;
|
||||
smb_krb5->reply = data_blob(NULL, 0);
|
||||
smb_krb5->partial = data_blob(NULL, 0);
|
||||
|
||||
while (!smb_krb5->timeout && (NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
|
||||
if (event_loop_once(ev) != 0) {
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status)));
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (smb_krb5->timeout) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
|
||||
if (ret) {
|
||||
talloc_free(smb_krb5);
|
||||
return ret;
|
||||
}
|
||||
talloc_free(smb_krb5);
|
||||
|
||||
break;
|
||||
}
|
||||
if (a) {
|
||||
return 0;
|
||||
}
|
||||
return KRB5_KDC_UNREACH;
|
||||
}
|
||||
|
||||
krb5_error_code smb_krb5_init_context(void *parent_ctx,
|
||||
struct smb_krb5_context **smb_krb5_context)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
struct event_context *ev;
|
||||
|
||||
initialize_krb5_error_table();
|
||||
|
||||
tmp_ctx = talloc_new(parent_ctx);
|
||||
*smb_krb5_context = talloc(tmp_ctx, struct smb_krb5_context);
|
||||
|
||||
if (!*smb_krb5_context || !tmp_ctx) {
|
||||
talloc_free(*smb_krb5_context);
|
||||
talloc_free(tmp_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = krb5_init_context(&(*smb_krb5_context)->krb5_context);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_init_context failed (%s)\n",
|
||||
error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_1);
|
||||
|
||||
if (lp_realm() && *lp_realm()) {
|
||||
char *upper_realm = strupper_talloc(tmp_ctx, lp_realm());
|
||||
if (!upper_realm) {
|
||||
DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm()));
|
||||
talloc_free(tmp_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
ret = krb5_set_default_realm((*smb_krb5_context)->krb5_context, upper_realm);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_set_default_realm failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Should we have a different name here? */
|
||||
ret = krb5_initlog((*smb_krb5_context)->krb5_context, "Samba", &(*smb_krb5_context)->logf);
|
||||
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_initlog failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_2);
|
||||
|
||||
ret = krb5_addlog_func((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf, 0 /* min */, -1 /* max */,
|
||||
smb_krb5_debug_wrapper, smb_krb5_debug_close, NULL);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_addlog_func failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
krb5_set_warn_dest((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf);
|
||||
|
||||
ev = event_context_find(*smb_krb5_context);
|
||||
/* Set use of our socket lib */
|
||||
ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
|
||||
smb_krb5_send_and_recv_func,
|
||||
ev);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*smb_krb5_context)->krb5_context->mem_ctx = *smb_krb5_context;
|
||||
|
||||
talloc_steal(parent_ctx, *smb_krb5_context);
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
/* Set options in kerberos */
|
||||
|
||||
krb5_set_dns_canonicalize_hostname((*smb_krb5_context)->krb5_context, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5 routines for active directory
|
||||
Copyright (C) Andrew Bartlett 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
struct smb_krb5_context {
|
||||
struct krb5_context_data *krb5_context;
|
||||
krb5_log_facility *logf;
|
||||
};
|
||||
|
||||
krb5_error_code smb_krb5_init_context(void *parent_ctx,
|
||||
struct smb_krb5_context **smb_krb5_context);
|
||||
void smb_krb5_free_context(struct smb_krb5_context *smb_krb5_context);
|
||||
|
||||
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
|
||||
void *data,
|
||||
krb5_krbhst_info *hi,
|
||||
const krb5_data *send_buf,
|
||||
krb5_data *recv_buf);
|
||||
@@ -0,0 +1,599 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
|
||||
Copyright (C) Gerald Carter 2003
|
||||
Copyright (C) Luke Kenneth Casson Leighton 1996-2000
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "librpc/gen_ndr/netlogon.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
|
||||
/****************************************************************************
|
||||
Core of smb password checking routine.
|
||||
****************************************************************************/
|
||||
|
||||
static BOOL smb_pwd_check_ntlmv1(TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *nt_response,
|
||||
const uint8_t *part_passwd,
|
||||
const DATA_BLOB *sec_blob,
|
||||
DATA_BLOB *user_sess_key)
|
||||
{
|
||||
/* Finish the encryption of part_passwd. */
|
||||
uint8_t p24[24];
|
||||
|
||||
if (part_passwd == NULL) {
|
||||
DEBUG(10,("No password set - DISALLOWING access\n"));
|
||||
/* No password set - always false ! */
|
||||
return False;
|
||||
}
|
||||
|
||||
if (sec_blob->length != 8) {
|
||||
DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect challenge size (%lu)\n",
|
||||
(unsigned long)sec_blob->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
if (nt_response->length != 24) {
|
||||
DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%lu)\n",
|
||||
(unsigned long)nt_response->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
SMBOWFencrypt(part_passwd, sec_blob->data, p24);
|
||||
|
||||
#if DEBUG_PASSWORD
|
||||
DEBUG(100,("Part password (P16) was |\n"));
|
||||
dump_data(100, part_passwd, 16);
|
||||
DEBUGADD(100,("Password from client was |\n"));
|
||||
dump_data(100, nt_response->data, nt_response->length);
|
||||
DEBUGADD(100,("Given challenge was |\n"));
|
||||
dump_data(100, sec_blob->data, sec_blob->length);
|
||||
DEBUGADD(100,("Value from encryption was |\n"));
|
||||
dump_data(100, p24, 24);
|
||||
#endif
|
||||
if (memcmp(p24, nt_response->data, 24) == 0) {
|
||||
if (user_sess_key != NULL) {
|
||||
*user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
SMBsesskeygen_ntv1(part_passwd, user_sess_key->data);
|
||||
}
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Core of smb password checking routine. (NTLMv2, LMv2)
|
||||
Note: The same code works with both NTLMv2 and LMv2.
|
||||
****************************************************************************/
|
||||
|
||||
static BOOL smb_pwd_check_ntlmv2(TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *ntv2_response,
|
||||
const uint8_t *part_passwd,
|
||||
const DATA_BLOB *sec_blob,
|
||||
const char *user, const char *domain,
|
||||
BOOL upper_case_domain, /* should the domain be transformed into upper case? */
|
||||
DATA_BLOB *user_sess_key)
|
||||
{
|
||||
/* Finish the encryption of part_passwd. */
|
||||
uint8_t kr[16];
|
||||
uint8_t value_from_encryption[16];
|
||||
DATA_BLOB client_key_data;
|
||||
|
||||
if (part_passwd == NULL) {
|
||||
DEBUG(10,("No password set - DISALLOWING access\n"));
|
||||
/* No password set - always False */
|
||||
return False;
|
||||
}
|
||||
|
||||
if (sec_blob->length != 8) {
|
||||
DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect challenge size (%lu)\n",
|
||||
(unsigned long)sec_blob->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
if (ntv2_response->length < 24) {
|
||||
/* We MUST have more than 16 bytes, or the stuff below will go
|
||||
crazy. No known implementation sends less than the 24 bytes
|
||||
for LMv2, let alone NTLMv2. */
|
||||
DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%lu)\n",
|
||||
(unsigned long)ntv2_response->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
|
||||
/*
|
||||
todo: should we be checking this for anything? We can't for LMv2,
|
||||
but for NTLMv2 it is meant to contain the current time etc.
|
||||
*/
|
||||
|
||||
if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
|
||||
|
||||
#if DEBUG_PASSWORD
|
||||
DEBUG(100,("Part password (P16) was |\n"));
|
||||
dump_data(100, part_passwd, 16);
|
||||
DEBUGADD(100,("Password from client was |\n"));
|
||||
dump_data(100, ntv2_response->data, ntv2_response->length);
|
||||
DEBUGADD(100,("Variable data from client was |\n"));
|
||||
dump_data(100, client_key_data.data, client_key_data.length);
|
||||
DEBUGADD(100,("Given challenge was |\n"));
|
||||
dump_data(100, sec_blob->data, sec_blob->length);
|
||||
DEBUGADD(100,("Value from encryption was |\n"));
|
||||
dump_data(100, value_from_encryption, 16);
|
||||
#endif
|
||||
data_blob_clear_free(&client_key_data);
|
||||
if (memcmp(value_from_encryption, ntv2_response->data, 16) == 0) {
|
||||
if (user_sess_key != NULL) {
|
||||
*user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
|
||||
}
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Core of smb password checking routine. (NTLMv2, LMv2)
|
||||
Note: The same code works with both NTLMv2 and LMv2.
|
||||
****************************************************************************/
|
||||
|
||||
static BOOL smb_sess_key_ntlmv2(TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *ntv2_response,
|
||||
const uint8_t *part_passwd,
|
||||
const DATA_BLOB *sec_blob,
|
||||
const char *user, const char *domain,
|
||||
BOOL upper_case_domain, /* should the domain be transformed into upper case? */
|
||||
DATA_BLOB *user_sess_key)
|
||||
{
|
||||
/* Finish the encryption of part_passwd. */
|
||||
uint8_t kr[16];
|
||||
uint8_t value_from_encryption[16];
|
||||
DATA_BLOB client_key_data;
|
||||
|
||||
if (part_passwd == NULL) {
|
||||
DEBUG(10,("No password set - DISALLOWING access\n"));
|
||||
/* No password set - always False */
|
||||
return False;
|
||||
}
|
||||
|
||||
if (sec_blob->length != 8) {
|
||||
DEBUG(0, ("smb_sess_key_ntlmv2: incorrect challenge size (%lu)\n",
|
||||
(unsigned long)sec_blob->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
if (ntv2_response->length < 24) {
|
||||
/* We MUST have more than 16 bytes, or the stuff below will go
|
||||
crazy. No known implementation sends less than the 24 bytes
|
||||
for LMv2, let alone NTLMv2. */
|
||||
DEBUG(0, ("smb_sess_key_ntlmv2: incorrect password length (%lu)\n",
|
||||
(unsigned long)ntv2_response->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
|
||||
|
||||
if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
|
||||
*user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare password hashes against those from the SAM
|
||||
*
|
||||
* @param mem_ctx talloc context
|
||||
* @param client_lanman LANMAN password hash, as supplied by the client
|
||||
* @param client_nt NT (MD4) password hash, as supplied by the client
|
||||
* @param username internal Samba username, for log messages
|
||||
* @param client_username username the client used
|
||||
* @param client_domain domain name the client used (may be mapped)
|
||||
* @param stored_lanman LANMAN password hash, as stored on the SAM
|
||||
* @param stored_nt NT (MD4) password hash, as stored on the SAM
|
||||
* @param user_sess_key User session key
|
||||
* @param lm_sess_key LM session key (first 8 bytes of the LM hash)
|
||||
*/
|
||||
|
||||
NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
|
||||
const struct samr_Password *client_lanman,
|
||||
const struct samr_Password *client_nt,
|
||||
const char *username,
|
||||
const struct samr_Password *stored_lanman,
|
||||
const struct samr_Password *stored_nt)
|
||||
{
|
||||
if (stored_nt == NULL) {
|
||||
DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
|
||||
username));
|
||||
}
|
||||
|
||||
if (client_nt && stored_nt) {
|
||||
if (memcmp(client_nt->hash, stored_nt->hash, sizeof(stored_nt->hash)) == 0) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(3,("ntlm_password_check: Interactive logon: NT password check failed for user %s\n",
|
||||
username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
} else if (client_lanman && stored_lanman) {
|
||||
if (!lp_lanman_auth()) {
|
||||
DEBUG(3,("ntlm_password_check: Interactive logon: only LANMAN password supplied for user %s, and LM passwords are disabled!\n",
|
||||
username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
if (strchr_m(username, '@')) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (memcmp(client_lanman->hash, stored_lanman->hash, sizeof(stored_lanman->hash)) == 0) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(3,("ntlm_password_check: Interactive logon: LANMAN password check failed for user %s\n",
|
||||
username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
}
|
||||
if (strchr_m(username, '@')) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a challenge-response password against the value of the NT or
|
||||
* LM password hash.
|
||||
*
|
||||
* @param mem_ctx talloc context
|
||||
* @param challenge 8-byte challenge. If all zero, forces plaintext comparison
|
||||
* @param nt_response 'unicode' NT response to the challenge, or unicode password
|
||||
* @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
|
||||
* @param username internal Samba username, for log messages
|
||||
* @param client_username username the client used
|
||||
* @param client_domain domain name the client used (may be mapped)
|
||||
* @param stored_lanman LANMAN ASCII password from our passdb or similar
|
||||
* @param stored_nt MD4 unicode password from our passdb or similar
|
||||
* @param user_sess_key User session key
|
||||
* @param lm_sess_key LM session key (first 8 bytes of the LM hash)
|
||||
*/
|
||||
|
||||
NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
|
||||
uint32_t logon_parameters,
|
||||
const DATA_BLOB *challenge,
|
||||
const DATA_BLOB *lm_response,
|
||||
const DATA_BLOB *nt_response,
|
||||
const char *username,
|
||||
const char *client_username,
|
||||
const char *client_domain,
|
||||
const struct samr_Password *stored_lanman,
|
||||
const struct samr_Password *stored_nt,
|
||||
DATA_BLOB *user_sess_key,
|
||||
DATA_BLOB *lm_sess_key)
|
||||
{
|
||||
static const uint8_t zeros[8];
|
||||
DATA_BLOB tmp_sess_key;
|
||||
|
||||
if (stored_nt == NULL) {
|
||||
DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
|
||||
username));
|
||||
}
|
||||
|
||||
*lm_sess_key = data_blob(NULL, 0);
|
||||
*user_sess_key = data_blob(NULL, 0);
|
||||
|
||||
/* Check for cleartext netlogon. Used by Exchange 5.5. */
|
||||
if ((logon_parameters & MSV1_0_CLEARTEXT_PASSWORD_ALLOWED)
|
||||
&& challenge->length == sizeof(zeros)
|
||||
&& (memcmp(challenge->data, zeros, challenge->length) == 0 )) {
|
||||
struct samr_Password client_nt;
|
||||
struct samr_Password client_lm;
|
||||
char *unix_pw = NULL;
|
||||
BOOL lm_ok;
|
||||
|
||||
DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n",
|
||||
username));
|
||||
mdfour(client_nt.hash, nt_response->data, nt_response->length);
|
||||
|
||||
if (lm_response->length &&
|
||||
(convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX,
|
||||
lm_response->data, lm_response->length,
|
||||
(void **)&unix_pw) != -1)) {
|
||||
if (E_deshash(unix_pw, client_lm.hash)) {
|
||||
lm_ok = True;
|
||||
} else {
|
||||
lm_ok = False;
|
||||
}
|
||||
} else {
|
||||
lm_ok = False;
|
||||
}
|
||||
return hash_password_check(mem_ctx,
|
||||
lm_ok ? &client_lm : NULL,
|
||||
nt_response->length ? &client_nt : NULL,
|
||||
username,
|
||||
stored_lanman, stored_nt);
|
||||
}
|
||||
|
||||
if (nt_response->length != 0 && nt_response->length < 24) {
|
||||
DEBUG(2,("ntlm_password_check: invalid NT password length (%lu) for user %s\n",
|
||||
(unsigned long)nt_response->length, username));
|
||||
}
|
||||
|
||||
if (nt_response->length > 24 && stored_nt) {
|
||||
/* We have the NT MD4 hash challenge available - see if we can
|
||||
use it
|
||||
*/
|
||||
DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with domain [%s]\n", client_domain));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
False,
|
||||
user_sess_key)) {
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with uppercased version of domain [%s]\n", client_domain));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
True,
|
||||
user_sess_key)) {
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(4,("ntlm_password_check: Checking NTLMv2 password without a domain\n"));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
"",
|
||||
False,
|
||||
user_sess_key)) {
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(3,("ntlm_password_check: NTLMv2 password check failed\n"));
|
||||
}
|
||||
} else if (nt_response->length == 24 && stored_nt) {
|
||||
if (lp_ntlm_auth()) {
|
||||
/* We have the NT MD4 hash challenge available - see if we can
|
||||
use it (ie. does it exist in the smbpasswd file).
|
||||
*/
|
||||
DEBUG(4,("ntlm_password_check: Checking NT MD4 password\n"));
|
||||
if (smb_pwd_check_ntlmv1(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
user_sess_key)) {
|
||||
/* The LM session key for this response is not very secure,
|
||||
so use it only if we otherwise allow LM authentication */
|
||||
|
||||
if (lp_lanman_auth() && stored_lanman) {
|
||||
*lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(3,("ntlm_password_check: NT MD4 password check failed for user %s\n",
|
||||
username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
} else {
|
||||
DEBUG(2,("ntlm_password_check: NTLMv1 passwords NOT PERMITTED for user %s\n",
|
||||
username));
|
||||
/* no return, becouse we might pick up LMv2 in the LM field */
|
||||
}
|
||||
}
|
||||
|
||||
if (lm_response->length == 0) {
|
||||
DEBUG(3,("ntlm_password_check: NEITHER LanMan nor NT password supplied for user %s\n",
|
||||
username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
if (lm_response->length < 24) {
|
||||
DEBUG(2,("ntlm_password_check: invalid LanMan password length (%lu) for user %s\n",
|
||||
(unsigned long)nt_response->length, username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
if (!lp_lanman_auth()) {
|
||||
DEBUG(3,("ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n",
|
||||
username));
|
||||
} else if (!stored_lanman) {
|
||||
DEBUG(3,("ntlm_password_check: NO LanMan password set for user %s (and no NT password supplied)\n",
|
||||
username));
|
||||
} else if (strchr_m(username, '@')) {
|
||||
DEBUG(3,("ntlm_password_check: NO LanMan password allowed for username@realm logins (user: %s)\n",
|
||||
username));
|
||||
} else {
|
||||
DEBUG(4,("ntlm_password_check: Checking LM password\n"));
|
||||
if (smb_pwd_check_ntlmv1(mem_ctx,
|
||||
lm_response,
|
||||
stored_lanman->hash, challenge,
|
||||
NULL)) {
|
||||
/* The session key for this response is still very odd.
|
||||
It not very secure, so use it only if we otherwise
|
||||
allow LM authentication */
|
||||
|
||||
if (lp_lanman_auth() && stored_lanman) {
|
||||
uint8_t first_8_lm_hash[16];
|
||||
memcpy(first_8_lm_hash, stored_lanman->hash, 8);
|
||||
memset(first_8_lm_hash + 8, '\0', 8);
|
||||
*user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
|
||||
*lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stored_nt) {
|
||||
DEBUG(4,("ntlm_password_check: LM password check failed for user, no NT password %s\n",username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
/* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes.
|
||||
- related to Win9X, legacy NAS pass-though authentication
|
||||
*/
|
||||
DEBUG(4,("ntlm_password_check: Checking LMv2 password with domain %s\n", client_domain));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
lm_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
False,
|
||||
&tmp_sess_key)) {
|
||||
if (nt_response->length > 24) {
|
||||
/* If NTLMv2 authentication has preceeded us
|
||||
* (even if it failed), then use the session
|
||||
* key from that. See the RPC-SAMLOGON
|
||||
* torture test */
|
||||
smb_sess_key_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
False,
|
||||
user_sess_key);
|
||||
} else {
|
||||
/* Otherwise, use the LMv2 session key */
|
||||
*user_sess_key = tmp_sess_key;
|
||||
}
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(4,("ntlm_password_check: Checking LMv2 password with upper-cased version of domain %s\n", client_domain));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
lm_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
True,
|
||||
&tmp_sess_key)) {
|
||||
if (nt_response->length > 24) {
|
||||
/* If NTLMv2 authentication has preceeded us
|
||||
* (even if it failed), then use the session
|
||||
* key from that. See the RPC-SAMLOGON
|
||||
* torture test */
|
||||
smb_sess_key_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
True,
|
||||
user_sess_key);
|
||||
} else {
|
||||
/* Otherwise, use the LMv2 session key */
|
||||
*user_sess_key = tmp_sess_key;
|
||||
}
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(4,("ntlm_password_check: Checking LMv2 password without a domain\n"));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
lm_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
"",
|
||||
False,
|
||||
&tmp_sess_key)) {
|
||||
if (nt_response->length > 24) {
|
||||
/* If NTLMv2 authentication has preceeded us
|
||||
* (even if it failed), then use the session
|
||||
* key from that. See the RPC-SAMLOGON
|
||||
* torture test */
|
||||
smb_sess_key_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
"",
|
||||
False,
|
||||
user_sess_key);
|
||||
} else {
|
||||
/* Otherwise, use the LMv2 session key */
|
||||
*user_sess_key = tmp_sess_key;
|
||||
}
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Apparently NT accepts NT responses in the LM field
|
||||
- I think this is related to Win9X pass-though authentication
|
||||
*/
|
||||
DEBUG(4,("ntlm_password_check: Checking NT MD4 password in LM field\n"));
|
||||
if (lp_ntlm_auth()) {
|
||||
if (smb_pwd_check_ntlmv1(mem_ctx,
|
||||
lm_response,
|
||||
stored_nt->hash, challenge,
|
||||
NULL)) {
|
||||
/* The session key for this response is still very odd.
|
||||
It not very secure, so use it only if we otherwise
|
||||
allow LM authentication */
|
||||
|
||||
if (lp_lanman_auth() && stored_lanman) {
|
||||
uint8_t first_8_lm_hash[16];
|
||||
memcpy(first_8_lm_hash, stored_lanman->hash, 8);
|
||||
memset(first_8_lm_hash + 8, '\0', 8);
|
||||
*user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
|
||||
*lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
DEBUG(3,("ntlm_password_check: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",username));
|
||||
} else {
|
||||
DEBUG(3,("ntlm_password_check: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",username));
|
||||
}
|
||||
|
||||
/* Try and match error codes */
|
||||
if (strchr_m(username, '@')) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
[SUBSYSTEM::MSRPC_PARSE]
|
||||
PRIVATE_PROTO_HEADER = msrpc_parse.h
|
||||
OBJ_FILES = ntlmssp_parse.o
|
||||
|
||||
################################################
|
||||
# Start MODULE gensec_ntlmssp
|
||||
[MODULE::gensec_ntlmssp]
|
||||
SUBSYSTEM = gensec
|
||||
INIT_FUNCTION = gensec_ntlmssp_init
|
||||
PRIVATE_PROTO_HEADER = proto.h
|
||||
OBJ_FILES = ntlmssp.o \
|
||||
ntlmssp_sign.o \
|
||||
ntlmssp_client.o \
|
||||
ntlmssp_server.o
|
||||
PUBLIC_DEPENDENCIES = auth MSRPC_PARSE
|
||||
OUTPUT_TYPE = INTEGRATED
|
||||
# End MODULE gensec_ntlmssp
|
||||
################################################
|
||||
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Version 3.0
|
||||
handle NLTMSSP, client server side parsing
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/ntlmssp/ntlmssp.h"
|
||||
#include "auth/ntlmssp/msrpc_parse.h"
|
||||
#include "librpc/gen_ndr/ndr_dcerpc.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
/**
|
||||
* Callbacks for NTLMSSP - for both client and server operating modes
|
||||
*
|
||||
*/
|
||||
|
||||
static const struct ntlmssp_callbacks {
|
||||
enum ntlmssp_role role;
|
||||
enum ntlmssp_message_type command;
|
||||
NTSTATUS (*sync_fn)(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
DATA_BLOB in, DATA_BLOB *out);
|
||||
} ntlmssp_callbacks[] = {
|
||||
{
|
||||
.role = NTLMSSP_CLIENT,
|
||||
.command = NTLMSSP_INITIAL,
|
||||
.sync_fn = ntlmssp_client_initial,
|
||||
},{
|
||||
.role = NTLMSSP_SERVER,
|
||||
.command = NTLMSSP_NEGOTIATE,
|
||||
.sync_fn = ntlmssp_server_negotiate,
|
||||
},{
|
||||
.role = NTLMSSP_CLIENT,
|
||||
.command = NTLMSSP_CHALLENGE,
|
||||
.sync_fn = ntlmssp_client_challenge,
|
||||
},{
|
||||
.role = NTLMSSP_SERVER,
|
||||
.command = NTLMSSP_AUTH,
|
||||
.sync_fn = ntlmssp_server_auth,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Print out the NTLMSSP flags for debugging
|
||||
* @param neg_flags The flags from the packet
|
||||
*/
|
||||
|
||||
void debug_ntlmssp_flags(uint32_t neg_flags)
|
||||
{
|
||||
DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags));
|
||||
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_OEM)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n"));
|
||||
if (neg_flags & NTLMSSP_REQUEST_TARGET)
|
||||
DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_SIGN)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_SEAL)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM_STYLE)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DATAGRAM_STYLE\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_NTLM)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n"));
|
||||
if (neg_flags & NTLMSSP_CHAL_ACCEPT_RESPONSE)
|
||||
DEBUGADD(4, (" NTLMSSP_CHAL_ACCEPT_RESPONSE\n"));
|
||||
if (neg_flags & NTLMSSP_CHAL_NON_NT_SESSION_KEY)
|
||||
DEBUGADD(4, (" NTLMSSP_CHAL_NON_NT_SESSION_KEY\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n"));
|
||||
if (neg_flags & NTLMSSP_CHAL_TARGET_INFO)
|
||||
DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_128)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_56)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_56\n"));
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_ntlmssp_magic(struct gensec_security *gensec_security,
|
||||
const DATA_BLOB *first_packet)
|
||||
{
|
||||
if (first_packet->length > 8 && memcmp("NTLMSSP\0", first_packet->data, 8) == 0) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_ntlmssp_update_find(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
const DATA_BLOB input, uint32_t *idx)
|
||||
{
|
||||
struct gensec_security *gensec_security = gensec_ntlmssp_state->gensec_security;
|
||||
uint32_t ntlmssp_command;
|
||||
uint32_t i;
|
||||
|
||||
if (gensec_ntlmssp_state->expected_state == NTLMSSP_DONE) {
|
||||
/* We are strict here because other modules, which we
|
||||
* don't fully control (such as GSSAPI) are also
|
||||
* strict, but are tested less often */
|
||||
|
||||
DEBUG(1, ("Called NTLMSSP after state machine was 'done'\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!input.length) {
|
||||
switch (gensec_ntlmssp_state->role) {
|
||||
case NTLMSSP_CLIENT:
|
||||
ntlmssp_command = NTLMSSP_INITIAL;
|
||||
break;
|
||||
case NTLMSSP_SERVER:
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_DATAGRAM_MODE) {
|
||||
/* 'datagram' mode - no neg packet */
|
||||
ntlmssp_command = NTLMSSP_NEGOTIATE;
|
||||
} else {
|
||||
/* This is normal in SPNEGO mech negotiation fallback */
|
||||
DEBUG(2, ("Failed to parse NTLMSSP packet: zero length\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!msrpc_parse(gensec_ntlmssp_state,
|
||||
&input, "Cd",
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command)) {
|
||||
DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n"));
|
||||
dump_data(2, input.data, input.length);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
if (ntlmssp_command != gensec_ntlmssp_state->expected_state) {
|
||||
DEBUG(2, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, gensec_ntlmssp_state->expected_state));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
for (i=0; i < ARRAY_SIZE(ntlmssp_callbacks); i++) {
|
||||
if (ntlmssp_callbacks[i].role == gensec_ntlmssp_state->role &&
|
||||
ntlmssp_callbacks[i].command == ntlmssp_command) {
|
||||
*idx = i;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n",
|
||||
gensec_ntlmssp_state->role, ntlmssp_command));
|
||||
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Next state function for the wrapped NTLMSSP state machine
|
||||
*
|
||||
* @param gensec_security GENSEC state, initialised to NTLMSSP
|
||||
* @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
|
||||
* @param in The request, as a DATA_BLOB
|
||||
* @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
|
||||
* @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
|
||||
* or NT_STATUS_OK if the user is authenticated.
|
||||
*/
|
||||
|
||||
static NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB input, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
NTSTATUS status;
|
||||
uint32_t i;
|
||||
|
||||
*out = data_blob(NULL, 0);
|
||||
|
||||
if (!out_mem_ctx) {
|
||||
/* if the caller doesn't want to manage/own the memory,
|
||||
we can put it on our context */
|
||||
out_mem_ctx = gensec_ntlmssp_state;
|
||||
}
|
||||
|
||||
status = gensec_ntlmssp_update_find(gensec_ntlmssp_state, input, &i);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = ntlmssp_callbacks[i].sync_fn(gensec_security, out_mem_ctx, input, out);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the NTLMSSP master session key
|
||||
*
|
||||
* @param gensec_ntlmssp_state NTLMSSP State
|
||||
*/
|
||||
|
||||
NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security,
|
||||
DATA_BLOB *session_key)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
if (!gensec_ntlmssp_state->session_key.data) {
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
*session_key = gensec_ntlmssp_state->session_key;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
void ntlmssp_handle_neg_flags(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
uint32_t neg_flags, BOOL allow_lm)
|
||||
{
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
|
||||
gensec_ntlmssp_state->unicode = True;
|
||||
} else {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE;
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
|
||||
gensec_ntlmssp_state->unicode = False;
|
||||
}
|
||||
|
||||
if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm && !gensec_ntlmssp_state->use_ntlmv2) {
|
||||
/* other end forcing us to use LM */
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
|
||||
} else {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_SIGN)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_SEAL)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SEAL;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_56)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_56;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH;
|
||||
}
|
||||
|
||||
/* Woop Woop - unknown flag for Windows compatibility...
|
||||
What does this really do ? JRA. */
|
||||
if (!(neg_flags & NTLMSSP_UNKNOWN_02000000)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_UNKNOWN_02000000;
|
||||
}
|
||||
|
||||
if ((neg_flags & NTLMSSP_REQUEST_TARGET)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Weaken NTLMSSP keys to cope with down-level clients and servers.
|
||||
|
||||
We probably should have some parameters to control this, but as
|
||||
it only occours for LM_KEY connections, and this is controlled
|
||||
by the client lanman auth/lanman auth parameters, it isn't too bad.
|
||||
*/
|
||||
|
||||
DATA_BLOB ntlmssp_weakend_key(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
DATA_BLOB weakened_key = data_blob_talloc(mem_ctx,
|
||||
gensec_ntlmssp_state->session_key.data,
|
||||
gensec_ntlmssp_state->session_key.length);
|
||||
/* Nothing to weaken. We certainly don't want to 'extend' the length... */
|
||||
if (weakened_key.length < 16) {
|
||||
/* perhaps there was no key? */
|
||||
return weakened_key;
|
||||
}
|
||||
|
||||
/* Key weakening not performed on the master key for NTLM2
|
||||
and does not occour for NTLM1. Therefore we only need
|
||||
to do this for the LM_KEY.
|
||||
*/
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
|
||||
/* LM key doesn't support 128 bit crypto, so this is
|
||||
* the best we can do. If you negotiate 128 bit, but
|
||||
* not 56, you end up with 40 bit... */
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
|
||||
weakened_key.data[7] = 0xa0;
|
||||
weakened_key.length = 8;
|
||||
} else { /* forty bits */
|
||||
weakened_key.data[5] = 0xe5;
|
||||
weakened_key.data[6] = 0x38;
|
||||
weakened_key.data[7] = 0xb0;
|
||||
weakened_key.length = 8;
|
||||
}
|
||||
}
|
||||
return weakened_key;
|
||||
}
|
||||
|
||||
static BOOL gensec_ntlmssp_have_feature(struct gensec_security *gensec_security,
|
||||
uint32_t feature)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
if (feature & GENSEC_FEATURE_SIGN) {
|
||||
if (!gensec_ntlmssp_state->session_key.length) {
|
||||
return False;
|
||||
}
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_SEAL) {
|
||||
if (!gensec_ntlmssp_state->session_key.length) {
|
||||
return False;
|
||||
}
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_SESSION_KEY) {
|
||||
if (gensec_ntlmssp_state->session_key.length) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_DCE_STYLE) {
|
||||
return True;
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state;
|
||||
|
||||
gensec_ntlmssp_state = talloc_zero(gensec_security, struct gensec_ntlmssp_state);
|
||||
if (!gensec_ntlmssp_state) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
gensec_ntlmssp_state->gensec_security = gensec_security;
|
||||
gensec_ntlmssp_state->auth_context = NULL;
|
||||
gensec_ntlmssp_state->server_info = NULL;
|
||||
|
||||
gensec_security->private_data = gensec_ntlmssp_state;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static const char *gensec_ntlmssp_oids[] = {
|
||||
GENSEC_OID_NTLMSSP,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
|
||||
.name = "ntlmssp",
|
||||
.sasl_name = "NTLM",
|
||||
.auth_type = DCERPC_AUTH_TYPE_NTLMSSP,
|
||||
.oid = gensec_ntlmssp_oids,
|
||||
.client_start = gensec_ntlmssp_client_start,
|
||||
.server_start = gensec_ntlmssp_server_start,
|
||||
.magic = gensec_ntlmssp_magic,
|
||||
.update = gensec_ntlmssp_update,
|
||||
.sig_size = gensec_ntlmssp_sig_size,
|
||||
.sign_packet = gensec_ntlmssp_sign_packet,
|
||||
.check_packet = gensec_ntlmssp_check_packet,
|
||||
.seal_packet = gensec_ntlmssp_seal_packet,
|
||||
.unseal_packet = gensec_ntlmssp_unseal_packet,
|
||||
.wrap = gensec_ntlmssp_wrap,
|
||||
.unwrap = gensec_ntlmssp_unwrap,
|
||||
.session_key = gensec_ntlmssp_session_key,
|
||||
.session_info = gensec_ntlmssp_session_info,
|
||||
.have_feature = gensec_ntlmssp_have_feature,
|
||||
.enabled = True,
|
||||
.priority = GENSEC_NTLMSSP
|
||||
};
|
||||
|
||||
|
||||
NTSTATUS gensec_ntlmssp_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
auth_init();
|
||||
|
||||
ret = gensec_register(&gensec_ntlmssp_security_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_ntlmssp_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB parameters and setup
|
||||
Copyright (C) Andrew Tridgell 1992-1997
|
||||
Copyright (C) Luke Kenneth Casson Leighton 1996-1997
|
||||
Copyright (C) Paul Ashton 1997
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "librpc/gen_ndr/samr.h"
|
||||
|
||||
/* NTLMSSP mode */
|
||||
enum ntlmssp_role
|
||||
{
|
||||
NTLMSSP_SERVER,
|
||||
NTLMSSP_CLIENT
|
||||
};
|
||||
|
||||
/* NTLMSSP message types */
|
||||
enum ntlmssp_message_type
|
||||
{
|
||||
NTLMSSP_INITIAL = 0 /* samba internal state */,
|
||||
NTLMSSP_NEGOTIATE = 1,
|
||||
NTLMSSP_CHALLENGE = 2,
|
||||
NTLMSSP_AUTH = 3,
|
||||
NTLMSSP_UNKNOWN = 4,
|
||||
NTLMSSP_DONE = 5 /* samba final state */
|
||||
};
|
||||
|
||||
/* NTLMSSP negotiation flags */
|
||||
#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
|
||||
#define NTLMSSP_NEGOTIATE_OEM 0x00000002
|
||||
#define NTLMSSP_REQUEST_TARGET 0x00000004
|
||||
#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* Message integrity */
|
||||
#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* Message confidentiality */
|
||||
#define NTLMSSP_NEGOTIATE_DATAGRAM_STYLE 0x00000040
|
||||
#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
|
||||
#define NTLMSSP_NEGOTIATE_NETWARE 0x00000100
|
||||
#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
|
||||
#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
|
||||
#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
|
||||
#define NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL 0x00004000
|
||||
#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
|
||||
#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000
|
||||
#define NTLMSSP_TARGET_TYPE_SERVER 0x20000
|
||||
#define NTLMSSP_CHAL_INIT_RESPONSE 0x00010000
|
||||
|
||||
#define NTLMSSP_CHAL_ACCEPT_RESPONSE 0x00020000
|
||||
#define NTLMSSP_CHAL_NON_NT_SESSION_KEY 0x00040000
|
||||
#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
|
||||
#define NTLMSSP_CHAL_TARGET_INFO 0x00800000
|
||||
#define NTLMSSP_UNKNOWN_02000000 0x02000000
|
||||
#define NTLMSSP_NEGOTIATE_128 0x20000000 /* 128-bit encryption */
|
||||
#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000
|
||||
#define NTLMSSP_NEGOTIATE_56 0x80000000
|
||||
|
||||
#define NTLMSSP_NAME_TYPE_SERVER 0x01
|
||||
#define NTLMSSP_NAME_TYPE_DOMAIN 0x02
|
||||
#define NTLMSSP_NAME_TYPE_SERVER_DNS 0x03
|
||||
#define NTLMSSP_NAME_TYPE_DOMAIN_DNS 0x04
|
||||
|
||||
#define NTLMSSP_SIGN_VERSION 1
|
||||
|
||||
#define NTLMSSP_SIG_SIZE 16
|
||||
|
||||
struct gensec_ntlmssp_state
|
||||
{
|
||||
struct gensec_security *gensec_security;
|
||||
|
||||
enum ntlmssp_role role;
|
||||
enum samr_Role server_role;
|
||||
uint32_t expected_state;
|
||||
|
||||
BOOL unicode;
|
||||
BOOL use_ntlmv2;
|
||||
BOOL use_nt_response; /* Set to 'False' to debug what happens when the NT response is omited */
|
||||
BOOL allow_lm_key; /* The LM_KEY code is not functional at this point, and it's not
|
||||
very secure anyway */
|
||||
|
||||
BOOL server_multiple_authentications; /* Set to 'True' to allow squid 2.5
|
||||
style 'challenge caching' */
|
||||
|
||||
char *user;
|
||||
char *domain;
|
||||
const char *workstation;
|
||||
char *server_domain;
|
||||
|
||||
DATA_BLOB internal_chal; /* Random challenge as supplied to the client for NTLM authentication */
|
||||
|
||||
DATA_BLOB chal; /* Random challenge as input into the actual NTLM (or NTLM2) authentication */
|
||||
DATA_BLOB lm_resp;
|
||||
DATA_BLOB nt_resp;
|
||||
DATA_BLOB session_key;
|
||||
|
||||
uint32_t neg_flags; /* the current state of negotiation with the NTLMSSP partner */
|
||||
|
||||
/* internal variables used by KEY_EXCH (client-supplied user session key */
|
||||
DATA_BLOB encrypted_session_key;
|
||||
|
||||
/**
|
||||
* Callback to get the 'challenge' used for NTLM authentication.
|
||||
*
|
||||
* @param ntlmssp_state This structure
|
||||
* @return 8 bytes of challenge data, determined by the server to be the challenge for NTLM authentication
|
||||
*
|
||||
*/
|
||||
const uint8_t *(*get_challenge)(const struct gensec_ntlmssp_state *);
|
||||
|
||||
/**
|
||||
* Callback to find if the challenge used by NTLM authentication may be modified
|
||||
*
|
||||
* The NTLM2 authentication scheme modifies the effective challenge, but this is not compatiable with the
|
||||
* current 'security=server' implementation..
|
||||
*
|
||||
* @param ntlmssp_state This structure
|
||||
* @return Can the challenge be set to arbitary values?
|
||||
*
|
||||
*/
|
||||
BOOL (*may_set_challenge)(const struct gensec_ntlmssp_state *);
|
||||
|
||||
/**
|
||||
* Callback to set the 'challenge' used for NTLM authentication.
|
||||
*
|
||||
* The callback may use the void *auth_context to store state information, but the same value is always available
|
||||
* from the DATA_BLOB chal on this structure.
|
||||
*
|
||||
* @param ntlmssp_state This structure
|
||||
* @param challenge 8 bytes of data, agreed by the client and server to be the effective challenge for NTLM2 authentication
|
||||
*
|
||||
*/
|
||||
NTSTATUS (*set_challenge)(struct gensec_ntlmssp_state *, DATA_BLOB *challenge);
|
||||
|
||||
/**
|
||||
* Callback to check the user's password.
|
||||
*
|
||||
* The callback must reads the feilds of this structure for the information it needs on the user
|
||||
* @param ntlmssp_state This structure
|
||||
* @param nt_session_key If an NT session key is returned by the authentication process, return it here
|
||||
* @param lm_session_key If an LM session key is returned by the authentication process, return it here
|
||||
*
|
||||
*/
|
||||
NTSTATUS (*check_password)(struct gensec_ntlmssp_state *,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key);
|
||||
|
||||
const char *server_name;
|
||||
const char *(*get_domain)(void);
|
||||
|
||||
BOOL doing_ntlm2;
|
||||
|
||||
union {
|
||||
/* NTLM */
|
||||
struct {
|
||||
uint32_t seq_num;
|
||||
struct arcfour_state *arcfour_state;
|
||||
} ntlm;
|
||||
|
||||
/* NTLM2 */
|
||||
struct {
|
||||
uint32_t send_seq_num;
|
||||
uint32_t recv_seq_num;
|
||||
DATA_BLOB send_sign_key;
|
||||
DATA_BLOB recv_sign_key;
|
||||
struct arcfour_state *send_seal_arcfour_state;
|
||||
struct arcfour_state *recv_seal_arcfour_state;
|
||||
|
||||
/* internal variables used by NTLM2 */
|
||||
uint8_t session_nonce[16];
|
||||
} ntlm2;
|
||||
} crypt;
|
||||
|
||||
struct auth_context *auth_context;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
};
|
||||
|
||||
struct auth_session_info;
|
||||
#include "auth/ntlmssp/proto.h"
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Version 3.0
|
||||
handle NLTMSSP, client server side parsing
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/ntlmssp/ntlmssp.h"
|
||||
#include "auth/ntlmssp/msrpc_parse.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
/*********************************************************************
|
||||
Client side NTLMSSP
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Next state function for the Initial packet
|
||||
*
|
||||
* @param ntlmssp_state NTLMSSP State
|
||||
* @param out_mem_ctx The DATA_BLOB *out will be allocated on this context
|
||||
* @param in A NULL data blob (input ignored)
|
||||
* @param out The initial negotiate request to the server, as an talloc()ed DATA_BLOB, on out_mem_ctx
|
||||
* @return Errors or NT_STATUS_OK.
|
||||
*/
|
||||
|
||||
NTSTATUS ntlmssp_client_initial(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
if (gensec_ntlmssp_state->unicode) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
|
||||
} else {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
|
||||
}
|
||||
|
||||
if (gensec_ntlmssp_state->use_ntlmv2) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
|
||||
}
|
||||
|
||||
/* generate the ntlmssp negotiate packet */
|
||||
msrpc_gen(out_mem_ctx,
|
||||
out, "CddAA",
|
||||
"NTLMSSP",
|
||||
NTLMSSP_NEGOTIATE,
|
||||
gensec_ntlmssp_state->neg_flags,
|
||||
gensec_ntlmssp_state->get_domain(),
|
||||
cli_credentials_get_workstation(gensec_security->credentials));
|
||||
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_CHALLENGE;
|
||||
|
||||
return NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Next state function for the Challenge Packet. Generate an auth packet.
|
||||
*
|
||||
* @param gensec_security GENSEC state
|
||||
* @param out_mem_ctx Memory context for *out
|
||||
* @param in The server challnege, as a DATA_BLOB. reply.data must be NULL
|
||||
* @param out The next request (auth packet) to the server, as an allocated DATA_BLOB, on the out_mem_ctx context
|
||||
* @return Errors or NT_STATUS_OK.
|
||||
*/
|
||||
|
||||
NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
uint32_t chal_flags, ntlmssp_command, unkn1, unkn2;
|
||||
DATA_BLOB server_domain_blob;
|
||||
DATA_BLOB challenge_blob;
|
||||
DATA_BLOB target_info = data_blob(NULL, 0);
|
||||
char *server_domain;
|
||||
const char *chal_parse_string;
|
||||
const char *auth_gen_string;
|
||||
DATA_BLOB lm_response = data_blob(NULL, 0);
|
||||
DATA_BLOB nt_response = data_blob(NULL, 0);
|
||||
DATA_BLOB session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB lm_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB encrypted_session_key = data_blob(NULL, 0);
|
||||
NTSTATUS nt_status;
|
||||
int flags = 0;
|
||||
const char *user, *domain;
|
||||
|
||||
TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (!msrpc_parse(mem_ctx,
|
||||
&in, "CdBd",
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&server_domain_blob,
|
||||
&chal_flags)) {
|
||||
DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
|
||||
dump_data(2, in.data, in.length);
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
data_blob_free(&server_domain_blob);
|
||||
|
||||
DEBUG(3, ("Got challenge flags:\n"));
|
||||
debug_ntlmssp_flags(chal_flags);
|
||||
|
||||
ntlmssp_handle_neg_flags(gensec_ntlmssp_state, chal_flags, gensec_ntlmssp_state->allow_lm_key);
|
||||
|
||||
if (gensec_ntlmssp_state->unicode) {
|
||||
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
|
||||
chal_parse_string = "CdUdbddB";
|
||||
} else {
|
||||
chal_parse_string = "CdUdbdd";
|
||||
}
|
||||
auth_gen_string = "CdBBUUUBd";
|
||||
} else {
|
||||
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
|
||||
chal_parse_string = "CdAdbddB";
|
||||
} else {
|
||||
chal_parse_string = "CdAdbdd";
|
||||
}
|
||||
|
||||
auth_gen_string = "CdBBAAABd";
|
||||
}
|
||||
|
||||
if (!msrpc_parse(mem_ctx,
|
||||
&in, chal_parse_string,
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&server_domain,
|
||||
&chal_flags,
|
||||
&challenge_blob, 8,
|
||||
&unkn1, &unkn2,
|
||||
&target_info)) {
|
||||
DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));
|
||||
dump_data(2, in.data, in.length);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gensec_ntlmssp_state->server_domain = server_domain;
|
||||
|
||||
if (challenge_blob.length != 8) {
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
cli_credentials_get_ntlm_username_domain(gensec_security->credentials, mem_ctx,
|
||||
&user, &domain);
|
||||
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
flags |= CLI_CRED_NTLM2;
|
||||
}
|
||||
if (gensec_ntlmssp_state->use_ntlmv2) {
|
||||
flags |= CLI_CRED_NTLMv2_AUTH;
|
||||
}
|
||||
if (gensec_ntlmssp_state->use_nt_response) {
|
||||
flags |= CLI_CRED_NTLM_AUTH;
|
||||
}
|
||||
if (lp_client_lanman_auth()) {
|
||||
flags |= CLI_CRED_LANMAN_AUTH;
|
||||
}
|
||||
|
||||
nt_status = cli_credentials_get_ntlm_response(gensec_security->credentials, mem_ctx,
|
||||
&flags, challenge_blob, target_info,
|
||||
&lm_response, &nt_response,
|
||||
&lm_session_key, &session_key);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (!(flags & CLI_CRED_LANMAN_AUTH)) {
|
||||
/* LM Key is still possible, just silly. Fortunetly
|
||||
* we require command line options to end up here */
|
||||
/* gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; */
|
||||
}
|
||||
|
||||
if (!(flags & CLI_CRED_NTLM2)) {
|
||||
/* NTLM2 is incompatible... */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
|
||||
}
|
||||
|
||||
if ((gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
|
||||
&& lp_client_lanman_auth() && lm_session_key.length == 16) {
|
||||
DATA_BLOB new_session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
if (lm_response.length == 24) {
|
||||
SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data,
|
||||
new_session_key.data);
|
||||
} else {
|
||||
static const uint8_t zeros[24];
|
||||
SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros,
|
||||
new_session_key.data);
|
||||
}
|
||||
session_key = new_session_key;
|
||||
dump_data_pw("LM session key\n", session_key.data, session_key.length);
|
||||
}
|
||||
|
||||
|
||||
/* Key exchange encryptes a new client-generated session key with
|
||||
the password-derived key */
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
|
||||
/* Make up a new session key */
|
||||
uint8_t client_session_key[16];
|
||||
generate_random_buffer(client_session_key, sizeof(client_session_key));
|
||||
|
||||
/* Encrypt the new session key with the old one */
|
||||
encrypted_session_key = data_blob_talloc(gensec_ntlmssp_state,
|
||||
client_session_key, sizeof(client_session_key));
|
||||
dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length);
|
||||
arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length);
|
||||
dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);
|
||||
|
||||
/* Mark the new session key as the 'real' session key */
|
||||
session_key = data_blob_talloc(mem_ctx, client_session_key, sizeof(client_session_key));
|
||||
}
|
||||
|
||||
DEBUG(3, ("NTLMSSP: Set final flags:\n"));
|
||||
debug_ntlmssp_flags(gensec_ntlmssp_state->neg_flags);
|
||||
|
||||
/* this generates the actual auth packet */
|
||||
if (!msrpc_gen(mem_ctx,
|
||||
out, auth_gen_string,
|
||||
"NTLMSSP",
|
||||
NTLMSSP_AUTH,
|
||||
lm_response.data, lm_response.length,
|
||||
nt_response.data, nt_response.length,
|
||||
domain,
|
||||
user,
|
||||
cli_credentials_get_workstation(gensec_security->credentials),
|
||||
encrypted_session_key.data, encrypted_session_key.length,
|
||||
gensec_ntlmssp_state->neg_flags)) {
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
gensec_ntlmssp_state->session_key = session_key;
|
||||
talloc_steal(gensec_ntlmssp_state, session_key.data);
|
||||
|
||||
talloc_steal(out_mem_ctx, out->data);
|
||||
|
||||
gensec_ntlmssp_state->chal = challenge_blob;
|
||||
gensec_ntlmssp_state->lm_resp = lm_response;
|
||||
talloc_steal(gensec_ntlmssp_state->lm_resp.data, lm_response.data);
|
||||
gensec_ntlmssp_state->nt_resp = nt_response;
|
||||
talloc_steal(gensec_ntlmssp_state->nt_resp.data, nt_response.data);
|
||||
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_DONE;
|
||||
|
||||
if (gensec_security->want_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) {
|
||||
nt_status = ntlmssp_sign_init(gensec_ntlmssp_state);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n",
|
||||
nt_errstr(nt_status)));
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
nt_status = gensec_ntlmssp_start(gensec_security);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
gensec_ntlmssp_state->role = NTLMSSP_CLIENT;
|
||||
|
||||
gensec_ntlmssp_state->get_domain = lp_workgroup;
|
||||
|
||||
gensec_ntlmssp_state->unicode = lp_parm_bool(-1, "ntlmssp_client", "unicode", True);
|
||||
|
||||
gensec_ntlmssp_state->use_nt_response = lp_parm_bool(-1, "ntlmssp_client", "send_nt_reponse", True);
|
||||
|
||||
gensec_ntlmssp_state->allow_lm_key = (lp_client_lanman_auth()
|
||||
&& (lp_parm_bool(-1, "ntlmssp_client", "allow_lm_key", False)
|
||||
|| lp_parm_bool(-1, "ntlmssp_client", "lm_key", False)));
|
||||
|
||||
gensec_ntlmssp_state->use_ntlmv2 = lp_client_ntlmv2_auth();
|
||||
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_INITIAL;
|
||||
|
||||
gensec_ntlmssp_state->neg_flags =
|
||||
NTLMSSP_NEGOTIATE_NTLM |
|
||||
NTLMSSP_REQUEST_TARGET;
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "128bit", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_128;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "56bit", False)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "lm_key", False)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "keyexchange", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "alwayssign", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "ntlm2", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
|
||||
} else {
|
||||
/* apparently we can't do ntlmv2 if we don't do ntlm2 */
|
||||
gensec_ntlmssp_state->use_ntlmv2 = False;
|
||||
}
|
||||
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
|
||||
/*
|
||||
* We need to set this to allow a later SetPassword
|
||||
* via the SAMR pipe to succeed. Strange.... We could
|
||||
* also add NTLMSSP_NEGOTIATE_SEAL here. JRA.
|
||||
*
|
||||
* Without this, Windows will not create the master key
|
||||
* that it thinks is only used for NTLMSSP signing and
|
||||
* sealing. (It is actually pulled out and used directly)
|
||||
*/
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
|
||||
}
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
|
||||
}
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
|
||||
}
|
||||
|
||||
gensec_security->private_data = gensec_ntlmssp_state;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5/SPNEGO routines
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
|
||||
Copyright (C) Andrew Bartlett 2002-2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "pstring.h"
|
||||
|
||||
/*
|
||||
this is a tiny msrpc packet generator. I am only using this to
|
||||
avoid tying this code to a particular varient of our rpc code. This
|
||||
generator is not general enough for all our rpc needs, its just
|
||||
enough for the spnego/ntlmssp code
|
||||
|
||||
format specifiers are:
|
||||
|
||||
U = unicode string (input is unix string)
|
||||
a = address (input is char *unix_string)
|
||||
(1 byte type, 1 byte length, unicode/ASCII string, all inline)
|
||||
A = ASCII string (input is unix string)
|
||||
B = data blob (pointer + length)
|
||||
b = data blob in header (pointer + length)
|
||||
D
|
||||
d = word (4 bytes)
|
||||
C = constant ascii string
|
||||
*/
|
||||
BOOL msrpc_gen(TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
|
||||
const char *format, ...)
|
||||
{
|
||||
int i;
|
||||
ssize_t n;
|
||||
va_list ap;
|
||||
char *s;
|
||||
uint8_t *b;
|
||||
int head_size=0, data_size=0;
|
||||
int head_ofs, data_ofs;
|
||||
int *intargs;
|
||||
|
||||
DATA_BLOB *pointers;
|
||||
|
||||
pointers = talloc_array(mem_ctx, DATA_BLOB, strlen(format));
|
||||
intargs = talloc_array(pointers, int, strlen(format));
|
||||
|
||||
/* first scan the format to work out the header and body size */
|
||||
va_start(ap, format);
|
||||
for (i=0; format[i]; i++) {
|
||||
switch (format[i]) {
|
||||
case 'U':
|
||||
s = va_arg(ap, char *);
|
||||
head_size += 8;
|
||||
n = push_ucs2_talloc(pointers, (void **)&pointers[i].data, s);
|
||||
if (n == -1) {
|
||||
return False;
|
||||
}
|
||||
pointers[i].length = n;
|
||||
pointers[i].length -= 2;
|
||||
data_size += pointers[i].length;
|
||||
break;
|
||||
case 'A':
|
||||
s = va_arg(ap, char *);
|
||||
head_size += 8;
|
||||
n = push_ascii_talloc(pointers, (char **)&pointers[i].data, s);
|
||||
if (n == -1) {
|
||||
return False;
|
||||
}
|
||||
pointers[i].length = n;
|
||||
pointers[i].length -= 1;
|
||||
data_size += pointers[i].length;
|
||||
break;
|
||||
case 'a':
|
||||
n = va_arg(ap, int);
|
||||
intargs[i] = n;
|
||||
s = va_arg(ap, char *);
|
||||
n = push_ucs2_talloc(pointers, (void **)&pointers[i].data, s);
|
||||
if (n == -1) {
|
||||
return False;
|
||||
}
|
||||
pointers[i].length = n;
|
||||
pointers[i].length -= 2;
|
||||
data_size += pointers[i].length + 4;
|
||||
break;
|
||||
case 'B':
|
||||
b = va_arg(ap, uint8_t *);
|
||||
head_size += 8;
|
||||
pointers[i].data = b;
|
||||
pointers[i].length = va_arg(ap, int);
|
||||
data_size += pointers[i].length;
|
||||
break;
|
||||
case 'b':
|
||||
b = va_arg(ap, uint8_t *);
|
||||
pointers[i].data = b;
|
||||
pointers[i].length = va_arg(ap, int);
|
||||
head_size += pointers[i].length;
|
||||
break;
|
||||
case 'd':
|
||||
n = va_arg(ap, int);
|
||||
intargs[i] = n;
|
||||
head_size += 4;
|
||||
break;
|
||||
case 'C':
|
||||
s = va_arg(ap, char *);
|
||||
pointers[i].data = (uint8_t *)s;
|
||||
pointers[i].length = strlen(s)+1;
|
||||
head_size += pointers[i].length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
/* allocate the space, then scan the format again to fill in the values */
|
||||
*blob = data_blob_talloc(mem_ctx, NULL, head_size + data_size);
|
||||
|
||||
head_ofs = 0;
|
||||
data_ofs = head_size;
|
||||
|
||||
va_start(ap, format);
|
||||
for (i=0; format[i]; i++) {
|
||||
switch (format[i]) {
|
||||
case 'U':
|
||||
case 'A':
|
||||
case 'B':
|
||||
n = pointers[i].length;
|
||||
SSVAL(blob->data, head_ofs, n); head_ofs += 2;
|
||||
SSVAL(blob->data, head_ofs, n); head_ofs += 2;
|
||||
SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
|
||||
if (pointers[i].data && n) /* don't follow null pointers... */
|
||||
memcpy(blob->data+data_ofs, pointers[i].data, n);
|
||||
data_ofs += n;
|
||||
break;
|
||||
case 'a':
|
||||
n = intargs[i];
|
||||
SSVAL(blob->data, data_ofs, n); data_ofs += 2;
|
||||
|
||||
n = pointers[i].length;
|
||||
SSVAL(blob->data, data_ofs, n); data_ofs += 2;
|
||||
if (n >= 0) {
|
||||
memcpy(blob->data+data_ofs, pointers[i].data, n);
|
||||
}
|
||||
data_ofs += n;
|
||||
break;
|
||||
case 'd':
|
||||
n = intargs[i];
|
||||
SIVAL(blob->data, head_ofs, n);
|
||||
head_ofs += 4;
|
||||
break;
|
||||
case 'b':
|
||||
n = pointers[i].length;
|
||||
memcpy(blob->data + head_ofs, pointers[i].data, n);
|
||||
head_ofs += n;
|
||||
break;
|
||||
case 'C':
|
||||
n = pointers[i].length;
|
||||
memcpy(blob->data + head_ofs, pointers[i].data, n);
|
||||
head_ofs += n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
talloc_free(pointers);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/* a helpful macro to avoid running over the end of our blob */
|
||||
#define NEED_DATA(amount) \
|
||||
if ((head_ofs + amount) > blob->length) { \
|
||||
return False; \
|
||||
}
|
||||
|
||||
/*
|
||||
this is a tiny msrpc packet parser. This the the partner of msrpc_gen
|
||||
|
||||
format specifiers are:
|
||||
|
||||
U = unicode string (output is unix string)
|
||||
A = ascii string
|
||||
B = data blob
|
||||
b = data blob in header
|
||||
d = word (4 bytes)
|
||||
C = constant ascii string
|
||||
*/
|
||||
|
||||
BOOL msrpc_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob,
|
||||
const char *format, ...)
|
||||
{
|
||||
int i;
|
||||
va_list ap;
|
||||
const char **ps, *s;
|
||||
DATA_BLOB *b;
|
||||
size_t head_ofs = 0;
|
||||
uint16_t len1, len2;
|
||||
uint32_t ptr;
|
||||
uint32_t *v;
|
||||
pstring p;
|
||||
|
||||
va_start(ap, format);
|
||||
for (i=0; format[i]; i++) {
|
||||
switch (format[i]) {
|
||||
case 'U':
|
||||
NEED_DATA(8);
|
||||
len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
|
||||
|
||||
ps = (const char **)va_arg(ap, char **);
|
||||
if (len1 == 0 && len2 == 0) {
|
||||
*ps = "";
|
||||
} else {
|
||||
/* make sure its in the right format - be strict */
|
||||
if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
|
||||
return False;
|
||||
}
|
||||
if (len1 & 1) {
|
||||
/* if odd length and unicode */
|
||||
return False;
|
||||
}
|
||||
if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data)
|
||||
return False;
|
||||
|
||||
if (0 < len1) {
|
||||
pull_string(p, blob->data + ptr, sizeof(p),
|
||||
len1, STR_UNICODE|STR_NOALIGN);
|
||||
(*ps) = talloc_strdup(mem_ctx, p);
|
||||
if (!(*ps)) {
|
||||
return False;
|
||||
}
|
||||
} else {
|
||||
(*ps) = "";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'A':
|
||||
NEED_DATA(8);
|
||||
len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
|
||||
|
||||
ps = (const char **)va_arg(ap, char **);
|
||||
/* make sure its in the right format - be strict */
|
||||
if (len1 == 0 && len2 == 0) {
|
||||
*ps = "";
|
||||
} else {
|
||||
if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data)
|
||||
return False;
|
||||
|
||||
if (0 < len1) {
|
||||
pull_string(p, blob->data + ptr, sizeof(p),
|
||||
len1, STR_ASCII|STR_NOALIGN);
|
||||
(*ps) = talloc_strdup(mem_ctx, p);
|
||||
if (!(*ps)) {
|
||||
return False;
|
||||
}
|
||||
} else {
|
||||
(*ps) = "";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
NEED_DATA(8);
|
||||
len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
|
||||
|
||||
b = (DATA_BLOB *)va_arg(ap, void *);
|
||||
if (len1 == 0 && len2 == 0) {
|
||||
*b = data_blob_talloc(mem_ctx, NULL, 0);
|
||||
} else {
|
||||
/* make sure its in the right format - be strict */
|
||||
if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data)
|
||||
return False;
|
||||
|
||||
*b = data_blob_talloc(mem_ctx, blob->data + ptr, len1);
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
b = (DATA_BLOB *)va_arg(ap, void *);
|
||||
len1 = va_arg(ap, uint_t);
|
||||
/* make sure its in the right format - be strict */
|
||||
NEED_DATA(len1);
|
||||
if (blob->data + head_ofs < (uint8_t *)head_ofs || blob->data + head_ofs < blob->data)
|
||||
return False;
|
||||
|
||||
*b = data_blob_talloc(mem_ctx, blob->data + head_ofs, len1);
|
||||
head_ofs += len1;
|
||||
break;
|
||||
case 'd':
|
||||
v = va_arg(ap, uint32_t *);
|
||||
NEED_DATA(4);
|
||||
*v = IVAL(blob->data, head_ofs); head_ofs += 4;
|
||||
break;
|
||||
case 'C':
|
||||
s = va_arg(ap, char *);
|
||||
|
||||
if (blob->data + head_ofs < (uint8_t *)head_ofs || blob->data + head_ofs < blob->data)
|
||||
return False;
|
||||
|
||||
head_ofs += pull_string(p, blob->data+head_ofs, sizeof(p),
|
||||
blob->length - head_ofs,
|
||||
STR_ASCII|STR_TERMINATE);
|
||||
if (strcmp(s, p) != 0) {
|
||||
return False;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return True;
|
||||
}
|
||||
@@ -0,0 +1,852 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Version 3.0
|
||||
handle NLTMSSP, client server side parsing
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/ntlmssp/ntlmssp.h"
|
||||
#include "auth/ntlmssp/msrpc_parse.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "pstring.h"
|
||||
#include "system/filesys.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
/**
|
||||
* Set a username on an NTLMSSP context - ensures it is talloc()ed
|
||||
*
|
||||
*/
|
||||
|
||||
static NTSTATUS ntlmssp_set_username(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *user)
|
||||
{
|
||||
if (!user) {
|
||||
/* it should be at least "" */
|
||||
DEBUG(1, ("NTLMSSP failed to set username - cannot accept NULL username\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
gensec_ntlmssp_state->user = talloc_strdup(gensec_ntlmssp_state, user);
|
||||
if (!gensec_ntlmssp_state->user) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a domain on an NTLMSSP context - ensures it is talloc()ed
|
||||
*
|
||||
*/
|
||||
static NTSTATUS ntlmssp_set_domain(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *domain)
|
||||
{
|
||||
gensec_ntlmssp_state->domain = talloc_strdup(gensec_ntlmssp_state, domain);
|
||||
if (!gensec_ntlmssp_state->domain) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a workstation on an NTLMSSP context - ensures it is talloc()ed
|
||||
*
|
||||
*/
|
||||
static NTSTATUS ntlmssp_set_workstation(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *workstation)
|
||||
{
|
||||
gensec_ntlmssp_state->workstation = talloc_strdup(gensec_ntlmssp_state, workstation);
|
||||
if (!gensec_ntlmssp_state->workstation) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine correct target name flags for reply, given server role
|
||||
* and negotiated flags
|
||||
*
|
||||
* @param gensec_ntlmssp_state NTLMSSP State
|
||||
* @param neg_flags The flags from the packet
|
||||
* @param chal_flags The flags to be set in the reply packet
|
||||
* @return The 'target name' string.
|
||||
*/
|
||||
|
||||
static const char *ntlmssp_target_name(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
uint32_t neg_flags, uint32_t *chal_flags)
|
||||
{
|
||||
if (neg_flags & NTLMSSP_REQUEST_TARGET) {
|
||||
*chal_flags |= NTLMSSP_CHAL_TARGET_INFO;
|
||||
*chal_flags |= NTLMSSP_REQUEST_TARGET;
|
||||
if (gensec_ntlmssp_state->server_role == ROLE_STANDALONE) {
|
||||
*chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
|
||||
return gensec_ntlmssp_state->server_name;
|
||||
} else {
|
||||
*chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
|
||||
return gensec_ntlmssp_state->get_domain();
|
||||
};
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Andrew, please remove these totally bogus calls when you get time
|
||||
*/
|
||||
static BOOL get_myfullname(char *my_name)
|
||||
{
|
||||
pstring hostname;
|
||||
|
||||
*hostname = 0;
|
||||
|
||||
/* get my host name */
|
||||
if (gethostname(hostname, sizeof(hostname)) == -1) {
|
||||
DEBUG(0,("gethostname failed\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Ensure null termination. */
|
||||
hostname[sizeof(hostname)-1] = '\0';
|
||||
|
||||
if (my_name)
|
||||
fstrcpy(my_name, hostname);
|
||||
return True;
|
||||
}
|
||||
|
||||
static BOOL get_mydomname(char *my_domname)
|
||||
{
|
||||
pstring hostname;
|
||||
char *p;
|
||||
|
||||
/* arrgh! relies on full name in system */
|
||||
|
||||
*hostname = 0;
|
||||
/* get my host name */
|
||||
if (gethostname(hostname, sizeof(hostname)) == -1) {
|
||||
DEBUG(0,("gethostname failed\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Ensure null termination. */
|
||||
hostname[sizeof(hostname)-1] = '\0';
|
||||
|
||||
p = strchr_m(hostname, '.');
|
||||
|
||||
if (!p)
|
||||
return False;
|
||||
|
||||
p++;
|
||||
|
||||
if (my_domname)
|
||||
fstrcpy(my_domname, p);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Next state function for the Negotiate packet
|
||||
*
|
||||
* @param gensec_security GENSEC state
|
||||
* @param out_mem_ctx Memory context for *out
|
||||
* @param in The request, as a DATA_BLOB. reply.data must be NULL
|
||||
* @param out The reply, as an allocated DATA_BLOB, caller to free.
|
||||
* @return Errors or MORE_PROCESSING_REQUIRED if (normal) a reply is required.
|
||||
*/
|
||||
|
||||
NTSTATUS ntlmssp_server_negotiate(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
DATA_BLOB struct_blob;
|
||||
fstring dnsname, dnsdomname;
|
||||
uint32_t neg_flags = 0;
|
||||
uint32_t ntlmssp_command, chal_flags;
|
||||
const uint8_t *cryptkey;
|
||||
const char *target_name;
|
||||
|
||||
/* parse the NTLMSSP packet */
|
||||
#if 0
|
||||
file_save("ntlmssp_negotiate.dat", request.data, request.length);
|
||||
#endif
|
||||
|
||||
if (in.length) {
|
||||
if ((in.length < 16) || !msrpc_parse(out_mem_ctx, &in, "Cdd",
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&neg_flags)) {
|
||||
DEBUG(1, ("ntlmssp_server_negotiate: failed to parse "
|
||||
"NTLMSSP Negotiate of length %u:\n",
|
||||
(unsigned int)in.length ));
|
||||
dump_data(2, in.data, in.length);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
debug_ntlmssp_flags(neg_flags);
|
||||
}
|
||||
|
||||
ntlmssp_handle_neg_flags(gensec_ntlmssp_state, neg_flags, gensec_ntlmssp_state->allow_lm_key);
|
||||
|
||||
/* Ask our caller what challenge they would like in the packet */
|
||||
cryptkey = gensec_ntlmssp_state->get_challenge(gensec_ntlmssp_state);
|
||||
|
||||
/* Check if we may set the challenge */
|
||||
if (!gensec_ntlmssp_state->may_set_challenge(gensec_ntlmssp_state)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
|
||||
}
|
||||
|
||||
/* The flags we send back are not just the negotiated flags,
|
||||
* they are also 'what is in this packet'. Therfore, we
|
||||
* operate on 'chal_flags' from here on
|
||||
*/
|
||||
|
||||
chal_flags = gensec_ntlmssp_state->neg_flags;
|
||||
|
||||
/* get the right name to fill in as 'target' */
|
||||
target_name = ntlmssp_target_name(gensec_ntlmssp_state,
|
||||
neg_flags, &chal_flags);
|
||||
if (target_name == NULL)
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
|
||||
gensec_ntlmssp_state->chal = data_blob_talloc(gensec_ntlmssp_state, cryptkey, 8);
|
||||
gensec_ntlmssp_state->internal_chal = data_blob_talloc(gensec_ntlmssp_state, cryptkey, 8);
|
||||
|
||||
/* This should be a 'netbios domain -> DNS domain' mapping */
|
||||
dnsdomname[0] = '\0';
|
||||
get_mydomname(dnsdomname);
|
||||
strlower_m(dnsdomname);
|
||||
|
||||
dnsname[0] = '\0';
|
||||
get_myfullname(dnsname);
|
||||
|
||||
/* This creates the 'blob' of names that appears at the end of the packet */
|
||||
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO)
|
||||
{
|
||||
const char *target_name_dns = "";
|
||||
if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) {
|
||||
target_name_dns = dnsdomname;
|
||||
} else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) {
|
||||
target_name_dns = dnsname;
|
||||
}
|
||||
|
||||
msrpc_gen(out_mem_ctx,
|
||||
&struct_blob, "aaaaa",
|
||||
NTLMSSP_NAME_TYPE_DOMAIN, target_name,
|
||||
NTLMSSP_NAME_TYPE_SERVER, gensec_ntlmssp_state->server_name,
|
||||
NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname,
|
||||
NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname,
|
||||
0, "");
|
||||
} else {
|
||||
struct_blob = data_blob(NULL, 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Marshel the packet in the right format, be it unicode or ASCII */
|
||||
const char *gen_string;
|
||||
if (gensec_ntlmssp_state->unicode) {
|
||||
gen_string = "CdUdbddB";
|
||||
} else {
|
||||
gen_string = "CdAdbddB";
|
||||
}
|
||||
|
||||
msrpc_gen(out_mem_ctx,
|
||||
out, gen_string,
|
||||
"NTLMSSP",
|
||||
NTLMSSP_CHALLENGE,
|
||||
target_name,
|
||||
chal_flags,
|
||||
cryptkey, 8,
|
||||
0, 0,
|
||||
struct_blob.data, struct_blob.length);
|
||||
}
|
||||
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_AUTH;
|
||||
|
||||
return NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Next state function for the Authenticate packet
|
||||
*
|
||||
* @param gensec_ntlmssp_state NTLMSSP State
|
||||
* @param request The request, as a DATA_BLOB
|
||||
* @return Errors or NT_STATUS_OK.
|
||||
*/
|
||||
|
||||
static NTSTATUS ntlmssp_server_preauth(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
const DATA_BLOB request)
|
||||
{
|
||||
uint32_t ntlmssp_command, auth_flags;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
uint8_t session_nonce_hash[16];
|
||||
|
||||
const char *parse_string;
|
||||
char *domain = NULL;
|
||||
char *user = NULL;
|
||||
char *workstation = NULL;
|
||||
|
||||
#if 0
|
||||
file_save("ntlmssp_auth.dat", request.data, request.length);
|
||||
#endif
|
||||
|
||||
if (gensec_ntlmssp_state->unicode) {
|
||||
parse_string = "CdBBUUUBd";
|
||||
} else {
|
||||
parse_string = "CdBBAAABd";
|
||||
}
|
||||
|
||||
/* zero these out */
|
||||
data_blob_free(&gensec_ntlmssp_state->lm_resp);
|
||||
data_blob_free(&gensec_ntlmssp_state->nt_resp);
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
|
||||
gensec_ntlmssp_state->user = NULL;
|
||||
gensec_ntlmssp_state->domain = NULL;
|
||||
gensec_ntlmssp_state->workstation = NULL;
|
||||
|
||||
/* now the NTLMSSP encoded auth hashes */
|
||||
if (!msrpc_parse(gensec_ntlmssp_state,
|
||||
&request, parse_string,
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&gensec_ntlmssp_state->lm_resp,
|
||||
&gensec_ntlmssp_state->nt_resp,
|
||||
&domain,
|
||||
&user,
|
||||
&workstation,
|
||||
&gensec_ntlmssp_state->encrypted_session_key,
|
||||
&auth_flags)) {
|
||||
DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n"));
|
||||
dump_data(10, request.data, request.length);
|
||||
|
||||
/* zero this out */
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
auth_flags = 0;
|
||||
|
||||
/* Try again with a shorter string (Win9X truncates this packet) */
|
||||
if (gensec_ntlmssp_state->unicode) {
|
||||
parse_string = "CdBBUUU";
|
||||
} else {
|
||||
parse_string = "CdBBAAA";
|
||||
}
|
||||
|
||||
/* now the NTLMSSP encoded auth hashes */
|
||||
if (!msrpc_parse(gensec_ntlmssp_state,
|
||||
&request, parse_string,
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&gensec_ntlmssp_state->lm_resp,
|
||||
&gensec_ntlmssp_state->nt_resp,
|
||||
&domain,
|
||||
&user,
|
||||
&workstation)) {
|
||||
DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
|
||||
dump_data(2, request.data, request.length);
|
||||
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
if (auth_flags)
|
||||
ntlmssp_handle_neg_flags(gensec_ntlmssp_state, auth_flags, gensec_ntlmssp_state->allow_lm_key);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(gensec_ntlmssp_state, domain))) {
|
||||
/* zero this out */
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(gensec_ntlmssp_state, user))) {
|
||||
/* zero this out */
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(gensec_ntlmssp_state, workstation))) {
|
||||
/* zero this out */
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n",
|
||||
gensec_ntlmssp_state->user, gensec_ntlmssp_state->domain, gensec_ntlmssp_state->workstation, (unsigned long)gensec_ntlmssp_state->lm_resp.length, (unsigned long)gensec_ntlmssp_state->nt_resp.length));
|
||||
|
||||
#if 0
|
||||
file_save("nthash1.dat", &gensec_ntlmssp_state->nt_resp.data, &gensec_ntlmssp_state->nt_resp.length);
|
||||
file_save("lmhash1.dat", &gensec_ntlmssp_state->lm_resp.data, &gensec_ntlmssp_state->lm_resp.length);
|
||||
#endif
|
||||
|
||||
/* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a
|
||||
client challenge
|
||||
|
||||
However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful.
|
||||
*/
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
if (gensec_ntlmssp_state->nt_resp.length == 24 && gensec_ntlmssp_state->lm_resp.length == 24) {
|
||||
struct MD5Context md5_session_nonce_ctx;
|
||||
SMB_ASSERT(gensec_ntlmssp_state->internal_chal.data
|
||||
&& gensec_ntlmssp_state->internal_chal.length == 8);
|
||||
|
||||
gensec_ntlmssp_state->doing_ntlm2 = True;
|
||||
|
||||
memcpy(gensec_ntlmssp_state->crypt.ntlm2.session_nonce, gensec_ntlmssp_state->internal_chal.data, 8);
|
||||
memcpy(&gensec_ntlmssp_state->crypt.ntlm2.session_nonce[8], gensec_ntlmssp_state->lm_resp.data, 8);
|
||||
|
||||
MD5Init(&md5_session_nonce_ctx);
|
||||
MD5Update(&md5_session_nonce_ctx, gensec_ntlmssp_state->crypt.ntlm2.session_nonce, 16);
|
||||
MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
|
||||
|
||||
gensec_ntlmssp_state->chal = data_blob_talloc(gensec_ntlmssp_state,
|
||||
session_nonce_hash, 8);
|
||||
|
||||
/* LM response is no longer useful, zero it out */
|
||||
data_blob_free(&gensec_ntlmssp_state->lm_resp);
|
||||
|
||||
/* We changed the effective challenge - set it */
|
||||
if (!NT_STATUS_IS_OK(nt_status =
|
||||
gensec_ntlmssp_state->set_challenge(gensec_ntlmssp_state,
|
||||
&gensec_ntlmssp_state->chal))) {
|
||||
/* zero this out */
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
}
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Next state function for the Authenticate packet
|
||||
* (after authentication - figures out the session keys etc)
|
||||
*
|
||||
* @param gensec_ntlmssp_state NTLMSSP State
|
||||
* @return Errors or NT_STATUS_OK.
|
||||
*/
|
||||
|
||||
static NTSTATUS ntlmssp_server_postauth(struct gensec_security *gensec_security,
|
||||
DATA_BLOB *user_session_key,
|
||||
DATA_BLOB *lm_session_key)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
NTSTATUS nt_status;
|
||||
DATA_BLOB session_key = data_blob(NULL, 0);
|
||||
|
||||
if (user_session_key)
|
||||
dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length);
|
||||
|
||||
if (lm_session_key)
|
||||
dump_data_pw("LM first-8:\n", lm_session_key->data, lm_session_key->length);
|
||||
|
||||
/* Handle the different session key derivation for NTLM2 */
|
||||
if (gensec_ntlmssp_state->doing_ntlm2) {
|
||||
if (user_session_key && user_session_key->data && user_session_key->length == 16) {
|
||||
session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
|
||||
hmac_md5(user_session_key->data, gensec_ntlmssp_state->crypt.ntlm2.session_nonce,
|
||||
sizeof(gensec_ntlmssp_state->crypt.ntlm2.session_nonce), session_key.data);
|
||||
DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n"));
|
||||
dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
|
||||
|
||||
} else {
|
||||
DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n"));
|
||||
session_key = data_blob(NULL, 0);
|
||||
}
|
||||
} else if ((gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
|
||||
/* Ensure we can never get here on NTLMv2 */
|
||||
&& (gensec_ntlmssp_state->nt_resp.length == 0 || gensec_ntlmssp_state->nt_resp.length == 24)) {
|
||||
|
||||
if (lm_session_key && lm_session_key->data && lm_session_key->length >= 8) {
|
||||
if (gensec_ntlmssp_state->lm_resp.data && gensec_ntlmssp_state->lm_resp.length == 24) {
|
||||
session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
|
||||
SMBsesskeygen_lm_sess_key(lm_session_key->data, gensec_ntlmssp_state->lm_resp.data,
|
||||
session_key.data);
|
||||
DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
|
||||
dump_data_pw("LM session key:\n", session_key.data, session_key.length);
|
||||
} else {
|
||||
|
||||
/* When there is no LM response, just use zeros */
|
||||
static const uint8_t zeros[24];
|
||||
session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
|
||||
SMBsesskeygen_lm_sess_key(zeros, zeros,
|
||||
session_key.data);
|
||||
DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
|
||||
dump_data_pw("LM session key:\n", session_key.data, session_key.length);
|
||||
}
|
||||
} else {
|
||||
/* LM Key not selected */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
|
||||
DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n"));
|
||||
session_key = data_blob(NULL, 0);
|
||||
}
|
||||
|
||||
} else if (user_session_key && user_session_key->data) {
|
||||
session_key = *user_session_key;
|
||||
DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n"));
|
||||
dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
|
||||
|
||||
/* LM Key not selected */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
|
||||
} else if (lm_session_key && lm_session_key->data) {
|
||||
/* Very weird to have LM key, but no user session key, but anyway.. */
|
||||
session_key = *lm_session_key;
|
||||
DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n"));
|
||||
dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
|
||||
|
||||
/* LM Key not selected */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
|
||||
} else {
|
||||
DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n"));
|
||||
session_key = data_blob(NULL, 0);
|
||||
|
||||
/* LM Key not selected */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
}
|
||||
|
||||
/* With KEY_EXCH, the client supplies the proposed session key,
|
||||
but encrypts it with the long-term key */
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
|
||||
if (!gensec_ntlmssp_state->encrypted_session_key.data
|
||||
|| gensec_ntlmssp_state->encrypted_session_key.length != 16) {
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n",
|
||||
(unsigned)gensec_ntlmssp_state->encrypted_session_key.length));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
} else if (!session_key.data || session_key.length != 16) {
|
||||
DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n",
|
||||
(unsigned)session_key.length));
|
||||
gensec_ntlmssp_state->session_key = session_key;
|
||||
} else {
|
||||
dump_data_pw("KEY_EXCH session key (enc):\n",
|
||||
gensec_ntlmssp_state->encrypted_session_key.data,
|
||||
gensec_ntlmssp_state->encrypted_session_key.length);
|
||||
arcfour_crypt(gensec_ntlmssp_state->encrypted_session_key.data,
|
||||
session_key.data,
|
||||
gensec_ntlmssp_state->encrypted_session_key.length);
|
||||
gensec_ntlmssp_state->session_key = data_blob_talloc(gensec_ntlmssp_state,
|
||||
gensec_ntlmssp_state->encrypted_session_key.data,
|
||||
gensec_ntlmssp_state->encrypted_session_key.length);
|
||||
dump_data_pw("KEY_EXCH session key:\n", gensec_ntlmssp_state->encrypted_session_key.data,
|
||||
gensec_ntlmssp_state->encrypted_session_key.length);
|
||||
}
|
||||
} else {
|
||||
gensec_ntlmssp_state->session_key = session_key;
|
||||
}
|
||||
|
||||
/* keep the session key around on the new context */
|
||||
talloc_steal(gensec_ntlmssp_state, session_key.data);
|
||||
|
||||
if ((gensec_security->want_features & GENSEC_FEATURE_SIGN)
|
||||
|| (gensec_security->want_features & GENSEC_FEATURE_SEAL)) {
|
||||
nt_status = ntlmssp_sign_init(gensec_ntlmssp_state);
|
||||
} else {
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
|
||||
/* allow arbitarily many authentications, but watch that this will cause a
|
||||
memory leak, until the gensec_ntlmssp_state is shutdown
|
||||
*/
|
||||
|
||||
if (gensec_ntlmssp_state->server_multiple_authentications) {
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_AUTH;
|
||||
} else {
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_DONE;
|
||||
}
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Next state function for the Authenticate packet
|
||||
*
|
||||
* @param gensec_security GENSEC state
|
||||
* @param out_mem_ctx Memory context for *out
|
||||
* @param in The request, as a DATA_BLOB. reply.data must be NULL
|
||||
* @param out The reply, as an allocated DATA_BLOB, caller to free.
|
||||
* @return Errors or NT_STATUS_OK if authentication sucessful
|
||||
*/
|
||||
|
||||
NTSTATUS ntlmssp_server_auth(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
DATA_BLOB user_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB lm_session_key = data_blob(NULL, 0);
|
||||
NTSTATUS nt_status;
|
||||
|
||||
TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* zero the outbound NTLMSSP packet */
|
||||
*out = data_blob_talloc(out_mem_ctx, NULL, 0);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(gensec_ntlmssp_state, in))) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth
|
||||
* is required (by "ntlm auth = no" and "lm auth = no" being set in the
|
||||
* smb.conf file) and no NTLMv2 response was sent then the password check
|
||||
* will fail here. JRA.
|
||||
*/
|
||||
|
||||
/* Finally, actually ask if the password is OK */
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = gensec_ntlmssp_state->check_password(gensec_ntlmssp_state, mem_ctx,
|
||||
&user_session_key, &lm_session_key))) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (gensec_security->want_features
|
||||
& (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL|GENSEC_FEATURE_SESSION_KEY)) {
|
||||
nt_status = ntlmssp_server_postauth(gensec_security, &user_session_key, &lm_session_key);
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
} else {
|
||||
gensec_ntlmssp_state->session_key = data_blob(NULL, 0);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the challenge as determined by the authentication subsystem
|
||||
* @return an 8 byte random challenge
|
||||
*/
|
||||
|
||||
static const uint8_t *auth_ntlmssp_get_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
|
||||
{
|
||||
NTSTATUS status;
|
||||
const uint8_t *chal;
|
||||
|
||||
status = auth_get_challenge(gensec_ntlmssp_state->auth_context, &chal);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return chal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some authentication methods 'fix' the challenge, so we may not be able to set it
|
||||
*
|
||||
* @return If the effective challenge used by the auth subsystem may be modified
|
||||
*/
|
||||
static BOOL auth_ntlmssp_may_set_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
|
||||
{
|
||||
return auth_challenge_may_be_modified(gensec_ntlmssp_state->auth_context);
|
||||
}
|
||||
|
||||
/**
|
||||
* NTLM2 authentication modifies the effective challenge,
|
||||
* @param challenge The new challenge value
|
||||
*/
|
||||
static NTSTATUS auth_ntlmssp_set_challenge(struct gensec_ntlmssp_state *gensec_ntlmssp_state, DATA_BLOB *challenge)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_context *auth_context = gensec_ntlmssp_state->auth_context;
|
||||
const uint8_t *chal;
|
||||
|
||||
if (challenge->length != 8) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
chal = challenge->data;
|
||||
|
||||
nt_status = auth_context_set_challenge(auth_context, chal, "NTLMSSP callback (NTLM2)");
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the password on an NTLMSSP login.
|
||||
*
|
||||
* Return the session keys used on the connection.
|
||||
*/
|
||||
|
||||
static NTSTATUS auth_ntlmssp_check_password(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_usersupplied_info *user_info = talloc(mem_ctx, struct auth_usersupplied_info);
|
||||
if (!user_info) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
user_info->logon_parameters = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
|
||||
user_info->flags = 0;
|
||||
user_info->mapped_state = False;
|
||||
user_info->client.account_name = gensec_ntlmssp_state->user;
|
||||
user_info->client.domain_name = gensec_ntlmssp_state->domain;
|
||||
user_info->workstation_name = gensec_ntlmssp_state->workstation;
|
||||
user_info->remote_host = gensec_get_peer_addr(gensec_ntlmssp_state->gensec_security);
|
||||
|
||||
user_info->password_state = AUTH_PASSWORD_RESPONSE;
|
||||
user_info->password.response.lanman = gensec_ntlmssp_state->lm_resp;
|
||||
user_info->password.response.lanman.data = talloc_steal(user_info, gensec_ntlmssp_state->lm_resp.data);
|
||||
user_info->password.response.nt = gensec_ntlmssp_state->nt_resp;
|
||||
user_info->password.response.nt.data = talloc_steal(user_info, gensec_ntlmssp_state->nt_resp.data);
|
||||
|
||||
nt_status = auth_check_password(gensec_ntlmssp_state->auth_context, mem_ctx,
|
||||
user_info, &gensec_ntlmssp_state->server_info);
|
||||
talloc_free(user_info);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
talloc_steal(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info);
|
||||
|
||||
if (gensec_ntlmssp_state->server_info->user_session_key.length) {
|
||||
DEBUG(10, ("Got NT session key of length %u\n",
|
||||
(unsigned)gensec_ntlmssp_state->server_info->user_session_key.length));
|
||||
if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->user_session_key.data)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
*user_session_key = gensec_ntlmssp_state->server_info->user_session_key;
|
||||
}
|
||||
if (gensec_ntlmssp_state->server_info->lm_session_key.length) {
|
||||
DEBUG(10, ("Got LM session key of length %u\n",
|
||||
(unsigned)gensec_ntlmssp_state->server_info->lm_session_key.length));
|
||||
if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->lm_session_key.data)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
*lm_session_key = gensec_ntlmssp_state->server_info->lm_session_key;
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the credentials of a logged on user, including session keys
|
||||
* etc.
|
||||
*
|
||||
* Only valid after a successful authentication
|
||||
*
|
||||
* May only be called once per authentication.
|
||||
*
|
||||
*/
|
||||
|
||||
NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_security,
|
||||
struct auth_session_info **session_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
nt_status = auth_generate_session_info(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info, session_info);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
(*session_info)->session_key = data_blob_talloc(*session_info,
|
||||
gensec_ntlmssp_state->session_key.data,
|
||||
gensec_ntlmssp_state->session_key.length);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start NTLMSSP on the server side
|
||||
*
|
||||
*/
|
||||
NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state;
|
||||
|
||||
nt_status = gensec_ntlmssp_start(gensec_security);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
gensec_ntlmssp_state->role = NTLMSSP_SERVER;
|
||||
|
||||
gensec_ntlmssp_state->workstation = NULL;
|
||||
gensec_ntlmssp_state->server_name = lp_netbios_name();
|
||||
|
||||
gensec_ntlmssp_state->get_domain = lp_workgroup;
|
||||
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_NEGOTIATE;
|
||||
|
||||
gensec_ntlmssp_state->allow_lm_key = (lp_lanman_auth()
|
||||
&& lp_parm_bool(-1, "ntlmssp_server", "allow_lm_key", False));
|
||||
|
||||
gensec_ntlmssp_state->server_multiple_authentications = False;
|
||||
|
||||
gensec_ntlmssp_state->neg_flags =
|
||||
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_UNKNOWN_02000000;
|
||||
|
||||
gensec_ntlmssp_state->lm_resp = data_blob(NULL, 0);
|
||||
gensec_ntlmssp_state->nt_resp = data_blob(NULL, 0);
|
||||
gensec_ntlmssp_state->encrypted_session_key = data_blob(NULL, 0);
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_server", "128bit", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_128;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_server", "56bit", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_server", "keyexchange", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_server", "alwayssign", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_server", "ntlm2", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
|
||||
}
|
||||
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
|
||||
}
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
|
||||
}
|
||||
|
||||
nt_status = auth_context_create(gensec_ntlmssp_state, lp_auth_methods(),
|
||||
gensec_security->event_ctx,
|
||||
gensec_security->msg_ctx,
|
||||
&gensec_ntlmssp_state->auth_context);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
gensec_ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge;
|
||||
gensec_ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge;
|
||||
gensec_ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge;
|
||||
gensec_ntlmssp_state->check_password = auth_ntlmssp_check_password;
|
||||
gensec_ntlmssp_state->server_role = lp_server_role();
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* Version 3.0
|
||||
* NTLMSSP Signing routines
|
||||
* Copyright (C) Luke Kenneth Casson Leighton 1996-2001
|
||||
* Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2005
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/ntlmssp/ntlmssp.h"
|
||||
#include "auth/ntlmssp/msrpc_parse.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
#define CLI_SIGN "session key to client-to-server signing key magic constant"
|
||||
#define CLI_SEAL "session key to client-to-server sealing key magic constant"
|
||||
#define SRV_SIGN "session key to server-to-client signing key magic constant"
|
||||
#define SRV_SEAL "session key to server-to-client sealing key magic constant"
|
||||
|
||||
/**
|
||||
* Some notes on the NTLM2 code:
|
||||
*
|
||||
* NTLM2 is a AEAD system. This means that the data encrypted is not
|
||||
* all the data that is signed. In DCE-RPC case, the headers of the
|
||||
* DCE-RPC packets are also signed. This prevents some of the
|
||||
* fun-and-games one might have by changing them.
|
||||
*
|
||||
*/
|
||||
|
||||
static void calc_ntlmv2_key(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *subkey,
|
||||
DATA_BLOB session_key,
|
||||
const char *constant)
|
||||
{
|
||||
struct MD5Context ctx3;
|
||||
*subkey = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
MD5Init(&ctx3);
|
||||
MD5Update(&ctx3, session_key.data, session_key.length);
|
||||
MD5Update(&ctx3, (const uint8_t *)constant, strlen(constant)+1);
|
||||
MD5Final(subkey->data, &ctx3);
|
||||
}
|
||||
|
||||
enum ntlmssp_direction {
|
||||
NTLMSSP_SEND,
|
||||
NTLMSSP_RECEIVE
|
||||
};
|
||||
|
||||
static NTSTATUS ntlmssp_make_packet_signature(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
enum ntlmssp_direction direction,
|
||||
DATA_BLOB *sig, BOOL encrypt_sig)
|
||||
{
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
|
||||
HMACMD5Context ctx;
|
||||
uint8_t digest[16];
|
||||
uint8_t seq_num[4];
|
||||
|
||||
*sig = data_blob_talloc(sig_mem_ctx, NULL, NTLMSSP_SIG_SIZE);
|
||||
if (!sig->data) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case NTLMSSP_SEND:
|
||||
SIVAL(seq_num, 0, gensec_ntlmssp_state->crypt.ntlm2.send_seq_num);
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_seq_num++;
|
||||
hmac_md5_init_limK_to_64(gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.data,
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.length, &ctx);
|
||||
break;
|
||||
case NTLMSSP_RECEIVE:
|
||||
SIVAL(seq_num, 0, gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num);
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num++;
|
||||
hmac_md5_init_limK_to_64(gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.data,
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.length, &ctx);
|
||||
break;
|
||||
}
|
||||
hmac_md5_update(seq_num, sizeof(seq_num), &ctx);
|
||||
hmac_md5_update(whole_pdu, pdu_length, &ctx);
|
||||
hmac_md5_final(digest, &ctx);
|
||||
|
||||
if (encrypt_sig && gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
|
||||
switch (direction) {
|
||||
case NTLMSSP_SEND:
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, digest, 8);
|
||||
break;
|
||||
case NTLMSSP_RECEIVE:
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state, digest, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SIVAL(sig->data, 0, NTLMSSP_SIGN_VERSION);
|
||||
memcpy(sig->data + 4, digest, 8);
|
||||
memcpy(sig->data + 12, seq_num, 4);
|
||||
|
||||
DEBUG(10, ("NTLM2: created signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
|
||||
dump_data(11, sig->data, sig->length);
|
||||
|
||||
} else {
|
||||
uint32_t crc;
|
||||
crc = crc32_calc_buffer(data, length);
|
||||
if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
gensec_ntlmssp_state->crypt.ntlm.seq_num++;
|
||||
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, sig->data+4, sig->length-4);
|
||||
|
||||
DEBUG(10, ("NTLM1: created signature over %llu bytes of input:\n", (unsigned long long)length));
|
||||
dump_data(11, sig->data, sig->length);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* TODO: make this non-public */
|
||||
_PUBLIC_ NTSTATUS gensec_ntlmssp_sign_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
return ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
|
||||
data, length,
|
||||
whole_pdu, pdu_length,
|
||||
NTLMSSP_SEND, sig, True);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the signature of an incoming packet
|
||||
*
|
||||
*/
|
||||
|
||||
NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
DATA_BLOB local_sig;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
if (!gensec_ntlmssp_state->session_key.length) {
|
||||
DEBUG(3, ("NO session key, cannot check packet signature\n"));
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
|
||||
if (sig->length < 8) {
|
||||
DEBUG(0, ("NTLMSSP packet check failed due to short signature (%lu bytes)!\n",
|
||||
(unsigned long)sig->length));
|
||||
}
|
||||
|
||||
nt_status = ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
|
||||
data, length,
|
||||
whole_pdu, pdu_length,
|
||||
NTLMSSP_RECEIVE, &local_sig, True);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
if (local_sig.length != sig->length ||
|
||||
memcmp(local_sig.data,
|
||||
sig->data, sig->length) != 0) {
|
||||
DEBUG(5, ("BAD SIG NTLM2: wanted signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
|
||||
dump_data(5, local_sig.data, local_sig.length);
|
||||
|
||||
DEBUG(5, ("BAD SIG: got signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
|
||||
dump_data(5, sig->data, sig->length);
|
||||
|
||||
DEBUG(0, ("NTLMSSP NTLM2 packet check failed due to invalid signature on %llu bytes of input!\n", (unsigned long long)pdu_length));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
} else {
|
||||
if (local_sig.length != sig->length ||
|
||||
memcmp(local_sig.data + 8,
|
||||
sig->data + 8, sig->length - 8) != 0) {
|
||||
DEBUG(5, ("BAD SIG NTLM1: wanted signature of %llu bytes of input:\n", (unsigned long long)length));
|
||||
dump_data(5, local_sig.data, local_sig.length);
|
||||
|
||||
DEBUG(5, ("BAD SIG: got signature of %llu bytes of input:\n", (unsigned long long)length));
|
||||
dump_data(5, sig->data, sig->length);
|
||||
|
||||
DEBUG(0, ("NTLMSSP NTLM1 packet check failed due to invalid signature on %llu bytes of input:\n", (unsigned long long)length));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
dump_data_pw("checked ntlmssp signature\n", sig->data, sig->length);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Seal data with the NTLMSSP algorithm
|
||||
*
|
||||
*/
|
||||
|
||||
NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
NTSTATUS nt_status;
|
||||
if (!gensec_ntlmssp_state->session_key.length) {
|
||||
DEBUG(3, ("NO session key, cannot seal packet\n"));
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
|
||||
DEBUG(10,("ntlmssp_seal_data: seal\n"));
|
||||
dump_data_pw("ntlmssp clear data\n", data, length);
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
/* The order of these two operations matters - we must first seal the packet,
|
||||
then seal the sequence number - this is becouse the send_seal_hash is not
|
||||
constant, but is is rather updated with each iteration */
|
||||
nt_status = ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
|
||||
data, length,
|
||||
whole_pdu, pdu_length,
|
||||
NTLMSSP_SEND, sig, False);
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, data, length);
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, sig->data+4, 8);
|
||||
}
|
||||
} else {
|
||||
uint32_t crc;
|
||||
crc = crc32_calc_buffer(data, length);
|
||||
if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* The order of these two operations matters - we must
|
||||
first seal the packet, then seal the sequence
|
||||
number - this is becouse the ntlmssp_hash is not
|
||||
constant, but is is rather updated with each
|
||||
iteration */
|
||||
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, data, length);
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, sig->data+4, sig->length-4);
|
||||
/* increment counter on send */
|
||||
gensec_ntlmssp_state->crypt.ntlm.seq_num++;
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
dump_data_pw("ntlmssp signature\n", sig->data, sig->length);
|
||||
dump_data_pw("ntlmssp sealed data\n", data, length);
|
||||
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unseal data with the NTLMSSP algorithm
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
wrappers for the ntlmssp_*() functions
|
||||
*/
|
||||
NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
if (!gensec_ntlmssp_state->session_key.length) {
|
||||
DEBUG(3, ("NO session key, cannot unseal packet\n"));
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
|
||||
dump_data_pw("ntlmssp sealed data\n", data, length);
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state, data, length);
|
||||
} else {
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, data, length);
|
||||
}
|
||||
dump_data_pw("ntlmssp clear data\n", data, length);
|
||||
return gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx, data, length, whole_pdu, pdu_length, sig);
|
||||
}
|
||||
|
||||
/**
|
||||
Initialise the state for NTLMSSP signing.
|
||||
*/
|
||||
/* TODO: make this non-public */
|
||||
_PUBLIC_ NTSTATUS ntlmssp_sign_init(struct gensec_ntlmssp_state *gensec_ntlmssp_state)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx = talloc_new(gensec_ntlmssp_state);
|
||||
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n"));
|
||||
debug_ntlmssp_flags(gensec_ntlmssp_state->neg_flags);
|
||||
|
||||
if (gensec_ntlmssp_state->session_key.length < 8) {
|
||||
talloc_free(mem_ctx);
|
||||
DEBUG(3, ("NO session key, cannot intialise signing\n"));
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
|
||||
{
|
||||
DATA_BLOB weak_session_key = gensec_ntlmssp_state->session_key;
|
||||
const char *send_sign_const;
|
||||
const char *send_seal_const;
|
||||
const char *recv_sign_const;
|
||||
const char *recv_seal_const;
|
||||
|
||||
DATA_BLOB send_seal_key;
|
||||
DATA_BLOB recv_seal_key;
|
||||
|
||||
switch (gensec_ntlmssp_state->role) {
|
||||
case NTLMSSP_CLIENT:
|
||||
send_sign_const = CLI_SIGN;
|
||||
send_seal_const = CLI_SEAL;
|
||||
recv_sign_const = SRV_SIGN;
|
||||
recv_seal_const = SRV_SEAL;
|
||||
break;
|
||||
case NTLMSSP_SERVER:
|
||||
send_sign_const = SRV_SIGN;
|
||||
send_seal_const = SRV_SEAL;
|
||||
recv_sign_const = CLI_SIGN;
|
||||
recv_seal_const = CLI_SEAL;
|
||||
break;
|
||||
default:
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state);
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state);
|
||||
|
||||
/**
|
||||
Weaken NTLMSSP keys to cope with down-level
|
||||
clients, servers and export restrictions.
|
||||
|
||||
We probably should have some parameters to control
|
||||
this, once we get NTLM2 working.
|
||||
*/
|
||||
|
||||
/* Key weakening was not performed on the master key
|
||||
* for NTLM2 (in ntlmssp_weaken_keys()), but must be
|
||||
* done on the encryption subkeys only. That is why
|
||||
* we don't have this code for the ntlmv1 case.
|
||||
*/
|
||||
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) {
|
||||
|
||||
} else if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
|
||||
weak_session_key.length = 7;
|
||||
} else { /* forty bits */
|
||||
weak_session_key.length = 5;
|
||||
}
|
||||
dump_data_pw("NTLMSSP weakend master key:\n",
|
||||
weak_session_key.data,
|
||||
weak_session_key.length);
|
||||
|
||||
/* SEND: sign key */
|
||||
calc_ntlmv2_key(gensec_ntlmssp_state,
|
||||
&gensec_ntlmssp_state->crypt.ntlm2.send_sign_key,
|
||||
gensec_ntlmssp_state->session_key, send_sign_const);
|
||||
dump_data_pw("NTLMSSP send sign key:\n",
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.data,
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.length);
|
||||
|
||||
/* SEND: seal ARCFOUR pad */
|
||||
calc_ntlmv2_key(mem_ctx,
|
||||
&send_seal_key,
|
||||
weak_session_key, send_seal_const);
|
||||
dump_data_pw("NTLMSSP send seal key:\n",
|
||||
send_seal_key.data,
|
||||
send_seal_key.length);
|
||||
arcfour_init(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state,
|
||||
&send_seal_key);
|
||||
dump_data_pw("NTLMSSP send sesl hash:\n",
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state->sbox,
|
||||
sizeof(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state->sbox));
|
||||
|
||||
/* RECV: sign key */
|
||||
calc_ntlmv2_key(gensec_ntlmssp_state,
|
||||
&gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key,
|
||||
gensec_ntlmssp_state->session_key, recv_sign_const);
|
||||
dump_data_pw("NTLMSSP recv sign key:\n",
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.data,
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.length);
|
||||
|
||||
/* RECV: seal ARCFOUR pad */
|
||||
calc_ntlmv2_key(mem_ctx,
|
||||
&recv_seal_key,
|
||||
weak_session_key, recv_seal_const);
|
||||
dump_data_pw("NTLMSSP recv seal key:\n",
|
||||
recv_seal_key.data,
|
||||
recv_seal_key.length);
|
||||
arcfour_init(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state,
|
||||
&recv_seal_key);
|
||||
dump_data_pw("NTLMSSP receive seal hash:\n",
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state->sbox,
|
||||
sizeof(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state->sbox));
|
||||
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_seq_num = 0;
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num = 0;
|
||||
|
||||
} else {
|
||||
DATA_BLOB weak_session_key = ntlmssp_weakend_key(gensec_ntlmssp_state, mem_ctx);
|
||||
DEBUG(5, ("NTLMSSP Sign/Seal - using NTLM1\n"));
|
||||
|
||||
gensec_ntlmssp_state->crypt.ntlm.arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm.arcfour_state);
|
||||
|
||||
arcfour_init(gensec_ntlmssp_state->crypt.ntlm.arcfour_state,
|
||||
&weak_session_key);
|
||||
dump_data_pw("NTLMSSP hash:\n", gensec_ntlmssp_state->crypt.ntlm.arcfour_state->sbox,
|
||||
sizeof(gensec_ntlmssp_state->crypt.ntlm.arcfour_state->sbox));
|
||||
|
||||
gensec_ntlmssp_state->crypt.ntlm.seq_num = 0;
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
size_t gensec_ntlmssp_sig_size(struct gensec_security *gensec_security, size_t data_size)
|
||||
{
|
||||
return NTLMSSP_SIG_SIZE;
|
||||
}
|
||||
|
||||
NTSTATUS gensec_ntlmssp_wrap(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out)
|
||||
{
|
||||
DATA_BLOB sig;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
|
||||
|
||||
*out = data_blob_talloc(sig_mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE);
|
||||
if (!out->data) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length);
|
||||
|
||||
nt_status = gensec_ntlmssp_seal_packet(gensec_security, sig_mem_ctx,
|
||||
out->data + NTLMSSP_SIG_SIZE,
|
||||
out->length - NTLMSSP_SIG_SIZE,
|
||||
out->data + NTLMSSP_SIG_SIZE,
|
||||
out->length - NTLMSSP_SIG_SIZE,
|
||||
&sig);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE);
|
||||
}
|
||||
return nt_status;
|
||||
|
||||
} else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
|
||||
|
||||
*out = data_blob_talloc(sig_mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE);
|
||||
if (!out->data) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length);
|
||||
|
||||
nt_status = gensec_ntlmssp_sign_packet(gensec_security, sig_mem_ctx,
|
||||
out->data + NTLMSSP_SIG_SIZE,
|
||||
out->length - NTLMSSP_SIG_SIZE,
|
||||
out->data + NTLMSSP_SIG_SIZE,
|
||||
out->length - NTLMSSP_SIG_SIZE,
|
||||
&sig);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE);
|
||||
}
|
||||
return nt_status;
|
||||
|
||||
} else {
|
||||
*out = *in;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS gensec_ntlmssp_unwrap(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out)
|
||||
{
|
||||
DATA_BLOB sig;
|
||||
|
||||
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
|
||||
if (in->length < NTLMSSP_SIG_SIZE) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
sig.data = in->data;
|
||||
sig.length = NTLMSSP_SIG_SIZE;
|
||||
|
||||
*out = data_blob_talloc(sig_mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE);
|
||||
|
||||
return gensec_ntlmssp_unseal_packet(gensec_security, sig_mem_ctx,
|
||||
out->data, out->length,
|
||||
out->data, out->length,
|
||||
&sig);
|
||||
|
||||
} else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
|
||||
if (in->length < NTLMSSP_SIG_SIZE) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
sig.data = in->data;
|
||||
sig.length = NTLMSSP_SIG_SIZE;
|
||||
|
||||
*out = data_blob_talloc(sig_mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE);
|
||||
|
||||
return gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx,
|
||||
out->data, out->length,
|
||||
out->data, out->length,
|
||||
&sig);
|
||||
} else {
|
||||
*out = *in;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* PAM error mapping functions
|
||||
* Copyright (C) Andrew Bartlett 2002
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#ifdef WITH_HAVE_SECURITY_PAM_APPL_H
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
#if defined(PAM_AUTHTOK_RECOVERY_ERR) && !defined(PAM_AUTHTOK_RECOVER_ERR)
|
||||
#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
|
||||
#endif
|
||||
|
||||
/* PAM -> NT_STATUS map */
|
||||
static const struct {
|
||||
int pam_code;
|
||||
NTSTATUS ntstatus;
|
||||
} pam_to_nt_status_map[] = {
|
||||
{PAM_OPEN_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_SYMBOL_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_SERVICE_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_SYSTEM_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_BUF_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_PERM_DENIED, NT_STATUS_ACCESS_DENIED},
|
||||
{PAM_AUTH_ERR, NT_STATUS_WRONG_PASSWORD},
|
||||
{PAM_CRED_INSUFFICIENT, NT_STATUS_INSUFFICIENT_LOGON_INFO}, /* FIXME: Is this correct? */
|
||||
{PAM_AUTHINFO_UNAVAIL, NT_STATUS_LOGON_FAILURE},
|
||||
{PAM_USER_UNKNOWN, NT_STATUS_NO_SUCH_USER},
|
||||
{PAM_MAXTRIES, NT_STATUS_REMOTE_SESSION_LIMIT}, /* FIXME: Is this correct? */
|
||||
{PAM_NEW_AUTHTOK_REQD, NT_STATUS_PASSWORD_MUST_CHANGE},
|
||||
{PAM_ACCT_EXPIRED, NT_STATUS_ACCOUNT_EXPIRED},
|
||||
{PAM_SESSION_ERR, NT_STATUS_INSUFFICIENT_RESOURCES},
|
||||
{PAM_CRED_UNAVAIL, NT_STATUS_NO_TOKEN}, /* FIXME: Is this correct? */
|
||||
{PAM_CRED_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, /* FIXME: Is this correct? */
|
||||
{PAM_CRED_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_AUTHTOK_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
#ifdef PAM_AUTHTOK_RECOVER_ERR
|
||||
{PAM_AUTHTOK_RECOVER_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
#endif
|
||||
{PAM_AUTHTOK_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
|
||||
{PAM_SUCCESS, NT_STATUS_OK}
|
||||
};
|
||||
|
||||
/* NT_STATUS -> PAM map */
|
||||
static const struct {
|
||||
NTSTATUS ntstatus;
|
||||
int pam_code;
|
||||
} nt_status_to_pam_map[] = {
|
||||
{NT_STATUS_UNSUCCESSFUL, PAM_SYSTEM_ERR},
|
||||
{NT_STATUS_NO_SUCH_USER, PAM_USER_UNKNOWN},
|
||||
{NT_STATUS_WRONG_PASSWORD, PAM_AUTH_ERR},
|
||||
{NT_STATUS_LOGON_FAILURE, PAM_AUTH_ERR},
|
||||
{NT_STATUS_ACCOUNT_EXPIRED, PAM_ACCT_EXPIRED},
|
||||
{NT_STATUS_PASSWORD_EXPIRED, PAM_AUTHTOK_EXPIRED},
|
||||
{NT_STATUS_PASSWORD_MUST_CHANGE, PAM_NEW_AUTHTOK_REQD},
|
||||
{NT_STATUS_OK, PAM_SUCCESS}
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
convert a PAM error to a NT status32 code
|
||||
*****************************************************************************/
|
||||
NTSTATUS pam_to_nt_status(int pam_error)
|
||||
{
|
||||
int i;
|
||||
if (pam_error == 0) return NT_STATUS_OK;
|
||||
|
||||
for (i=0; NT_STATUS_V(pam_to_nt_status_map[i].ntstatus); i++) {
|
||||
if (pam_error == pam_to_nt_status_map[i].pam_code)
|
||||
return pam_to_nt_status_map[i].ntstatus;
|
||||
}
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
convert an NT status32 code to a PAM error
|
||||
*****************************************************************************/
|
||||
int nt_status_to_pam(NTSTATUS nt_status)
|
||||
{
|
||||
int i;
|
||||
if NT_STATUS_IS_OK(nt_status) return PAM_SUCCESS;
|
||||
|
||||
for (i=0; NT_STATUS_V(nt_status_to_pam_map[i].ntstatus); i++) {
|
||||
if (NT_STATUS_EQUAL(nt_status,nt_status_to_pam_map[i].ntstatus))
|
||||
return nt_status_to_pam_map[i].pam_code;
|
||||
}
|
||||
return PAM_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*****************************************************************************
|
||||
convert a PAM error to a NT status32 code
|
||||
*****************************************************************************/
|
||||
NTSTATUS pam_to_nt_status(int pam_error)
|
||||
{
|
||||
if (pam_error == 0) return NT_STATUS_OK;
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
convert an NT status32 code to a PAM error
|
||||
*****************************************************************************/
|
||||
int nt_status_to_pam(NTSTATUS nt_status)
|
||||
{
|
||||
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OK)) return 0;
|
||||
return 4; /* PAM_SYSTEM_ERR */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
|
||||
Copyright (C) Gerald Carter 2003
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/time.h"
|
||||
#include "auth/auth.h"
|
||||
#include "db_wrap.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon.h"
|
||||
|
||||
const char *user_attrs[] = {
|
||||
/* required for the krb5 kdc */
|
||||
"objectClass",
|
||||
"sAMAccountName",
|
||||
"userPrincipalName",
|
||||
"servicePrincipalName",
|
||||
"msDS-KeyVersionNumber",
|
||||
"krb5Key",
|
||||
|
||||
/* passwords */
|
||||
"lmPwdHash",
|
||||
"ntPwdHash",
|
||||
|
||||
"userAccountControl",
|
||||
|
||||
"pwdLastSet",
|
||||
"accountExpires",
|
||||
|
||||
"objectSid",
|
||||
|
||||
/* check 'allowed workstations' */
|
||||
"userWorkstations",
|
||||
|
||||
/* required for server_info, not access control: */
|
||||
"displayName",
|
||||
"scriptPath",
|
||||
"profilePath",
|
||||
"homeDirectory",
|
||||
"homeDrive",
|
||||
"lastLogon",
|
||||
"lastLogoff",
|
||||
"accountExpires",
|
||||
"badPwdCount",
|
||||
"logonCount",
|
||||
"primaryGroupID",
|
||||
NULL,
|
||||
};
|
||||
|
||||
const char *domain_ref_attrs[] = {"nETBIOSName", "nCName",
|
||||
"dnsRoot", "objectClass", NULL};
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Do a specific test for a SAM_ACCOUNT being vaild for this connection
|
||||
(ie not disabled, expired and the like).
|
||||
****************************************************************************/
|
||||
_PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
|
||||
struct ldb_context *sam_ctx,
|
||||
uint32_t logon_parameters,
|
||||
struct ldb_message *msg,
|
||||
struct ldb_message *msg_domain_ref,
|
||||
const char *logon_workstation,
|
||||
const char *name_for_logs)
|
||||
{
|
||||
uint16_t acct_flags;
|
||||
const char *workstation_list;
|
||||
NTTIME acct_expiry;
|
||||
NTTIME must_change_time;
|
||||
NTTIME last_set_time;
|
||||
|
||||
struct ldb_dn *domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", ldb_dn_new(mem_ctx, sam_ctx, NULL));
|
||||
|
||||
NTTIME now;
|
||||
DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
|
||||
|
||||
acct_flags = samdb_result_acct_flags(msg, "userAccountControl");
|
||||
|
||||
acct_expiry = samdb_result_nttime(msg, "accountExpires", 0);
|
||||
must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx,
|
||||
domain_dn, msg);
|
||||
last_set_time = samdb_result_nttime(msg, "pwdLastSet", 0);
|
||||
|
||||
workstation_list = samdb_result_string(msg, "userWorkstations", NULL);
|
||||
|
||||
/* Quit if the account was disabled. */
|
||||
if (acct_flags & ACB_DISABLED) {
|
||||
DEBUG(1,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
|
||||
return NT_STATUS_ACCOUNT_DISABLED;
|
||||
}
|
||||
|
||||
/* Quit if the account was locked out. */
|
||||
if (acct_flags & ACB_AUTOLOCK) {
|
||||
DEBUG(1,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
|
||||
return NT_STATUS_ACCOUNT_LOCKED_OUT;
|
||||
}
|
||||
|
||||
/* Test account expire time */
|
||||
unix_to_nt_time(&now, time(NULL));
|
||||
if (now > acct_expiry) {
|
||||
DEBUG(1,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
|
||||
DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
|
||||
nt_time_string(mem_ctx, acct_expiry)));
|
||||
return NT_STATUS_ACCOUNT_EXPIRED;
|
||||
}
|
||||
|
||||
if (!(acct_flags & ACB_PWNOEXP)) {
|
||||
/* check for immediate expiry "must change at next logon" */
|
||||
if (must_change_time == 0 && last_set_time != 0) {
|
||||
DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n",
|
||||
name_for_logs));
|
||||
return NT_STATUS_PASSWORD_MUST_CHANGE;
|
||||
}
|
||||
|
||||
/* check for expired password */
|
||||
if ((must_change_time != 0) && (must_change_time < now)) {
|
||||
DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n",
|
||||
name_for_logs));
|
||||
DEBUG(1,("sam_account_ok: Password expired at '%s' unix time.\n",
|
||||
nt_time_string(mem_ctx, must_change_time)));
|
||||
return NT_STATUS_PASSWORD_EXPIRED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test workstation. Workstation list is comma separated. */
|
||||
if (logon_workstation && workstation_list && *workstation_list) {
|
||||
BOOL invalid_ws = True;
|
||||
int i;
|
||||
const char **workstations = str_list_make(mem_ctx, workstation_list, ",");
|
||||
|
||||
for (i = 0; workstations && workstations[i]; i++) {
|
||||
DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
|
||||
workstations[i], logon_workstation));
|
||||
|
||||
if (strequal(workstations[i], logon_workstation) == 0) {
|
||||
invalid_ws = False;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(workstations);
|
||||
|
||||
if (invalid_ws) {
|
||||
return NT_STATUS_INVALID_WORKSTATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (acct_flags & ACB_DOMTRUST) {
|
||||
DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
|
||||
return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
|
||||
}
|
||||
|
||||
if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
|
||||
if (acct_flags & ACB_SVRTRUST) {
|
||||
DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
|
||||
return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
|
||||
}
|
||||
}
|
||||
if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
|
||||
if (acct_flags & ACB_WSTRUST) {
|
||||
DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
|
||||
return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
|
||||
struct ldb_message *msg,
|
||||
struct ldb_message *msg_domain_ref,
|
||||
DATA_BLOB user_sess_key, DATA_BLOB lm_sess_key,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
struct auth_serversupplied_info *server_info;
|
||||
struct ldb_message **group_msgs;
|
||||
int group_ret;
|
||||
const char *group_attrs[3] = { "sAMAccountType", "objectSid", NULL };
|
||||
/* find list of sids */
|
||||
struct dom_sid **groupSIDs = NULL;
|
||||
struct dom_sid *account_sid;
|
||||
struct dom_sid *primary_group_sid;
|
||||
const char *str;
|
||||
struct ldb_dn *ncname;
|
||||
int i;
|
||||
uint_t rid;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
|
||||
|
||||
group_ret = gendb_search(sam_ctx,
|
||||
tmp_ctx, NULL, &group_msgs, group_attrs,
|
||||
"(&(member=%s)(sAMAccountType=*))",
|
||||
ldb_dn_get_linearized(msg->dn));
|
||||
if (group_ret == -1) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
if (group_ret > 0) {
|
||||
groupSIDs = talloc_array(server_info, struct dom_sid *, group_ret);
|
||||
NT_STATUS_HAVE_NO_MEMORY(groupSIDs);
|
||||
}
|
||||
|
||||
/* Need to unroll some nested groups, but not aliases */
|
||||
for (i = 0; i < group_ret; i++) {
|
||||
groupSIDs[i] = samdb_result_dom_sid(groupSIDs,
|
||||
group_msgs[i], "objectSid");
|
||||
NT_STATUS_HAVE_NO_MEMORY(groupSIDs[i]);
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
account_sid = samdb_result_dom_sid(server_info, msg, "objectSid");
|
||||
NT_STATUS_HAVE_NO_MEMORY(account_sid);
|
||||
|
||||
primary_group_sid = dom_sid_dup(server_info, account_sid);
|
||||
NT_STATUS_HAVE_NO_MEMORY(primary_group_sid);
|
||||
|
||||
rid = samdb_result_uint(msg, "primaryGroupID", ~0);
|
||||
if (rid == ~0) {
|
||||
if (group_ret > 0) {
|
||||
primary_group_sid = groupSIDs[0];
|
||||
} else {
|
||||
primary_group_sid = NULL;
|
||||
}
|
||||
} else {
|
||||
primary_group_sid->sub_auths[primary_group_sid->num_auths-1] = rid;
|
||||
}
|
||||
|
||||
server_info->account_sid = account_sid;
|
||||
server_info->primary_group_sid = primary_group_sid;
|
||||
|
||||
server_info->n_domain_groups = group_ret;
|
||||
server_info->domain_groups = groupSIDs;
|
||||
|
||||
server_info->account_name = talloc_steal(server_info, samdb_result_string(msg, "sAMAccountName", NULL));
|
||||
|
||||
server_info->domain_name = talloc_steal(server_info, samdb_result_string(msg_domain_ref, "nETBIOSName", NULL));
|
||||
|
||||
str = samdb_result_string(msg, "displayName", "");
|
||||
server_info->full_name = talloc_strdup(server_info, str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
|
||||
|
||||
str = samdb_result_string(msg, "scriptPath", "");
|
||||
server_info->logon_script = talloc_strdup(server_info, str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
|
||||
|
||||
str = samdb_result_string(msg, "profilePath", "");
|
||||
server_info->profile_path = talloc_strdup(server_info, str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
|
||||
|
||||
str = samdb_result_string(msg, "homeDirectory", "");
|
||||
server_info->home_directory = talloc_strdup(server_info, str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
|
||||
|
||||
str = samdb_result_string(msg, "homeDrive", "");
|
||||
server_info->home_drive = talloc_strdup(server_info, str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
|
||||
|
||||
server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
|
||||
|
||||
server_info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
|
||||
server_info->last_logoff = samdb_result_nttime(msg, "lastLogoff", 0);
|
||||
server_info->acct_expiry = samdb_result_nttime(msg, "accountExpires", 0);
|
||||
server_info->last_password_change = samdb_result_nttime(msg, "pwdLastSet", 0);
|
||||
|
||||
ncname = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", NULL);
|
||||
if (!ncname) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
server_info->allow_password_change
|
||||
= samdb_result_allow_password_change(sam_ctx, mem_ctx,
|
||||
ncname, msg, "pwdLastSet");
|
||||
server_info->force_password_change
|
||||
= samdb_result_force_password_change(sam_ctx, mem_ctx,
|
||||
ncname, msg);
|
||||
|
||||
server_info->logon_count = samdb_result_uint(msg, "logonCount", 0);
|
||||
server_info->bad_password_count = samdb_result_uint(msg, "badPwdCount", 0);
|
||||
|
||||
server_info->acct_flags = samdb_result_acct_flags(msg, "userAccountControl");
|
||||
|
||||
server_info->user_session_key = user_sess_key;
|
||||
server_info->lm_session_key = lm_sess_key;
|
||||
|
||||
server_info->authenticated = True;
|
||||
|
||||
*_server_info = server_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
|
||||
TALLOC_CTX *mem_ctx, const char *principal,
|
||||
struct ldb_message ***msgs,
|
||||
struct ldb_message ***msgs_domain_ref)
|
||||
{
|
||||
struct ldb_dn *user_dn, *domain_dn;
|
||||
NTSTATUS nt_status;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
|
||||
int ret;
|
||||
struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
|
||||
|
||||
if (!tmp_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal, &user_dn, &domain_dn);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* grab domain info from the reference */
|
||||
ret = gendb_search(sam_ctx, tmp_ctx, partitions_basedn, msgs_domain_ref, domain_ref_attrs,
|
||||
"(ncName=%s)", ldb_dn_get_linearized(domain_dn));
|
||||
|
||||
if (ret != 1) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
/* pull the user attributes */
|
||||
ret = gendb_search_dn(sam_ctx, tmp_ctx, user_dn, msgs, user_attrs);
|
||||
if (ret != 1) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
talloc_steal(mem_ctx, *msgs);
|
||||
talloc_steal(mem_ctx, *msgs_domain_ref);
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available */
|
||||
NTSTATUS sam_get_server_info_principal(TALLOC_CTX *mem_ctx, const char *principal,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
DATA_BLOB user_sess_key = data_blob(NULL, 0);
|
||||
DATA_BLOB lm_sess_key = data_blob(NULL, 0);
|
||||
|
||||
struct ldb_message **msgs;
|
||||
struct ldb_message **msgs_domain_ref;
|
||||
struct ldb_context *sam_ctx;
|
||||
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
|
||||
if (!tmp_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
sam_ctx = samdb_connect(tmp_ctx, system_session(tmp_ctx));
|
||||
if (sam_ctx == NULL) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INVALID_SYSTEM_SERVICE;
|
||||
}
|
||||
|
||||
nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
|
||||
&msgs, &msgs_domain_ref);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, msgs[0], msgs_domain_ref[0],
|
||||
user_sess_key, lm_sess_key,
|
||||
server_info);
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_steal(mem_ctx, *server_info);
|
||||
}
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Run this script to build samba from SVN.
|
||||
|
||||
## insert all possible names (only works with
|
||||
## autoconf 2.x
|
||||
TESTAUTOHEADER="autoheader autoheader-2.53 autoheader2.50 autoheader259 autoheader253"
|
||||
TESTAUTOCONF="autoconf autoconf-2.53 autoconf2.50 autoconf259 autoconf253"
|
||||
|
||||
AUTOHEADERFOUND="0"
|
||||
AUTOCONFFOUND="0"
|
||||
|
||||
|
||||
##
|
||||
## Look for autoheader
|
||||
##
|
||||
for i in $TESTAUTOHEADER; do
|
||||
if which $i > /dev/null 2>&1; then
|
||||
if test `$i --version | head -n 1 | cut -d. -f 2 | tr -d [:alpha:]` -ge 53; then
|
||||
AUTOHEADER=$i
|
||||
AUTOHEADERFOUND="1"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
##
|
||||
## Look for autoconf
|
||||
##
|
||||
|
||||
for i in $TESTAUTOCONF; do
|
||||
if which $i > /dev/null 2>&1; then
|
||||
if test `$i --version | head -n 1 | cut -d. -f 2 | tr -d [:alpha:]` -ge 53; then
|
||||
AUTOCONF=$i
|
||||
AUTOCONFFOUND="1"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
##
|
||||
## do we have it?
|
||||
##
|
||||
if test "$AUTOCONFFOUND" = "0" -o "$AUTOHEADERFOUND" = "0"; then
|
||||
echo "$0: need autoconf 2.53 or later to build samba from SVN" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$0: running script/mkversion.sh"
|
||||
./script/mkversion.sh || exit 1
|
||||
|
||||
rm -rf autom4te*.cache
|
||||
rm -f configure include/config_tmp.h*
|
||||
|
||||
IPATHS="-I. -Ilib/replace"
|
||||
|
||||
echo "$0: running $AUTOHEADER $IPATHS"
|
||||
$AUTOHEADER $IPATHS || exit 1
|
||||
|
||||
echo "$0: running $AUTOCONF $IPATHS"
|
||||
$AUTOCONF $IPATHS || exit 1
|
||||
|
||||
rm -rf autom4te*.cache
|
||||
|
||||
echo "Now run ./configure and then make."
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
dnl @synopsis AX_CFLAGS_GCC_OPTION (optionflag [,[shellvar][,[A][,[NA]]])
|
||||
dnl
|
||||
dnl AX_CFLAGS_GCC_OPTION(-fvomit-frame) would show a message as like
|
||||
dnl "checking CFLAGS for gcc -fvomit-frame ... yes" and adds
|
||||
dnl the optionflag to CFLAGS if it is understood. You can override
|
||||
dnl the shellvar-default of CFLAGS of course. The order of arguments
|
||||
dnl stems from the explicit macros like AX_CFLAGS_WARN_ALL.
|
||||
dnl
|
||||
dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or
|
||||
dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options.
|
||||
dnl However, if you use this macro in a few places, it would be great
|
||||
dnl if you would make up a new function-macro and submit it to the
|
||||
dnl ac-archive.
|
||||
dnl
|
||||
dnl - $1 option-to-check-for : required ("-option" as non-value)
|
||||
dnl - $2 shell-variable-to-add-to : CFLAGS
|
||||
dnl - $3 action-if-found : add value to shellvariable
|
||||
dnl - $4 action-if-not-found : nothing
|
||||
dnl
|
||||
dnl note: in earlier versions, $1-$2 were swapped. We try to detect the
|
||||
dnl situation and accept a $2=~/-/ as being the old option-to-check-for.
|
||||
dnl
|
||||
dnl also: there are other variants that emerged from the original macro
|
||||
dnl variant which did just test an option to be possibly added. However,
|
||||
dnl some compilers accept an option silently, or possibly for just
|
||||
dnl another option that was not intended. Therefore, we have to do a
|
||||
dnl generic test for a compiler family. For gcc we check "-pedantic"
|
||||
dnl being accepted which is also understood by compilers who just want
|
||||
dnl to be compatible with gcc even when not being made from gcc sources.
|
||||
dnl
|
||||
dnl see also:
|
||||
dnl AX_CFLAGS_SUN_OPTION AX_CFLAGS_HPUX_OPTION
|
||||
dnl AX_CFLAGS_AIX_OPTION AX_CFLAGS_IRIX_OPTION
|
||||
dnl
|
||||
dnl @, tested, experimental
|
||||
dnl @version $Id: ax_cflags_gcc_option.m4,v 1.5 2003/11/29 08:13:25 guidod Exp $
|
||||
dnl @author Guido Draheim <guidod@gmx.de>
|
||||
dnl http://ac-archive.sourceforge.net/C_Support/ax_cflags_gcc_option.m4
|
||||
dnl
|
||||
AC_DEFUN([AX_CFLAGS_GCC_OPTION_OLD], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$2])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-pedantic % m4_ifval($2,$2,-option)" dnl GCC
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
|
||||
m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
|
||||
dnl -------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([AX_CFLAGS_GCC_OPTION_NEW], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$1])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-pedantic % m4_ifval($1,$1,-option)" dnl GCC
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
|
||||
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([AX_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
|
||||
[AX_CFLAGS_GCC_OPTION_NEW($@)],[AX_CFLAGS_GCC_OPTION_OLD($@)])])
|
||||
@@ -0,0 +1,174 @@
|
||||
dnl @synopsis AX_CFLAGS_IRIX_OPTION (optionflag [,[shellvar][,[A][,[NA]]])
|
||||
dnl
|
||||
dnl AX_CFLAGS_IRIX_OPTION(-go_for_it) would show a message as like
|
||||
dnl "checking CFLAGS for irix/cc -go_for_it ... yes" and adds the
|
||||
dnl optionflag to CFLAGS if it is understood. You can override the
|
||||
dnl shellvar-default of CFLAGS of course. The order of arguments stems
|
||||
dnl from the explicit macros like AX_CFLAGS_WARN_ALL.
|
||||
dnl
|
||||
dnl The cousin AX_CXXFLAGS_IRIX_OPTION would check for an option to add
|
||||
dnl to CXXFLAGS - and it uses the autoconf setup for C++ instead of C
|
||||
dnl (since it is possible to use different compilers for C and C++).
|
||||
dnl
|
||||
dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or
|
||||
dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options.
|
||||
dnl However, if you use this macro in a few places, it would be great
|
||||
dnl if you would make up a new function-macro and submit it to the
|
||||
dnl ac-archive.
|
||||
dnl
|
||||
dnl - $1 option-to-check-for : required ("-option" as non-value)
|
||||
dnl - $2 shell-variable-to-add-to : CFLAGS (or CXXFLAGS in the other case)
|
||||
dnl - $3 action-if-found : add value to shellvariable
|
||||
dnl - $4 action-if-not-found : nothing
|
||||
dnl
|
||||
dnl note: in earlier versions, $1-$2 were swapped. We try to detect the
|
||||
dnl situation and accept a $2=~/-/ as being the old
|
||||
dnl option-to-check-for.
|
||||
dnl
|
||||
dnl see also: AX_CFLAGS_GCC_OPTION for the widely used original
|
||||
dnl variant.
|
||||
dnl
|
||||
dnl @category C
|
||||
dnl @author Guido Draheim <guidod@gmx.de>
|
||||
dnl @version 2005-01-21
|
||||
dnl @license GPLWithACException
|
||||
|
||||
AC_DEFUN([AX_CFLAGS_IRIX_OPTION_OLD], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_irix_option_$2])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for irix/cc m4_ifval($2,$2,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-fullwarn -use_readonly_const % m4_ifval($2,$2,-option)" dnl IRIX C
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
|
||||
m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
dnl the only difference - the LANG selection... and the default FLAGS
|
||||
|
||||
AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION_OLD], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_irix_option_$2])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for irix/cc m4_ifval($2,$2,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_CXX
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-fullwarn -use_readonly_const % m4_ifval($2,$2,-option)" dnl IRIX C
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
|
||||
m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
dnl --------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([AX_CFLAGS_IRIX_OPTION_NEW], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_irix_option_$1])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for irix/cc m4_ifval($1,$1,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-fullwarn -use_readonly_const % m4_ifval($1,$1,-option)" dnl IRIX C
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
|
||||
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
dnl the only difference - the LANG selection... and the default FLAGS
|
||||
|
||||
AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION_NEW], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_irix_option_$1])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for irix/cc m4_ifval($1,$1,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_CXX
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-fullwarn -use_readonly_const % m4_ifval($1,$1,-option)" dnl IRIX C
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
|
||||
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
AC_DEFUN([AX_CFLAGS_IRIX_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
|
||||
[AX_CFLAGS_IRIX_OPTION_NEW($@)],[AX_CFLAGS_IRIX_OPTION_OLD($@)])])
|
||||
|
||||
AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
|
||||
[AX_CXXFLAGS_IRIX_OPTION_NEW($@)],[AX_CXXFLAGS_IRIX_OPTION_OLD($@)])])
|
||||
@@ -0,0 +1,154 @@
|
||||
dnl SMB Build Environment CC Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
AC_LIBREPLACE_CC_CHECKS
|
||||
|
||||
#
|
||||
# Set the debug symbol option if we have
|
||||
# --enable-*developer or --enable-debug
|
||||
# and the compiler supports it
|
||||
#
|
||||
if test x$ac_cv_prog_cc_g = xyes -a x$debug = xyes; then
|
||||
CFLAGS="${CFLAGS} -g"
|
||||
fi
|
||||
|
||||
############################################
|
||||
# check if the compiler handles c99 struct initialization
|
||||
LIBREPLACE_C99_STRUCT_INIT(samba_cv_c99_struct_initialization=yes,
|
||||
samba_cv_c99_struct_initialization=no)
|
||||
|
||||
if test x"$samba_cv_c99_struct_initialization" != x"yes"; then
|
||||
AC_MSG_WARN([C compiler does not support c99 struct initialization!])
|
||||
AC_MSG_ERROR([Please Install gcc from http://gcc.gnu.org/])
|
||||
fi
|
||||
|
||||
############################################
|
||||
# check if the compiler can handle negative enum values
|
||||
# and don't truncate the values to INT_MAX
|
||||
# a runtime test is needed here
|
||||
AC_CACHE_CHECK([that the C compiler understands negative enum values],SMB_BUILD_CC_NEGATIVE_ENUM_VALUES, [
|
||||
AC_TRY_RUN(
|
||||
[
|
||||
#include <stdio.h>
|
||||
enum negative_values { NEGATIVE_VALUE = 0xFFFFFFFF };
|
||||
int main(void) {
|
||||
enum negative_values v1 = NEGATIVE_VALUE;
|
||||
unsigned v2 = 0xFFFFFFFF;
|
||||
if (v1 != v2) {
|
||||
printf("v1=0x%08x v2=0x%08x\n", v1, v2);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
],
|
||||
SMB_BUILD_CC_NEGATIVE_ENUM_VALUES=yes,SMB_BUILD_CC_NEGATIVE_ENUM_VALUES=no)])
|
||||
if test x"$SMB_BUILD_CC_NEGATIVE_ENUM_VALUES" != x"yes"; then
|
||||
AC_MSG_WARN([using --unit-enums for pidl])
|
||||
PIDL_ARGS="$PIDL_ARGS --uint-enums"
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([for test routines])
|
||||
AC_TRY_RUN([#include "${srcdir-.}/build/tests/trivial.c"],
|
||||
AC_MSG_RESULT(yes),
|
||||
AC_MSG_ERROR([cant find test code. Aborting config]),
|
||||
AC_MSG_WARN([cannot run when cross-compiling]))
|
||||
|
||||
#
|
||||
# Check if the compiler support ELF visibility for symbols
|
||||
#
|
||||
|
||||
visibility_attribute=no
|
||||
VISIBILITY_CFLAGS=""
|
||||
if test x"$GCC" = x"yes" ; then
|
||||
AX_CFLAGS_GCC_OPTION([-fvisibility=hidden], VISIBILITY_CFLAGS)
|
||||
fi
|
||||
|
||||
if test -n "$VISIBILITY_CFLAGS"; then
|
||||
AC_MSG_CHECKING([whether the C compiler supports the visibility attribute])
|
||||
OLD_CFLAGS="$CFLAGS"
|
||||
|
||||
CFLAGS="$CFLAGS $VISIBILITY_CFLAGS"
|
||||
AC_TRY_RUN([
|
||||
void vis_foo1(void) {}
|
||||
__attribute__((visibility("default"))) void vis_foo2(void) {}
|
||||
#include "${srcdir-.}/build/tests/trivial.c"
|
||||
],[
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(HAVE_VISIBILITY_ATTR,1,[Whether the C compiler supports the visibility attribute])
|
||||
visibility_attribute=yes
|
||||
],[
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
CFLAGS="$OLD_CFLAGS"
|
||||
fi
|
||||
AC_SUBST(visibility_attribute)
|
||||
|
||||
#
|
||||
# Check if the compiler can handle the options we selected by
|
||||
# --enable-*developer
|
||||
#
|
||||
DEVELOPER_CFLAGS=""
|
||||
if test x$developer = xyes; then
|
||||
OLD_CFLAGS="${CFLAGS}"
|
||||
|
||||
CFLAGS="${CFLAGS} -D_SAMBA_DEVELOPER_DONNOT_USE_O2_"
|
||||
DEVELOPER_CFLAGS="-DDEBUG_PASSWORD -DDEVELOPER"
|
||||
if test x"$GCC" = x"yes" ; then
|
||||
#
|
||||
# warnings we want...
|
||||
#
|
||||
AX_CFLAGS_GCC_OPTION(-Wall, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wshadow, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Werror-implicit-function-declaration, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wstrict-prototypes, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wpointer-arith, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wcast-qual, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wcast-align, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wwrite-strings, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wmissing-format-attribute, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wformat=2, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wdeclaration-after-statement, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wunused-macros, DEVELOPER_CFLAGS)
|
||||
# AX_CFLAGS_GCC_OPTION(-Wextra, DEVELOPER_CFLAGS)
|
||||
# AX_CFLAGS_GCC_OPTION(-Wc++-compat, DEVELOPER_CFLAGS)
|
||||
# AX_CFLAGS_GCC_OPTION(-Wmissing-prototypes, DEVELOPER_CFLAGS)
|
||||
# AX_CFLAGS_GCC_OPTION(-Wmissing-declarations, DEVELOPER_CFLAGS)
|
||||
# AX_CFLAGS_GCC_OPTION(-Wmissing-field-initializers, DEVELOPER_CFLAGS)
|
||||
#
|
||||
# warnings we don't want...
|
||||
#
|
||||
AX_CFLAGS_GCC_OPTION(-Wno-format-y2k, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wno-unused-parameter, DEVELOPER_CFLAGS)
|
||||
else
|
||||
AX_CFLAGS_IRIX_OPTION(-fullwarn, DEVELOPER_CFLAGS)
|
||||
fi
|
||||
|
||||
CFLAGS="${OLD_CFLAGS}"
|
||||
fi
|
||||
if test -n "$DEVELOPER_CFLAGS"; then
|
||||
OLD_CFLAGS="${CFLAGS}"
|
||||
CFLAGS="${CFLAGS} ${DEVELOPER_CFLAGS}"
|
||||
AC_MSG_CHECKING([that the C compiler can use the DEVELOPER_CFLAGS])
|
||||
AC_TRY_COMPILE([],[],
|
||||
AC_MSG_RESULT(yes),
|
||||
DEVELOPER_CFLAGS=""; AC_MSG_RESULT(no))
|
||||
CFLAGS="${OLD_CFLAGS}"
|
||||
fi
|
||||
|
||||
# allow for --with-hostcc=gcc
|
||||
AC_ARG_WITH(hostcc,[ --with-hostcc=compiler choose host compiler],
|
||||
[HOSTCC=$withval],
|
||||
[
|
||||
if test z"$cross_compiling" = "yes"; then
|
||||
HOSTCC=cc
|
||||
else
|
||||
HOSTCC=$CC
|
||||
fi
|
||||
])
|
||||
AC_SUBST(HOSTCC)
|
||||
|
||||
AC_PATH_PROG(GCOV,gcov)
|
||||
@@ -0,0 +1 @@
|
||||
AC_PATH_PROG(XSLTPROC, xsltproc)
|
||||
@@ -0,0 +1,208 @@
|
||||
dnl SMB Build Environment LD Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Copyright (C) Jelmer Vernooij 2006
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
AC_PATH_PROG(PROG_LD,ld)
|
||||
LD=${PROG_LD}
|
||||
AC_PROG_LD_GNU
|
||||
LD=""
|
||||
AC_PATH_PROG(PROG_AR, ar)
|
||||
|
||||
AC_SUBST(STLD)
|
||||
AC_SUBST(STLD_FLAGS)
|
||||
AC_SUBST(BLDSHARED)
|
||||
AC_SUBST(LD)
|
||||
AC_SUBST(LDFLAGS)
|
||||
AC_SUBST(SHLD)
|
||||
AC_SUBST(SHLD_FLAGS)
|
||||
AC_SUBST(SHLD_UNDEF_FLAGS)
|
||||
AC_SUBST(SHLIBEXT)
|
||||
AC_SUBST(SONAMEFLAG)
|
||||
AC_SUBST(PICFLAG)
|
||||
|
||||
# Assume non-shared by default and override below
|
||||
# these are the defaults, good for lots of systems
|
||||
STLD=${PROG_AR}
|
||||
STLD_FLAGS="-rcs"
|
||||
BLDSHARED="false"
|
||||
LD="${CC}"
|
||||
LDFLAGS=""
|
||||
SHLD="${CC}"
|
||||
SHLD_FLAGS="-shared"
|
||||
SHLIBEXT="so"
|
||||
SONAMEFLAG=""
|
||||
PICFLAG=""
|
||||
|
||||
AC_MSG_CHECKING([ability to build shared libraries])
|
||||
|
||||
# and these are for particular systems
|
||||
case "$host_os" in
|
||||
*linux*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-shared -Wl,-Bsymbolic"
|
||||
SHLD_UNDEF_FLAGS="-Wl,--allow-shlib-undefined"
|
||||
LDFLAGS="-Wl,--export-dynamic"
|
||||
PICFLAG="-fPIC"
|
||||
SONAMEFLAG="-Wl,-soname="
|
||||
;;
|
||||
*solaris*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-G"
|
||||
SONAMEFLAG="-h "
|
||||
if test "${GCC}" = "yes"; then
|
||||
PICFLAG="-fPIC"
|
||||
SONAMEFLAG="-Wl,-soname="
|
||||
if test "${ac_cv_prog_gnu_ld}" = "yes"; then
|
||||
LDFLAGS="-Wl,-E"
|
||||
fi
|
||||
else
|
||||
PICFLAG="-KPIC"
|
||||
## ${CFLAGS} added for building 64-bit shared
|
||||
## libs using Sun's Compiler
|
||||
SHLD_FLAGS="-G \${CFLAGS}"
|
||||
fi
|
||||
;;
|
||||
*sunos*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-G"
|
||||
SONAMEFLAG="-Wl,-h,"
|
||||
PICFLAG="-KPIC" # Is this correct for SunOS
|
||||
;;
|
||||
*netbsd* | *freebsd* | *dragonfly* )
|
||||
BLDSHARED="true"
|
||||
LDFLAGS="-Wl,--export-dynamic"
|
||||
SONAMEFLAG="-Wl,-soname,"
|
||||
PICFLAG="-fPIC -DPIC"
|
||||
;;
|
||||
*openbsd*)
|
||||
BLDSHARED="true"
|
||||
LDFLAGS="-Wl,-Bdynamic"
|
||||
SONAMEFLAG="-Wl,-soname,"
|
||||
PICFLAG="-fPIC"
|
||||
;;
|
||||
*irix*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-set_version sgi1.0 -shared"
|
||||
SONAMEFLAG="-soname "
|
||||
SHLD="${PROG_LD}"
|
||||
if test "${GCC}" = "yes"; then
|
||||
PICFLAG="-fPIC"
|
||||
else
|
||||
PICFLAG="-KPIC"
|
||||
fi
|
||||
;;
|
||||
*aix*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-Wl,-G,-bexpall,-bbigtoc"
|
||||
LDFLAGS="-Wl,-brtl,-bexpall,-bbigtoc"
|
||||
# as AIX code is always position independent...
|
||||
PICFLAG="-O2"
|
||||
;;
|
||||
*hpux*)
|
||||
# Use special PIC flags for the native HP-UX compiler.
|
||||
if test $ac_cv_prog_cc_Ae = yes; then
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-b -Wl,-B,symbolic,-b,-z"
|
||||
SONAMEFLAG="-Wl,+h "
|
||||
PICFLAG="+z"
|
||||
elif test "${GCC}" = "yes"; then
|
||||
BLDSHARED="true" # I hope this is correct
|
||||
PICFLAG="-fPIC"
|
||||
fi
|
||||
if test "$host_cpu" = "ia64"; then
|
||||
SHLIBEXT="so"
|
||||
LDFLAGS="-Wl,-E,+b/usr/local/lib/hpux32:/usr/lib/hpux32"
|
||||
else
|
||||
SHLIBEXT="sl"
|
||||
LDFLAGS="-Wl,-E,+b/usr/local/lib:/usr/lib"
|
||||
fi
|
||||
;;
|
||||
*osf*)
|
||||
BLDSHARED="true"
|
||||
SONAMEFLAG="-Wl,-soname,"
|
||||
PICFLAG="-fPIC"
|
||||
;;
|
||||
*unixware*)
|
||||
BLDSHARED="true"
|
||||
SONAMEFLAG="-Wl,-soname,"
|
||||
PICFLAG="-KPIC"
|
||||
;;
|
||||
*darwin*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-bundle -flat_namespace -undefined suppress"
|
||||
SHLIBEXT="dylib"
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_MSG_RESULT($BLDSHARED)
|
||||
|
||||
AC_MSG_CHECKING([LD])
|
||||
AC_MSG_RESULT([$LD])
|
||||
AC_MSG_CHECKING([LDFLAGS])
|
||||
AC_MSG_RESULT([$LDFLAGS])
|
||||
|
||||
AC_MSG_CHECKING([STLD])
|
||||
AC_MSG_RESULT([$STLD])
|
||||
AC_MSG_CHECKING([STLD_FLAGS])
|
||||
AC_MSG_RESULT([$STLD_FLAGS])
|
||||
|
||||
#######################################################
|
||||
# test whether building a shared library actually works
|
||||
if test $BLDSHARED = true; then
|
||||
|
||||
AC_MSG_CHECKING([SHLD])
|
||||
AC_MSG_RESULT([$SHLD])
|
||||
AC_MSG_CHECKING([SHLD_FLAGS])
|
||||
AC_MSG_RESULT([$SHLD_FLAGS])
|
||||
|
||||
AC_DEFINE_UNQUOTED(SHLIBEXT, "$SHLIBEXT", [Shared library extension])
|
||||
AC_MSG_CHECKING([SHLIBEXT])
|
||||
AC_MSG_RESULT([$SHLIBEXT])
|
||||
AC_MSG_CHECKING([SONAMEFLAG])
|
||||
AC_MSG_RESULT([$SONAMEFLAG])
|
||||
|
||||
AC_MSG_CHECKING([PICFLAG])
|
||||
AC_MSG_RESULT([$PICFLAG])
|
||||
|
||||
AC_CACHE_CHECK([whether building shared libraries actually works],
|
||||
[ac_cv_shlib_works],[
|
||||
ac_cv_shlib_works=no
|
||||
# try building a trivial shared library
|
||||
${CC} ${CFLAGS} ${PICFLAG} -c ${srcdir-.}/build/tests/shlib.c -o shlib.o &&
|
||||
${SHLD} `eval echo ${SHLD_FLAGS} ` -o shlib.${SHLIBEXT} shlib.o &&
|
||||
ac_cv_shlib_works=yes
|
||||
rm -f shlib.${SHLIBEXT} shlib.o
|
||||
])
|
||||
if test $ac_cv_shlib_works = no; then
|
||||
BLDSHARED=false
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if we can link using the selected flags])
|
||||
AC_TRY_RUN([#include "${srcdir-.}/build/tests/trivial.c"],
|
||||
AC_MSG_RESULT(yes),
|
||||
AC_MSG_ERROR([we cannot link with the selected cc and ld flags. Aborting configure]),
|
||||
AC_MSG_WARN([cannot run when cross-compiling]))
|
||||
|
||||
|
||||
USESHARED=false
|
||||
AC_SUBST(USESHARED)
|
||||
|
||||
AC_ARG_ENABLE(dso,
|
||||
[ --enable-dso Enable using shared libraries internally (experimental)],
|
||||
[],[enable_dso=no])
|
||||
|
||||
if test x"$enable_dso" = x"yes" -a x"$BLDSHARED" != x"true"; then
|
||||
AC_MSG_ERROR([--enable-dso: no support for shared libraries])
|
||||
fi
|
||||
|
||||
if test x"$enable_dso" != x"no"; then
|
||||
USESHARED=$BLDSHARED
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if binaries will use shared libraries])
|
||||
AC_MSG_RESULT([$USESHARED])
|
||||
@@ -0,0 +1,48 @@
|
||||
dnl SMB Build Environment make Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Copyright (C) Jelmer Vernooij 2005
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
AC_PATH_PROG(MAKE,make)
|
||||
|
||||
AC_CACHE_CHECK([whether we have GNU make], samba_cv_gnu_make, [
|
||||
if $ac_cv_path_MAKE --version | head -1 | grep GNU 2>/dev/null >/dev/null
|
||||
then
|
||||
samba_cv_gnu_make=yes
|
||||
else
|
||||
samba_cv_gnu_make=no
|
||||
fi
|
||||
])
|
||||
|
||||
GNU_MAKE=$samba_cv_gnu_make
|
||||
AC_SUBST(GNU_MAKE)
|
||||
|
||||
if test "x$GNU_MAKE" = x"yes"; then
|
||||
AC_CACHE_CHECK([GNU make version], samba_cv_gnu_make_version,[
|
||||
samba_cv_gnu_make_version=`$ac_cv_path_MAKE --version | head -1 | cut -d " " -f 3 2>/dev/null`
|
||||
])
|
||||
GNU_MAKE_VERSION=$samba_cv_gnu_make_version
|
||||
AC_SUBST(GNU_MAKE_VERSION)
|
||||
fi
|
||||
|
||||
|
||||
new_make=no
|
||||
AC_MSG_CHECKING([for GNU make >= 3.81])
|
||||
if test x$GNU_MAKE = x"yes"; then
|
||||
if $PERL -e " \$_ = '$GNU_MAKE_VERSION'; s/@<:@^\d\.@:>@.*//g; exit (\$_ < 3.81);"; then
|
||||
new_make=yes
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT($new_make)
|
||||
automatic_dependencies=no
|
||||
AX_CFLAGS_GCC_OPTION([-M -MT conftest.d -MF conftest.o], [], [ automatic_dependencies=$new_make ], [])
|
||||
AC_MSG_CHECKING([Whether to use automatic dependencies])
|
||||
AC_ARG_ENABLE(automatic-dependencies,
|
||||
[ --enable-automatic-dependencies Enable automatic dependencies],
|
||||
[ automatic_dependencies=$enableval ],
|
||||
[ automatic_dependencies=no ])
|
||||
AC_MSG_RESULT($automatic_dependencies)
|
||||
AC_SUBST(automatic_dependencies)
|
||||
@@ -0,0 +1,169 @@
|
||||
dnl SMB Build Environment Path Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
AC_LIBREPLACE_LOCATION_CHECKS
|
||||
|
||||
#################################################
|
||||
# Directory handling stuff to support both the
|
||||
# legacy SAMBA directories and FHS compliant
|
||||
# ones...
|
||||
AC_PREFIX_DEFAULT(/usr/local/samba)
|
||||
|
||||
# Defaults and --without-fhs
|
||||
logfilebase="${localstatedir}"
|
||||
lockdir="${localstatedir}/locks"
|
||||
piddir="${localstatedir}/run"
|
||||
privatedir="\${prefix}/private"
|
||||
modulesdir="\${prefix}/modules"
|
||||
winbindd_socket_dir="${localstatedir}/run/winbind_pipe"
|
||||
|
||||
AC_ARG_WITH(fhs,
|
||||
[ --with-fhs Use FHS-compliant paths (default=no)],
|
||||
lockdir="${localstatedir}/lib/samba"
|
||||
piddir="${localstatedir}/run/samba"
|
||||
logfilebase="${localstatedir}/log/samba"
|
||||
privatedir="${localstatedir}/lib/samba/private"
|
||||
sysconfdir="${sysconfdir}/samba"
|
||||
modulesdir="${libdir}/samba"
|
||||
datadir="${datadir}/samba"
|
||||
includedir="${includedir}/samba-4.0"
|
||||
winbindd_socket_dir="${localstatedir}/run/samba/winbind_pipe"
|
||||
)
|
||||
|
||||
#################################################
|
||||
# set private directory location
|
||||
AC_ARG_WITH(privatedir,
|
||||
[ --with-privatedir=DIR Where to put sam.ldb and other private files containing key material ($ac_default_prefix/private)],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
#
|
||||
# Just in case anybody calls it without argument
|
||||
#
|
||||
AC_MSG_WARN([--with-privatedir called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
privatedir="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
#################################################
|
||||
# set where the winbindd socket should be put
|
||||
AC_ARG_WITH(winbindd-socket-dir,
|
||||
[ --with-winbindd-socket-dir=DIR Where to put the winbindd socket ($ac_default_prefix/run/winbind_pipe)],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
#
|
||||
# Just in case anybody calls it without argument
|
||||
#
|
||||
AC_MSG_WARN([--with-winbind-socketdir called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
winbindd_socket_dir="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
#################################################
|
||||
# set lock directory location
|
||||
AC_ARG_WITH(lockdir,
|
||||
[ --with-lockdir=DIR Where to put lock files ($ac_default_prefix/var/locks)],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
#
|
||||
# Just in case anybody calls it without argument
|
||||
#
|
||||
AC_MSG_WARN([--with-lockdir called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
lockdir="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
#################################################
|
||||
# set pid directory location
|
||||
AC_ARG_WITH(piddir,
|
||||
[ --with-piddir=DIR Where to put pid files ($ac_default_prefix/var/locks)],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
#
|
||||
# Just in case anybody calls it without argument
|
||||
#
|
||||
AC_MSG_WARN([--with-piddir called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
piddir="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
#################################################
|
||||
# set log directory location
|
||||
AC_ARG_WITH(logfilebase,
|
||||
[ --with-logfilebase=DIR Where to put log files (\$(VARDIR))],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
#
|
||||
# Just in case anybody does it
|
||||
#
|
||||
AC_MSG_WARN([--with-logfilebase called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
logfilebase="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
|
||||
AC_SUBST(lockdir)
|
||||
AC_SUBST(piddir)
|
||||
AC_SUBST(logfilebase)
|
||||
AC_SUBST(privatedir)
|
||||
AC_SUBST(bindir)
|
||||
AC_SUBST(sbindir)
|
||||
AC_SUBST(winbindd_socket_dir)
|
||||
AC_SUBST(modulesdir)
|
||||
|
||||
#################################################
|
||||
# set prefix for 'make test'
|
||||
# this is needed to workarround the 108 char
|
||||
# unix socket path limitation!
|
||||
#
|
||||
selftest_prefix="./st"
|
||||
AC_SUBST(selftest_prefix)
|
||||
AC_ARG_WITH(selftest-prefix,
|
||||
[ --with-selftest-prefix=DIR The prefix where make test will be runned ($selftest_prefix)],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
AC_MSG_WARN([--with-selftest-prefix called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
selftest_prefix="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
debug=no
|
||||
AC_ARG_ENABLE(debug,
|
||||
[ --enable-debug Turn on compiler debugging information (default=no)],
|
||||
[if test x$enable_debug = xyes; then
|
||||
debug=yes
|
||||
fi])
|
||||
|
||||
developer=no
|
||||
AC_SUBST(developer)
|
||||
AC_ARG_ENABLE(developer,
|
||||
[ --enable-developer Turn on developer warnings and debugging (default=no)],
|
||||
[if test x$enable_developer = xyes; then
|
||||
debug=yes
|
||||
developer=yes
|
||||
fi])
|
||||
|
||||
dnl disable these external libs
|
||||
AC_ARG_WITH(disable-ext-lib,
|
||||
[ --with-disable-ext-lib=LIB Comma-seperated list of external libraries],
|
||||
[ if test $withval; then
|
||||
for i in `echo $withval | sed -e's/,/ /g'`
|
||||
do
|
||||
eval SMB_$i=NO
|
||||
done
|
||||
fi ])
|
||||
@@ -0,0 +1,31 @@
|
||||
dnl SMB Build Environment Perl Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
case "$host_os" in
|
||||
*irix*)
|
||||
# On IRIX, we prefer Freeware or Nekoware Perl, because the
|
||||
# system perl is so ancient.
|
||||
AC_PATH_PROG(PERL, perl, "", "/usr/freeware/bin:/usr/nekoware/bin:$PATH")
|
||||
;;
|
||||
*)
|
||||
AC_PATH_PROG(PERL, perl)
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x"$PERL" = x""; then
|
||||
AC_MSG_WARN([No version of perl was found!])
|
||||
AC_MSG_ERROR([Please install perl from http://www.perl.com/])
|
||||
fi
|
||||
if test x"$debug" = x"yes";then
|
||||
PERL="$PERL -W"
|
||||
fi
|
||||
export PERL
|
||||
|
||||
AC_PATH_PROG(YAPP, yapp, false)
|
||||
|
||||
PIDL_ARGS=""
|
||||
AC_SUBST(PIDL_ARGS)
|
||||
@@ -0,0 +1,32 @@
|
||||
dnl SMB Build Environment Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
AC_SUBST(srcdir)
|
||||
export srcdir;
|
||||
|
||||
# we always set builddir to "." as that's nicer than
|
||||
# having the absolute path of the current work directory
|
||||
builddir=.
|
||||
AC_SUBST(builddir)
|
||||
export builddir;
|
||||
|
||||
AC_SUBST(datarootdir)
|
||||
|
||||
SMB_VERSION_STRING=`cat ${srcdir}/version.h | grep 'SAMBA_VERSION_OFFICIAL_STRING' | cut -d '"' -f2`
|
||||
echo "SAMBA VERSION: ${SMB_VERSION_STRING}"
|
||||
|
||||
SAMBA_VERSION_SVN_REVISION=`cat ${srcdir}/version.h | grep 'SAMBA_VERSION_SVN_REVISION' | cut -d ' ' -f3-`
|
||||
if test -n "${SAMBA_VERSION_SVN_REVISION}";then
|
||||
echo "BUILD REVISION: ${SAMBA_VERSION_SVN_REVISION}"
|
||||
fi
|
||||
|
||||
m4_include(build/m4/check_path.m4)
|
||||
m4_include(build/m4/check_perl.m4)
|
||||
m4_include(build/m4/check_cc.m4)
|
||||
m4_include(build/m4/check_ld.m4)
|
||||
m4_include(build/m4/check_make.m4)
|
||||
m4_include(build/m4/check_doc.m4)
|
||||
@@ -0,0 +1,119 @@
|
||||
dnl SMB Build System
|
||||
dnl ----------------
|
||||
dnl Copyright (C) 2004 Stefan Metzmacher
|
||||
dnl Copyright (C) 2004-2005 Jelmer Vernooij
|
||||
dnl Published under the GPL
|
||||
dnl
|
||||
dnl SMB_SUBSYSTEM(name,obj_files,required_subsystems)
|
||||
dnl
|
||||
dnl SMB_EXT_LIB_FROM_PKGCONFIG(name,pkg-config name)
|
||||
dnl
|
||||
dnl SMB_EXT_LIB(name,libs,cflags,cppflags,ldflags)
|
||||
dnl
|
||||
dnl SMB_ENABLE(name,default_build)
|
||||
dnl
|
||||
dnl #######################################################
|
||||
dnl ### And now the implementation ###
|
||||
dnl #######################################################
|
||||
|
||||
dnl SMB_SUBSYSTEM(name,obj_files,required_subsystems,cflags)
|
||||
AC_DEFUN([SMB_SUBSYSTEM],
|
||||
[
|
||||
SMB_INFO_SUBSYSTEMS="$SMB_INFO_SUBSYSTEMS
|
||||
###################################
|
||||
# Start Subsystem $1
|
||||
@<:@SUBSYSTEM::$1@:>@
|
||||
OBJ_FILES = $2
|
||||
PRIVATE_DEPENDENCIES = $3
|
||||
CFLAGS = $4
|
||||
ENABLE = YES
|
||||
# End Subsystem $1
|
||||
###################################
|
||||
"
|
||||
])
|
||||
|
||||
dnl SMB_EXT_LIB_FROM_PKGCONFIG(name,pkg-config name)
|
||||
AC_DEFUN([SMB_EXT_LIB_FROM_PKGCONFIG],
|
||||
[
|
||||
dnl Figure out the correct variables and call SMB_EXT_LIB()
|
||||
|
||||
if test -z "$PKG_CONFIG"; then
|
||||
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
|
||||
fi
|
||||
|
||||
if test "$PKG_CONFIG" = "no" ; then
|
||||
echo "*** The pkg-config script could not be found. Make sure it is"
|
||||
echo "*** in your path, or set the PKG_CONFIG environment variable"
|
||||
echo "*** to the full path to pkg-config."
|
||||
echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config."
|
||||
SMB_EXT_LIB($1)
|
||||
SMB_ENABLE($1, NO)
|
||||
else
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.9.0; then
|
||||
AC_MSG_CHECKING(for $2)
|
||||
|
||||
if test "$SMB_$1"x = "NO"x ; then
|
||||
SMB_ENABLE($1, NO)
|
||||
AC_MSG_RESULT(disabled)
|
||||
elif $PKG_CONFIG --exists '$2' ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
|
||||
|
||||
$1_CFLAGS="`$PKG_CONFIG --cflags '$2'`"
|
||||
OLD_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $$1_CFLAGS"
|
||||
AC_MSG_CHECKING([that the C compiler can use the $1_CFLAGS])
|
||||
AC_TRY_RUN([#include "${srcdir-.}/build/tests/trivial.c"],
|
||||
SMB_ENABLE($1, YES)
|
||||
AC_MSG_RESULT(yes),
|
||||
AC_MSG_RESULT(no),
|
||||
AC_MSG_WARN([cannot run when cross-compiling]))
|
||||
CFLAGS="$OLD_CFLAGS"
|
||||
|
||||
|
||||
SMB_EXT_LIB($1,
|
||||
[`$PKG_CONFIG --libs-only-l '$2'`],
|
||||
[`$PKG_CONFIG --cflags-only-other '$2'`],
|
||||
[`$PKG_CONFIG --cflags-only-I '$2'`],
|
||||
[`$PKG_CONFIG --libs-only-other '$2'` `$PKG_CONFIG --libs-only-L '$2'`])
|
||||
|
||||
else
|
||||
SMB_EXT_LIB($1)
|
||||
SMB_ENABLE($1, NO)
|
||||
AC_MSG_RESULT(no)
|
||||
$PKG_CONFIG --errors-to-stdout --print-errors '$2'
|
||||
fi
|
||||
else
|
||||
echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer."
|
||||
echo "*** See http://www.freedesktop.org/software/pkgconfig"
|
||||
SMB_EXT_LIB($1)
|
||||
SMB_ENABLE($1, NO)
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
dnl SMB_EXT_LIB(name,libs,cflags,cppflags,ldflags)
|
||||
AC_DEFUN([SMB_EXT_LIB],
|
||||
[
|
||||
|
||||
SMB_INFO_EXT_LIBS="$SMB_INFO_EXT_LIBS
|
||||
###################################
|
||||
# Start Ext Lib $1
|
||||
@<:@EXT_LIB::$1@:>@
|
||||
LIBS = $2
|
||||
CFLAGS = $3
|
||||
CPPFLAGS = $4
|
||||
LDFLAGS = $5
|
||||
# End Ext Lib $1
|
||||
###################################
|
||||
"
|
||||
])
|
||||
|
||||
dnl SMB_ENABLE(name,default_build)
|
||||
AC_DEFUN([SMB_ENABLE],
|
||||
[
|
||||
[SMB_ENABLE_][$1]="$2";
|
||||
|
||||
SMB_INFO_ENABLES="$SMB_INFO_ENABLES
|
||||
\$enabled{$1} = \"$2\";"
|
||||
])
|
||||
@@ -0,0 +1,5 @@
|
||||
asn1.pm: asn1.yp
|
||||
yapp -s asn1.yp
|
||||
|
||||
clean:
|
||||
rm -f asn1.pm
|
||||
@@ -0,0 +1,306 @@
|
||||
########################
|
||||
# ASN.1 Parse::Yapp parser
|
||||
# Copyright (C) Stefan (metze) Metzmacher <metze@samba.org>
|
||||
# released under the GNU GPL version 2 or later
|
||||
|
||||
|
||||
|
||||
# the precedence actually doesn't matter at all for this grammer, but
|
||||
# by providing a precedence we reduce the number of conflicts
|
||||
# enormously
|
||||
%left '-' '+' '&' '|' '*' '>' '.' '/' '(' ')' '[' ']' ':' ',' ';'
|
||||
|
||||
|
||||
################
|
||||
# grammer
|
||||
%%
|
||||
|
||||
asn1:
|
||||
identifier asn1_definitions asn1_delimitter asn1_begin asn1_decls asn1_end
|
||||
{{
|
||||
"OBJECT" => "ASN1_DEFINITION",
|
||||
"IDENTIFIER" => $_[1],
|
||||
"DATA" => $_[5]
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_delimitter:
|
||||
delimitter
|
||||
;
|
||||
|
||||
asn1_definitions:
|
||||
'DEFINITIONS'
|
||||
;
|
||||
|
||||
asn1_begin:
|
||||
'BEGIN'
|
||||
;
|
||||
|
||||
asn1_end:
|
||||
'END'
|
||||
;
|
||||
|
||||
asn1_decls:
|
||||
asn1_def
|
||||
{ [ $_[1] ] }
|
||||
| asn1_decls asn1_def
|
||||
{ push(@{$_[1]}, $_[2]); $_[1] }
|
||||
;
|
||||
|
||||
|
||||
|
||||
asn1_def:
|
||||
asn1_target asn1_delimitter asn1_application asn1_type
|
||||
{{
|
||||
"OBJECT" => "ASN1_DEF",
|
||||
"IDENTIFIER" => $_[1],
|
||||
"APPLICATION" => $_[3],
|
||||
"STRUCTURE" => $_[4]
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_target:
|
||||
identifier
|
||||
;
|
||||
|
||||
asn1_application:
|
||||
#empty
|
||||
| '[' 'APPLICATION' constant ']'
|
||||
{ $_[3] }
|
||||
;
|
||||
|
||||
asn1_type:
|
||||
asn1_boolean
|
||||
| asn1_integer
|
||||
| asn1_bit_string
|
||||
| asn1_octet_string
|
||||
| asn1_null
|
||||
| asn1_object_identifier
|
||||
| asn1_real
|
||||
| asn1_enumerated
|
||||
| asn1_sequence
|
||||
| identifier
|
||||
;
|
||||
|
||||
asn1_boolean:
|
||||
'BOOLEAN'
|
||||
{{
|
||||
"TYPE" => "BOOLEAN",
|
||||
"TAG" => 1
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_integer:
|
||||
'INTEGER'
|
||||
{{
|
||||
"TYPE" => "INTEGER",
|
||||
"TAG" => 2
|
||||
}}
|
||||
| 'INTEGER' '(' constant '.' '.' constant ')'
|
||||
{{
|
||||
"TYPE" => "INTEGER",
|
||||
"TAG" => 2,
|
||||
"RANGE_LOW" => $_[3],
|
||||
"RENAGE_HIGH" => $_[6]
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_bit_string:
|
||||
'BIT' 'STRING'
|
||||
{{
|
||||
"TYPE" => "BIT STRING",
|
||||
"TAG" => 3
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_octet_string:
|
||||
'OCTET' 'STRING'
|
||||
{{
|
||||
"TYPE" => "OCTET STRING",
|
||||
"TAG" => 4
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_null:
|
||||
'NULL'
|
||||
{{
|
||||
"TYPE" => "NULL",
|
||||
"TAG" => 5
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_object_identifier:
|
||||
'OBJECT' 'IDENTIFIER'
|
||||
{{
|
||||
"TYPE" => "OBJECT IDENTIFIER",
|
||||
"TAG" => 6
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_real:
|
||||
'REAL'
|
||||
{{
|
||||
"TYPE" => "REAL",
|
||||
"TAG" => 9
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_enumerated:
|
||||
'ENUMERATED'
|
||||
{{
|
||||
"TYPE" => "ENUMERATED",
|
||||
"TAG" => 10
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_sequence:
|
||||
'SEQUENCE' '{' asn1_var_dec_list '}'
|
||||
{{
|
||||
"TYPE" => "SEQUENCE",
|
||||
"TAG" => 16,
|
||||
"STRUCTURE" => $_[3]
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_var_dec_list:
|
||||
asn1_var_dec
|
||||
{ [ $_[1] ] }
|
||||
| asn1_var_dec_list ',' asn1_var_dec
|
||||
{ push(@{$_[1]}, $_[3]); $_[1] }
|
||||
;
|
||||
|
||||
asn1_var_dec:
|
||||
identifier asn1_type
|
||||
{{
|
||||
"NAME" => $_[1],
|
||||
"TYPE" => $_[2]
|
||||
}}
|
||||
;
|
||||
|
||||
anytext: #empty { "" }
|
||||
| identifier | constant | text
|
||||
| anytext '-' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '.' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '*' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '>' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '|' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '&' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '/' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '+' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '(' anytext ')' anytext { "$_[1]$_[2]$_[3]$_[4]$_[5]" }
|
||||
;
|
||||
|
||||
delimitter: DELIMITTER
|
||||
;
|
||||
|
||||
identifier: IDENTIFIER
|
||||
;
|
||||
|
||||
constant: CONSTANT
|
||||
;
|
||||
|
||||
text: TEXT { "\"$_[1]\"" }
|
||||
;
|
||||
|
||||
#####################################
|
||||
# start code
|
||||
%%
|
||||
|
||||
use util;
|
||||
|
||||
sub _ASN1_Error {
|
||||
if (exists $_[0]->YYData->{ERRMSG}) {
|
||||
print $_[0]->YYData->{ERRMSG};
|
||||
delete $_[0]->YYData->{ERRMSG};
|
||||
return;
|
||||
};
|
||||
my $line = $_[0]->YYData->{LINE};
|
||||
my $last_token = $_[0]->YYData->{LAST_TOKEN};
|
||||
my $file = $_[0]->YYData->{INPUT_FILENAME};
|
||||
|
||||
print "$file:$line: Syntax error near '$last_token'\n";
|
||||
}
|
||||
|
||||
sub _ASN1_Lexer($)
|
||||
{
|
||||
my($parser)=shift;
|
||||
|
||||
$parser->YYData->{INPUT}
|
||||
or return('',undef);
|
||||
|
||||
again:
|
||||
$parser->YYData->{INPUT} =~ s/^[ \t]*//;
|
||||
|
||||
for ($parser->YYData->{INPUT}) {
|
||||
if (/^\#/) {
|
||||
if (s/^\# (\d+) \"(.*?)\"( \d+|)//) {
|
||||
$parser->YYData->{LINE} = $1-1;
|
||||
$parser->YYData->{INPUT_FILENAME} = $2;
|
||||
goto again;
|
||||
}
|
||||
if (s/^\#line (\d+) \"(.*?)\"( \d+|)//) {
|
||||
$parser->YYData->{LINE} = $1-1;
|
||||
$parser->YYData->{INPUT_FILENAME} = $2;
|
||||
goto again;
|
||||
}
|
||||
if (s/^(\#.*)$//m) {
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
if (s/^(\n)//) {
|
||||
$parser->YYData->{LINE}++;
|
||||
goto again;
|
||||
}
|
||||
if (s/^(--.*\n)//) {
|
||||
$parser->YYData->{LINE}++;
|
||||
goto again;
|
||||
}
|
||||
if (s/^(::=)//) {
|
||||
$parser->YYData->{LAST_TOKEN} = $1;
|
||||
return('DELIMITTER',$1);
|
||||
}
|
||||
if (s/^\"(.*?)\"//) {
|
||||
$parser->YYData->{LAST_TOKEN} = $1;
|
||||
return('TEXT',$1);
|
||||
}
|
||||
if (s/^(\d+)(\W|$)/$2/) {
|
||||
$parser->YYData->{LAST_TOKEN} = $1;
|
||||
return('CONSTANT',$1);
|
||||
}
|
||||
if (s/^([\w_-]+)//) {
|
||||
$parser->YYData->{LAST_TOKEN} = $1;
|
||||
if ($1 =~
|
||||
/^(SEQUENCE|INTEGER|OCTET|STRING|
|
||||
APPLICATION|OPTIONAL|NULL|COMPONENTS|OF|
|
||||
BOOLEAN|ENUMERATED|CHOISE|REAL|BIT|OBJECT|IDENTIFIER|
|
||||
DEFAULT|FALSE|TRUE|SET|DEFINITIONS|BEGIN|END)$/x) {
|
||||
return $1;
|
||||
}
|
||||
return('IDENTIFIER',$1);
|
||||
}
|
||||
if (s/^(.)//s) {
|
||||
$parser->YYData->{LAST_TOKEN} = $1;
|
||||
return($1,$1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub parse_asn1($$)
|
||||
{
|
||||
my $self = shift;
|
||||
my $filename = shift;
|
||||
|
||||
my $saved_delim = $/;
|
||||
undef $/;
|
||||
my $cpp = $ENV{CPP};
|
||||
if (! defined $cpp) {
|
||||
$cpp = "cpp"
|
||||
}
|
||||
my $data = `$cpp -xc $filename`;
|
||||
$/ = $saved_delim;
|
||||
|
||||
$self->YYData->{INPUT} = $data;
|
||||
$self->YYData->{LINE} = 0;
|
||||
$self->YYData->{LAST_TOKEN} = "NONE";
|
||||
return $self->YYParse( yylex => \&_ASN1_Lexer, yyerror => \&_ASN1_Error );
|
||||
}
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
###################################################
|
||||
# package to parse ASN.1 files and generate code for
|
||||
# LDAP functions in Samba
|
||||
# Copyright tridge@samba.org 2002-2003
|
||||
# Copyright metze@samba.org 2004
|
||||
|
||||
# released under the GNU GPL
|
||||
|
||||
use strict;
|
||||
|
||||
use FindBin qw($RealBin);
|
||||
use lib "$RealBin";
|
||||
use lib "$RealBin/lib";
|
||||
use Getopt::Long;
|
||||
use File::Basename;
|
||||
use asn1;
|
||||
use util;
|
||||
|
||||
my($opt_help) = 0;
|
||||
my($opt_output);
|
||||
|
||||
my $asn1_parser = new asn1;
|
||||
|
||||
#####################################################################
|
||||
# parse an ASN.1 file returning a structure containing all the data
|
||||
sub ASN1Parse($)
|
||||
{
|
||||
my $filename = shift;
|
||||
my $asn1 = $asn1_parser->parse_asn1($filename);
|
||||
util::CleanData($asn1);
|
||||
return $asn1;
|
||||
}
|
||||
|
||||
|
||||
#########################################
|
||||
# display help text
|
||||
sub ShowHelp()
|
||||
{
|
||||
print "
|
||||
perl ASN.1 parser and code generator
|
||||
Copyright (C) tridge\@samba.org
|
||||
Copyright (C) metze\@samba.org
|
||||
|
||||
Usage: pasn1.pl [options] <asn1file>
|
||||
|
||||
Options:
|
||||
--help this help page
|
||||
--output OUTNAME put output in OUTNAME
|
||||
\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
# main program
|
||||
GetOptions (
|
||||
'help|h|?' => \$opt_help,
|
||||
'output|o=s' => \$opt_output,
|
||||
);
|
||||
|
||||
if ($opt_help) {
|
||||
ShowHelp();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
sub process_file($)
|
||||
{
|
||||
my $input_file = shift;
|
||||
my $output_file;
|
||||
my $pasn1;
|
||||
|
||||
my $basename = basename($input_file, ".asn1");
|
||||
|
||||
if (!defined($opt_output)) {
|
||||
$output_file = util::ChangeExtension($input_file, ".pasn1");
|
||||
} else {
|
||||
$output_file = $opt_output;
|
||||
}
|
||||
|
||||
# if (file is .pasn1) {
|
||||
# $pasn1 = util::LoadStructure($pasn1_file);
|
||||
# defined $pasn1 || die "Failed to load $pasn1_file - maybe you need --parse\n";
|
||||
# } else {
|
||||
$pasn1 = ASN1Parse($input_file);
|
||||
defined $pasn1 || die "Failed to parse $input_file";
|
||||
util::SaveStructure($output_file, $pasn1) ||
|
||||
die "Failed to save $output_file\n";
|
||||
#}
|
||||
}
|
||||
|
||||
foreach my $filename (@ARGV) {
|
||||
process_file($filename);
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
###################################################
|
||||
# utility functions to support pidl
|
||||
# Copyright tridge@samba.org 2000
|
||||
# released under the GNU GPL
|
||||
package util;
|
||||
|
||||
#####################################################################
|
||||
# load a data structure from a file (as saved with SaveStructure)
|
||||
sub LoadStructure($)
|
||||
{
|
||||
my $f = shift;
|
||||
my $contents = FileLoad($f);
|
||||
defined $contents || return undef;
|
||||
return eval "$contents";
|
||||
}
|
||||
|
||||
use strict;
|
||||
|
||||
#####################################################################
|
||||
# flatten an array of arrays into a single array
|
||||
sub FlattenArray2($)
|
||||
{
|
||||
my $a = shift;
|
||||
my @b;
|
||||
for my $d (@{$a}) {
|
||||
for my $d1 (@{$d}) {
|
||||
push(@b, $d1);
|
||||
}
|
||||
}
|
||||
return \@b;
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# flatten an array of arrays into a single array
|
||||
sub FlattenArray($)
|
||||
{
|
||||
my $a = shift;
|
||||
my @b;
|
||||
for my $d (@{$a}) {
|
||||
for my $d1 (@{$d}) {
|
||||
push(@b, $d1);
|
||||
}
|
||||
}
|
||||
return \@b;
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# flatten an array of hashes into a single hash
|
||||
sub FlattenHash($)
|
||||
{
|
||||
my $a = shift;
|
||||
my %b;
|
||||
for my $d (@{$a}) {
|
||||
for my $k (keys %{$d}) {
|
||||
$b{$k} = $d->{$k};
|
||||
}
|
||||
}
|
||||
return \%b;
|
||||
}
|
||||
|
||||
|
||||
#####################################################################
|
||||
# traverse a perl data structure removing any empty arrays or
|
||||
# hashes and any hash elements that map to undef
|
||||
sub CleanData($)
|
||||
{
|
||||
sub CleanData($);
|
||||
my($v) = shift;
|
||||
if (ref($v) eq "ARRAY") {
|
||||
foreach my $i (0 .. $#{$v}) {
|
||||
CleanData($v->[$i]);
|
||||
if (ref($v->[$i]) eq "ARRAY" && $#{$v->[$i]}==-1) {
|
||||
$v->[$i] = undef;
|
||||
next;
|
||||
}
|
||||
}
|
||||
# this removes any undefined elements from the array
|
||||
@{$v} = grep { defined $_ } @{$v};
|
||||
} elsif (ref($v) eq "HASH") {
|
||||
foreach my $x (keys %{$v}) {
|
||||
CleanData($v->{$x});
|
||||
if (!defined $v->{$x}) { delete($v->{$x}); next; }
|
||||
if (ref($v->{$x}) eq "ARRAY" && $#{$v->{$x}}==-1) { delete($v->{$x}); next; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#####################################################################
|
||||
# return the modification time of a file
|
||||
sub FileModtime($)
|
||||
{
|
||||
my($filename) = shift;
|
||||
return (stat($filename))[9];
|
||||
}
|
||||
|
||||
|
||||
#####################################################################
|
||||
# read a file into a string
|
||||
sub FileLoad($)
|
||||
{
|
||||
my($filename) = shift;
|
||||
local(*INPUTFILE);
|
||||
open(INPUTFILE, $filename) || return undef;
|
||||
my($saved_delim) = $/;
|
||||
undef $/;
|
||||
my($data) = <INPUTFILE>;
|
||||
close(INPUTFILE);
|
||||
$/ = $saved_delim;
|
||||
return $data;
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# write a string into a file
|
||||
sub FileSave($$)
|
||||
{
|
||||
my($filename) = shift;
|
||||
my($v) = shift;
|
||||
local(*FILE);
|
||||
open(FILE, ">$filename") || die "can't open $filename";
|
||||
print FILE $v;
|
||||
close(FILE);
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# return a filename with a changed extension
|
||||
sub ChangeExtension($$)
|
||||
{
|
||||
my($fname) = shift;
|
||||
my($ext) = shift;
|
||||
if ($fname =~ /^(.*)\.(.*?)$/) {
|
||||
return "$1$ext";
|
||||
}
|
||||
return "$fname$ext";
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# a dumper wrapper to prevent dependence on the Data::Dumper module
|
||||
# unless we actually need it
|
||||
sub MyDumper($)
|
||||
{
|
||||
require Data::Dumper;
|
||||
my $s = shift;
|
||||
return Data::Dumper::Dumper($s);
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# save a data structure into a file
|
||||
sub SaveStructure($$)
|
||||
{
|
||||
my($filename) = shift;
|
||||
my($v) = shift;
|
||||
FileSave($filename, MyDumper($v));
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# see if a pidl property list contains a give property
|
||||
sub has_property($$)
|
||||
{
|
||||
my($e) = shift;
|
||||
my($p) = shift;
|
||||
|
||||
if (!defined $e->{PROPERTIES}) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
return $e->{PROPERTIES}->{$p};
|
||||
}
|
||||
|
||||
|
||||
sub is_scalar_type($)
|
||||
{
|
||||
my($type) = shift;
|
||||
|
||||
if ($type =~ /^u?int\d+/) {
|
||||
return 1;
|
||||
}
|
||||
if ($type =~ /char|short|long|NTTIME|
|
||||
time_t|error_status_t|boolean32|unsigned32|
|
||||
HYPER_T|wchar_t|DATA_BLOB/x) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
# return the NDR alignment for a type
|
||||
sub type_align($)
|
||||
{
|
||||
my($e) = shift;
|
||||
my $type = $e->{TYPE};
|
||||
|
||||
if (need_wire_pointer($e)) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 4, if ($type eq "uint32");
|
||||
return 4, if ($type eq "long");
|
||||
return 2, if ($type eq "short");
|
||||
return 1, if ($type eq "char");
|
||||
return 1, if ($type eq "uint8");
|
||||
return 2, if ($type eq "uint16");
|
||||
return 4, if ($type eq "NTTIME");
|
||||
return 4, if ($type eq "time_t");
|
||||
return 8, if ($type eq "HYPER_T");
|
||||
return 2, if ($type eq "wchar_t");
|
||||
return 4, if ($type eq "DATA_BLOB");
|
||||
|
||||
# it must be an external type - all we can do is guess
|
||||
return 4;
|
||||
}
|
||||
|
||||
# this is used to determine if the ndr push/pull functions will need
|
||||
# a ndr_flags field to split by buffers/scalars
|
||||
sub is_builtin_type($)
|
||||
{
|
||||
my($type) = shift;
|
||||
|
||||
return 1, if (is_scalar_type($type));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
# determine if an element needs a reference pointer on the wire
|
||||
# in its NDR representation
|
||||
sub need_wire_pointer($)
|
||||
{
|
||||
my $e = shift;
|
||||
if ($e->{POINTERS} &&
|
||||
!has_property($e, "ref")) {
|
||||
return $e->{POINTERS};
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
# determine if an element is a pass-by-reference structure
|
||||
sub is_ref_struct($)
|
||||
{
|
||||
my $e = shift;
|
||||
if (!is_scalar_type($e->{TYPE}) &&
|
||||
has_property($e, "ref")) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# determine if an element is a pure scalar. pure scalars do not
|
||||
# have a "buffers" section in NDR
|
||||
sub is_pure_scalar($)
|
||||
{
|
||||
my $e = shift;
|
||||
if (has_property($e, "ref")) {
|
||||
return 1;
|
||||
}
|
||||
if (is_scalar_type($e->{TYPE}) &&
|
||||
!$e->{POINTERS} &&
|
||||
!array_size($e)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# determine the array size (size_is() or ARRAY_LEN)
|
||||
sub array_size($)
|
||||
{
|
||||
my $e = shift;
|
||||
my $size = has_property($e, "size_is");
|
||||
if ($size) {
|
||||
return $size;
|
||||
}
|
||||
$size = $e->{ARRAY_LEN};
|
||||
if ($size) {
|
||||
return $size;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
# see if a variable needs to be allocated by the NDR subsystem on pull
|
||||
sub need_alloc($)
|
||||
{
|
||||
my $e = shift;
|
||||
|
||||
if (has_property($e, "ref")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($e->{POINTERS} || array_size($e)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
# determine the C prefix used to refer to a variable when passing to a push
|
||||
# function. This will be '*' for pointers to scalar types, '' for scalar
|
||||
# types and normal pointers and '&' for pass-by-reference structures
|
||||
sub c_push_prefix($)
|
||||
{
|
||||
my $e = shift;
|
||||
|
||||
if ($e->{TYPE} =~ "string") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (is_scalar_type($e->{TYPE}) &&
|
||||
$e->{POINTERS}) {
|
||||
return "*";
|
||||
}
|
||||
if (!is_scalar_type($e->{TYPE}) &&
|
||||
!$e->{POINTERS} &&
|
||||
!array_size($e)) {
|
||||
return "&";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
# determine the C prefix used to refer to a variable when passing to a pull
|
||||
# return '&' or ''
|
||||
sub c_pull_prefix($)
|
||||
{
|
||||
my $e = shift;
|
||||
|
||||
if (!$e->{POINTERS} && !array_size($e)) {
|
||||
return "&";
|
||||
}
|
||||
|
||||
if ($e->{TYPE} =~ "string") {
|
||||
return "&";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
# determine if an element has a direct buffers component
|
||||
sub has_direct_buffers($)
|
||||
{
|
||||
my $e = shift;
|
||||
if ($e->{POINTERS} || array_size($e)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# return 1 if the string is a C constant
|
||||
sub is_constant($)
|
||||
{
|
||||
my $s = shift;
|
||||
if ($s =~ /^\d/) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# return 1 if this is a fixed array
|
||||
sub is_fixed_array($)
|
||||
{
|
||||
my $e = shift;
|
||||
my $len = $e->{"ARRAY_LEN"};
|
||||
if (defined $len && is_constant($len)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# return 1 if this is a inline array
|
||||
sub is_inline_array($)
|
||||
{
|
||||
my $e = shift;
|
||||
my $len = $e->{"ARRAY_LEN"};
|
||||
if (is_fixed_array($e) ||
|
||||
defined $len && $len ne "*") {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
The Samba Build System
|
||||
----------------------
|
||||
----------------------
|
||||
|
||||
The build system basically has two main parts: the autoconf-generated
|
||||
shell scripts which check for availability of functions and libraries
|
||||
which is stored in the .m4 files and the information about the various
|
||||
subsystems which is stored in the .mk files.
|
||||
|
||||
Object Types
|
||||
------------
|
||||
the build system knows about the following object types
|
||||
|
||||
SUBSYSTEM:
|
||||
a SUBSYSTEM is basicly a collection of functions, which provide an
|
||||
an generic API for a specific problem (e.g. libldb provides an api
|
||||
for gneric ldb databases. libldb_plugin provides a generic api
|
||||
for calling ldb plugins, so 'libldb' and 'libldb_plugin' are subsystems)
|
||||
|
||||
MODULE:
|
||||
a MODULE is a specify implementation of a API provided by a SUBSYSTEM.
|
||||
(e.g. 'libldb_tdb' and 'libldb_ldap' are implementations of the subsystem 'libldb' API,
|
||||
and 'libldb_plugin_timestamp' is a module of the 'libldb_plugin' subsystem)
|
||||
|
||||
EXT_LIB:
|
||||
an EXT_LIB is an external library which is needed by a SUBSYSTEM, MODULE, BINARY or LIBRARY.
|
||||
(e.g. 'gtk' or 'KRB5')
|
||||
|
||||
BINARY:
|
||||
a BINARY means a executable binary file.
|
||||
(e.g. 'smbtorture' or 'ldbedit')
|
||||
a BINARY typicly has only commandline handling and basic
|
||||
functionality code in it and depends on the functions of
|
||||
SUBSYSTEM's (REQUIRED_SUBSYSTEMS).
|
||||
|
||||
LIBRARY:
|
||||
a LIBRARY means a static and/or shared library,
|
||||
which depends on the used OS.
|
||||
(e.g. for libldb 'libldb.so', 'libldb.so.0' 'libldb.so.0.0.1'
|
||||
and libldb.a are created on linux)
|
||||
a LIBRARY typicly has only glue code in it and depends on
|
||||
SUBSYSTEM's (REQUIRED_SUBSYSTEMS).
|
||||
|
||||
File summary:
|
||||
-------------
|
||||
public.m4 - public M4 macros of the build system
|
||||
config_mk.pm - Support for reading .mk files
|
||||
dot.pm - Support for generating .dot files for analysis of dependencies
|
||||
input.pm - Input validation
|
||||
main.pm - Main
|
||||
makefile.pm - Makefile generation
|
||||
output.pm - Dependency calculation
|
||||
header.pm - build.h generation
|
||||
cflags.pm - Generates cflags.txt for file-specific cflags
|
||||
|
||||
Layout
|
||||
-------
|
||||
|
||||
Toplevel file: configure.in
|
||||
- included by autogen.sh: aclocal.m4
|
||||
which includes the SMB_YXZ*() macros
|
||||
|
||||
- default tests of the build system
|
||||
are in build/smb_build/check_*.m4
|
||||
(mostly compiler and basic C type and function
|
||||
checks)
|
||||
|
||||
- subsystem specific stuff should be included by 'SMB_INCLUDE_M4()'
|
||||
|
||||
|
||||
Generating the configure file
|
||||
-------------------------
|
||||
you need to rerun ./autogen.sh when 'configure.in' or any
|
||||
'.m4' file was modified, then you need to rerun configure.
|
||||
|
||||
|
||||
Generating config.status
|
||||
-----------------------------
|
||||
you need to run ./config.status (or 'configure') after a '.mk'
|
||||
file was changed.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
for now please take a look at the .m4 and .mk files
|
||||
you find in the source tree, they should be a good reference to start.
|
||||
@@ -0,0 +1,25 @@
|
||||
- use pkg-config files in the buildsystem?
|
||||
- let the build system implement some make functions($(patsubst),$(wildcard),...) and use our own implementations where `make' does not support them
|
||||
- include extra_flags.txt using Makefile construction if
|
||||
supported by current make
|
||||
- fix shared module loading for selftest during builds without install
|
||||
- remove recursive dependency between LIBSOCKET, LIBCLI_NBT and LIBCLI_RESOLVE
|
||||
- clearer distinction between dcerpc and ndr. seperate interface tables? Maybe get rid of
|
||||
NDR's table altogether and use dlopen/dlsym ?
|
||||
- saner names for:
|
||||
libcli.so.0.0.1 (rename to libsmb?)
|
||||
libcli_cldap.so.0.0.1 (rename to libcldap?)
|
||||
libcli_nbt.so.0.0.1 (rename to libnbt?)
|
||||
libcli_wrepl.so.0.0.1 (rename to libwrepl?)
|
||||
- generate headermap.txt
|
||||
|
||||
set of test scripts that check the code:
|
||||
- configure_check_unused.pl
|
||||
- find_unused_macros.pl
|
||||
- find_unused_makefilevars.pl
|
||||
- find_unused_options.sh
|
||||
- findstatic.pl
|
||||
- minimal_includes.pl
|
||||
- check dependencies based on #include lines ?
|
||||
- check whether private headers are not used outside their own subsystem
|
||||
- undocumented (no manpage) installed binaries
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
# SMB Build System
|
||||
#
|
||||
# Copyright (C) Jelmer Vernooij 2006
|
||||
# Released under the GNU GPL
|
||||
|
||||
package cflags;
|
||||
use strict;
|
||||
|
||||
sub create_cflags($$)
|
||||
{
|
||||
my ($CTX, $file) = @_;
|
||||
|
||||
open(CFLAGS_TXT,">$file") || die ("Can't open `$file'\n");
|
||||
|
||||
foreach my $key (values %{$CTX}) {
|
||||
next unless defined ($key->{OBJ_LIST});
|
||||
|
||||
next unless defined ($key->{FINAL_CFLAGS});
|
||||
next unless ($#{$key->{FINAL_CFLAGS}} >= 0);
|
||||
|
||||
my $cflags = join(' ', @{$key->{FINAL_CFLAGS}});
|
||||
|
||||
foreach (@{$key->{OBJ_LIST}}) {
|
||||
my $ofile = $_;
|
||||
my $dfile = $_;
|
||||
$dfile =~ s/\.o$/.d/;
|
||||
$dfile =~ s/\.ho$/.d/;
|
||||
print CFLAGS_TXT "$ofile $dfile: CFLAGS+= $cflags\n";
|
||||
}
|
||||
}
|
||||
close(CFLAGS_TXT);
|
||||
|
||||
print __FILE__.": creating $file\n";
|
||||
}
|
||||
1;
|
||||
@@ -0,0 +1,263 @@
|
||||
# Samba Build System
|
||||
# - config.mk parsing functions
|
||||
#
|
||||
# Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
# Copyright (C) Jelmer Vernooij 2005
|
||||
# Released under the GNU GPL
|
||||
#
|
||||
|
||||
package smb_build::config_mk;
|
||||
use smb_build::input;
|
||||
use File::Basename;
|
||||
|
||||
use strict;
|
||||
|
||||
my $section_types = {
|
||||
"EXT_LIB" => {
|
||||
"LIBS" => "list",
|
||||
"CFLAGS" => "list",
|
||||
"CPPFLAGS" => "list",
|
||||
"LDFLAGS" => "list",
|
||||
},
|
||||
"SUBSYSTEM" => {
|
||||
"OBJ_FILES" => "list",
|
||||
|
||||
"PRIVATE_DEPENDENCIES" => "list",
|
||||
"PUBLIC_DEPENDENCIES" => "list",
|
||||
|
||||
"ENABLE" => "bool",
|
||||
|
||||
"MANPAGE" => "string",
|
||||
|
||||
"PUBLIC_PROTO_HEADER" => "string",
|
||||
"PRIVATE_PROTO_HEADER" => "string",
|
||||
|
||||
"PUBLIC_HEADERS" => "list",
|
||||
|
||||
"CFLAGS" => "list",
|
||||
"LDFLAGS" => "list",
|
||||
"STANDARD_VISIBILITY" => "string"
|
||||
},
|
||||
"MODULE" => {
|
||||
"SUBSYSTEM" => "string",
|
||||
|
||||
"INIT_FUNCTION" => "string",
|
||||
"OBJ_FILES" => "list",
|
||||
|
||||
"PUBLIC_DEPENDENCIES" => "list",
|
||||
"PRIVATE_DEPENDENCIES" => "list",
|
||||
|
||||
"ALIASES" => "list",
|
||||
|
||||
"ENABLE" => "bool",
|
||||
|
||||
"OUTPUT_TYPE" => "list",
|
||||
|
||||
"MANPAGE" => "string",
|
||||
"PRIVATE_PROTO_HEADER" => "string",
|
||||
"PUBLIC_PROTO_HEADER" => "string",
|
||||
|
||||
|
||||
"PUBLIC_HEADERS" => "list",
|
||||
|
||||
"CFLAGS" => "list"
|
||||
},
|
||||
"BINARY" => {
|
||||
"OBJ_FILES" => "list",
|
||||
|
||||
"PRIVATE_DEPENDENCIES" => "list",
|
||||
|
||||
"ENABLE" => "bool",
|
||||
|
||||
"MANPAGE" => "string",
|
||||
"INSTALLDIR" => "string",
|
||||
"PRIVATE_PROTO_HEADER" => "string",
|
||||
"PUBLIC_PROTO_HEADER" => "string",
|
||||
"PUBLIC_HEADERS" => "list",
|
||||
|
||||
"CFLAGS" => "list",
|
||||
"LDFLAGS" => "list",
|
||||
"STANDARD_VISIBILITY" => "string",
|
||||
|
||||
"USE_HOSTCC" => "bool"
|
||||
},
|
||||
"LIBRARY" => {
|
||||
"VERSION" => "string",
|
||||
"SO_VERSION" => "string",
|
||||
"LIBRARY_REALNAME" => "string",
|
||||
|
||||
"INIT_FUNCTION_TYPE" => "string",
|
||||
|
||||
"OBJ_FILES" => "list",
|
||||
|
||||
"DESCRIPTION" => "string",
|
||||
|
||||
"PRIVATE_DEPENDENCIES" => "list",
|
||||
"PUBLIC_DEPENDENCIES" => "list",
|
||||
|
||||
"ENABLE" => "bool",
|
||||
|
||||
"MANPAGE" => "string",
|
||||
|
||||
"PUBLIC_HEADERS" => "list",
|
||||
|
||||
"PUBLIC_PROTO_HEADER" => "string",
|
||||
"PRIVATE_PROTO_HEADER" => "string",
|
||||
|
||||
"CFLAGS" => "list",
|
||||
"LDFLAGS" => "list",
|
||||
"STANDARD_VISIBILITY" => "string"
|
||||
}
|
||||
};
|
||||
|
||||
use vars qw(@parsed_files);
|
||||
|
||||
@parsed_files = ();
|
||||
|
||||
###########################################################
|
||||
# The parsing function which parses the file
|
||||
#
|
||||
# $result = _parse_config_mk($filename)
|
||||
#
|
||||
# $filename - the path of the config.mk file
|
||||
# which should be parsed
|
||||
sub run_config_mk($$$$)
|
||||
{
|
||||
sub run_config_mk($$$$);
|
||||
my ($input, $srcdir, $builddir, $filename) = @_;
|
||||
my $result;
|
||||
my $linenum = -1;
|
||||
my $infragment = 0;
|
||||
my $section = "GLOBAL";
|
||||
my $makefile = "";
|
||||
|
||||
my $parsing_file = $filename;
|
||||
my $retry_parsing_file = undef;
|
||||
|
||||
$ENV{samba_builddir} = $builddir;
|
||||
$ENV{samba_srcdir} = $srcdir;
|
||||
|
||||
if (($srcdir ne ".") or ($builddir ne ".")) {
|
||||
$parsing_file = $builddir."/".$filename;
|
||||
$retry_parsing_file = $srcdir."/".$filename;
|
||||
}
|
||||
|
||||
if (open(CONFIG_MK, $parsing_file)) {
|
||||
$retry_parsing_file = undef;
|
||||
} else {
|
||||
die("Can't open $parsing_file") unless defined($retry_parsing_file);
|
||||
}
|
||||
|
||||
if (defined($retry_parsing_file)) {
|
||||
if (open(CONFIG_MK, $parsing_file)) {
|
||||
$parsing_file = $retry_parsing_file;
|
||||
$retry_parsing_file = undef;
|
||||
} else {
|
||||
die("Can't open neither '$parsing_file' nor '$retry_parsing_file'\n");
|
||||
}
|
||||
}
|
||||
|
||||
push (@parsed_files, $parsing_file);
|
||||
|
||||
|
||||
my @lines = <CONFIG_MK>;
|
||||
close(CONFIG_MK);
|
||||
|
||||
my $line = "";
|
||||
my $prev = "";
|
||||
|
||||
foreach (@lines) {
|
||||
$linenum++;
|
||||
|
||||
# lines beginning with '#' are ignored
|
||||
next if (/^\#.*$/);
|
||||
|
||||
if (/^(.*)\\$/) {
|
||||
$prev .= $1;
|
||||
next;
|
||||
} else {
|
||||
$line = "$prev$_";
|
||||
$prev = "";
|
||||
}
|
||||
|
||||
if ($line =~ /^\[([-a-zA-Z0-9_:]+)\][\t ]*$/)
|
||||
{
|
||||
$section = $1;
|
||||
$infragment = 0;
|
||||
next;
|
||||
}
|
||||
|
||||
# include
|
||||
if ($line =~ /^include (.*)$/) {
|
||||
my $subfile= $1;
|
||||
my $subdir = dirname($filename);
|
||||
$subdir =~ s/^\.$//g;
|
||||
$subdir =~ s/^\.\///g;
|
||||
$subdir .= "/" if ($subdir ne "");
|
||||
$makefile .= run_config_mk($input, $srcdir, $builddir, $subdir.$subfile);
|
||||
next;
|
||||
}
|
||||
|
||||
# empty line
|
||||
if ($line =~ /^[ \t]*$/) {
|
||||
$section = "GLOBAL";
|
||||
if ($infragment) { $makefile.="\n"; }
|
||||
next;
|
||||
}
|
||||
|
||||
# global stuff is considered part of the makefile
|
||||
if ($section eq "GLOBAL") {
|
||||
if (!$infragment) { $makefile.="\n"; }
|
||||
$makefile .= $line;
|
||||
$infragment = 1;
|
||||
next;
|
||||
}
|
||||
|
||||
|
||||
# Assignment
|
||||
if ($line =~ /^([a-zA-Z0-9_]+)[\t ]*=(.*)$/) {
|
||||
$result->{$section}{$1}{VAL} = $2;
|
||||
$result->{$section}{$1}{KEY} = $1;
|
||||
|
||||
next;
|
||||
}
|
||||
|
||||
die("$parsing_file:$linenum: Bad line while parsing $parsing_file");
|
||||
}
|
||||
|
||||
foreach my $section (keys %{$result}) {
|
||||
my ($type, $name) = split(/::/, $section, 2);
|
||||
|
||||
my $sectype = $section_types->{$type};
|
||||
if (not defined($sectype)) {
|
||||
die($parsing_file.":[".$section."] unknown section type \"".$type."\"!");
|
||||
}
|
||||
|
||||
$input->{$name}{NAME} = $name;
|
||||
$input->{$name}{TYPE} = $type;
|
||||
$input->{$name}{MK_FILE} = $parsing_file;
|
||||
$input->{$name}{BASEDIR} = dirname($filename);
|
||||
|
||||
foreach my $key (values %{$result->{$section}}) {
|
||||
$key->{VAL} = smb_build::input::strtrim($key->{VAL});
|
||||
my $vartype = $sectype->{$key->{KEY}};
|
||||
if (not defined($vartype)) {
|
||||
die($parsing_file.":[".$section."]: unknown attribute type \"$key->{KEY}\"!");
|
||||
}
|
||||
if ($vartype eq "string") {
|
||||
$input->{$name}{$key->{KEY}} = $key->{VAL};
|
||||
} elsif ($vartype eq "list") {
|
||||
$input->{$name}{$key->{KEY}} = [smb_build::input::str2array($key->{VAL})];
|
||||
} elsif ($vartype eq "bool") {
|
||||
if (($key->{VAL} ne "YES") and ($key->{VAL} ne "NO")) {
|
||||
die("Invalid value for bool attribute $key->{KEY}: $key->{VAL} in section $section");
|
||||
}
|
||||
$input->{$name}{$key->{KEY}} = $key->{VAL};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $makefile;
|
||||
}
|
||||
|
||||
1;
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/perl
|
||||
# Samba4 Dependency Graph Generator
|
||||
# (C) 2004-2005 Jelmer Vernooij <jelmer@samba.org>
|
||||
# Published under the GNU GPL
|
||||
|
||||
use strict;
|
||||
use lib 'build';
|
||||
use smb_build::config_mk;
|
||||
|
||||
my $subsys = shift @ARGV;
|
||||
|
||||
sub contains($$)
|
||||
{
|
||||
my ($haystack,$needle) = @_;
|
||||
foreach (@$haystack) {
|
||||
return 1 if ($_ eq $needle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub generate($$$)
|
||||
{
|
||||
my ($depend,$only,$name) = @_;
|
||||
my $res = "digraph $name {\n";
|
||||
|
||||
foreach my $part (values %{$depend}) {
|
||||
next if (defined($only) and not contains($only,$part->{NAME}));
|
||||
foreach my $elem (@{$part->{PUBLIC_DEPENDENCIES}},
|
||||
@{$part->{PRIVATE_DEPENDENCIES}}) {
|
||||
$res .= "\t\"$part->{NAME}\" -> \"$elem\";\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $res . "}\n";
|
||||
}
|
||||
|
||||
my $INPUT = {};
|
||||
smb_build::config_mk::run_config_mk($INPUT, '.', '.', "main.mk");
|
||||
|
||||
my $name = "samba4";
|
||||
|
||||
my $only;
|
||||
if (defined($subsys)) {
|
||||
my $DEPEND = smb_build::input::check($INPUT, \%config::enabled,
|
||||
"STATIC_LIBRARY", "SHARED_LIBRARY", "SHARED_LIBRARY");
|
||||
|
||||
die("No such subsystem $subsys") unless (defined($DEPEND->{$subsys}));
|
||||
|
||||
$only = $DEPEND->{$subsys}->{UNIQUE_DEPENDENCIES_ALL};
|
||||
push (@$only, "$subsys");
|
||||
|
||||
$name = $subsys;
|
||||
}
|
||||
|
||||
my $fname = "$name-deps.dot";
|
||||
print __FILE__.": creating $fname\n";
|
||||
open DOTTY, ">$fname";
|
||||
print DOTTY generate($INPUT, $only, $name);
|
||||
close DOTTY;
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,116 @@
|
||||
# Environment class
|
||||
#
|
||||
# Samba Build Environment
|
||||
#
|
||||
# (C) 2005 Jelmer Vernooij <jelmer@samba.org>
|
||||
#
|
||||
# Published under the GNU GPL
|
||||
|
||||
package smb_build::env;
|
||||
use smb_build::input;
|
||||
use File::Path;
|
||||
use File::Basename;
|
||||
|
||||
use strict;
|
||||
|
||||
sub new($$)
|
||||
{
|
||||
my ($name, $config) = @_;
|
||||
my $self = { };
|
||||
bless $self, $name;
|
||||
|
||||
$self->{items} = {};
|
||||
$self->{info} = {};
|
||||
|
||||
$self->_set_config($config);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub _set_config($$)
|
||||
{
|
||||
my ($self, $config) = @_;
|
||||
|
||||
$self->{config} = $config;
|
||||
|
||||
if (not defined($self->{config}->{srcdir})) {
|
||||
$self->{config}->{srcdir} = '.';
|
||||
}
|
||||
|
||||
if (not defined($self->{config}->{builddir})) {
|
||||
$self->{config}->{builddir} = '.';
|
||||
}
|
||||
|
||||
if ($self->{config}->{prefix} eq "NONE") {
|
||||
$self->{config}->{prefix} = $self->{config}->{ac_default_prefix};
|
||||
}
|
||||
|
||||
if ($self->{config}->{exec_prefix} eq "NONE") {
|
||||
$self->{config}->{exec_prefix} = $self->{config}->{prefix};
|
||||
}
|
||||
|
||||
$self->{developer} = ($self->{config}->{developer} eq "yes");
|
||||
$self->{automatic_deps} = ($self->{config}->{automatic_dependencies} eq "yes");
|
||||
}
|
||||
|
||||
sub PkgConfig($$$$$$$$$$$$)
|
||||
{
|
||||
my ($self,$path,$name,$libs,$privlibs,$cflags,$version,$desc,$hasmodules,$pubdep,$privdep,$dirs) = @_;
|
||||
|
||||
print __FILE__.": creating $path\n";
|
||||
|
||||
if ($self->{config}->{libreplace_cv_immediate_structures} eq "yes") {
|
||||
$cflags .= " -DHAVE_IMMEDIATE_STRUCTURES=1";
|
||||
}
|
||||
|
||||
mkpath(dirname($path),0,0755);
|
||||
open(OUT, ">$path") or die("Can't open $path: $!");
|
||||
|
||||
foreach (@$dirs) {
|
||||
print OUT "$_\n";
|
||||
}
|
||||
if ($hasmodules) {
|
||||
print OUT "modulesdir=$self->{config}->{modulesdir}/$name\n" ;
|
||||
}
|
||||
|
||||
print OUT "\n";
|
||||
|
||||
print OUT "Name: $name\n";
|
||||
if (defined($desc)) {
|
||||
print OUT "Description: $desc\n";
|
||||
}
|
||||
print OUT "Requires: $pubdep\n" if defined($pubdep);
|
||||
print OUT "Requires.private: $privdep\n" if defined($privdep);
|
||||
print OUT "Version: $version\n";
|
||||
print OUT "Libs: $libs\n";
|
||||
print OUT "Libs.private: $privlibs\n" if (defined($privlibs));
|
||||
print OUT "Cflags: -I\${includedir} $cflags\n";
|
||||
|
||||
close(OUT);
|
||||
}
|
||||
|
||||
sub Import($$)
|
||||
{
|
||||
my ($self,$items) = @_;
|
||||
|
||||
foreach (keys %$items) {
|
||||
if (defined($self->{items})) {
|
||||
print "Warning: Importing $_ twice!\n";
|
||||
}
|
||||
$self->{items}->{$_} = $items->{$_};
|
||||
}
|
||||
}
|
||||
|
||||
sub GetInfo($$)
|
||||
{
|
||||
my ($self,$name) = @_;
|
||||
|
||||
unless (defined($self->{info}->{$name}))
|
||||
{
|
||||
$self->{info}->{$name} = $self->{items}->Build($self);
|
||||
}
|
||||
|
||||
return $self->{info}->{$name};
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,84 @@
|
||||
# SMB Build System
|
||||
# - create output for build.h
|
||||
#
|
||||
# Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
# Copyright (C) Jelmer Vernooij 2005
|
||||
# Released under the GNU GPL
|
||||
|
||||
package header;
|
||||
use strict;
|
||||
|
||||
sub _add_define_section($)
|
||||
{
|
||||
my $DEFINE = shift;
|
||||
my $output = "";
|
||||
|
||||
$output .= "
|
||||
/* $DEFINE->{COMMENT} */
|
||||
#define $DEFINE->{KEY} $DEFINE->{VAL}
|
||||
";
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub _prepare_build_h($)
|
||||
{
|
||||
my $depend = shift;
|
||||
my @defines = ();
|
||||
my $output = "";
|
||||
|
||||
foreach my $key (values %$depend) {
|
||||
my $DEFINE = ();
|
||||
next if ($key->{TYPE} ne "LIBRARY" and
|
||||
$key->{TYPE} ne "MODULE" and
|
||||
$key->{TYPE} ne "SUBSYSTEM" and
|
||||
$key->{TYPE} ne "BINARY");
|
||||
next unless defined($key->{INIT_FUNCTIONS});
|
||||
|
||||
my $name = $key->{NAME};
|
||||
$name =~ s/-/_/g;
|
||||
$DEFINE->{COMMENT} = "$key->{TYPE} $key->{NAME} INIT";
|
||||
$DEFINE->{KEY} = "STATIC_$name\_MODULES";
|
||||
$DEFINE->{VAL} = "{ \\\n";
|
||||
foreach (@{$key->{INIT_FUNCTIONS}}) {
|
||||
$DEFINE->{VAL} .= "\t$_, \\\n";
|
||||
my $fn = $key->{INIT_FUNCTION_TYPE};
|
||||
unless(defined($fn)) { $fn = "NTSTATUS (*) (void)"; }
|
||||
$fn =~ s/\(\*\)/$_/;
|
||||
$output .= "$fn;\n";
|
||||
}
|
||||
|
||||
$DEFINE->{VAL} .= "\tNULL \\\n }";
|
||||
|
||||
push(@defines,$DEFINE);
|
||||
}
|
||||
|
||||
#
|
||||
# loop over all BUILD_H define sections
|
||||
#
|
||||
foreach (@defines) { $output .= _add_define_section($_); }
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
###########################################################
|
||||
# This function creates include/build.h from the SMB_BUILD
|
||||
# context
|
||||
#
|
||||
# create_build_h($SMB_BUILD_CTX)
|
||||
#
|
||||
# $SMB_BUILD_CTX - the global SMB_BUILD context
|
||||
#
|
||||
# $output - the resulting output buffer
|
||||
sub create_smb_build_h($$)
|
||||
{
|
||||
my ($CTX, $file) = @_;
|
||||
|
||||
open(BUILD_H,">$file") || die ("Can't open `$file'\n");
|
||||
print BUILD_H "/* autogenerated by build/smb_build/main.pl */\n";
|
||||
print BUILD_H _prepare_build_h($CTX);
|
||||
close(BUILD_H);
|
||||
|
||||
print __FILE__.": creating $file\n";
|
||||
}
|
||||
1;
|
||||
@@ -0,0 +1,259 @@
|
||||
# Samba Build System
|
||||
# - the input checking functions
|
||||
#
|
||||
# Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
# Copyright (C) Jelmer Vernooij 2004
|
||||
# Released under the GNU GPL
|
||||
|
||||
use smb_build::config;
|
||||
use strict;
|
||||
package smb_build::input;
|
||||
|
||||
my $srcdir = $config::config{srcdir};
|
||||
|
||||
sub strtrim($)
|
||||
{
|
||||
$_ = shift;
|
||||
s/^[\t\n ]*//g;
|
||||
s/[\t\n ]*$//g;
|
||||
return $_;
|
||||
}
|
||||
|
||||
sub str2array($)
|
||||
{
|
||||
$_ = shift;
|
||||
s/^[\t\n ]*//g;
|
||||
s/[\t\n ]*$//g;
|
||||
s/([\t\n ]+)/ /g;
|
||||
|
||||
return () if (length($_)==0);
|
||||
return split /[ \t\n]/;
|
||||
}
|
||||
|
||||
sub add_libreplace($)
|
||||
{
|
||||
my ($part) = @_;
|
||||
|
||||
return if ($part->{NAME} eq "LIBREPLACE");
|
||||
return if ($part->{NAME} eq "LIBREPLACE_HOSTCC");
|
||||
return if ($part->{NAME} eq "REPLACE_READDIR");
|
||||
|
||||
foreach my $n (@{$part->{PRIVATE_DEPENDENCIES}}) {
|
||||
return if ($n eq "LIBREPLACE");
|
||||
return if ($n eq "LIBREPLACE_HOSTCC");
|
||||
}
|
||||
foreach my $n (@{$part->{PUBLIC_DEPENDENCIES}}) {
|
||||
return if ($n eq "LIBREPLACE");
|
||||
return if ($n eq "LIBREPLACE_HOSTCC");
|
||||
}
|
||||
|
||||
if (defined($part->{USE_HOSTCC}) && $part->{USE_HOSTCC} eq "YES") {
|
||||
unshift (@{$part->{PRIVATE_DEPENDENCIES}}, "LIBREPLACE_HOSTCC");
|
||||
} else {
|
||||
unshift (@{$part->{PRIVATE_DEPENDENCIES}}, "LIBREPLACE");
|
||||
}
|
||||
}
|
||||
|
||||
sub check_subsystem($$$)
|
||||
{
|
||||
my ($INPUT, $subsys, $default_ot) = @_;
|
||||
return if ($subsys->{ENABLE} ne "YES");
|
||||
|
||||
unless(defined($subsys->{OUTPUT_TYPE})) {
|
||||
$subsys->{OUTPUT_TYPE} = $default_ot;
|
||||
}
|
||||
add_libreplace($subsys);
|
||||
}
|
||||
|
||||
sub check_module($$$)
|
||||
{
|
||||
my ($INPUT, $mod, $default_ot) = @_;
|
||||
|
||||
die("Module $mod->{NAME} does not have a SUBSYSTEM set") if not defined($mod->{SUBSYSTEM});
|
||||
|
||||
my $use_default = 0;
|
||||
|
||||
if (not exists($INPUT->{$mod->{SUBSYSTEM}}{INIT_FUNCTIONS})) {
|
||||
$INPUT->{$mod->{SUBSYSTEM}}{INIT_FUNCTIONS} = [];
|
||||
}
|
||||
|
||||
if (!(defined($INPUT->{$mod->{SUBSYSTEM}}))) {
|
||||
$mod->{ENABLE} = "NO";
|
||||
return;
|
||||
}
|
||||
|
||||
return if ($mod->{ENABLE} ne "YES");
|
||||
|
||||
if (exists($INPUT->{$mod->{SUBSYSTEM}}{INIT_FUNCTION_TYPE})) {
|
||||
$mod->{INIT_FUNCTION_TYPE} = $INPUT->{$mod->{SUBSYSTEM}}{INIT_FUNCTION_TYPE};
|
||||
} else {
|
||||
$mod->{INIT_FUNCTION_TYPE} = "NTSTATUS (*) (void)";
|
||||
}
|
||||
|
||||
if (not defined($mod->{OUTPUT_TYPE})) {
|
||||
$mod->{OUTPUT_TYPE} = $default_ot;
|
||||
}
|
||||
|
||||
if (grep(/SHARED_LIBRARY/, @{$mod->{OUTPUT_TYPE}})) {
|
||||
$mod->{INSTALLDIR} = "MODULESDIR/$mod->{SUBSYSTEM}";
|
||||
push (@{$mod->{PRIVATE_DEPENDENCIES}}, $mod->{SUBSYSTEM});
|
||||
}
|
||||
if (grep(/INTEGRATED/, @{$mod->{OUTPUT_TYPE}})) {
|
||||
push (@{$INPUT->{$mod->{SUBSYSTEM}}{INIT_FUNCTIONS}}, $mod->{INIT_FUNCTION}) if defined($mod->{INIT_FUNCTION});
|
||||
}
|
||||
add_libreplace($mod);
|
||||
}
|
||||
|
||||
sub check_library($$$)
|
||||
{
|
||||
my ($INPUT, $lib, $default_ot) = @_;
|
||||
|
||||
return if ($lib->{ENABLE} ne "YES");
|
||||
|
||||
$lib->{OUTPUT_TYPE} = $default_ot;
|
||||
|
||||
if (defined($lib->{VERSION}) and not defined($lib->{SO_VERSION})) {
|
||||
print "$lib->{NAME}: Please specify SO_VERSION when specifying VERSION\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (defined($lib->{SO_VERSION}) and not defined($lib->{VERSION})) {
|
||||
print "$lib->{NAME}: Please specify VERSION when specifying SO_VERSION\n";
|
||||
return;
|
||||
}
|
||||
|
||||
unless (defined($lib->{INIT_FUNCTION_TYPE})) {
|
||||
$lib->{INIT_FUNCTION_TYPE} = "NTSTATUS (*) (void)";
|
||||
}
|
||||
|
||||
$lib->{INSTALLDIR} = "LIBDIR";
|
||||
add_libreplace($lib);
|
||||
}
|
||||
|
||||
sub check_binary($$)
|
||||
{
|
||||
my ($INPUT, $bin) = @_;
|
||||
|
||||
return if ($bin->{ENABLE} ne "YES");
|
||||
|
||||
($bin->{BINARY} = (lc $bin->{NAME})) if not defined($bin->{BINARY});
|
||||
|
||||
$bin->{OUTPUT_TYPE} = ["BINARY"];
|
||||
add_libreplace($bin);
|
||||
}
|
||||
|
||||
sub import_integrated($$)
|
||||
{
|
||||
my ($lib, $depend) = @_;
|
||||
|
||||
foreach my $mod (values %$depend) {
|
||||
next if(not defined($mod->{OUTPUT_TYPE}));
|
||||
next if(not grep(/INTEGRATED/, @{$mod->{OUTPUT_TYPE}}));
|
||||
next if(not defined($mod->{SUBSYSTEM}));
|
||||
next if($mod->{SUBSYSTEM} ne $lib->{NAME});
|
||||
next if($mod->{ENABLE} ne "YES");
|
||||
|
||||
push (@{$lib->{FULL_OBJ_LIST}}, "\$($mod->{TYPE}_$mod->{NAME}_FULL_OBJ_LIST)");
|
||||
push (@{$lib->{LINK_FLAGS}}, "\$($mod->{TYPE}_$mod->{NAME}_LINK_FLAGS)");
|
||||
push (@{$lib->{PRIVATE_DEPENDENCIES}}, @{$mod->{PUBLIC_DEPENDENCIES}}) if defined($mod->{PUBLIC_DEPENDENCIES});
|
||||
push (@{$lib->{PRIVATE_DEPENDENCIES}}, @{$mod->{PRIVATE_DEPENDENCIES}}) if defined($mod->{PRIVATE_DEPENDENCIES});
|
||||
|
||||
$mod->{ENABLE} = "NO";
|
||||
}
|
||||
}
|
||||
|
||||
sub calc_unique_deps($$$$$$)
|
||||
{
|
||||
sub calc_unique_deps($$$$$$);
|
||||
my ($name, $INPUT, $deps, $udeps, $withlibs, $busy) = @_;
|
||||
|
||||
foreach my $n (@$deps) {
|
||||
die("Dependency unknown: $n") unless (defined($INPUT->{$n}));
|
||||
die("Recursive dependency: $n, list: " . join(',', @$busy)) if (grep (/^$n$/, @$busy));
|
||||
next if (grep /^$n$/, @$udeps);
|
||||
my $dep = $INPUT->{$n};
|
||||
|
||||
if (defined ($dep->{OUTPUT_TYPE}) &&
|
||||
($withlibs or
|
||||
(@{$dep->{OUTPUT_TYPE}}[0] eq "INTEGRATED") or
|
||||
(@{$dep->{OUTPUT_TYPE}}[0] eq "STATIC_LIBRARY"))) {
|
||||
push (@$busy, $dep->{NAME});
|
||||
calc_unique_deps($dep->{NAME}, $INPUT, $dep->{PUBLIC_DEPENDENCIES}, $udeps, $withlibs, $busy);
|
||||
calc_unique_deps($dep->{NAME}, $INPUT, $dep->{PRIVATE_DEPENDENCIES}, $udeps, $withlibs, $busy);
|
||||
pop (@$busy);
|
||||
}
|
||||
|
||||
unshift (@{$udeps}, $dep->{NAME});
|
||||
}
|
||||
}
|
||||
|
||||
sub check($$$$$)
|
||||
{
|
||||
my ($INPUT, $enabled, $subsys_ot, $lib_ot, $module_ot) = @_;
|
||||
|
||||
foreach my $part (values %$INPUT) {
|
||||
unless (defined($part->{STANDARD_VISIBILITY})) {
|
||||
if ($part->{TYPE} eq "MODULE" or $part->{TYPE} eq "BINARY") {
|
||||
$part->{STANDARD_VISIBILITY} = "hidden";
|
||||
} else {
|
||||
$part->{STANDARD_VISIBILITY} = "default";
|
||||
}
|
||||
}
|
||||
|
||||
unless (defined($part->{PUBLIC_HEADERS})) {
|
||||
$part->{PUBLIC_HEADERS} = [];
|
||||
}
|
||||
|
||||
if (defined($part->{PUBLIC_PROTO_HEADER})) {
|
||||
push (@{$part->{PUBLIC_HEADERS}}, $part->{PUBLIC_PROTO_HEADER});
|
||||
}
|
||||
|
||||
if (defined($enabled->{$part->{NAME}})) {
|
||||
$part->{ENABLE} = $enabled->{$part->{NAME}};
|
||||
next;
|
||||
}
|
||||
|
||||
unless(defined($part->{ENABLE})) {
|
||||
if ($part->{TYPE} eq "EXT_LIB") {
|
||||
$part->{ENABLE} = "NO";
|
||||
} else {
|
||||
$part->{ENABLE} = "YES";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $k (keys %$INPUT) {
|
||||
my $part = $INPUT->{$k};
|
||||
|
||||
$part->{LINK_FLAGS} = [];
|
||||
$part->{FULL_OBJ_LIST} = ["\$($part->{TYPE}_$part->{NAME}_OBJ_LIST)"];
|
||||
|
||||
check_subsystem($INPUT, $part, $subsys_ot) if ($part->{TYPE} eq "SUBSYSTEM");
|
||||
check_module($INPUT, $part, $module_ot) if ($part->{TYPE} eq "MODULE");
|
||||
check_library($INPUT, $part, $lib_ot) if ($part->{TYPE} eq "LIBRARY");
|
||||
check_binary($INPUT, $part) if ($part->{TYPE} eq "BINARY");
|
||||
}
|
||||
|
||||
foreach my $part (values %$INPUT) {
|
||||
if (defined($part->{INIT_FUNCTIONS})) {
|
||||
push (@{$part->{LINK_FLAGS}}, "\$(DYNEXP)");
|
||||
}
|
||||
import_integrated($part, $INPUT);
|
||||
}
|
||||
|
||||
foreach my $part (values %$INPUT) {
|
||||
$part->{UNIQUE_DEPENDENCIES} = [];
|
||||
calc_unique_deps($part->{NAME}, $INPUT, $part->{PUBLIC_DEPENDENCIES}, $part->{UNIQUE_DEPENDENCIES}, 0, []);
|
||||
calc_unique_deps($part->{NAME}, $INPUT, $part->{PRIVATE_DEPENDENCIES}, $part->{UNIQUE_DEPENDENCIES}, 0, []);
|
||||
}
|
||||
|
||||
foreach my $part (values %$INPUT) {
|
||||
$part->{UNIQUE_DEPENDENCIES_ALL} = [];
|
||||
calc_unique_deps($part->{NAME}, $INPUT, $part->{PUBLIC_DEPENDENCIES}, $part->{UNIQUE_DEPENDENCIES_ALL}, 1, []);
|
||||
calc_unique_deps($part->{NAME}, $INPUT, $part->{PRIVATE_DEPENDENCIES}, $part->{UNIQUE_DEPENDENCIES_ALL}, 1, []);
|
||||
}
|
||||
|
||||
return $INPUT;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,73 @@
|
||||
# Samba Build System
|
||||
# - the main program
|
||||
#
|
||||
# Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
# Copyright (C) Jelmer Vernooij 2005
|
||||
# Released under the GNU GPL
|
||||
|
||||
use smb_build::makefile;
|
||||
use smb_build::header;
|
||||
use smb_build::input;
|
||||
use smb_build::config_mk;
|
||||
use smb_build::output;
|
||||
use smb_build::env;
|
||||
use smb_build::cflags;
|
||||
use smb_build::summary;
|
||||
use smb_build::config;
|
||||
use strict;
|
||||
|
||||
my $INPUT = {};
|
||||
my $mkfile = smb_build::config_mk::run_config_mk($INPUT, $config::config{srcdir}, $config::config{builddir}, "main.mk");
|
||||
|
||||
my $library_output_type;
|
||||
if ($config::config{USESHARED} eq "true") {
|
||||
$library_output_type = ["SHARED_LIBRARY", "STATIC_LIBRARY"];
|
||||
} else {
|
||||
$library_output_type = ["STATIC_LIBRARY"];
|
||||
push (@$library_output_type, "SHARED_LIBRARY") if
|
||||
($config::config{BLDSHARED} eq "true")
|
||||
}
|
||||
|
||||
my $module_output_type;
|
||||
if ($config::config{USESHARED} eq "true") {
|
||||
$module_output_type = ["SHARED_LIBRARY"];
|
||||
} else {
|
||||
$module_output_type = ["INTEGRATED"];
|
||||
}
|
||||
|
||||
my $DEPEND = smb_build::input::check($INPUT, \%config::enabled,
|
||||
["STATIC_LIBRARY"], $library_output_type, $module_output_type);
|
||||
my $OUTPUT = output::create_output($DEPEND, \%config::config);
|
||||
$config::config{SUBSYSTEM_OUTPUT_TYPE} = ["STATIC_LIBRARY"];
|
||||
$config::config{LIBRARY_OUTPUT_TYPE} = $library_output_type;
|
||||
$config::config{MODULE_OUTPUT_TYPE} = $module_output_type;
|
||||
my $mkenv = new smb_build::makefile(\%config::config, $mkfile);
|
||||
|
||||
foreach my $key (values %$OUTPUT) {
|
||||
next unless defined $key->{OUTPUT_TYPE};
|
||||
|
||||
$mkenv->Integrated($key) if grep(/INTEGRATED/, @{$key->{OUTPUT_TYPE}});
|
||||
}
|
||||
|
||||
foreach my $key (values %$OUTPUT) {
|
||||
next unless defined $key->{OUTPUT_TYPE};
|
||||
|
||||
$mkenv->StaticLibrary($key) if grep(/STATIC_LIBRARY/, @{$key->{OUTPUT_TYPE}});
|
||||
$mkenv->PkgConfig($key, $OUTPUT) if $key->{TYPE} eq "LIBRARY"
|
||||
and defined($key->{VERSION});
|
||||
$mkenv->SharedLibrary($key) if grep(/SHARED_LIBRARY/, @{$key->{OUTPUT_TYPE}});
|
||||
$mkenv->Binary($key) if grep(/BINARY/, @{$key->{OUTPUT_TYPE}});
|
||||
$mkenv->Manpage($key) if defined($key->{MANPAGE});
|
||||
$mkenv->Header($key) if defined($key->{PUBLIC_HEADERS});
|
||||
$mkenv->ProtoHeader($key) if defined($key->{PRIVATE_PROTO_HEADER}) or
|
||||
defined($key->{PUBLIC_PROTO_HEADER});
|
||||
}
|
||||
|
||||
$mkenv->write("Makefile");
|
||||
header::create_smb_build_h($OUTPUT, "include/build.h");
|
||||
|
||||
cflags::create_cflags($OUTPUT, "extra_cflags.txt");
|
||||
|
||||
summary::show($OUTPUT, \%config::config);
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,570 @@
|
||||
# Samba Build System
|
||||
# - create output for Makefile
|
||||
#
|
||||
# Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
# Copyright (C) Jelmer Vernooij 2005
|
||||
# Released under the GNU GPL
|
||||
|
||||
package smb_build::makefile;
|
||||
use smb_build::env;
|
||||
use strict;
|
||||
|
||||
use base 'smb_build::env';
|
||||
use Cwd 'abs_path';
|
||||
|
||||
sub new($$$)
|
||||
{
|
||||
my ($myname, $config, $mkfile) = @_;
|
||||
my $self = new smb_build::env($config);
|
||||
|
||||
bless($self, $myname);
|
||||
|
||||
$self->{manpages} = [];
|
||||
$self->{sbin_progs} = [];
|
||||
$self->{bin_progs} = [];
|
||||
$self->{torture_progs} = [];
|
||||
$self->{static_libs} = [];
|
||||
$self->{shared_libs} = [];
|
||||
$self->{installable_shared_libs} = [];
|
||||
$self->{headers} = [];
|
||||
$self->{shared_modules} = [];
|
||||
$self->{plugins} = [];
|
||||
$self->{install_plugins} = "";
|
||||
$self->{uninstall_plugins} = "";
|
||||
$self->{pc_files} = [];
|
||||
$self->{proto_headers} = [];
|
||||
$self->{output} = "";
|
||||
|
||||
$self->{mkfile} = $mkfile;
|
||||
|
||||
$self->output("#!gmake\n");
|
||||
$self->output("################################################\n");
|
||||
$self->output("# Autogenerated by build/smb_build/makefile.pm #\n");
|
||||
$self->output("################################################\n");
|
||||
$self->output("\n");
|
||||
|
||||
$self->output("default: all\n\n");
|
||||
|
||||
$self->_prepare_path_vars();
|
||||
$self->_prepare_compiler_linker();
|
||||
|
||||
if (!$self->{automatic_deps}) {
|
||||
$self->output("ALL_PREDEP = proto\n");
|
||||
$self->output(".NOTPARALLEL:\n");
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub output($$)
|
||||
{
|
||||
my ($self, $text) = @_;
|
||||
|
||||
$self->{output} .= $text;
|
||||
}
|
||||
|
||||
sub _prepare_path_vars($)
|
||||
{
|
||||
my ($self) = @_;
|
||||
|
||||
$self->output(<< "__EOD__"
|
||||
prefix = $self->{config}->{prefix}
|
||||
exec_prefix = $self->{config}->{exec_prefix}
|
||||
selftest_prefix = $self->{config}->{selftest_prefix}
|
||||
VPATH = $self->{config}->{srcdir}:heimdal_build:heimdal/lib/asn1:heimdal/lib/krb5:heimdal/lib/gssapi:heimdal/lib/hdb:heimdal/lib/roken:heimdal/lib/des
|
||||
srcdir = $self->{config}->{srcdir}
|
||||
builddir = $self->{config}->{builddir}
|
||||
datarootdir = $self->{config}->{datarootdir}
|
||||
|
||||
BASEDIR = $self->{config}->{prefix}
|
||||
BINDIR = $self->{config}->{bindir}
|
||||
SBINDIR = $self->{config}->{sbindir}
|
||||
LIBDIR = $self->{config}->{libdir}
|
||||
TORTUREDIR = $self->{config}->{libdir}/torture
|
||||
MODULESDIR = $self->{config}->{modulesdir}
|
||||
INCLUDEDIR = $self->{config}->{includedir}
|
||||
CONFIGDIR = $self->{config}->{sysconfdir}
|
||||
DATADIR = $self->{config}->{datadir}
|
||||
SWATDIR = $self->{config}->{datadir}/swat
|
||||
SERVICESDIR = $self->{config}->{datadir}/services
|
||||
JSDIR = $self->{config}->{datadir}/js
|
||||
SETUPDIR = $self->{config}->{datadir}/setup
|
||||
VARDIR = $self->{config}->{localstatedir}
|
||||
LOGFILEBASE = $self->{config}->{logfilebase}
|
||||
NCALRPCDIR = $self->{config}->{localstatedir}/ncalrpc
|
||||
LOCKDIR = $self->{config}->{lockdir}
|
||||
PIDDIR = $self->{config}->{piddir}
|
||||
MANDIR = $self->{config}->{mandir}
|
||||
PRIVATEDIR = $self->{config}->{privatedir}
|
||||
WINBINDD_SOCKET_DIR = $self->{config}->{winbindd_socket_dir}
|
||||
|
||||
__EOD__
|
||||
);
|
||||
}
|
||||
|
||||
sub _prepare_compiler_linker($)
|
||||
{
|
||||
my ($self) = @_;
|
||||
|
||||
my $builddir_headers = "";
|
||||
my $libdir;
|
||||
my $extra_link_flags = "";
|
||||
|
||||
if ($self->{config}->{USESHARED} eq "true") {
|
||||
$libdir = "\$(builddir)/bin/shared";
|
||||
$extra_link_flags = "-Wl,-rpath-link,\$(builddir)/bin/shared";
|
||||
} else {
|
||||
$libdir = "\$(builddir)/bin/static";
|
||||
}
|
||||
|
||||
if (!(abs_path($self->{config}->{srcdir}) eq abs_path($self->{config}->{builddir}))) {
|
||||
$builddir_headers= "-I\$(builddir)/include -I\$(builddir) -I\$(builddir)/lib ";
|
||||
}
|
||||
|
||||
$self->output(<< "__EOD__"
|
||||
SHELL=$self->{config}->{SHELL}
|
||||
|
||||
PERL=$self->{config}->{PERL}
|
||||
|
||||
CPP=$self->{config}->{CPP}
|
||||
CPPFLAGS=$builddir_headers-I\$(srcdir)/include -I\$(srcdir) -I\$(srcdir)/lib -I\$(srcdir)/lib/replace -D_SAMBA_BUILD_=4 -DHAVE_CONFIG_H $self->{config}->{CPPFLAGS}
|
||||
|
||||
CC=$self->{config}->{CC}
|
||||
CFLAGS=$self->{config}->{CFLAGS} \$(CPPFLAGS)
|
||||
PICFLAG=$self->{config}->{PICFLAG}
|
||||
|
||||
HOSTCC=$self->{config}->{HOSTCC}
|
||||
HOSTCC_CFLAGS=-D_SAMBA_HOSTCC_ $self->{config}->{CFLAGS} \$(CPPFLAGS)
|
||||
|
||||
INSTALL_LINK_FLAGS=$extra_link_flags
|
||||
|
||||
LD=$self->{config}->{LD}
|
||||
LDFLAGS=$self->{config}->{LDFLAGS} -L$libdir
|
||||
|
||||
STLD=$self->{config}->{STLD}
|
||||
STLD_FLAGS=$self->{config}->{STLD_FLAGS}
|
||||
|
||||
SHLD=$self->{config}->{SHLD}
|
||||
SHLD_FLAGS=$self->{config}->{SHLD_FLAGS} -L\$(builddir)/bin/shared
|
||||
SHLD_UNDEF_FLAGS=$self->{config}->{SHLD_UNDEF_FLAGS}
|
||||
SHLIBEXT=$self->{config}->{SHLIBEXT}
|
||||
|
||||
XSLTPROC=$self->{config}->{XSLTPROC}
|
||||
|
||||
LEX=$self->{config}->{LEX}
|
||||
YACC=$self->{config}->{YACC}
|
||||
YAPP=$self->{config}->{YAPP}
|
||||
PIDL_ARGS=$self->{config}->{PIDL_ARGS}
|
||||
|
||||
GCOV=$self->{config}->{GCOV}
|
||||
|
||||
DEFAULT_TEST_TARGET=$self->{config}->{DEFAULT_TEST_TARGET}
|
||||
|
||||
__EOD__
|
||||
);
|
||||
}
|
||||
|
||||
sub _prepare_mk_files($)
|
||||
{
|
||||
my $self = shift;
|
||||
my @tmp = ();
|
||||
|
||||
foreach (@smb_build::config_mk::parsed_files) {
|
||||
s/ .*$//g;
|
||||
push (@tmp, $_);
|
||||
}
|
||||
|
||||
$self->output("MK_FILES = " . array2oneperline(\@tmp) . "\n");
|
||||
}
|
||||
|
||||
sub array2oneperline($)
|
||||
{
|
||||
my $array = shift;
|
||||
my $output = "";
|
||||
|
||||
foreach (@$array) {
|
||||
next unless defined($_);
|
||||
|
||||
$output .= " \\\n\t\t$_";
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
sub _prepare_list($$$)
|
||||
{
|
||||
my ($self,$ctx,$var) = @_;
|
||||
|
||||
my $tmplist = array2oneperline($ctx->{$var});
|
||||
return if ($tmplist eq "");
|
||||
|
||||
$self->output("$ctx->{TYPE}\_$ctx->{NAME}_$var =$tmplist\n");
|
||||
}
|
||||
|
||||
sub Integrated($$)
|
||||
{
|
||||
my ($self,$ctx) = @_;
|
||||
|
||||
$self->_prepare_list($ctx, "OBJ_LIST");
|
||||
$self->_prepare_list($ctx, "FULL_OBJ_LIST");
|
||||
$self->_prepare_list($ctx, "LINK_FLAGS");
|
||||
}
|
||||
|
||||
sub SharedLibrary($$)
|
||||
{
|
||||
my ($self,$ctx) = @_;
|
||||
|
||||
my $init_obj = "";
|
||||
|
||||
if ($ctx->{TYPE} eq "LIBRARY") {
|
||||
push (@{$self->{shared_libs}}, "$ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}") if (defined($ctx->{SO_VERSION}));
|
||||
push (@{$self->{installable_shared_libs}}, "$ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}") if (defined($ctx->{SO_VERSION}));
|
||||
} elsif ($ctx->{TYPE} eq "MODULE") {
|
||||
push (@{$self->{shared_modules}}, "$ctx->{TARGET_SHARED_LIBRARY}");
|
||||
push (@{$self->{plugins}}, "$ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}");
|
||||
|
||||
$self->{install_plugins} .= "\t\@echo Installing $ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME} as \$(DESTDIR)\$(MODULESDIR)/$ctx->{SUBSYSTEM}/$ctx->{LIBRARY_REALNAME}\n";
|
||||
$self->{install_plugins} .= "\t\@mkdir -p \$(DESTDIR)\$(MODULESDIR)/$ctx->{SUBSYSTEM}/\n";
|
||||
$self->{install_plugins} .= "\t\@cp $ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME} \$(DESTDIR)\$(MODULESDIR)/$ctx->{SUBSYSTEM}/$ctx->{LIBRARY_REALNAME}\n";
|
||||
$self->{uninstall_plugins} .= "\t\@echo Uninstalling \$(DESTDIR)\$(MODULESDIR)/$ctx->{SUBSYSTEM}/$ctx->{LIBRARY_REALNAME}\n";
|
||||
$self->{uninstall_plugins} .= "\t\@-rm \$(DESTDIR)\$(MODULESDIR)/$ctx->{SUBSYSTEM}/$ctx->{LIBRARY_REALNAME}\n";
|
||||
if (defined($ctx->{ALIASES})) {
|
||||
foreach (@{$ctx->{ALIASES}}) {
|
||||
$self->{install_plugins} .= "\t\@ln -fs $ctx->{LIBRARY_REALNAME} \$(DESTDIR)\$(MODULESDIR)/$ctx->{SUBSYSTEM}/$_.\$(SHLIBEXT)\n";
|
||||
$self->{uninstall_plugins} .= "\t\@-rm \$(DESTDIR)\$(MODULESDIR)/$ctx->{SUBSYSTEM}/$_.\$(SHLIBEXT)\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$self->output("$ctx->{TYPE}_$ctx->{NAME}_OUTPUT = $ctx->{OUTPUT}\n");
|
||||
$self->_prepare_list($ctx, "OBJ_LIST");
|
||||
$self->_prepare_list($ctx, "FULL_OBJ_LIST");
|
||||
$self->_prepare_list($ctx, "DEPEND_LIST");
|
||||
$self->_prepare_list($ctx, "LINK_FLAGS");
|
||||
|
||||
push(@{$self->{all_objs}}, "\$($ctx->{TYPE}_$ctx->{NAME}_FULL_OBJ_LIST)");
|
||||
|
||||
my $extraflags = "";
|
||||
if ($ctx->{TYPE} eq "MODULE" and defined($ctx->{INIT_FUNCTION})) {
|
||||
my $init_fn = $ctx->{INIT_FUNCTION_TYPE};
|
||||
$init_fn =~ s/\(\*\)/init_module/;
|
||||
my $proto_fn = $ctx->{INIT_FUNCTION_TYPE};
|
||||
$proto_fn =~ s/\(\*\)/$ctx->{INIT_FUNCTION}/;
|
||||
$extraflags = "\$(SHLD_UNDEF_FLAGS)";
|
||||
|
||||
$self->output(<< "__EOD__"
|
||||
bin/$ctx->{NAME}_init_module.c:
|
||||
\@echo Creating \$\@
|
||||
\@echo \"#include \\\"includes.h\\\"\" > \$\@
|
||||
\@echo \"$proto_fn;\" >> \$\@
|
||||
\@echo -e \"_PUBLIC_ $init_fn \\n{\\n\\treturn $ctx->{INIT_FUNCTION}();\\n}\\n\" >> \$\@
|
||||
__EOD__
|
||||
);
|
||||
$init_obj = "bin/$ctx->{NAME}_init_module.o";
|
||||
}
|
||||
|
||||
my $soarg = "";
|
||||
my $soargdebug = "";
|
||||
if ($self->{config}->{SONAMEFLAG} ne "" and defined($ctx->{LIBRARY_SONAME})) {
|
||||
$soarg = "$self->{config}->{SONAMEFLAG}$ctx->{LIBRARY_SONAME} ";
|
||||
if ($ctx->{LIBRARY_REALNAME} ne $ctx->{LIBRARY_SONAME}) {
|
||||
$soargdebug = "\n\t\@ln -fs $ctx->{LIBRARY_REALNAME} $ctx->{SHAREDDIR}/$ctx->{LIBRARY_SONAME}";
|
||||
}
|
||||
}
|
||||
|
||||
my $singlesoarg = "";
|
||||
|
||||
if ($self->{config}->{SONAMEFLAG} ne "" and
|
||||
defined($ctx->{LIBRARY_SONAME}) and
|
||||
$ctx->{LIBRARY_REALNAME} ne $ctx->{LIBRARY_SONAME}) {
|
||||
$singlesoarg = "\n\t\@ln -fs $ctx->{LIBRARY_REALNAME} $ctx->{SHAREDDIR}/$ctx->{LIBRARY_SONAME}";
|
||||
}
|
||||
|
||||
$self->output(<< "__EOD__"
|
||||
#
|
||||
|
||||
$ctx->{SHAREDDIR}/$ctx->{LIBRARY_REALNAME}: \$($ctx->{TYPE}_$ctx->{NAME}_DEPEND_LIST) \$($ctx->{TYPE}_$ctx->{NAME}_FULL_OBJ_LIST) $init_obj
|
||||
\@echo Linking \$\@
|
||||
\@mkdir -p $ctx->{SHAREDDIR}
|
||||
\@\$(SHLD) \$(SHLD_FLAGS) -o \$\@ \$(INSTALL_LINK_FLAGS) \\
|
||||
\$($ctx->{TYPE}_$ctx->{NAME}\_FULL_OBJ_LIST) \\
|
||||
\$($ctx->{TYPE}_$ctx->{NAME}_LINK_FLAGS) $extraflags \\
|
||||
$soarg \\
|
||||
$init_obj $singlesoarg$soargdebug
|
||||
__EOD__
|
||||
);
|
||||
|
||||
if (defined($ctx->{ALIASES})) {
|
||||
foreach (@{$ctx->{ALIASES}}) {
|
||||
$self->output("\t\@ln -fs $ctx->{LIBRARY_REALNAME} $ctx->{SHAREDDIR}/$_.\$(SHLIBEXT)\n");
|
||||
}
|
||||
}
|
||||
$self->output("\n");
|
||||
}
|
||||
|
||||
sub StaticLibrary($$)
|
||||
{
|
||||
my ($self,$ctx) = @_;
|
||||
|
||||
return unless (defined($ctx->{OBJ_FILES}));
|
||||
|
||||
push (@{$self->{static_libs}}, $ctx->{TARGET_STATIC_LIBRARY}) if ($ctx->{TYPE} eq "LIBRARY");
|
||||
|
||||
$self->output("$ctx->{TYPE}_$ctx->{NAME}_OUTPUT = $ctx->{OUTPUT}\n");
|
||||
$self->_prepare_list($ctx, "OBJ_LIST");
|
||||
$self->_prepare_list($ctx, "FULL_OBJ_LIST");
|
||||
|
||||
push(@{$self->{all_objs}}, "\$($ctx->{TYPE}_$ctx->{NAME}_FULL_OBJ_LIST)");
|
||||
|
||||
$self->output(<< "__EOD__"
|
||||
#
|
||||
$ctx->{TARGET_STATIC_LIBRARY}: \$($ctx->{TYPE}_$ctx->{NAME}_FULL_OBJ_LIST)
|
||||
\@echo Linking \$@
|
||||
\@rm -f \$@
|
||||
\@\$(STLD) \$(STLD_FLAGS) \$@ \$($ctx->{TYPE}_$ctx->{NAME}_FULL_OBJ_LIST)
|
||||
|
||||
__EOD__
|
||||
);
|
||||
}
|
||||
|
||||
sub Header($$)
|
||||
{
|
||||
my ($self,$ctx) = @_;
|
||||
|
||||
my $dir = $ctx->{BASEDIR};
|
||||
|
||||
$dir =~ s/^\.\///g;
|
||||
|
||||
foreach (@{$ctx->{PUBLIC_HEADERS}}) {
|
||||
push (@{$self->{headers}}, "$dir/$_");
|
||||
}
|
||||
}
|
||||
|
||||
sub Binary($$)
|
||||
{
|
||||
my ($self,$ctx) = @_;
|
||||
|
||||
my $installdir;
|
||||
my $extradir = "";
|
||||
|
||||
if (defined($ctx->{INSTALLDIR}) && $ctx->{INSTALLDIR} =~ /^TORTUREDIR/) {
|
||||
$extradir = "/torture" . substr($ctx->{INSTALLDIR}, length("TORTUREDIR"));
|
||||
}
|
||||
my $localdir = "bin$extradir";
|
||||
|
||||
$installdir = "bin$extradir";
|
||||
|
||||
push(@{$self->{all_objs}}, "\$($ctx->{TYPE}_$ctx->{NAME}_FULL_OBJ_LIST)");
|
||||
|
||||
unless (defined($ctx->{INSTALLDIR})) {
|
||||
} elsif ($ctx->{INSTALLDIR} eq "SBINDIR") {
|
||||
push (@{$self->{sbin_progs}}, "$installdir/$ctx->{BINARY}");
|
||||
} elsif ($ctx->{INSTALLDIR} eq "BINDIR") {
|
||||
push (@{$self->{bin_progs}}, "$installdir/$ctx->{BINARY}");
|
||||
} elsif ($ctx->{INSTALLDIR} =~ /^TORTUREDIR/) {
|
||||
push (@{$self->{torture_progs}}, "$installdir/$ctx->{BINARY}");
|
||||
}
|
||||
|
||||
|
||||
push (@{$self->{binaries}}, "$localdir/$ctx->{BINARY}");
|
||||
|
||||
$self->_prepare_list($ctx, "OBJ_LIST");
|
||||
$self->_prepare_list($ctx, "FULL_OBJ_LIST");
|
||||
$self->_prepare_list($ctx, "DEPEND_LIST");
|
||||
$self->_prepare_list($ctx, "LINK_FLAGS");
|
||||
|
||||
$self->output(<< "__EOD__"
|
||||
$installdir/$ctx->{BINARY}: \$($ctx->{TYPE}_$ctx->{NAME}_DEPEND_LIST) \$($ctx->{TYPE}_$ctx->{NAME}_FULL_OBJ_LIST)
|
||||
\@echo Linking \$\@
|
||||
\@\$(LD) \$(LDFLAGS) -o \$\@ \$(INSTALL_LINK_FLAGS) \\
|
||||
\$\($ctx->{TYPE}_$ctx->{NAME}_LINK_FLAGS)
|
||||
|
||||
__EOD__
|
||||
);
|
||||
}
|
||||
|
||||
sub Manpage($$)
|
||||
{
|
||||
my ($self,$ctx) = @_;
|
||||
|
||||
my $dir = $ctx->{BASEDIR};
|
||||
|
||||
$dir =~ s/^\.\///g;
|
||||
|
||||
push (@{$self->{manpages}}, "$dir/$ctx->{MANPAGE}");
|
||||
}
|
||||
|
||||
sub PkgConfig($$$)
|
||||
{
|
||||
my ($self,$ctx,$other) = @_;
|
||||
|
||||
my $link_name = $ctx->{NAME};
|
||||
|
||||
$link_name =~ s/^LIB//g;
|
||||
$link_name = lc($link_name);
|
||||
|
||||
return if (not defined($ctx->{DESCRIPTION}));
|
||||
|
||||
my $path = "$ctx->{BASEDIR}/$link_name.pc";
|
||||
|
||||
push (@{$self->{pc_files}}, $path);
|
||||
|
||||
my $pubs;
|
||||
my $privs;
|
||||
my $privlibs;
|
||||
|
||||
if (defined($ctx->{PUBLIC_DEPENDENCIES})) {
|
||||
foreach (@{$ctx->{PUBLIC_DEPENDENCIES}}) {
|
||||
next if ($other->{$_}->{ENABLE} eq "NO");
|
||||
if ($other->{$_}->{TYPE} eq "LIBRARY") {
|
||||
s/^LIB//g;
|
||||
$_ = lc($_);
|
||||
|
||||
$pubs .= "$_ ";
|
||||
} else {
|
||||
s/^LIB//g;
|
||||
$_ = lc($_);
|
||||
|
||||
$privlibs .= "-l$_ ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (defined($ctx->{PRIVATE_DEPENDENCIES})) {
|
||||
foreach (@{$ctx->{PRIVATE_DEPENDENCIES}}) {
|
||||
next if ($other->{$_}->{ENABLE} eq "NO");
|
||||
if ($other->{$_}->{TYPE} eq "LIBRARY") {
|
||||
s/^LIB//g;
|
||||
$_ = lc($_);
|
||||
|
||||
$privs .= "$_ ";
|
||||
} else {
|
||||
s/^LIB//g;
|
||||
$_ = lc($_);
|
||||
|
||||
$privlibs .= "-l$_ ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
smb_build::env::PkgConfig($self,
|
||||
$path,
|
||||
$link_name,
|
||||
"-L\${libdir} -l$link_name",
|
||||
$privlibs,
|
||||
"",
|
||||
"$ctx->{VERSION}",
|
||||
$ctx->{DESCRIPTION},
|
||||
defined($ctx->{INIT_FUNCTIONS}),
|
||||
$pubs,
|
||||
$privs,
|
||||
[
|
||||
"prefix=$self->{config}->{prefix}",
|
||||
"exec_prefix=$self->{config}->{exec_prefix}",
|
||||
"libdir=$self->{config}->{libdir}",
|
||||
"includedir=$self->{config}->{includedir}"
|
||||
]
|
||||
);
|
||||
smb_build::env::PkgConfig($self,
|
||||
"bin/pkgconfig/$link_name-uninstalled.pc",
|
||||
$link_name,
|
||||
"-Lbin/shared -Lbin/static -l$link_name",
|
||||
$privlibs,
|
||||
"-I. -Iinclude -Ilib -Ilib/replace",
|
||||
"$ctx->{VERSION}",
|
||||
$ctx->{DESCRIPTION},
|
||||
defined($ctx->{INIT_FUNCTIONS}),
|
||||
$pubs,
|
||||
$privs,
|
||||
[
|
||||
"prefix=bin/",
|
||||
"includedir=$ctx->{BASEDIR}"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
sub ProtoHeader($$)
|
||||
{
|
||||
my ($self,$ctx) = @_;
|
||||
|
||||
my $dir = $ctx->{BASEDIR};
|
||||
|
||||
$dir =~ s/^\.\///g;
|
||||
|
||||
my $target = "";
|
||||
|
||||
my $comment = "Creating ";
|
||||
if (defined($ctx->{PRIVATE_PROTO_HEADER})) {
|
||||
$target.= "$dir/$ctx->{PRIVATE_PROTO_HEADER}";
|
||||
$comment.= "$dir/$ctx->{PRIVATE_PROTO_HEADER}";
|
||||
if (defined($ctx->{PUBLIC_PROTO_HEADER})) {
|
||||
$comment .= " and ";
|
||||
$target.= " ";
|
||||
}
|
||||
push (@{$self->{proto_headers}}, "$dir/$ctx->{PRIVATE_PROTO_HEADER}");
|
||||
} else {
|
||||
$ctx->{PRIVATE_PROTO_HEADER} = $ctx->{PUBLIC_PROTO_HEADER};
|
||||
}
|
||||
|
||||
if (defined($ctx->{PUBLIC_PROTO_HEADER})) {
|
||||
$comment.= "$dir/$ctx->{PUBLIC_PROTO_HEADER}";
|
||||
$target .= "$dir/$ctx->{PUBLIC_PROTO_HEADER}";
|
||||
push (@{$self->{proto_headers}}, "$dir/$ctx->{PUBLIC_PROTO_HEADER}");
|
||||
} else {
|
||||
$ctx->{PUBLIC_PROTO_HEADER} = $ctx->{PRIVATE_PROTO_HEADER};
|
||||
}
|
||||
|
||||
$self->output("$dir/$ctx->{PUBLIC_PROTO_HEADER}: $ctx->{MK_FILE} \$($ctx->{TYPE}_$ctx->{NAME}_OBJ_LIST:.o=.c) \$(srcdir)/script/mkproto.pl\n");
|
||||
$self->output("\t\@echo \"$comment\"\n");
|
||||
|
||||
$self->output("\t\@\$(PERL) \$(srcdir)/script/mkproto.pl --srcdir=\$(srcdir) --builddir=\$(builddir) --private=$dir/$ctx->{PRIVATE_PROTO_HEADER} --public=$dir/$ctx->{PUBLIC_PROTO_HEADER} \$($ctx->{TYPE}_$ctx->{NAME}_OBJ_LIST)\n\n");
|
||||
}
|
||||
|
||||
sub write($$)
|
||||
{
|
||||
my ($self,$file) = @_;
|
||||
|
||||
$self->output("MANPAGES = ".array2oneperline($self->{manpages})."\n");
|
||||
$self->output("BIN_PROGS = " . array2oneperline($self->{bin_progs}) . "\n");
|
||||
$self->output("SBIN_PROGS = " . array2oneperline($self->{sbin_progs}) . "\n");
|
||||
$self->output("TORTURE_PROGS = " . array2oneperline($self->{torture_progs}) . "\n");
|
||||
$self->output("BINARIES = " . array2oneperline($self->{binaries}) . "\n");
|
||||
$self->output("STATIC_LIBS = " . array2oneperline($self->{static_libs}) . "\n");
|
||||
$self->output("SHARED_LIBS = " . array2oneperline($self->{shared_libs}) . "\n");
|
||||
$self->output("INSTALLABLE_SHARED_LIBS = " . array2oneperline($self->{installable_shared_libs}) . "\n");
|
||||
$self->output("PUBLIC_HEADERS = " . array2oneperline($self->{headers}) . "\n");
|
||||
$self->output("PC_FILES = " . array2oneperline($self->{pc_files}) . "\n");
|
||||
$self->output("ALL_OBJS = " . array2oneperline($self->{all_objs}) . "\n");
|
||||
$self->output("PROTO_HEADERS = " . array2oneperline($self->{proto_headers}) . "\n");
|
||||
$self->output("SHARED_MODULES = " . array2oneperline($self->{shared_modules}) . "\n");
|
||||
$self->output("PLUGINS = " . array2oneperline($self->{plugins}) . "\n");
|
||||
|
||||
$self->output("\ninstallplugins: \$(PLUGINS)\n".$self->{install_plugins}."\n");
|
||||
$self->output("\nuninstallplugins:\n".$self->{uninstall_plugins}."\n");
|
||||
|
||||
$self->_prepare_mk_files();
|
||||
|
||||
$self->output($self->{mkfile});
|
||||
|
||||
if ($self->{automatic_deps}) {
|
||||
$self->output("
|
||||
ifneq (\$(MAKECMDGOALS),clean)
|
||||
ifneq (\$(MAKECMDGOALS),distclean)
|
||||
ifneq (\$(MAKECMDGOALS),realdistclean)
|
||||
-include \$(DEP_FILES)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
");
|
||||
} else {
|
||||
$self->output("include \$(srcdir)/static_deps.mk\n");
|
||||
}
|
||||
|
||||
open(MAKEFILE,">$file") || die ("Can't open $file\n");
|
||||
print MAKEFILE $self->{output};
|
||||
close(MAKEFILE);
|
||||
|
||||
print __FILE__.": creating $file\n";
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,178 @@
|
||||
# SMB Build System
|
||||
# - the output generating functions
|
||||
#
|
||||
# Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
# Copyright (C) Jelmer Vernooij 2004
|
||||
# Released under the GNU GPL
|
||||
|
||||
package output;
|
||||
use strict;
|
||||
|
||||
sub add_dir($$)
|
||||
{
|
||||
my ($dir,$files) = @_;
|
||||
my @ret = ();
|
||||
my $dirsep = "/";
|
||||
|
||||
$dir =~ s/^\.$//g;
|
||||
$dir =~ s/^\.\///g;
|
||||
|
||||
$dirsep = "" if ($dir eq "");
|
||||
|
||||
foreach (@$files) {
|
||||
if (substr($_, 0, 1) ne "\$") {
|
||||
$_ = "$dir$dirsep$_";
|
||||
s/([^\/\.]+)\/\.\.\///g;
|
||||
s/([^\/\.]+)\/\.\.\///g;
|
||||
}
|
||||
push (@ret, $_);
|
||||
}
|
||||
|
||||
return @ret;
|
||||
}
|
||||
|
||||
sub generate_shared_library($)
|
||||
{
|
||||
my $lib = shift;
|
||||
my $link_name;
|
||||
my $lib_name;
|
||||
|
||||
$lib->{DEPEND_LIST} = [];
|
||||
|
||||
$link_name = lc($lib->{NAME});
|
||||
$lib_name = $link_name;
|
||||
|
||||
if ($lib->{TYPE} eq "LIBRARY") {
|
||||
$link_name = $lib->{NAME};
|
||||
$link_name =~ s/^LIB//;
|
||||
$link_name = lc($link_name);
|
||||
$lib_name = "lib$link_name";
|
||||
}
|
||||
|
||||
if (defined($lib->{LIBRARY_REALNAME})) {
|
||||
$lib->{BASEDIR} =~ s/^\.\///g;
|
||||
$lib->{LIBRARY_REALNAME} = "$lib->{LIBRARY_REALNAME}";
|
||||
$lib->{SHAREDDIR} = $lib->{BASEDIR};
|
||||
} else {
|
||||
if ($lib->{TYPE} eq "MODULE") {
|
||||
$lib->{SHAREDDIR} = "bin/modules/$lib->{SUBSYSTEM}";
|
||||
$lib->{LIBRARY_REALNAME} = $link_name;
|
||||
$lib->{LIBRARY_REALNAME} =~ s/^$lib->{SUBSYSTEM}_//g;
|
||||
$lib->{LIBRARY_REALNAME}.= ".\$(SHLIBEXT)";
|
||||
} else {
|
||||
$lib->{SHAREDDIR} = "bin/shared";
|
||||
$lib->{LIBRARY_REALNAME} = "$lib_name.\$(SHLIBEXT)";
|
||||
}
|
||||
}
|
||||
|
||||
if (defined($lib->{VERSION})) {
|
||||
$lib->{LIBRARY_SONAME} = "$lib->{LIBRARY_REALNAME}.$lib->{SO_VERSION}";
|
||||
$lib->{LIBRARY_REALNAME} = "$lib->{LIBRARY_REALNAME}.$lib->{VERSION}";
|
||||
}
|
||||
|
||||
$lib->{TARGET_SHARED_LIBRARY} = "$lib->{SHAREDDIR}/$lib->{LIBRARY_REALNAME}";
|
||||
$lib->{OUTPUT_SHARED_LIBRARY} = $lib->{TARGET_SHARED_LIBRARY};
|
||||
}
|
||||
|
||||
sub generate_static_library($)
|
||||
{
|
||||
my $lib = shift;
|
||||
my $link_name;
|
||||
|
||||
$lib->{DEPEND_LIST} = [];
|
||||
|
||||
$link_name = $lib->{NAME};
|
||||
$link_name =~ s/^LIB//;
|
||||
|
||||
$lib->{LIBRARY_NAME} = "lib".lc($link_name).".a";
|
||||
|
||||
if (defined($lib->{OBJ_FILES})) {
|
||||
$lib->{TARGET_STATIC_LIBRARY} = "bin/static/$lib->{LIBRARY_NAME}";
|
||||
} else {
|
||||
$lib->{TARGET_STATIC_LIBRARY} = "";
|
||||
}
|
||||
$lib->{OUTPUT_STATIC_LIBRARY} = $lib->{TARGET_STATIC_LIBRARY};
|
||||
}
|
||||
|
||||
sub generate_binary($)
|
||||
{
|
||||
my $bin = shift;
|
||||
|
||||
$bin->{DEPEND_LIST} = [];
|
||||
push(@{$bin->{LINK_FLAGS}}, "\$($bin->{TYPE}_$bin->{NAME}\_OBJ_LIST)");
|
||||
|
||||
$bin->{DEBUGDIR} = "bin/";
|
||||
$bin->{TARGET_BINARY} = $bin->{OUTPUT_BINARY} = "$bin->{DEBUGDIR}/$bin->{NAME}";
|
||||
$bin->{BINARY} = $bin->{NAME};
|
||||
}
|
||||
|
||||
sub merge_array($$)
|
||||
{
|
||||
# $dest is a reference to an array
|
||||
# $src is an array
|
||||
my ($dest, $src) = @_;
|
||||
|
||||
return unless defined($src);
|
||||
return unless ($#{$src} >= 0);
|
||||
|
||||
foreach my $line (@{$src}) {
|
||||
next if (grep /^$line$/, @{$$dest});
|
||||
push(@{$$dest}, $line);
|
||||
}
|
||||
}
|
||||
|
||||
sub create_output($$)
|
||||
{
|
||||
my ($depend, $config) = @_;
|
||||
my $part;
|
||||
|
||||
foreach $part (values %{$depend}) {
|
||||
next unless(defined($part->{OUTPUT_TYPE}));
|
||||
|
||||
# Combine object lists
|
||||
push(@{$part->{OBJ_LIST}}, add_dir($part->{BASEDIR}, $part->{OBJ_FILES})) if defined($part->{OBJ_FILES});
|
||||
|
||||
generate_binary($part) if grep(/BINARY/, @{$part->{OUTPUT_TYPE}});
|
||||
generate_shared_library($part) if grep(/SHARED_LIBRARY/, @{$part->{OUTPUT_TYPE}});
|
||||
generate_static_library($part) if grep(/STATIC_LIBRARY/, @{$part->{OUTPUT_TYPE}});
|
||||
$part->{OUTPUT} = $part->{"OUTPUT_" . @{$part->{OUTPUT_TYPE}}[0]};
|
||||
$part->{TARGET} = $part->{"TARGET_" . @{$part->{OUTPUT_TYPE}}[0]};
|
||||
}
|
||||
|
||||
foreach $part (values %{$depend}) {
|
||||
next if not defined($part->{OUTPUT_TYPE});
|
||||
|
||||
merge_array(\$part->{FINAL_CFLAGS}, $part->{CPPFLAGS});
|
||||
merge_array(\$part->{FINAL_CFLAGS}, $part->{CFLAGS});
|
||||
|
||||
foreach (reverse @{$part->{UNIQUE_DEPENDENCIES_ALL}}) {
|
||||
my $elem = $depend->{$_};
|
||||
next if $elem == $part;
|
||||
|
||||
merge_array(\$part->{FINAL_CFLAGS}, $elem->{CPPFLAGS});
|
||||
merge_array(\$part->{FINAL_CFLAGS}, $elem->{CFLAGS});
|
||||
}
|
||||
|
||||
# Always import the link options of the unique dependencies
|
||||
foreach (@{$part->{UNIQUE_DEPENDENCIES}}) {
|
||||
my $elem = $depend->{$_};
|
||||
next if $elem == $part;
|
||||
|
||||
push(@{$part->{LINK_FLAGS}}, $elem->{OUTPUT}) if defined($elem->{OUTPUT});
|
||||
push(@{$part->{LINK_FLAGS}}, @{$elem->{LIBS}}) if defined($elem->{LIBS});
|
||||
push(@{$part->{LINK_FLAGS}},@{$elem->{LDFLAGS}}) if defined($elem->{LDFLAGS});
|
||||
push(@{$part->{DEPEND_LIST}}, $elem->{TARGET}) if defined($elem->{TARGET});
|
||||
}
|
||||
}
|
||||
|
||||
foreach $part (values %{$depend}) {
|
||||
if (($part->{STANDARD_VISIBILITY} ne "default") and
|
||||
($config->{visibility_attribute} eq "yes")) {
|
||||
push(@{$part->{FINAL_CFLAGS}}, "-fvisibility=$part->{STANDARD_VISIBILITY}");
|
||||
}
|
||||
}
|
||||
|
||||
return $depend;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,71 @@
|
||||
# Samba Build System
|
||||
# - write out summary
|
||||
#
|
||||
# Copyright (C) Jelmer Vernooij 2006
|
||||
# Released under the GNU GPL
|
||||
|
||||
package summary;
|
||||
use strict;
|
||||
|
||||
sub enabled($)
|
||||
{
|
||||
my ($val) = @_;
|
||||
|
||||
return (defined($val) && $val =~ m/yes|true/i);
|
||||
}
|
||||
|
||||
sub showitem($$$)
|
||||
{
|
||||
my ($output,$desc,$items) = @_;
|
||||
|
||||
my @need = ();
|
||||
|
||||
foreach (@$items) {
|
||||
if (!enabled($output->{$_}->{ENABLE})) {
|
||||
push (@need, $_);
|
||||
}
|
||||
}
|
||||
|
||||
print "Support for $desc: ";
|
||||
if ($#need >= 0) {
|
||||
print "no (install " . join(',',@need) . ")\n";
|
||||
} else {
|
||||
print "yes\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub show($$)
|
||||
{
|
||||
my ($output,$config) = @_;
|
||||
|
||||
print "Summary:\n\n";
|
||||
showitem($output, "GTK+ frontends", ["gtk","gconf"]);
|
||||
showitem($output, "SSL in SWAT and LDAP", ["GNUTLS"]);
|
||||
showitem($output, "threads in smbd (see --with-pthread)", ["PTHREAD"]);
|
||||
showitem($output, "intelligent command line editing", ["READLINE"]);
|
||||
showitem($output, "changing process titles (see --with-setproctitle)", ["SETPROCTITLE"]);
|
||||
showitem($output, "using extended attributes", ["XATTR"]);
|
||||
showitem($output, "using libblkid", ["BLKID"]);
|
||||
showitem($output, "using iconv", ["ICONV"]);
|
||||
showitem($output, "using pam", ["PAM"]);
|
||||
print "Using external popt: ".
|
||||
(($output->{LIBPOPT}->{TYPE} eq "EXT_LIB")?"yes":"no")."\n";
|
||||
print "Developer mode: ".(enabled($config->{developer})?"yes":"no")."\n";
|
||||
print "Automatic dependencies: ".
|
||||
(enabled($config->{automatic_dependencies})
|
||||
? "yes" : "no (install GNU make >= 3.81 and see --enable-automatic-dependencies)") .
|
||||
"\n";
|
||||
|
||||
print "Building shared libraries: " .
|
||||
(enabled($config->{BLDSHARED})
|
||||
? "yes" : "no (not supported on this system)") .
|
||||
"\n";
|
||||
print "Using shared libraries internally: " .
|
||||
(enabled($config->{USESHARED})
|
||||
? "yes" : "no (specify --enable-dso)") .
|
||||
"\n";
|
||||
|
||||
print "\n";
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,10 @@
|
||||
This directory contains autoconf test programs that are too large to
|
||||
comfortably fit in configure.in.
|
||||
|
||||
These programs should test one feature of the OS and exit(0) if it
|
||||
works or exit(1) if it doesn't work (do _not_ use return)
|
||||
|
||||
The programs should be kept simple and to the point. Beautiful/fast
|
||||
code is not necessary
|
||||
|
||||
|
||||
@@ -0,0 +1,852 @@
|
||||
#if defined(HAVE_UNISTD_H)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_CRYPT)
|
||||
|
||||
/*
|
||||
This bit of code was derived from the UFC-crypt package which
|
||||
carries the following copyright
|
||||
|
||||
Modified for use by Samba by Andrew Tridgell, October 1994
|
||||
|
||||
Note that this routine is only faster on some machines. Under Linux 1.1.51
|
||||
libc 4.5.26 I actually found this routine to be slightly slower.
|
||||
|
||||
Under SunOS I found a huge speedup by using these routines
|
||||
(a factor of 20 or so)
|
||||
|
||||
Warning: I've had a report from Steve Kennedy <steve@gbnet.org>
|
||||
that this crypt routine may sometimes get the wrong answer. Only
|
||||
use UFC_CRYT if you really need it.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* UFC-crypt: ultra fast crypt(3) implementation
|
||||
*
|
||||
* Copyright (C) 1991-1998, Free Software Foundation, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free
|
||||
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* @(#)crypt_util.c 2.31 02/08/92
|
||||
*
|
||||
* Support routines
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef long32
|
||||
#if (SIZEOF_INT == 4)
|
||||
#define long32 int
|
||||
#elif (SIZEOF_LONG == 4)
|
||||
#define long32 long
|
||||
#elif (SIZEOF_SHORT == 4)
|
||||
#define long32 short
|
||||
#else
|
||||
/* uggh - no 32 bit type?? probably a CRAY. just hope this works ... */
|
||||
#define long32 int
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef long64
|
||||
#ifdef HAVE_LONGLONG
|
||||
#define long64 long long long
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ufc_long
|
||||
#define ufc_long unsigned
|
||||
#endif
|
||||
|
||||
#ifndef _UFC_64_
|
||||
#define _UFC_32_
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Permutation done once on the 56 bit
|
||||
* key derived from the original 8 byte ASCII key.
|
||||
*/
|
||||
static int pc1[56] = {
|
||||
57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
|
||||
10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
|
||||
63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
|
||||
14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
|
||||
};
|
||||
|
||||
/*
|
||||
* How much to rotate each 28 bit half of the pc1 permutated
|
||||
* 56 bit key before using pc2 to give the i' key
|
||||
*/
|
||||
static int rots[16] = {
|
||||
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
|
||||
};
|
||||
|
||||
/*
|
||||
* Permutation giving the key
|
||||
* of the i' DES round
|
||||
*/
|
||||
static int pc2[48] = {
|
||||
14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
|
||||
23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
|
||||
41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
|
||||
44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
|
||||
};
|
||||
|
||||
/*
|
||||
* The E expansion table which selects
|
||||
* bits from the 32 bit intermediate result.
|
||||
*/
|
||||
static int esel[48] = {
|
||||
32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
|
||||
8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
|
||||
16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
|
||||
24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1
|
||||
};
|
||||
static int e_inverse[64];
|
||||
|
||||
/*
|
||||
* Permutation done on the
|
||||
* result of sbox lookups
|
||||
*/
|
||||
static int perm32[32] = {
|
||||
16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
|
||||
2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
|
||||
};
|
||||
|
||||
/*
|
||||
* The sboxes
|
||||
*/
|
||||
static int sbox[8][4][16]= {
|
||||
{ { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 },
|
||||
{ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8 },
|
||||
{ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0 },
|
||||
{ 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 }
|
||||
},
|
||||
|
||||
{ { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 },
|
||||
{ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5 },
|
||||
{ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15 },
|
||||
{ 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 }
|
||||
},
|
||||
|
||||
{ { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 },
|
||||
{ 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1 },
|
||||
{ 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7 },
|
||||
{ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 }
|
||||
},
|
||||
|
||||
{ { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 },
|
||||
{ 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9 },
|
||||
{ 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4 },
|
||||
{ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 }
|
||||
},
|
||||
|
||||
{ { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 },
|
||||
{ 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6 },
|
||||
{ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14 },
|
||||
{ 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 }
|
||||
},
|
||||
|
||||
{ { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 },
|
||||
{ 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8 },
|
||||
{ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6 },
|
||||
{ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 }
|
||||
},
|
||||
|
||||
{ { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 },
|
||||
{ 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6 },
|
||||
{ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2 },
|
||||
{ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 }
|
||||
},
|
||||
|
||||
{ { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 },
|
||||
{ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2 },
|
||||
{ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8 },
|
||||
{ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 }
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the final
|
||||
* permutation matrix
|
||||
*/
|
||||
static int final_perm[64] = {
|
||||
40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
|
||||
38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
|
||||
36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
|
||||
34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
|
||||
};
|
||||
|
||||
/*
|
||||
* The 16 DES keys in BITMASK format
|
||||
*/
|
||||
#ifdef _UFC_32_
|
||||
long32 _ufc_keytab[16][2];
|
||||
#endif
|
||||
|
||||
#ifdef _UFC_64_
|
||||
long64 _ufc_keytab[16];
|
||||
#endif
|
||||
|
||||
|
||||
#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
|
||||
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
|
||||
|
||||
/* Macro to set a bit (0..23) */
|
||||
#define BITMASK(i) ( (1<<(11-(i)%12+3)) << ((i)<12?16:0) )
|
||||
|
||||
/*
|
||||
* sb arrays:
|
||||
*
|
||||
* Workhorses of the inner loop of the DES implementation.
|
||||
* They do sbox lookup, shifting of this value, 32 bit
|
||||
* permutation and E permutation for the next round.
|
||||
*
|
||||
* Kept in 'BITMASK' format.
|
||||
*/
|
||||
|
||||
#ifdef _UFC_32_
|
||||
long32 _ufc_sb0[8192], _ufc_sb1[8192], _ufc_sb2[8192], _ufc_sb3[8192];
|
||||
static long32 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3};
|
||||
#endif
|
||||
|
||||
#ifdef _UFC_64_
|
||||
long64 _ufc_sb0[4096], _ufc_sb1[4096], _ufc_sb2[4096], _ufc_sb3[4096];
|
||||
static long64 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* eperm32tab: do 32 bit permutation and E selection
|
||||
*
|
||||
* The first index is the byte number in the 32 bit value to be permuted
|
||||
* - second - is the value of this byte
|
||||
* - third - selects the two 32 bit values
|
||||
*
|
||||
* The table is used and generated internally in init_des to speed it up
|
||||
*/
|
||||
static ufc_long eperm32tab[4][256][2];
|
||||
|
||||
/*
|
||||
* do_pc1: permform pc1 permutation in the key schedule generation.
|
||||
*
|
||||
* The first index is the byte number in the 8 byte ASCII key
|
||||
* - second - - the two 28 bits halfs of the result
|
||||
* - third - selects the 7 bits actually used of each byte
|
||||
*
|
||||
* The result is kept with 28 bit per 32 bit with the 4 most significant
|
||||
* bits zero.
|
||||
*/
|
||||
static ufc_long do_pc1[8][2][128];
|
||||
|
||||
/*
|
||||
* do_pc2: permform pc2 permutation in the key schedule generation.
|
||||
*
|
||||
* The first index is the septet number in the two 28 bit intermediate values
|
||||
* - second - - - septet values
|
||||
*
|
||||
* Knowledge of the structure of the pc2 permutation is used.
|
||||
*
|
||||
* The result is kept with 28 bit per 32 bit with the 4 most significant
|
||||
* bits zero.
|
||||
*/
|
||||
static ufc_long do_pc2[8][128];
|
||||
|
||||
/*
|
||||
* efp: undo an extra e selection and do final
|
||||
* permutation giving the DES result.
|
||||
*
|
||||
* Invoked 6 bit a time on two 48 bit values
|
||||
* giving two 32 bit longs.
|
||||
*/
|
||||
static ufc_long efp[16][64][2];
|
||||
|
||||
static unsigned char bytemask[8] = {
|
||||
0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
|
||||
};
|
||||
|
||||
static ufc_long longmask[32] = {
|
||||
0x80000000, 0x40000000, 0x20000000, 0x10000000,
|
||||
0x08000000, 0x04000000, 0x02000000, 0x01000000,
|
||||
0x00800000, 0x00400000, 0x00200000, 0x00100000,
|
||||
0x00080000, 0x00040000, 0x00020000, 0x00010000,
|
||||
0x00008000, 0x00004000, 0x00002000, 0x00001000,
|
||||
0x00000800, 0x00000400, 0x00000200, 0x00000100,
|
||||
0x00000080, 0x00000040, 0x00000020, 0x00000010,
|
||||
0x00000008, 0x00000004, 0x00000002, 0x00000001
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Silly rewrite of 'bzero'. I do so
|
||||
* because some machines don't have
|
||||
* bzero and some don't have memset.
|
||||
*/
|
||||
|
||||
static void clearmem(char *start, int cnt)
|
||||
{ while(cnt--)
|
||||
*start++ = '\0';
|
||||
}
|
||||
|
||||
static int initialized = 0;
|
||||
|
||||
/* lookup a 6 bit value in sbox */
|
||||
|
||||
#define s_lookup(i,s) sbox[(i)][(((s)>>4) & 0x2)|((s) & 0x1)][((s)>>1) & 0xf];
|
||||
|
||||
/*
|
||||
* Initialize unit - may be invoked directly
|
||||
* by fcrypt users.
|
||||
*/
|
||||
|
||||
static void ufc_init_des(void)
|
||||
{ int comes_from_bit;
|
||||
int bit, sg;
|
||||
ufc_long j;
|
||||
ufc_long mask1, mask2;
|
||||
|
||||
/*
|
||||
* Create the do_pc1 table used
|
||||
* to affect pc1 permutation
|
||||
* when generating keys
|
||||
*/
|
||||
for(bit = 0; bit < 56; bit++) {
|
||||
comes_from_bit = pc1[bit] - 1;
|
||||
mask1 = bytemask[comes_from_bit % 8 + 1];
|
||||
mask2 = longmask[bit % 28 + 4];
|
||||
for(j = 0; j < 128; j++) {
|
||||
if(j & mask1)
|
||||
do_pc1[comes_from_bit / 8][bit / 28][j] |= mask2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the do_pc2 table used
|
||||
* to affect pc2 permutation when
|
||||
* generating keys
|
||||
*/
|
||||
for(bit = 0; bit < 48; bit++) {
|
||||
comes_from_bit = pc2[bit] - 1;
|
||||
mask1 = bytemask[comes_from_bit % 7 + 1];
|
||||
mask2 = BITMASK(bit % 24);
|
||||
for(j = 0; j < 128; j++) {
|
||||
if(j & mask1)
|
||||
do_pc2[comes_from_bit / 7][j] |= mask2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now generate the table used to do combined
|
||||
* 32 bit permutation and e expansion
|
||||
*
|
||||
* We use it because we have to permute 16384 32 bit
|
||||
* longs into 48 bit in order to initialize sb.
|
||||
*
|
||||
* Looping 48 rounds per permutation becomes
|
||||
* just too slow...
|
||||
*
|
||||
*/
|
||||
|
||||
clearmem((char*)eperm32tab, sizeof(eperm32tab));
|
||||
|
||||
for(bit = 0; bit < 48; bit++) {
|
||||
ufc_long inner_mask1,comes_from;
|
||||
|
||||
comes_from = perm32[esel[bit]-1]-1;
|
||||
inner_mask1 = bytemask[comes_from % 8];
|
||||
|
||||
for(j = 256; j--;) {
|
||||
if(j & inner_mask1)
|
||||
eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the sb tables:
|
||||
*
|
||||
* For each 12 bit segment of an 48 bit intermediate
|
||||
* result, the sb table precomputes the two 4 bit
|
||||
* values of the sbox lookups done with the two 6
|
||||
* bit halves, shifts them to their proper place,
|
||||
* sends them through perm32 and finally E expands
|
||||
* them so that they are ready for the next
|
||||
* DES round.
|
||||
*
|
||||
*/
|
||||
for(sg = 0; sg < 4; sg++) {
|
||||
int j1, j2;
|
||||
int s1, s2;
|
||||
|
||||
for(j1 = 0; j1 < 64; j1++) {
|
||||
s1 = s_lookup(2 * sg, j1);
|
||||
for(j2 = 0; j2 < 64; j2++) {
|
||||
ufc_long to_permute, inx;
|
||||
|
||||
s2 = s_lookup(2 * sg + 1, j2);
|
||||
to_permute = ((s1 << 4) | s2) << (24 - 8 * sg);
|
||||
|
||||
#ifdef _UFC_32_
|
||||
inx = ((j1 << 6) | j2) << 1;
|
||||
sb[sg][inx ] = eperm32tab[0][(to_permute >> 24) & 0xff][0];
|
||||
sb[sg][inx+1] = eperm32tab[0][(to_permute >> 24) & 0xff][1];
|
||||
sb[sg][inx ] |= eperm32tab[1][(to_permute >> 16) & 0xff][0];
|
||||
sb[sg][inx+1] |= eperm32tab[1][(to_permute >> 16) & 0xff][1];
|
||||
sb[sg][inx ] |= eperm32tab[2][(to_permute >> 8) & 0xff][0];
|
||||
sb[sg][inx+1] |= eperm32tab[2][(to_permute >> 8) & 0xff][1];
|
||||
sb[sg][inx ] |= eperm32tab[3][(to_permute) & 0xff][0];
|
||||
sb[sg][inx+1] |= eperm32tab[3][(to_permute) & 0xff][1];
|
||||
#endif
|
||||
#ifdef _UFC_64_
|
||||
inx = ((j1 << 6) | j2);
|
||||
sb[sg][inx] =
|
||||
((long64)eperm32tab[0][(to_permute >> 24) & 0xff][0] << 32) |
|
||||
(long64)eperm32tab[0][(to_permute >> 24) & 0xff][1];
|
||||
sb[sg][inx] |=
|
||||
((long64)eperm32tab[1][(to_permute >> 16) & 0xff][0] << 32) |
|
||||
(long64)eperm32tab[1][(to_permute >> 16) & 0xff][1];
|
||||
sb[sg][inx] |=
|
||||
((long64)eperm32tab[2][(to_permute >> 8) & 0xff][0] << 32) |
|
||||
(long64)eperm32tab[2][(to_permute >> 8) & 0xff][1];
|
||||
sb[sg][inx] |=
|
||||
((long64)eperm32tab[3][(to_permute) & 0xff][0] << 32) |
|
||||
(long64)eperm32tab[3][(to_permute) & 0xff][1];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an inverse matrix for esel telling
|
||||
* where to plug out bits if undoing it
|
||||
*/
|
||||
for(bit=48; bit--;) {
|
||||
e_inverse[esel[bit] - 1 ] = bit;
|
||||
e_inverse[esel[bit] - 1 + 32] = bit + 48;
|
||||
}
|
||||
|
||||
/*
|
||||
* create efp: the matrix used to
|
||||
* undo the E expansion and effect final permutation
|
||||
*/
|
||||
clearmem((char*)efp, sizeof efp);
|
||||
for(bit = 0; bit < 64; bit++) {
|
||||
int o_bit, o_long;
|
||||
ufc_long word_value, inner_mask1, inner_mask2;
|
||||
int comes_from_f_bit, comes_from_e_bit;
|
||||
int comes_from_word, bit_within_word;
|
||||
|
||||
/* See where bit i belongs in the two 32 bit long's */
|
||||
o_long = bit / 32; /* 0..1 */
|
||||
o_bit = bit % 32; /* 0..31 */
|
||||
|
||||
/*
|
||||
* And find a bit in the e permutated value setting this bit.
|
||||
*
|
||||
* Note: the e selection may have selected the same bit several
|
||||
* times. By the initialization of e_inverse, we only look
|
||||
* for one specific instance.
|
||||
*/
|
||||
comes_from_f_bit = final_perm[bit] - 1; /* 0..63 */
|
||||
comes_from_e_bit = e_inverse[comes_from_f_bit]; /* 0..95 */
|
||||
comes_from_word = comes_from_e_bit / 6; /* 0..15 */
|
||||
bit_within_word = comes_from_e_bit % 6; /* 0..5 */
|
||||
|
||||
inner_mask1 = longmask[bit_within_word + 26];
|
||||
inner_mask2 = longmask[o_bit];
|
||||
|
||||
for(word_value = 64; word_value--;) {
|
||||
if(word_value & inner_mask1)
|
||||
efp[comes_from_word][word_value][o_long] |= inner_mask2;
|
||||
}
|
||||
}
|
||||
initialized++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the elements of the sb table permuting the
|
||||
* bits swapped in the expansion by the current salt.
|
||||
*/
|
||||
|
||||
#ifdef _UFC_32_
|
||||
static void shuffle_sb(long32 *k, ufc_long saltbits)
|
||||
{ ufc_long j;
|
||||
long32 x;
|
||||
for(j=4096; j--;) {
|
||||
x = (k[0] ^ k[1]) & (long32)saltbits;
|
||||
*k++ ^= x;
|
||||
*k++ ^= x;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _UFC_64_
|
||||
static void shuffle_sb(long64 *k, ufc_long saltbits)
|
||||
{ ufc_long j;
|
||||
long64 x;
|
||||
for(j=4096; j--;) {
|
||||
x = ((*k >> 32) ^ *k) & (long64)saltbits;
|
||||
*k++ ^= (x << 32) | x;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Setup the unit for a new salt
|
||||
* Hopefully we'll not see a new salt in each crypt call.
|
||||
*/
|
||||
|
||||
static unsigned char current_salt[3] = "&&"; /* invalid value */
|
||||
static ufc_long current_saltbits = 0;
|
||||
static int direction = 0;
|
||||
|
||||
static void setup_salt(const char *s1)
|
||||
{ ufc_long i, j, saltbits;
|
||||
const unsigned char *s2 = (const unsigned char *)s1;
|
||||
|
||||
if(!initialized)
|
||||
ufc_init_des();
|
||||
|
||||
if(s2[0] == current_salt[0] && s2[1] == current_salt[1])
|
||||
return;
|
||||
current_salt[0] = s2[0]; current_salt[1] = s2[1];
|
||||
|
||||
/*
|
||||
* This is the only crypt change to DES:
|
||||
* entries are swapped in the expansion table
|
||||
* according to the bits set in the salt.
|
||||
*/
|
||||
saltbits = 0;
|
||||
for(i = 0; i < 2; i++) {
|
||||
long c=ascii_to_bin(s2[i]);
|
||||
if(c < 0 || c > 63)
|
||||
c = 0;
|
||||
for(j = 0; j < 6; j++) {
|
||||
if((c >> j) & 0x1)
|
||||
saltbits |= BITMASK(6 * i + j);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Permute the sb table values
|
||||
* to reflect the changed e
|
||||
* selection table
|
||||
*/
|
||||
shuffle_sb(_ufc_sb0, current_saltbits ^ saltbits);
|
||||
shuffle_sb(_ufc_sb1, current_saltbits ^ saltbits);
|
||||
shuffle_sb(_ufc_sb2, current_saltbits ^ saltbits);
|
||||
shuffle_sb(_ufc_sb3, current_saltbits ^ saltbits);
|
||||
|
||||
current_saltbits = saltbits;
|
||||
}
|
||||
|
||||
static void ufc_mk_keytab(char *key)
|
||||
{ ufc_long v1, v2, *k1;
|
||||
int i;
|
||||
#ifdef _UFC_32_
|
||||
long32 v, *k2 = &_ufc_keytab[0][0];
|
||||
#endif
|
||||
#ifdef _UFC_64_
|
||||
long64 v, *k2 = &_ufc_keytab[0];
|
||||
#endif
|
||||
|
||||
v1 = v2 = 0; k1 = &do_pc1[0][0][0];
|
||||
for(i = 8; i--;) {
|
||||
v1 |= k1[*key & 0x7f]; k1 += 128;
|
||||
v2 |= k1[*key++ & 0x7f]; k1 += 128;
|
||||
}
|
||||
|
||||
for(i = 0; i < 16; i++) {
|
||||
k1 = &do_pc2[0][0];
|
||||
|
||||
v1 = (v1 << rots[i]) | (v1 >> (28 - rots[i]));
|
||||
v = k1[(v1 >> 21) & 0x7f]; k1 += 128;
|
||||
v |= k1[(v1 >> 14) & 0x7f]; k1 += 128;
|
||||
v |= k1[(v1 >> 7) & 0x7f]; k1 += 128;
|
||||
v |= k1[(v1 ) & 0x7f]; k1 += 128;
|
||||
|
||||
#ifdef _UFC_32_
|
||||
*k2++ = v;
|
||||
v = 0;
|
||||
#endif
|
||||
#ifdef _UFC_64_
|
||||
v <<= 32;
|
||||
#endif
|
||||
|
||||
v2 = (v2 << rots[i]) | (v2 >> (28 - rots[i]));
|
||||
v |= k1[(v2 >> 21) & 0x7f]; k1 += 128;
|
||||
v |= k1[(v2 >> 14) & 0x7f]; k1 += 128;
|
||||
v |= k1[(v2 >> 7) & 0x7f]; k1 += 128;
|
||||
v |= k1[(v2 ) & 0x7f];
|
||||
|
||||
*k2++ = v;
|
||||
}
|
||||
|
||||
direction = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Undo an extra E selection and do final permutations
|
||||
*/
|
||||
|
||||
ufc_long *_ufc_dofinalperm(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2)
|
||||
{ ufc_long v1, v2, x;
|
||||
static ufc_long ary[2];
|
||||
|
||||
x = (l1 ^ l2) & current_saltbits; l1 ^= x; l2 ^= x;
|
||||
x = (r1 ^ r2) & current_saltbits; r1 ^= x; r2 ^= x;
|
||||
|
||||
v1=v2=0; l1 >>= 3; l2 >>= 3; r1 >>= 3; r2 >>= 3;
|
||||
|
||||
v1 |= efp[15][ r2 & 0x3f][0]; v2 |= efp[15][ r2 & 0x3f][1];
|
||||
v1 |= efp[14][(r2 >>= 6) & 0x3f][0]; v2 |= efp[14][ r2 & 0x3f][1];
|
||||
v1 |= efp[13][(r2 >>= 10) & 0x3f][0]; v2 |= efp[13][ r2 & 0x3f][1];
|
||||
v1 |= efp[12][(r2 >>= 6) & 0x3f][0]; v2 |= efp[12][ r2 & 0x3f][1];
|
||||
|
||||
v1 |= efp[11][ r1 & 0x3f][0]; v2 |= efp[11][ r1 & 0x3f][1];
|
||||
v1 |= efp[10][(r1 >>= 6) & 0x3f][0]; v2 |= efp[10][ r1 & 0x3f][1];
|
||||
v1 |= efp[ 9][(r1 >>= 10) & 0x3f][0]; v2 |= efp[ 9][ r1 & 0x3f][1];
|
||||
v1 |= efp[ 8][(r1 >>= 6) & 0x3f][0]; v2 |= efp[ 8][ r1 & 0x3f][1];
|
||||
|
||||
v1 |= efp[ 7][ l2 & 0x3f][0]; v2 |= efp[ 7][ l2 & 0x3f][1];
|
||||
v1 |= efp[ 6][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 6][ l2 & 0x3f][1];
|
||||
v1 |= efp[ 5][(l2 >>= 10) & 0x3f][0]; v2 |= efp[ 5][ l2 & 0x3f][1];
|
||||
v1 |= efp[ 4][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 4][ l2 & 0x3f][1];
|
||||
|
||||
v1 |= efp[ 3][ l1 & 0x3f][0]; v2 |= efp[ 3][ l1 & 0x3f][1];
|
||||
v1 |= efp[ 2][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 2][ l1 & 0x3f][1];
|
||||
v1 |= efp[ 1][(l1 >>= 10) & 0x3f][0]; v2 |= efp[ 1][ l1 & 0x3f][1];
|
||||
v1 |= efp[ 0][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 0][ l1 & 0x3f][1];
|
||||
|
||||
ary[0] = v1; ary[1] = v2;
|
||||
return ary;
|
||||
}
|
||||
|
||||
/*
|
||||
* crypt only: convert from 64 bit to 11 bit ASCII
|
||||
* prefixing with the salt
|
||||
*/
|
||||
|
||||
static char *output_conversion(ufc_long v1, ufc_long v2, const char *salt)
|
||||
{ static char outbuf[14];
|
||||
int i, s;
|
||||
|
||||
outbuf[0] = salt[0];
|
||||
outbuf[1] = salt[1] ? salt[1] : salt[0];
|
||||
|
||||
for(i = 0; i < 5; i++)
|
||||
outbuf[i + 2] = bin_to_ascii((v1 >> (26 - 6 * i)) & 0x3f);
|
||||
|
||||
s = (v2 & 0xf) << 2;
|
||||
v2 = (v2 >> 2) | ((v1 & 0x3) << 30);
|
||||
|
||||
for(i = 5; i < 10; i++)
|
||||
outbuf[i + 2] = bin_to_ascii((v2 >> (56 - 6 * i)) & 0x3f);
|
||||
|
||||
outbuf[12] = bin_to_ascii(s);
|
||||
outbuf[13] = 0;
|
||||
|
||||
return outbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* UNIX crypt function
|
||||
*/
|
||||
|
||||
static ufc_long *_ufc_doit(ufc_long , ufc_long, ufc_long, ufc_long, ufc_long);
|
||||
|
||||
char *ufc_crypt(const char *key,const char *salt)
|
||||
{ ufc_long *s;
|
||||
char ktab[9];
|
||||
|
||||
/*
|
||||
* Hack DES tables according to salt
|
||||
*/
|
||||
setup_salt(salt);
|
||||
|
||||
/*
|
||||
* Setup key schedule
|
||||
*/
|
||||
clearmem(ktab, sizeof ktab);
|
||||
strncpy(ktab, key, 8);
|
||||
ufc_mk_keytab(ktab);
|
||||
|
||||
/*
|
||||
* Go for the 25 DES encryptions
|
||||
*/
|
||||
s = _ufc_doit((ufc_long)0, (ufc_long)0,
|
||||
(ufc_long)0, (ufc_long)0, (ufc_long)25);
|
||||
|
||||
/*
|
||||
* And convert back to 6 bit ASCII
|
||||
*/
|
||||
return output_conversion(s[0], s[1], salt);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _UFC_32_
|
||||
|
||||
/*
|
||||
* 32 bit version
|
||||
*/
|
||||
|
||||
extern long32 _ufc_keytab[16][2];
|
||||
extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
|
||||
|
||||
#define SBA(sb, v) (*(long32*)((char*)(sb)+(v)))
|
||||
|
||||
static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
|
||||
{ int i;
|
||||
long32 s, *k;
|
||||
|
||||
while(itr--) {
|
||||
k = &_ufc_keytab[0][0];
|
||||
for(i=8; i--; ) {
|
||||
s = *k++ ^ r1;
|
||||
l1 ^= SBA(_ufc_sb1, s & 0xffff); l2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);
|
||||
l1 ^= SBA(_ufc_sb0, s >>= 16); l2 ^= SBA(_ufc_sb0, (s) +4);
|
||||
s = *k++ ^ r2;
|
||||
l1 ^= SBA(_ufc_sb3, s & 0xffff); l2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
|
||||
l1 ^= SBA(_ufc_sb2, s >>= 16); l2 ^= SBA(_ufc_sb2, (s) +4);
|
||||
|
||||
s = *k++ ^ l1;
|
||||
r1 ^= SBA(_ufc_sb1, s & 0xffff); r2 ^= SBA(_ufc_sb1, (s & 0xffff)+4);
|
||||
r1 ^= SBA(_ufc_sb0, s >>= 16); r2 ^= SBA(_ufc_sb0, (s) +4);
|
||||
s = *k++ ^ l2;
|
||||
r1 ^= SBA(_ufc_sb3, s & 0xffff); r2 ^= SBA(_ufc_sb3, (s & 0xffff)+4);
|
||||
r1 ^= SBA(_ufc_sb2, s >>= 16); r2 ^= SBA(_ufc_sb2, (s) +4);
|
||||
}
|
||||
s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s;
|
||||
}
|
||||
return _ufc_dofinalperm(l1, l2, r1, r2);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _UFC_64_
|
||||
|
||||
/*
|
||||
* 64 bit version
|
||||
*/
|
||||
|
||||
extern long64 _ufc_keytab[16];
|
||||
extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
|
||||
|
||||
#define SBA(sb, v) (*(long64*)((char*)(sb)+(v)))
|
||||
|
||||
static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr)
|
||||
{ int i;
|
||||
long64 l, r, s, *k;
|
||||
|
||||
l = (((long64)l1) << 32) | ((long64)l2);
|
||||
r = (((long64)r1) << 32) | ((long64)r2);
|
||||
|
||||
while(itr--) {
|
||||
k = &_ufc_keytab[0];
|
||||
for(i=8; i--; ) {
|
||||
s = *k++ ^ r;
|
||||
l ^= SBA(_ufc_sb3, (s >> 0) & 0xffff);
|
||||
l ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
|
||||
l ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
|
||||
l ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
|
||||
|
||||
s = *k++ ^ l;
|
||||
r ^= SBA(_ufc_sb3, (s >> 0) & 0xffff);
|
||||
r ^= SBA(_ufc_sb2, (s >> 16) & 0xffff);
|
||||
r ^= SBA(_ufc_sb1, (s >> 32) & 0xffff);
|
||||
r ^= SBA(_ufc_sb0, (s >> 48) & 0xffff);
|
||||
}
|
||||
s=l; l=r; r=s;
|
||||
}
|
||||
|
||||
l1 = l >> 32; l2 = l & 0xffffffff;
|
||||
r1 = r >> 32; r2 = r & 0xffffffff;
|
||||
return _ufc_dofinalperm(l1, l2, r1, r2);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define crypt ufc_crypt
|
||||
#endif
|
||||
|
||||
main()
|
||||
{
|
||||
char passwd[9];
|
||||
char salt[9];
|
||||
char c_out1[256];
|
||||
char c_out2[256];
|
||||
|
||||
char expected_out[14];
|
||||
|
||||
strcpy(expected_out, "12yJ.Of/NQ.Pk");
|
||||
strcpy(passwd, "12345678");
|
||||
strcpy(salt, "12345678");
|
||||
|
||||
strcpy(c_out1, crypt(passwd, salt));
|
||||
salt[2] = '\0';
|
||||
strcpy(c_out2, crypt(passwd, salt));
|
||||
|
||||
/*
|
||||
* If the non-trucated salt fails but the
|
||||
* truncated salt succeeds then exit 1.
|
||||
*/
|
||||
|
||||
if((strcmp(c_out1, expected_out) != 0) &&
|
||||
(strcmp(c_out2, expected_out) == 0))
|
||||
exit(1);
|
||||
|
||||
#ifdef HAVE_BIGCRYPT
|
||||
/*
|
||||
* Try the same with bigcrypt...
|
||||
*/
|
||||
|
||||
{
|
||||
char big_passwd[17];
|
||||
char big_salt[17];
|
||||
char big_c_out1[256];
|
||||
char big_c_out2[256];
|
||||
char big_expected_out[27];
|
||||
|
||||
strcpy(big_passwd, "1234567812345678");
|
||||
strcpy(big_salt, "1234567812345678");
|
||||
strcpy(big_expected_out, "12yJ.Of/NQ.PklfyCuHi/rwM");
|
||||
|
||||
strcpy(big_c_out1, bigcrypt(big_passwd, big_salt));
|
||||
big_salt[2] = '\0';
|
||||
strcpy(big_c_out2, bigcrypt(big_passwd, big_salt));
|
||||
|
||||
/*
|
||||
* If the non-trucated salt fails but the
|
||||
* truncated salt succeeds then exit 1.
|
||||
*/
|
||||
|
||||
if((strcmp(big_c_out1, big_expected_out) != 0) &&
|
||||
(strcmp(big_c_out2, big_expected_out) == 0))
|
||||
exit(1);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/* test whether fcntl locking works on this system */
|
||||
|
||||
#if defined(HAVE_UNISTD_H)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_FCNTL_H
|
||||
#include <sys/fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#define DATA "conftest.fcntl"
|
||||
|
||||
#ifndef SEEK_SET
|
||||
#define SEEK_SET 0
|
||||
#endif
|
||||
|
||||
/* lock a byte range in a open file */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct flock lock;
|
||||
int fd, ret, status=1;
|
||||
pid_t pid;
|
||||
char *testdir = NULL;
|
||||
|
||||
testdir = getenv("TESTDIR");
|
||||
if (testdir) chdir(testdir);
|
||||
|
||||
alarm(10);
|
||||
|
||||
if (!(pid=fork())) {
|
||||
sleep(2);
|
||||
fd = open(DATA, O_RDONLY);
|
||||
|
||||
if (fd == -1) {
|
||||
fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n",
|
||||
DATA, (int)errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 4;
|
||||
lock.l_pid = getpid();
|
||||
|
||||
lock.l_type = F_WRLCK;
|
||||
|
||||
/* check if a lock applies */
|
||||
ret = fcntl(fd,F_GETLK,&lock);
|
||||
|
||||
if ((ret == -1) ||
|
||||
(lock.l_type == F_UNLCK)) {
|
||||
fprintf(stderr,"ERROR: lock test failed (ret=%d errno=%d)\n", ret, (int)errno);
|
||||
exit(1);
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
unlink(DATA);
|
||||
fd = open(DATA, O_RDWR|O_CREAT|O_EXCL, 0600);
|
||||
|
||||
if (fd == -1) {
|
||||
fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n",
|
||||
DATA, (int)errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 4;
|
||||
lock.l_pid = getpid();
|
||||
|
||||
/* set a 4 byte write lock */
|
||||
fcntl(fd,F_SETLK,&lock);
|
||||
|
||||
waitpid(pid, &status, 0);
|
||||
|
||||
unlink(DATA);
|
||||
|
||||
#if defined(WIFEXITED) && defined(WEXITSTATUS)
|
||||
if(WIFEXITED(status)) {
|
||||
status = WEXITSTATUS(status);
|
||||
} else {
|
||||
status = 1;
|
||||
}
|
||||
#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
|
||||
status = (status == 0) ? 0 : 1;
|
||||
#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
|
||||
|
||||
if (status) {
|
||||
fprintf(stderr,"ERROR: lock test failed with status=%d\n",
|
||||
status);
|
||||
}
|
||||
|
||||
exit(status);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/* test whether 64 bit fcntl locking really works on this system */
|
||||
|
||||
#if defined(HAVE_UNISTD_H)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_FCNTL_H
|
||||
#include <sys/fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#define DATA "conftest.fcntl64"
|
||||
|
||||
/* lock a byte range in a open file */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct flock64 lock;
|
||||
int fd, ret, status=1;
|
||||
pid_t pid;
|
||||
|
||||
if (!(pid=fork())) {
|
||||
sleep(2);
|
||||
fd = open64(DATA, O_RDONLY);
|
||||
|
||||
if (fd == -1) exit(1);
|
||||
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 4;
|
||||
lock.l_pid = getpid();
|
||||
|
||||
lock.l_type = F_WRLCK;
|
||||
|
||||
/* check if a lock applies */
|
||||
ret = fcntl(fd,F_GETLK64,&lock);
|
||||
|
||||
if ((ret == -1) ||
|
||||
(lock.l_type == F_UNLCK)) {
|
||||
/* printf("No lock conflict\n"); */
|
||||
exit(1);
|
||||
} else {
|
||||
/* printf("lock conflict\n"); */
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
fd = open64(DATA, O_RDWR|O_CREAT|O_TRUNC, 0600);
|
||||
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
#if defined(COMPILER_SUPPORTS_LL)
|
||||
lock.l_start = 0x100000000LL;
|
||||
#else
|
||||
lock.l_start = 0x100000000;
|
||||
#endif
|
||||
lock.l_len = 4;
|
||||
lock.l_pid = getpid();
|
||||
|
||||
/* set a 4 byte write lock */
|
||||
fcntl(fd,F_SETLK64,&lock);
|
||||
|
||||
sys_waitpid(pid, &status, 0);
|
||||
|
||||
#if defined(WIFEXITED) && defined(WEXITSTATUS)
|
||||
if(WIFEXITED(status)) {
|
||||
status = WEXITSTATUS(status);
|
||||
} else {
|
||||
status = 1;
|
||||
}
|
||||
#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
|
||||
status = (status == 0) ? 0 : 1;
|
||||
#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
|
||||
|
||||
unlink(DATA);
|
||||
|
||||
exit(status);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/* test whether fcntl locking works between threads on this Linux system */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/fcntl.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static int sys_waitpid(pid_t pid,int *status,int options)
|
||||
{
|
||||
return waitpid(pid,status,options);
|
||||
}
|
||||
|
||||
#define DATA "conftest.fcntl"
|
||||
|
||||
#define SEEK_SET 0
|
||||
|
||||
static void *test_thread(void *thread_parm)
|
||||
{
|
||||
int *status = thread_parm;
|
||||
int fd, ret;
|
||||
struct flock lock;
|
||||
|
||||
sleep(2);
|
||||
fd = open(DATA, O_RDWR);
|
||||
|
||||
if (fd == -1) {
|
||||
fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n",
|
||||
DATA, (int)errno);
|
||||
pthread_exit(thread_parm);
|
||||
}
|
||||
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 4;
|
||||
lock.l_pid = 0;
|
||||
|
||||
/* check if a lock applies */
|
||||
ret = fcntl(fd,F_SETLK,&lock);
|
||||
if ((ret != -1)) {
|
||||
fprintf(stderr,"ERROR: lock test failed (ret=%d errno=%d)\n", ret, (int)errno);
|
||||
} else {
|
||||
*status = 0; /* SUCCESS! */
|
||||
}
|
||||
pthread_exit(thread_parm);
|
||||
}
|
||||
|
||||
/* lock a byte range in a open file */
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct flock lock;
|
||||
int fd, ret, status=1, rc;
|
||||
pid_t pid;
|
||||
char *testdir = NULL;
|
||||
pthread_t thread_id;
|
||||
pthread_attr_t thread_attr;
|
||||
|
||||
testdir = getenv("TESTDIR");
|
||||
if (testdir) chdir(testdir);
|
||||
|
||||
alarm(10);
|
||||
|
||||
pthread_attr_init(&thread_attr);
|
||||
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
|
||||
rc = pthread_create(&thread_id, &thread_attr, &test_thread, &status);
|
||||
pthread_attr_destroy(&thread_attr);
|
||||
if (rc == 0) {
|
||||
fprintf(stderr,"created thread_id=%lu\n",
|
||||
(unsigned long int)thread_id);
|
||||
} else {
|
||||
fprintf(stderr,"ERROR: thread create failed, rc=%d\n", rc);
|
||||
}
|
||||
|
||||
unlink(DATA);
|
||||
fd = open(DATA, O_RDWR|O_CREAT|O_RDWR, 0600);
|
||||
|
||||
if (fd == -1) {
|
||||
fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n",
|
||||
DATA, (int)errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lock.l_type = F_WRLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 4;
|
||||
lock.l_pid = getpid();
|
||||
|
||||
/* set a 4 byte write lock */
|
||||
fcntl(fd,F_SETLK,&lock);
|
||||
|
||||
sleep(4); /* allow thread to try getting lock */
|
||||
|
||||
unlink(DATA);
|
||||
|
||||
#if defined(WIFEXITED) && defined(WEXITSTATUS)
|
||||
if(WIFEXITED(status)) {
|
||||
status = WEXITSTATUS(status);
|
||||
} else {
|
||||
status = 1;
|
||||
}
|
||||
#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
|
||||
status = (status == 0) ? 0 : 1;
|
||||
#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
|
||||
|
||||
if (status) {
|
||||
fprintf(stderr,"ERROR: lock test failed with status=%d\n",
|
||||
status);
|
||||
}
|
||||
|
||||
exit(status);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* this tests whether we can use a shared writeable mmap on a file -
|
||||
as needed for the mmap variant of FAST_SHARE_MODES */
|
||||
|
||||
#if defined(HAVE_UNISTD_H)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define DATA "conftest.mmap"
|
||||
|
||||
#ifndef MAP_FILE
|
||||
#define MAP_FILE 0
|
||||
#endif
|
||||
|
||||
main()
|
||||
{
|
||||
int *buf;
|
||||
int i;
|
||||
int fd = open(DATA,O_RDWR|O_CREAT|O_TRUNC,0666);
|
||||
int count=7;
|
||||
|
||||
if (fd == -1) exit(1);
|
||||
|
||||
for (i=0;i<10000;i++) {
|
||||
write(fd,&i,sizeof(i));
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
if (fork() == 0) {
|
||||
fd = open(DATA,O_RDWR);
|
||||
if (fd == -1) exit(1);
|
||||
|
||||
buf = (int *)mmap(NULL, 10000*sizeof(int),
|
||||
(PROT_READ | PROT_WRITE),
|
||||
MAP_FILE | MAP_SHARED,
|
||||
fd, 0);
|
||||
|
||||
if (buf == (int *)-1) exit(1);
|
||||
|
||||
while (count-- && buf[9124] != 55732) sleep(1);
|
||||
|
||||
if (count <= 0) exit(1);
|
||||
|
||||
buf[1763] = 7268;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
fd = open(DATA,O_RDWR);
|
||||
if (fd == -1) exit(1);
|
||||
|
||||
buf = (int *)mmap(NULL, 10000*sizeof(int),
|
||||
(PROT_READ | PROT_WRITE),
|
||||
MAP_FILE | MAP_SHARED,
|
||||
fd, 0);
|
||||
|
||||
if (buf == (int *)-1) exit(1);
|
||||
|
||||
buf[9124] = 55732;
|
||||
|
||||
while (count-- && buf[1763] != 7268) sleep(1);
|
||||
|
||||
unlink(DATA);
|
||||
|
||||
if (count > 0) exit(0);
|
||||
exit(1);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/* a trivial function used to test building shared libraries */
|
||||
|
||||
int foo(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <stdio.h>
|
||||
|
||||
void exit(int);
|
||||
|
||||
main()
|
||||
{
|
||||
#if !(defined(HAVE_IFACE_IFCONF) || defined(HAVE_IFACE_IFREQ) || defined(HAVE_IFACE_AIX))
|
||||
printf("WARNING: No automated network interface determination\n");
|
||||
#endif
|
||||
|
||||
#if !(defined(STAT_STATVFS) || defined(STAT_STATVFS64) || defined(STAT_STATFS3_OSF1) || defined(STAT_STATFS2_BSIZE) || defined(STAT_STATFS4) || defined(STAT_STATFS2_FSIZE) || defined(STAT_STATFS2_FS_DATA))
|
||||
printf("ERROR: No disk free routine!\n");
|
||||
exit(1);
|
||||
#endif
|
||||
|
||||
#if !((defined(HAVE_RANDOM) || defined(HAVE_RAND)) && (defined(HAVE_SRANDOM) || defined(HAVE_SRAND)))
|
||||
printf("ERROR: No random or srandom routine!\n");
|
||||
exit(1);
|
||||
#endif
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
void exit(int);
|
||||
|
||||
main()
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/* -*- c-file-style: "linux" -*-
|
||||
*
|
||||
* Try creating a Unix-domain socket, opening it, and reading from it.
|
||||
* The POSIX name for these is AF_LOCAL/PF_LOCAL.
|
||||
*
|
||||
* This is used by the Samba autoconf scripts to detect systems which
|
||||
* don't have Unix-domain sockets, such as (probably) VMS, or systems
|
||||
* on which they are broken under some conditions, such as RedHat 7.0
|
||||
* (unpatched). We can't build WinBind there at the moment.
|
||||
*
|
||||
* Coding standard says to always use exit() for this, not return, so
|
||||
* we do.
|
||||
*
|
||||
* Martin Pool <mbp@samba.org>, June 2000. */
|
||||
|
||||
/* TODO: Look for AF_LOCAL (most standard), AF_UNIX, and AF_FILE. */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
# include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
# include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_WAIT_H
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_ERRNO_DECL
|
||||
# include <errno.h>
|
||||
#else
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
static int bind_socket(char const *filename)
|
||||
{
|
||||
int sock_fd;
|
||||
struct sockaddr_un name;
|
||||
size_t size;
|
||||
|
||||
/* Create the socket. */
|
||||
if ((sock_fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
|
||||
perror ("socket(PF_LOCAL, SOCK_STREAM)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Bind a name to the socket. */
|
||||
name.sun_family = AF_LOCAL;
|
||||
strncpy(name.sun_path, filename, sizeof (name.sun_path));
|
||||
|
||||
/* The size of the address is
|
||||
the offset of the start of the filename,
|
||||
plus its length,
|
||||
plus one for the terminating null byte.
|
||||
Alternatively you can just do:
|
||||
size = SUN_LEN (&name);
|
||||
*/
|
||||
size = SUN_LEN(&name);
|
||||
/* XXX: This probably won't work on unfriendly libcs */
|
||||
|
||||
if (bind(sock_fd, (struct sockaddr *) &name, size) < 0) {
|
||||
perror ("bind");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return sock_fd;
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int sock_fd;
|
||||
int kid;
|
||||
char const *filename = "conftest.unixsock.sock";
|
||||
|
||||
/* abolish hanging */
|
||||
alarm(15); /* secs */
|
||||
|
||||
if ((sock_fd = bind_socket(filename)) < 0)
|
||||
exit(1);
|
||||
|
||||
/* the socket will be deleted when autoconf cleans up these
|
||||
files. */
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
CLDAP server task
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "smbd/service_task.h"
|
||||
#include "smbd/service.h"
|
||||
#include "cldap_server/cldap_server.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/socket/netif.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "lib/ldb/include/ldb_errors.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "db_wrap.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
/*
|
||||
handle incoming cldap requests
|
||||
*/
|
||||
static void cldapd_request_handler(struct cldap_socket *cldap,
|
||||
struct ldap_message *ldap_msg,
|
||||
struct socket_address *src)
|
||||
{
|
||||
struct ldap_SearchRequest *search;
|
||||
if (ldap_msg->type != LDAP_TAG_SearchRequest) {
|
||||
DEBUG(0,("Invalid CLDAP request type %d from %s:%d\n",
|
||||
ldap_msg->type, src->addr, src->port));
|
||||
cldap_error_reply(cldap, ldap_msg->messageid, src,
|
||||
LDAP_OPERATIONS_ERROR, "Invalid CLDAP request");
|
||||
return;
|
||||
}
|
||||
|
||||
search = &ldap_msg->r.SearchRequest;
|
||||
|
||||
if (strcmp("", search->basedn) != 0) {
|
||||
DEBUG(0,("Invalid CLDAP basedn '%s' from %s:%d\n",
|
||||
search->basedn, src->addr, src->port));
|
||||
cldap_error_reply(cldap, ldap_msg->messageid, src,
|
||||
LDAP_OPERATIONS_ERROR, "Invalid CLDAP basedn");
|
||||
return;
|
||||
}
|
||||
|
||||
if (search->scope != LDAP_SEARCH_SCOPE_BASE) {
|
||||
DEBUG(0,("Invalid CLDAP scope %d from %s:%d\n",
|
||||
search->scope, src->addr, src->port));
|
||||
cldap_error_reply(cldap, ldap_msg->messageid, src,
|
||||
LDAP_OPERATIONS_ERROR, "Invalid CLDAP scope");
|
||||
return;
|
||||
}
|
||||
|
||||
if (search->num_attributes == 1 &&
|
||||
strcasecmp(search->attributes[0], "netlogon") == 0) {
|
||||
cldapd_netlogon_request(cldap, ldap_msg->messageid,
|
||||
search->tree, src);
|
||||
return;
|
||||
}
|
||||
|
||||
cldapd_rootdse_request(cldap, ldap_msg->messageid,
|
||||
search, src);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
start listening on the given address
|
||||
*/
|
||||
static NTSTATUS cldapd_add_socket(struct cldapd_server *cldapd, const char *address)
|
||||
{
|
||||
struct cldap_socket *cldapsock;
|
||||
struct socket_address *socket_address;
|
||||
NTSTATUS status;
|
||||
|
||||
/* listen for unicasts on the CLDAP port (389) */
|
||||
cldapsock = cldap_socket_init(cldapd, cldapd->task->event_ctx);
|
||||
NT_STATUS_HAVE_NO_MEMORY(cldapsock);
|
||||
|
||||
socket_address = socket_address_from_strings(cldapsock, cldapsock->sock->backend_name,
|
||||
address, lp_cldap_port());
|
||||
if (!socket_address) {
|
||||
talloc_free(cldapsock);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = socket_listen(cldapsock->sock, socket_address, 0, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("Failed to bind to %s:%d - %s\n",
|
||||
address, lp_cldap_port(), nt_errstr(status)));
|
||||
talloc_free(cldapsock);
|
||||
return status;
|
||||
}
|
||||
|
||||
talloc_free(socket_address);
|
||||
|
||||
cldap_set_incoming_handler(cldapsock, cldapd_request_handler, cldapd);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
setup our listening sockets on the configured network interfaces
|
||||
*/
|
||||
static NTSTATUS cldapd_startup_interfaces(struct cldapd_server *cldapd)
|
||||
{
|
||||
int num_interfaces = iface_count();
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(cldapd);
|
||||
NTSTATUS status;
|
||||
|
||||
/* if we are allowing incoming packets from any address, then
|
||||
we need to bind to the wildcard address */
|
||||
if (!lp_bind_interfaces_only()) {
|
||||
status = cldapd_add_socket(cldapd, "0.0.0.0");
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
} else {
|
||||
int i;
|
||||
|
||||
for (i=0; i<num_interfaces; i++) {
|
||||
const char *address = talloc_strdup(tmp_ctx, iface_n_ip(i));
|
||||
status = cldapd_add_socket(cldapd, address);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
startup the cldapd task
|
||||
*/
|
||||
static void cldapd_task_init(struct task_server *task)
|
||||
{
|
||||
struct cldapd_server *cldapd;
|
||||
NTSTATUS status;
|
||||
|
||||
if (iface_count() == 0) {
|
||||
task_server_terminate(task, "cldapd: no network interfaces configured");
|
||||
return;
|
||||
}
|
||||
|
||||
task_server_set_title(task, "task[cldapd]");
|
||||
|
||||
cldapd = talloc(task, struct cldapd_server);
|
||||
if (cldapd == NULL) {
|
||||
task_server_terminate(task, "cldapd: out of memory");
|
||||
return;
|
||||
}
|
||||
|
||||
cldapd->task = task;
|
||||
cldapd->samctx = samdb_connect(cldapd, anonymous_session(cldapd));
|
||||
if (cldapd->samctx == NULL) {
|
||||
task_server_terminate(task, "cldapd failed to open samdb");
|
||||
return;
|
||||
}
|
||||
|
||||
/* start listening on the configured network interfaces */
|
||||
status = cldapd_startup_interfaces(cldapd);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
task_server_terminate(task, "cldapd failed to setup interfaces");
|
||||
return;
|
||||
}
|
||||
|
||||
irpc_add_name(task->msg_ctx, "cldap_server");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
initialise the cldapd server
|
||||
*/
|
||||
static NTSTATUS cldapd_init(struct event_context *event_ctx, const struct model_ops *model_ops)
|
||||
{
|
||||
return task_server_startup(event_ctx, model_ops, cldapd_task_init);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
register ourselves as a available server
|
||||
*/
|
||||
NTSTATUS server_service_cldapd_init(void)
|
||||
{
|
||||
return register_server_service("cldap", cldapd_init);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
CLDAP server structures
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "libcli/cldap/cldap.h"
|
||||
|
||||
/*
|
||||
top level context structure for the cldap server
|
||||
*/
|
||||
struct cldapd_server {
|
||||
struct task_server *task;
|
||||
struct ldb_context *samctx;
|
||||
};
|
||||
|
||||
#include "cldap_server/proto.h"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user