wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+85
View File
@@ -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
}
+107
View File
@@ -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=
+59
View File
@@ -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)
+510
View File
@@ -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();
}
+195
View File
@@ -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_ */
+77
View File
@@ -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;
}
+207
View File
@@ -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;
}
+459
View File
@@ -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;
}
+151
View File
@@ -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;
}
+378
View File
@@ -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;
}
+93
View File
@@ -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;
}
+839
View File
@@ -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;
}
+669
View File
@@ -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;
}
+275
View File
@@ -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;
}
+30
View File
@@ -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
+79
View File
@@ -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
#######################
+24
View File
@@ -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
#################################
+717
View File
@@ -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);
}
+118
View File
@@ -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;
}
+2
View File
@@ -0,0 +1,2 @@
SMB_ENABLE(gensec_krb5, $HAVE_KRB5)
SMB_ENABLE(gensec_gssapi, $HAVE_KRB5)
+91
View File
@@ -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
################################################
+424
View File
@@ -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
+175
View File
@@ -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
+792
View File
@@ -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;
}
+275
View File
@@ -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;
}
+37
View File
@@ -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;
};
+285
View File
@@ -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;
}
+285
View File
@@ -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;
}
+530
View File
@@ -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
};
+51
View File
@@ -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
+66
View File
@@ -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"
+373
View File
@@ -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;
}
+210
View File
@@ -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
+540
View File
@@ -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}])
+15
View File
@@ -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
#################################
+114
View File
@@ -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.
+124
View File
@@ -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
+158
View File
@@ -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;
}
+617
View File
@@ -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;
}
+700
View File
@@ -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);
+599
View File
@@ -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;
}
+18
View File
@@ -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
################################################
+441
View File
@@ -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;
}
+190
View File
@@ -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"
+368
View File
@@ -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;
}
+336
View File
@@ -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;
}
+852
View File
@@ -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;
}
+549
View File
@@ -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;
}
}
+126
View File
@@ -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
+396
View File
@@ -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;
}
+68
View File
@@ -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($@)])])
+154
View File
@@ -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)
+1
View File
@@ -0,0 +1 @@
AC_PATH_PROG(XSLTPROC, xsltproc)
+208
View File
@@ -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])
+48
View File
@@ -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)
+169
View File
@@ -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 ])
+31
View File
@@ -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)
+32
View File
@@ -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)
+119
View File
@@ -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\";"
])
+5
View File
@@ -0,0 +1,5 @@
asn1.pm: asn1.yp
yapp -s asn1.yp
clean:
rm -f asn1.pm
+306
View File
@@ -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 );
}
+93
View File
@@ -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);
}
+379
View File
@@ -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;
+86
View File
@@ -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.
+25
View File
@@ -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
+35
View File
@@ -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;
+263
View File
@@ -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;
+61
View File
@@ -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;
+116
View File
@@ -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;
+84
View File
@@ -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;
+259
View File
@@ -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;
+73
View File
@@ -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;
+570
View File
@@ -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;
+178
View File
@@ -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;
+71
View File
@@ -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;
+10
View File
@@ -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
+852
View File
@@ -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);
}
+112
View File
@@ -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);
}
+87
View File
@@ -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);
}
+70
View File
@@ -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);
}
+6
View File
@@ -0,0 +1,6 @@
/* a trivial function used to test building shared libraries */
int foo(void)
{
return 1;
}
+22
View File
@@ -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);
}
+7
View File
@@ -0,0 +1,7 @@
void exit(int);
main()
{
exit(0);
}
+93
View File
@@ -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);
}
+203
View File
@@ -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);
}
+33
View File
@@ -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