wmi-1.3.16 from opsview.com
This commit is contained in:
+228
@@ -0,0 +1,228 @@
|
||||
###########################################################################
|
||||
#
|
||||
# This program is part of Zenoss Core, an open source monitoring platform.
|
||||
# Copyright (C) 2008-2010, Zenoss Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License version 2, or (at your
|
||||
# option) any later version, as published by the Free Software Foundation.
|
||||
#
|
||||
# For complete information please visit: http://www.zenoss.com/oss/
|
||||
#
|
||||
###########################################################################
|
||||
build: pywmi-build
|
||||
install: pywmi-installed
|
||||
all: build install
|
||||
.PHONY: clean debug tarball
|
||||
|
||||
WMI_BUILD_TARGETS = proto bin/wmic bin/winexe libraries
|
||||
SAMBA_SRCDIR = Samba/source
|
||||
ZENOSS_BINDIR = $(ZENHOME)/bin
|
||||
ZENPYTHON = $(ZENOSS_BINDIR)/python
|
||||
PYTHON ?= $(ZENPYTHON)
|
||||
PYTHON_EXISTS := $(wildcard $(PYTHON))
|
||||
|
||||
# Assuming python was found, tease out path to headers we should
|
||||
# compile against: e.g., $ZENHOME/include/python#.#
|
||||
# Extract WMI version for subversion tagging as desired.
|
||||
#
|
||||
ifeq ($(PYTHON_EXISTS),$(PYTHON))
|
||||
PY_INCDIR = $(shell $(PYTHON) pyinclude.py)
|
||||
GET_VERSION = "import version as v; print v.VERSION"
|
||||
WMI_VERSION := $(shell cd pysamba ; $(PYTHON) -c $(GET_VERSION))
|
||||
WMI_CPPFLAGS := -I$(PY_INCDIR)
|
||||
endif
|
||||
|
||||
# Install dir for libasync_wmi_lib.so.* and pysamba wrapper code.
|
||||
# e.g., Samba/source/bin/shared/libasync_wmi_lib.so.0.0.1 --> $(PY_LIBDIR)
|
||||
#
|
||||
PY_LIBDIR = $(ZENHOME)/lib/python
|
||||
|
||||
#-------------------------------------------------------------------------#
|
||||
# A key deliverable of this build process is the libasync_wmi shared #
|
||||
# library. Derive the complete filename for this target from config data #
|
||||
# and the build platform so we know precisely what should get built and #
|
||||
# installed. #
|
||||
# #
|
||||
# e.g., libasync_wmi_lib.dylib versus libasync_wmi_lib.so.0.0.2 #
|
||||
#-------------------------------------------------------------------------#
|
||||
LIBASYNC_WMI_LIB_BASENAME = libasync_wmi_lib
|
||||
WMI_CONFIG_MK = $(SAMBA_SRCDIR)/wmi/config.mk
|
||||
LIBASYNC_WMI_LIB_VERSION_nnn = $(shell fgrep -A1 "[LIBRARY::async_wmi_lib]" $(WMI_CONFIG_MK) | sed -e "s/^VERSION=\(.*\)/\1/g" | tail -1)
|
||||
LIBASYNC_WMI_LIB_VERSION_n = $(shell fgrep -A2 "[LIBRARY::async_wmi_lib]" $(WMI_CONFIG_MK) | sed -e "s/^SO_VERSION=\(.*\)/\1/g" | tail -1)
|
||||
ifeq ($(shell uname), Darwin)
|
||||
LIBASYNC_WMI_LIB = $(LIBASYNC_WMI_LIB_BASENAME).dylib.$(LIBASYNC_WMI_LIB_VERSION_nnn)
|
||||
else
|
||||
# e.g., libasync_wmi_lib.so.0.0.2 and libasync_wmi_lib.so.0 respectively
|
||||
LIBASYNC_WMI_LIB := $(LIBASYNC_WMI_LIB_BASENAME).so.$(LIBASYNC_WMI_LIB_VERSION_nnn)
|
||||
LIBASYNC_WMI_LIB_SO_N := $(LIBASYNC_WMI_LIB_BASENAME).so.$(LIBASYNC_WMI_LIB_VERSION_n)
|
||||
endif
|
||||
PATHED_LIBASYNC_WMI_LIB := $(SAMBA_SRCDIR)/bin/shared/$(LIBASYNC_WMI_LIB)
|
||||
|
||||
#-------------------------------------------------------------------------#
|
||||
# Google Breakpad Integration #
|
||||
#-------------------------------------------------------------------------#
|
||||
# libasync_wmi_lib.so can be built with google-breakpad crash reporting. #
|
||||
# http://code.google.com/p/google-breakpad #
|
||||
# #
|
||||
# Minidumps are typically written to /tmp. #
|
||||
# See: Samba/source/librpc/rpc/dcerpc.c #
|
||||
#-------------------------------------------------------------------------#
|
||||
# Comment out the next line to disable google-breakpad dependency.
|
||||
#ifneq ($(shell uname), Darwin)
|
||||
#USE_BREAKPAD = 1
|
||||
#endif
|
||||
|
||||
ifneq ($(USE_BREAKPAD),)
|
||||
breakpad_CPPFLAGS = -DBREAKPAD
|
||||
WMI_CPPFLAGS += $(breakpad_CPPFLAGS)
|
||||
breakpad_LIB = libbreakpad_client.a
|
||||
breakpad_LIBDIR ?= $(ZENHOME)/lib
|
||||
_fqp_breakpad_LIB := $(DESTDIR)$(breakpad_LIBDIR)/$(breakpad_LIB)
|
||||
fqp_breakpad_LIB = $(patsubst //%,/%,$(_fqp_breakpad_LIB))
|
||||
breakpad_LIB_SYMLINK = $(SAMBA_SRCDIR)/bin/static/$(breakpad_LIB)
|
||||
endif
|
||||
#-------------------------------------------------------------------------#
|
||||
|
||||
# Check existence of a directory or file. Bail out of the build if it is missing.
|
||||
#
|
||||
define check
|
||||
@if [ "$1" = "directory" ]; then \
|
||||
if [ ! -d "$2" ];then \
|
||||
echo $3 | awk '{printf("Missing: %-20s\n",$$1)}';\
|
||||
exit 1 ;\
|
||||
else \
|
||||
echo "$3 $2" | awk '{printf("Found: %-20s %20s\n",$$1,$$2)}' 1>/dev/null;\
|
||||
fi ;\
|
||||
fi
|
||||
@if [ "$1" = "file" ]; then \
|
||||
if [ ! -f "$2" ];then \
|
||||
echo "$3 $2" | awk '{printf("Missing: %-20s %s\n",$$1,$$2)}';\
|
||||
exit 1 ;\
|
||||
else \
|
||||
echo "$3 $2" | awk '{printf("Found: %-20s %20s\n",$$1,$$2)}' 1>/dev/null;\
|
||||
fi ;\
|
||||
fi
|
||||
endef
|
||||
|
||||
build-prereqs:
|
||||
@echo
|
||||
@echo "Checking prequisites for building WMI"
|
||||
$(call check,directory,$(ZENHOME),"ZENHOME")
|
||||
$(call check,file,$(PYTHON),"PYTHON")
|
||||
$(call check,directory,$(PY_INCDIR),"PY_INCDIR")
|
||||
ifneq ($(USE_BREAKPAD),)
|
||||
@if [ ! -f "$(fqp_breakpad_LIB)" ];then \
|
||||
echo "Unable to find the google breakpad client library we require at:" ;\
|
||||
echo " $(fqp_breakpad_LIB)" ;\
|
||||
echo ;\
|
||||
echo "Either comment out USE_BREAKPAD in this makefile or build the" ;\
|
||||
echo "breakpad library." ;\
|
||||
echo ;\
|
||||
exit 1 ;\
|
||||
fi
|
||||
endif
|
||||
@touch $@
|
||||
|
||||
install-prereqs:
|
||||
@echo
|
||||
@echo "Checking prequisites for installing WMI"
|
||||
$(call check,directory,$(ZENHOME),"ZENHOME")
|
||||
$(call check,directory,$(DESTDIR)$(ZENOSS_BINDIR),"ZENOSS_BINDIR")
|
||||
$(call check,directory,$(DESTDIR)$(PY_LIBDIR),"PY_LIBDIR")
|
||||
@touch $@
|
||||
|
||||
LIBRPC_CONFIG_MK = $(SAMBA_SRCDIR)/librpc/config.mk
|
||||
ifeq ($(USE_BREAKPAD),)
|
||||
LIBRPC_CONFIG_MK_NOBP = $(SAMBA_SRCDIR)/librpc/config.mk.nobreakpad
|
||||
$(LIBRPC_CONFIG_MK): $(LIBRPC_CONFIG_MK_NOBP)
|
||||
cp $< $@
|
||||
else
|
||||
# Tell the build how to link against the breakpad library.
|
||||
# e.g., Muck with Samba/source/librpc/config.mk to provide that visibility.
|
||||
#
|
||||
LIBRPC_CONFIG_MK_BP = $(SAMBA_SRCDIR)/librpc/config.mk.breakpad
|
||||
LIBRPC_CONFIG_TAG := $(fqp_breakpad_LIB)
|
||||
$(LIBRPC_CONFIG_MK): $(LIBRPC_CONFIG_MK_BP)
|
||||
sed -e "s|_sed_tag_libbreakpad_client_path_|$(LIBRPC_CONFIG_TAG)|" $< >$@ || rm $@
|
||||
|
||||
# Create symlink to actual google breakpad library.
|
||||
# e.g., Samba/source/bin/static/libbreakpad_client.a -> /actual/path/to/libbreakpad_client.a
|
||||
$(breakpad_LIB_SYMLINK): $(fqp_breakpad_LIB)
|
||||
@if [ ! -d "$(@D)" ];then \
|
||||
mkdir -p $(@D) ;\
|
||||
fi
|
||||
ln -sf $(fqp_breakpad_LIB) $@
|
||||
endif
|
||||
|
||||
$(SAMBA_SRCDIR)/Makefile: $(SAMBA_SRCDIR)/autogen.sh
|
||||
cd $(SAMBA_SRCDIR) ;\
|
||||
./autogen.sh ;\
|
||||
CPPFLAGS="$(WMI_CPPFLAGS)" ./configure --without-readline --enable-debug
|
||||
|
||||
ifeq ($(USE_BREAKPAD),)
|
||||
pywmi-build: build-prereqs $(LIBRPC_CONFIG_MK) $(SAMBA_SRCDIR)/Makefile
|
||||
else
|
||||
pywmi-build: build-prereqs $(LIBRPC_CONFIG_MK) $(SAMBA_SRCDIR)/Makefile $(breakpad_LIB_SYMLINK)
|
||||
endif
|
||||
cd $(SAMBA_SRCDIR);\
|
||||
$(MAKE) $(WMI_BUILD_TARGETS) ;\
|
||||
touch $@
|
||||
|
||||
pywmi-installed: install-prereqs $(DESTDIR)$(PY_LIBDIR) $(DESTDIR)$(ZENOSS_BINDIR) $(SAMBA_SRCDIR)/bin/wmic $(SAMBA_SRCDIR)/bin/winexe $(PATHED_LIBASYNC_WMI_LIB)
|
||||
cp $(SAMBA_SRCDIR)/bin/wmic $(DESTDIR)$(ZENOSS_BINDIR)
|
||||
cp $(SAMBA_SRCDIR)/bin/winexe $(DESTDIR)$(ZENOSS_BINDIR)
|
||||
ifeq ($(shell uname), Darwin)
|
||||
-(cd $(DESTDIR)$(PY_LIBDIR) && rm -f $(LIBASYNC_WMI_LIB_BASENAME)*)
|
||||
cp $(PATHED_LIBASYNC_WMI_LIB) $(DESTDIR)$(PY_LIBDIR)/$(LIBASYNC_WMI_LIB_BASENAME).$(LIBASYNC_WMI_LIB_VERSION_nnn).dylib
|
||||
(cd $(DESTDIR)$(PY_LIBDIR) && ln -sf $(LIBASYNC_WMI_LIB_BASENAME).$(LIBASYNC_WMI_LIB_VERSION_nnn).dylib $(LIBASYNC_WMI_LIB_BASENAME).dylib)
|
||||
else
|
||||
-(cd $(DESTDIR)$(PY_LIBDIR) && rm -f $(LIBASYNC_WMI_LIB_BASENAME)*)
|
||||
cp $(PATHED_LIBASYNC_WMI_LIB) $(DESTDIR)$(PY_LIBDIR)
|
||||
(cd $(DESTDIR)$(PY_LIBDIR) && ln -sf $(LIBASYNC_WMI_LIB) $(LIBASYNC_WMI_LIB_SO_N))
|
||||
endif
|
||||
rm -rf $(DESTDIR)$(PY_LIBDIR)/pysamba
|
||||
cp -r pysamba $(DESTDIR)$(PY_LIBDIR)
|
||||
|
||||
$(DESTDIR)$(ZENOSS_BINDIR) $(DESTDIR)$(PY_LIBDIR):
|
||||
mkdir -p $@
|
||||
|
||||
clean: $(LIBRPC_CONFIG_MK)
|
||||
-if [ -f "$(SAMBA_SRCDIR)/Makefile" ] ; then\
|
||||
cd $(SAMBA_SRCDIR) ;\
|
||||
make distclean ;\
|
||||
fi
|
||||
rm -f $(SAMBA_SRCDIR)/bin/shared/*
|
||||
rm -f $(SAMBA_SRCDIR)/bin/static/*
|
||||
rm -f $(SAMBA_SRCDIR)/heimdal/lib/des/hcrypto
|
||||
rm -f build-prereqs
|
||||
rm -f install-prereqs
|
||||
rm -f $(LIBRPC_CONFIG_MK)
|
||||
@-[ -L $(breakpad_LIB_SYMLINK) ] && rm -f $(breakpad_LIB_SYMLINK)
|
||||
|
||||
tarball:
|
||||
-svn rm -m 'cleanup' http://dev.zenoss.org/svn/tags/wmi-$(WMI_VERSION)
|
||||
svn cp -m "tagging wmi-$(WMI_VERSION)" http://dev.zenoss.org/svn/trunk/wmi http://dev.zenoss.org/svn/tags/wmi-$(WMI_VERSION)
|
||||
svn export http://dev.zenoss.org/svn/tags/wmi-$(WMI_VERSION)
|
||||
tar -cjf ../wmi-$(WMI_VERSION).tar.bz2 wmi-$(WMI_VERSION)
|
||||
rm -rf wmi-$(WMI_VERSION)
|
||||
|
||||
debug:
|
||||
@echo "WMI_VERSION = $(WMI_VERSION)"
|
||||
@echo "SAMBA_SRCDIR = $(SAMBA_SRCDIR)"
|
||||
@echo "PY_INCDIR = $(PY_INCDIR)"
|
||||
@echo "PY_LIBDIR = $(PY_LIBDIR)"
|
||||
@echo "ZENOSS_BINDIR = $(ZENOSS_BINDIR)"
|
||||
@echo "PYTHON = $(PYTHON_EXISTS)"
|
||||
@echo "WMI_CONFIGURE CPPFLAGS="$(WMI_CPPFLAGS)" ./configure --without-readline --enable-debug"
|
||||
@echo "WMI_MAKE $(MAKE) $(WMI_BUILD_TARGETS)"
|
||||
ifeq ($(USE_BREAKPAD),)
|
||||
@echo "USE_BREAKPAD [ disabled ]"
|
||||
else
|
||||
@echo "LIBRPC_CONFIG_TAG = $(LIBRPC_CONFIG_TAG)"
|
||||
@echo "USE_BREAKPAD [ enabled ]"
|
||||
@echo "breakpad_CPPFLAGS = $(breakpad_CPPFLAGS)"
|
||||
@echo "breakpad_LIB = $(breakpad_LIB)"
|
||||
@echo "breakpad_LIBDIR = $(breakpad_LIBDIR)"
|
||||
@echo "fqp_breakpad_LIB = $(fqp_breakpad_LIB)"
|
||||
endif
|
||||
@@ -0,0 +1,134 @@
|
||||
source/lib/gencache/gencache.h
|
||||
source/lib/ldb/bin
|
||||
*.pc
|
||||
autom4te.cache
|
||||
*.d
|
||||
*.o
|
||||
*.x
|
||||
*.hd
|
||||
*.ho
|
||||
Makefile
|
||||
configure
|
||||
source/bin/*
|
||||
config.log
|
||||
source/config.mk
|
||||
config.status
|
||||
config.cache
|
||||
source/extra_cflags.txt
|
||||
source/version.h
|
||||
source/heimdal/lib/des/hcrypto
|
||||
source/build/smb_build/config.pm
|
||||
source/auth/auth_proto.h
|
||||
source/auth/auth_sam.h
|
||||
source/auth/pam_errors.h
|
||||
source/auth/credentials/credentials_proto.h
|
||||
source/auth/gensec/gensec_proto.h
|
||||
source/auth/gensec/schannel_proto.h
|
||||
source/auth/gensec/schannel_state.h
|
||||
source/auth/gensec/spnego_proto.h
|
||||
source/auth/kerberos/proto.h
|
||||
source/auth/ntlmssp/msrpc_parse.h
|
||||
source/auth/ntlmssp/proto.h
|
||||
source/cldap_server/proto.h
|
||||
source/dsdb/samdb/samdb_proto.h
|
||||
source/heimdal/lib/asn1/asn1_*
|
||||
source/heimdal/lib/asn1/krb5_asn1.h
|
||||
source/heimdal/lib/asn1/krb5_asn1_files
|
||||
source/heimdal/lib/gssapi/asn1_*.c
|
||||
source/heimdal/lib/gssapi/spnego_asn1.h
|
||||
source/heimdal/lib/gssapi/spnego_asn1_files
|
||||
source/heimdal/lib/hdb/asn1_*.c
|
||||
source/heimdal/lib/hdb/hdb_asn1.h
|
||||
source/heimdal/lib/hdb/hdb_asn1_files
|
||||
source/heimdal/lib/hdb/hdb_err.?
|
||||
source/heimdal/lib/krb5/heim_err.?
|
||||
source/heimdal/lib/krb5/k524_err.?
|
||||
source/heimdal/lib/krb5/krb5_err.?
|
||||
source/heimdal/lib/roken/vis.h
|
||||
source/include/build.h
|
||||
config.h
|
||||
config.h.in
|
||||
source/include/config_tmp.h
|
||||
source/include/config_tmp.h.in
|
||||
source/ldap_server/proto.h
|
||||
source/lib/db_wrap_proto.h
|
||||
source/lib/charset/charset_proto.h
|
||||
source/lib/cmdline/credentials.h
|
||||
source/lib/ldb/samba/ldif_handlers.h
|
||||
source/lib/registry/reg_backend_rpc.h
|
||||
source/lib/registry/regf.h
|
||||
source/lib/registry/registry_proto.h
|
||||
source/lib/registry/tdr_regf.c
|
||||
source/lib/registry/tdr_regf.h
|
||||
source/lib/samba3/samba3_proto.h
|
||||
source/lib/socket/netif_proto.h
|
||||
source/lib/tdr/tdr_proto.h
|
||||
source/lib/util/pidfile.h
|
||||
source/lib/util/unix_privs.h
|
||||
source/lib/util/util_proto.h
|
||||
source/lib/util/wrap_xattr.h
|
||||
source/libcli/finddcs.h
|
||||
source/libcli/libcli_proto.h
|
||||
source/libcli/auth/proto.h
|
||||
source/libcli/composite/proto.h
|
||||
source/libcli/ldap/ldap_proto.h
|
||||
source/libcli/nbt/nbt_proto.h
|
||||
source/libcli/nbt/nbtname.h
|
||||
source/libcli/raw/raw_proto.h
|
||||
source/libcli/resolve/proto.h
|
||||
source/libcli/security/proto.h
|
||||
source/libcli/smb2/smb2_proto.h
|
||||
source/libcli/smb_composite/proto.h
|
||||
source/libcli/util/asn1_proto.h
|
||||
source/libcli/util/clilsa.h
|
||||
source/libcli/util/proto.h
|
||||
source/libcli/wrepl/winsrepl_proto.h
|
||||
source/libnet/libnet_proto.h
|
||||
source/librpc/gen_ndr
|
||||
source/librpc/ndr/libndr_proto.h
|
||||
source/librpc/ndr/ndr_compression.h
|
||||
source/librpc/ndr/ndr_spoolss_buf.h
|
||||
source/librpc/rpc/dcerpc_proto.h
|
||||
source/librpc/rpc/dcerpc_table.h
|
||||
source/nbt_server/nbt_server_proto.h
|
||||
source/nbt_server/dgram/proto.h
|
||||
source/nbt_server/wins/winsdb_proto.h
|
||||
source/nbt_server/wins/winsserver_proto.h
|
||||
source/ntptr/ntptr_proto.h
|
||||
source/ntvfs/ntvfs_proto.h
|
||||
source/ntvfs/common/proto.h
|
||||
source/ntvfs/ipc/proto.h
|
||||
source/ntvfs/posix/vfs_posix_proto.h
|
||||
source/ntvfs/simple/proto.h
|
||||
source/param/proto.h
|
||||
source/param/share_proto.h
|
||||
source/passdb/proto.h
|
||||
source/rpc_server/dcerpc_server_proto.h
|
||||
source/rpc_server/common/proto.h
|
||||
source/rpc_server/samr/proto.h
|
||||
source/rpc_server/srvsvc/proto.h
|
||||
source/scripting/ejs/proto.h
|
||||
source/smb_server/service_smb_proto.h
|
||||
source/smb_server/smb_server_proto.h
|
||||
source/smb_server/smb/smb_proto.h
|
||||
source/smb_server/smb2/smb2_proto.h
|
||||
source/smbd/process_model_proto.h
|
||||
source/smbd/service_proto.h
|
||||
source/torture/proto.h
|
||||
source/torture/util.h
|
||||
source/torture/auth/proto.h
|
||||
source/torture/basic/proto.h
|
||||
source/torture/ldap/proto.h
|
||||
source/torture/libnet/proto.h
|
||||
source/torture/local/proto.h
|
||||
source/torture/nbench/proto.h
|
||||
source/torture/nbt/proto.h
|
||||
source/torture/raw/proto.h
|
||||
source/torture/rpc/proto.h
|
||||
source/torture/smb2/proto.h
|
||||
source/utils/net/net_proto.h
|
||||
source/web_server/proto.h
|
||||
source/winbind/wb_helper.h
|
||||
source/winbind/wb_proto.h
|
||||
source/wrepl_server/wrepl_server_proto.h
|
||||
tags
|
||||
@@ -0,0 +1,6 @@
|
||||
Samba 4 is still feature incomplete. If you are using it for anything other
|
||||
than education you are insane.
|
||||
|
||||
Please file bug reports at https://bugzilla.samba.org/, product: Samba4.
|
||||
Please include as much information as possible, such as SVN revision number
|
||||
and backtraces.
|
||||
+339
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
|
||||
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.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
+512
@@ -0,0 +1,512 @@
|
||||
This file aims to document the major changes since the latest released version
|
||||
of Samba, 3.0. Samba 4.0 contains rewrites of several subsystems
|
||||
and uses a different internal format for most data. Since this
|
||||
file is an initial draft, please update missing items.
|
||||
|
||||
One of the main goals of Samba 4 was Active Directory Domain Controller
|
||||
support. This means Samba now implements several protocols that are required
|
||||
by AD such as Kerberos and DNS.
|
||||
|
||||
An (experimental) upgrade script that performs a one-way upgrade
|
||||
from Samba 3 is available in source/setup/upgrade.
|
||||
|
||||
Removal of nmbd and introduction of process models
|
||||
==================================================
|
||||
smbd now implements several network protocols other than just CIFS and
|
||||
DCE/RPC. nmbd's functionality has been merged into smbd. smbd supports
|
||||
various 'process models' that specify how concurrent connections are
|
||||
handled (when to fork, use threads, etc).
|
||||
|
||||
Introduction of LDB
|
||||
===================
|
||||
Samba now stores most of its persistent data in a LDAP-like database
|
||||
called LDB (see ldb(7) for more info).
|
||||
|
||||
Much improved SWAT
|
||||
==================
|
||||
SWAT has had some rather large improvements and is now more than just a
|
||||
direct editor for smb.conf. Its layout has been improved. SWAT can now also
|
||||
be used for editing run-time data - maintaining user information, provisioning,
|
||||
etc. TLS is supported out of the box.
|
||||
|
||||
Built-in KDC
|
||||
============
|
||||
Samba4 ships with an integrated KDC (Kerberos Key Distribution
|
||||
Center). Backed directly onto our main internal database, and
|
||||
integrated with custom code to handle the PAC, Samba4's KDC is an
|
||||
integral part of our support for AD logon protocols.
|
||||
|
||||
Built-in LDAP Server
|
||||
====================
|
||||
Like the situation with the KDC, Samba4 ships with it's own LDAP
|
||||
server, included to provide simple, built-in LDAP services in an AD
|
||||
(rather than distinctly standards) matching manner. The database is
|
||||
LDB, and it shares that in common with the rest of Samba.
|
||||
|
||||
Changed configuration options
|
||||
=============================
|
||||
Several configuration options have been removed in Samba4 while others have
|
||||
been introduced. This section contains a summary of changes to smb.conf and
|
||||
where these settings moved. Configuration options that have disappeared may be
|
||||
re-added later when the functionality that uses them gets reimplemented in
|
||||
Samba 4.
|
||||
|
||||
The 'security' parameter has been split up. It is now only used to choose
|
||||
between the 'user' and 'share' security levels (the latter is not supported
|
||||
in Samba 4 yet). The other values of this option and the 'domain master' and
|
||||
'domain logons' parameters have been merged into a 'server role' parameter
|
||||
that can be either 'bdc', 'pdc', 'member server' or 'standalone'. Note that
|
||||
member server support does not work yet.
|
||||
|
||||
The following parameters have been removed:
|
||||
- passdb backend: accounts are now stored in a LDB-based SAM database,
|
||||
see 'sam database' below.
|
||||
- update encrypted
|
||||
- public
|
||||
- guest ok
|
||||
- client schannel
|
||||
- server schannel
|
||||
- allow trusted domains
|
||||
- hosts equiv
|
||||
- map to guest
|
||||
- smb passwd file
|
||||
- algorithmic rid base
|
||||
- root directory
|
||||
- root dir
|
||||
- root
|
||||
- guest account
|
||||
- enable privileges
|
||||
- pam password change
|
||||
- passwd program
|
||||
- passwd chat debug
|
||||
- passwd chat timeout
|
||||
- check password script
|
||||
- username map
|
||||
- username level
|
||||
- unix password sync
|
||||
- restrict anonymous
|
||||
- username
|
||||
- user
|
||||
- users
|
||||
- invalid users
|
||||
- valid users
|
||||
- admin users
|
||||
- read list
|
||||
- write list
|
||||
- printer admin
|
||||
- force user
|
||||
- force group
|
||||
- group
|
||||
- write ok
|
||||
- writeable
|
||||
- writable
|
||||
- acl check permissions
|
||||
- acl group control
|
||||
- acl map full control
|
||||
- create mask
|
||||
- create mode
|
||||
- force create mode
|
||||
- security mask
|
||||
- force security mode
|
||||
- directory mask
|
||||
- directory mode
|
||||
- force directory mode
|
||||
- directory security mask
|
||||
- force directory security mode
|
||||
- force unknown acl user
|
||||
- inherit permissions
|
||||
- inherit acls
|
||||
- inherit owner
|
||||
- guest only
|
||||
- only guest
|
||||
- only user
|
||||
- allow hosts
|
||||
- deny hosts
|
||||
- preload modules
|
||||
- use kerberos keytab
|
||||
- syslog
|
||||
- syslog only
|
||||
- max log size
|
||||
- debug timestamp
|
||||
- timestamp logs
|
||||
- debug hires timestamp
|
||||
- debug pid
|
||||
- debug uid
|
||||
- allocation roundup size
|
||||
- aio read size
|
||||
- aio write size
|
||||
- aio write behind
|
||||
- large readwrite
|
||||
- protocol
|
||||
- read bmpx
|
||||
- reset on zero vc
|
||||
- acl compatibility
|
||||
- defer sharing violations
|
||||
- ea support
|
||||
- nt acl support
|
||||
- nt pipe support
|
||||
- profile acls
|
||||
- map acl inherit
|
||||
- afs share
|
||||
- max ttl
|
||||
- client use spnego
|
||||
- enable asu support
|
||||
- svcctl list
|
||||
- block size
|
||||
- change notify timeout
|
||||
- deadtime
|
||||
- getwd cache
|
||||
- keepalive
|
||||
- kernel change notify
|
||||
- lpq cache time
|
||||
- max smbd processes
|
||||
- max disk size
|
||||
- max open files
|
||||
- min print space
|
||||
- strict allocate
|
||||
- sync always
|
||||
- use mmap
|
||||
- use sendfile
|
||||
- hostname lookups
|
||||
- write cache size
|
||||
- name cache timeout
|
||||
- max reported print jobs
|
||||
- load printers
|
||||
- printcap cache time
|
||||
- printcap name
|
||||
- printcap
|
||||
- printing
|
||||
- cups options
|
||||
- cups server
|
||||
- iprint server
|
||||
- print command
|
||||
- disable spoolss
|
||||
- enable spoolss
|
||||
- lpq command
|
||||
- lprm command
|
||||
- lppause command
|
||||
- lpresume command
|
||||
- queuepause command
|
||||
- queueresume command
|
||||
- enumports command
|
||||
- addprinter command
|
||||
- deleteprinter command
|
||||
- show add printer wizard
|
||||
- os2 driver map
|
||||
- use client driver
|
||||
- default devmode
|
||||
- force printername
|
||||
- mangling method
|
||||
- mangle prefix
|
||||
- default case
|
||||
- case sensitive
|
||||
- casesignames
|
||||
- preserve case
|
||||
- short preserve case
|
||||
- mangling char
|
||||
- hide dot files
|
||||
- hide special files
|
||||
- hide unreadable
|
||||
- hide unwriteable files
|
||||
- delete veto files
|
||||
- veto files
|
||||
- hide files
|
||||
- veto oplock files
|
||||
- map readonly
|
||||
- mangled names
|
||||
- mangled map
|
||||
- max stat cache size
|
||||
- stat cache
|
||||
- store dos attributes
|
||||
- machine password timeout
|
||||
- add user script
|
||||
- rename user script
|
||||
- delete user script
|
||||
- add group script
|
||||
- delete group script
|
||||
- add user to group script
|
||||
- delete user from group script
|
||||
- set primary group script
|
||||
- add machine script
|
||||
- shutdown script
|
||||
- abort shutdown script
|
||||
- username map script
|
||||
- logon script
|
||||
- logon path
|
||||
- logon drive
|
||||
- logon home
|
||||
- domain logons
|
||||
- os level
|
||||
- lm announce
|
||||
- lm interval
|
||||
- domain master
|
||||
- browse list
|
||||
- enhanced browsing
|
||||
- wins proxy
|
||||
- wins hook
|
||||
- wins partners
|
||||
- blocking locks
|
||||
- fake oplocks
|
||||
- kernel oplocks
|
||||
- locking
|
||||
- lock spin count
|
||||
- lock spin time
|
||||
- oplocks
|
||||
- level2 oplocks
|
||||
- oplock break wait time
|
||||
- oplock contention limit
|
||||
- posix locking
|
||||
- share modes
|
||||
- ldap server
|
||||
- ldap port
|
||||
- ldap admin dn
|
||||
- ldap delete dn
|
||||
- ldap group suffix
|
||||
- ldap idmap suffix
|
||||
- ldap machine suffix
|
||||
- ldap passwd sync
|
||||
- ldap password sync
|
||||
- ldap replication sleep
|
||||
- ldap suffix
|
||||
- ldap ssl
|
||||
- ldap timeout
|
||||
- ldap page size
|
||||
- ldap user suffix
|
||||
- add share command
|
||||
- change share command
|
||||
- delete share command
|
||||
- eventlog list
|
||||
- utmp directory
|
||||
- wtmp directory
|
||||
- utmp
|
||||
- default service
|
||||
- default
|
||||
- message command
|
||||
- dfree cache time
|
||||
- dfree command
|
||||
- get quota command
|
||||
- set quota command
|
||||
- remote announce
|
||||
- remote browse sync
|
||||
- homedir map
|
||||
- afs username map
|
||||
- afs token lifetime
|
||||
- log nt token command
|
||||
- time offset
|
||||
- NIS homedir
|
||||
- preexec
|
||||
- exec
|
||||
- preexec close
|
||||
- postexec
|
||||
- root preexec
|
||||
- root preexec close
|
||||
- root postexec
|
||||
- set directory
|
||||
- wide links
|
||||
- follow symlinks
|
||||
- dont descend
|
||||
- magic script
|
||||
- magic output
|
||||
- delete readonly
|
||||
- dos filemode
|
||||
- dos filetimes
|
||||
- dos filetime resolution
|
||||
- fake directory create times
|
||||
- panic action
|
||||
- vfs objects
|
||||
- vfs object
|
||||
- msdfs root
|
||||
- msdfs proxy
|
||||
- host msdfs
|
||||
- enable rid algorithm
|
||||
- passdb expand explicit
|
||||
- idmap backend
|
||||
- idmap uid
|
||||
- winbind uid
|
||||
- idmap gid
|
||||
- winbind gid
|
||||
- template homedir
|
||||
- template shell
|
||||
- winbind separator
|
||||
- winbind cache time
|
||||
- winbind enum users
|
||||
- winbind enum groups
|
||||
- winbind use default domain
|
||||
- winbind trusted domains only
|
||||
- winbind nested groups
|
||||
- winbind max idle children
|
||||
- winbind nss info
|
||||
|
||||
The following parameters have been added:
|
||||
+ rpc big endian (G)
|
||||
Make Samba fake it is running on a bigendian machine when using DCE/RPC.
|
||||
Useful for debugging.
|
||||
|
||||
Default: no
|
||||
|
||||
+ case insensitive filesystem (S)
|
||||
Set to true if this share is located on a case-insensitive filesystem.
|
||||
This disables looking for a filename by trying all possible combinations of
|
||||
uppercase/lowercase characters and thus speeds up operations when a
|
||||
file cannot be found.
|
||||
|
||||
Default: no
|
||||
|
||||
+ js include (G)
|
||||
Path to JavaScript library.
|
||||
|
||||
Default: Set at compile-time
|
||||
|
||||
+ setup directory
|
||||
Path to data used by provisioning script.
|
||||
|
||||
Default: Set at compile-time
|
||||
|
||||
+ ncalrpc dir
|
||||
Directory to use for UNIX sockets used by the 'ncalrpc' DCE/RPC transport.
|
||||
|
||||
Default: Set at compile-time
|
||||
|
||||
+ ntvfs handler
|
||||
Backend to the NT VFS to use (more than one can be specified). Available
|
||||
backends include:
|
||||
|
||||
- posix:
|
||||
Maps POSIX FS semantics to NT semantics
|
||||
|
||||
- simple:
|
||||
Very simple backend (original testing backend).
|
||||
|
||||
- unixuid:
|
||||
Sets up user credentials based on POSIX gid/uid.
|
||||
|
||||
- cifs:
|
||||
Proxies a remote CIFS FS. Mainly useful for testing.
|
||||
|
||||
- nbench:
|
||||
Filter module that saves data useful to the nbench benchmark suite.
|
||||
|
||||
- ipc:
|
||||
Allows using SMB for inter process communication. Only used for
|
||||
the IPC$ share.
|
||||
|
||||
- print:
|
||||
Allows printing over SMB. This is LANMAN-style printing (?), not
|
||||
the be confused with the spoolss DCE/RPC interface used by later
|
||||
versions of Windows.
|
||||
|
||||
Default: unixuid default
|
||||
|
||||
+ ntptr providor
|
||||
FIXME
|
||||
|
||||
+ dcerpc endpoint servers
|
||||
What DCE/RPC servers to start.
|
||||
|
||||
Default: epmapper srvsvc wkssvc rpcecho samr netlogon lsarpc spoolss drsuapi winreg dssetup
|
||||
|
||||
+ server services
|
||||
Services Samba should provide.
|
||||
|
||||
Default: smb rpc nbt wrepl ldap cldap web kdc
|
||||
|
||||
+ sam database
|
||||
Location of the SAM (account database) database. This should be a
|
||||
LDB URL.
|
||||
|
||||
Default: set at compile-time
|
||||
|
||||
+ spoolss database
|
||||
Spoolss (printer) DCE/RPC server database. This should be a LDB URL.
|
||||
|
||||
Default: set at compile-time
|
||||
|
||||
+ wins config database
|
||||
WINS configuration database location. This should be a LDB URL.
|
||||
|
||||
Default: set at compile-time
|
||||
|
||||
+ wins database
|
||||
WINS database location. This should be a LDB URL.
|
||||
|
||||
Default: set at compile-time
|
||||
|
||||
+ client use spnego principal
|
||||
Tells the client to use the Kerberos service principal specified by the
|
||||
server during the security protocol negotation rather than
|
||||
looking up the principal itself (cifs/hostname).
|
||||
|
||||
Default: false
|
||||
|
||||
+ nbt port
|
||||
TCP/IP Port used by the NetBIOS over TCP/IP (NBT) implementation.
|
||||
|
||||
Default: 137
|
||||
|
||||
+ dgram port
|
||||
UDP/IP port used by the NetBIOS over TCP/IP (NBT) implementation.
|
||||
|
||||
Default: 138
|
||||
|
||||
+ cldap port
|
||||
UDP/IP port used by the CLDAP protocol.
|
||||
|
||||
Default: 389
|
||||
|
||||
+ krb5 port
|
||||
IP port used by the kerberos KDC.
|
||||
|
||||
Default: 88
|
||||
|
||||
+ kpasswd port
|
||||
IP port used by the kerberos password change protocol.
|
||||
|
||||
Default: 464
|
||||
|
||||
+ web port
|
||||
TCP/IP port SWAT should listen on.
|
||||
|
||||
Default: 901
|
||||
|
||||
+ tls enabled
|
||||
Enable TLS support for SWAT
|
||||
|
||||
Default: true
|
||||
|
||||
+ tls keyfile
|
||||
Path to TLS key file (PEM format) to be used by SWAT. If no
|
||||
path is specified, Samba will create a key.
|
||||
|
||||
Default: none
|
||||
|
||||
+ tls certfile
|
||||
Path to TLS certificate file (PEM format) to be used by SWAT. If no
|
||||
path is specified, Samba will create a certificate.
|
||||
|
||||
Default: none
|
||||
|
||||
+ tls cafile
|
||||
Path to CA authority file Samba will use to sign TLS keys it generates. If
|
||||
no path is specified, Samba will create a self-signed CA certificate.
|
||||
|
||||
Default: none
|
||||
|
||||
+ tls crlfile
|
||||
Path to TLS certificate revocation lists file.
|
||||
|
||||
Default: none
|
||||
|
||||
+ swat directory
|
||||
SWAT data directory.
|
||||
|
||||
Default: set at compile-time
|
||||
|
||||
+ large readwrite
|
||||
Indicate the CIFS server is able to do large reads/writes.
|
||||
|
||||
Default: true
|
||||
|
||||
+ unicode
|
||||
Enable/disable unicode support in the protocol.
|
||||
|
||||
Default: true
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
This directory contains Samba's very simple COM implementation.
|
||||
It is by no means finished yet.
|
||||
|
||||
The main aim of this implementation is for use by our DCOM implementation,
|
||||
which lives in the dcom subdirectory. The local version is used mostly for
|
||||
testing.
|
||||
|
||||
More information on this effort can be found in the DCOM whitepaper in
|
||||
the lorikeet repository.
|
||||
Samba 4 is the ambitious next version of the Samba suite that is being
|
||||
developed in parallel to the stable 3.0 series. The main emphasis in
|
||||
this branch is support for the Active Directory logon protocols used
|
||||
by Windows 2000 and above.
|
||||
|
||||
Samba 4 is currently not yet in a state where it is usable in
|
||||
production environments. Note the WARNINGS below, and the STATUS file,
|
||||
which aims to document what should and should not work.
|
||||
|
||||
With 3 years of development under our belt since Tridge first proposed
|
||||
a new Virtual File System (VFS) layer for Samba3 (a project which
|
||||
eventually lead to our Active Directory efforts), it was felt that we
|
||||
should create something we could 'show off' to our users. This is a
|
||||
Technology Preview (TP), aimed at allowing users, managers and
|
||||
developers to see how we have progressed, and to invite feedback and
|
||||
support.
|
||||
|
||||
WARNINGS
|
||||
========
|
||||
|
||||
Samba4 TP is currently a pre-alpha technology. It may eat your cat, but
|
||||
is far more likely to choose to munch on your password database. We
|
||||
recommend against upgrading any production servers from Samba 3 to
|
||||
Samba 4 at this stage. If you are upgrading an experimental server,
|
||||
you should backup all configuration and data.
|
||||
|
||||
We expect that format changes will require that the user database be
|
||||
rebuilt from scratch a number of times before we make a final release,
|
||||
losing password data each time.
|
||||
|
||||
Samba 4 Technology Preview includes basic Access Control List (ACL)
|
||||
protection on the main user database, but due to time constraints,
|
||||
none on the registry at this stage. We also do not currently have
|
||||
ACLs on the SWAT web-based management tool. This means that Samba 4
|
||||
Technology Preview is not secure.
|
||||
|
||||
File system access should occur as the logged in user, much as Samba3
|
||||
does.
|
||||
|
||||
Again, we strongly recommend against use in a production environment
|
||||
at this stage.
|
||||
|
||||
NEW FEATURES
|
||||
============
|
||||
|
||||
Samba4 supports the server-side of the Active Directory logon environment
|
||||
used by Windows 2000 and later, so we can do full domain join
|
||||
and domain logon operations with these clients.
|
||||
|
||||
Our Domain Controller (DC) implementation includes our own built-in
|
||||
LDAP server and Kerberos Key Distribution Center (KDC) as well as the
|
||||
Samba3-like logon services provided over CIFS. We correctly generate
|
||||
the infamous Kerberos PAC, and include it with the Kerberos tickets we
|
||||
issue.
|
||||
|
||||
SWAT is now integrated into Samba 4 as the user-friendly interface to
|
||||
Samba server management. SWAT provides easy access to our
|
||||
setup and migration tools. Using SWAT, you can migrate windows
|
||||
domains in Samba 4, allowing easy setup of initial user databases, and
|
||||
upgrades from Samba 3.
|
||||
|
||||
The new VFS features in Samba 4 adapts the filesystem on the server to
|
||||
match the Windows client semantics, allowing Samba 4 to better match
|
||||
windows behaviour and application expectations. This includes file
|
||||
annotation information (in streams) and NT ACLs in particular. The
|
||||
VFS is backed with an extensive automated test suite.
|
||||
|
||||
A new scripting interface has been added to Samba 4, allowing
|
||||
JavaScript programs to interface to Samba's internals.
|
||||
|
||||
The Samba 4 architecture is based around an LDAP-like database that
|
||||
can use a range of modular backends. One of the backends supports
|
||||
standards compliant LDAP servers (including OpenLDAP), and we are
|
||||
working on modules to map between AD-like behaviours and this backend.
|
||||
We are aiming for Samba 4 to be powerful frontend to large
|
||||
directories.
|
||||
|
||||
CHANGES
|
||||
=======
|
||||
|
||||
Those familiar with Samba 3 can find a list of user-visible changes
|
||||
since that release series in the NEWS file.
|
||||
|
||||
- An optional password is no longer supported as the second argument to
|
||||
smbclient.
|
||||
|
||||
- The default location of smb.conf in non-FHS builds has changed from the
|
||||
PREFIX/lib directory to the PREFIX/etc directory.
|
||||
|
||||
KNOWN ISSUES
|
||||
============
|
||||
|
||||
- Standalone server and domain member roles are not currently
|
||||
supported. While we have much of the infrastructure required, we
|
||||
have not collected these pieces together.
|
||||
|
||||
- There is no printing support in the current release.
|
||||
|
||||
- SWAT can be painful with <TAB> and forms. Just use the mouse, as
|
||||
the JavaScript layer doing this will change.
|
||||
|
||||
- Domain logons (using Kerberos) from windows clients incorrectly
|
||||
state that the password expires today.
|
||||
|
||||
RUNNING Samba4
|
||||
==============
|
||||
|
||||
A short guide to setting up Samba 4 can be found in the howto.txt file
|
||||
in root of the tarball.
|
||||
|
||||
DEVELOPMENT and FEEDBACK
|
||||
========================
|
||||
Bugs can be filed at https://bugzilla.samba.org/. Please
|
||||
look at the STATUS file before filing a bug to see if a particular
|
||||
is supposed to work yet.
|
||||
|
||||
Development and general discussion about Samba 4 happens mainly on
|
||||
the #samba-technical IRC channel (on irc.freenode.net) and
|
||||
the samba-technical mailing list (see http://lists.samba.org/ for
|
||||
details).
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
This file documents the features that are known to work or known to
|
||||
still need work in the current version of Samba 4.
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
source/build/smb_build/TODO
|
||||
source/lib/registry/TODO
|
||||
source/lib/tdr/TODO
|
||||
source/pidl/TODO
|
||||
|
||||
upgrade process (from Samba3):
|
||||
- Rename upgrade to upgrade3 (to avoid confusion with upgrades
|
||||
from earlier Samba4 releases in the future)
|
||||
- Add support for reading WINS TDB files as well as WINS dat files.
|
||||
|
||||
- seperate adminlog mechanism (as opposed to the current DEBUG log,
|
||||
which is not really aimed at administrators but more at developers)
|
||||
Perhaps similar to eventlog so we can also use eventlog to retrieve the data?
|
||||
- improve handling of test results in testsuite
|
||||
|
||||
- testsuite for the 'net' tool
|
||||
|
||||
Configuration options
|
||||
=====================
|
||||
|
||||
The following options don't exist in Samba4 yet
|
||||
or are not converted by the upgrade script
|
||||
or will be removed:
|
||||
|
||||
- update encrypted
|
||||
- public
|
||||
- guest ok
|
||||
- client schannel
|
||||
- server schannel
|
||||
- allow trusted domains
|
||||
- hosts equiv
|
||||
- map to guest
|
||||
- algorithmic rid base
|
||||
- root directory
|
||||
- root dir
|
||||
- root
|
||||
- guest account
|
||||
- enable privileges
|
||||
- pam password change
|
||||
- passwd program
|
||||
- passwd chat debug
|
||||
- passwd chat timeout
|
||||
- check password script
|
||||
- username map
|
||||
- username level
|
||||
- unix password sync
|
||||
- restrict anonymous
|
||||
- username
|
||||
- user
|
||||
- users
|
||||
- invalid users
|
||||
- valid users
|
||||
- admin users
|
||||
- read list
|
||||
- write list
|
||||
- printer admin
|
||||
- force user
|
||||
- force group
|
||||
- group
|
||||
- write ok
|
||||
- writeable
|
||||
- writable
|
||||
- acl check permissions
|
||||
- acl group control
|
||||
- acl map full control
|
||||
- create mask
|
||||
- create mode
|
||||
- force create mode
|
||||
- security mask
|
||||
- force security mode
|
||||
- directory mask
|
||||
- directory mode
|
||||
- force directory mode
|
||||
- directory security mask
|
||||
- force directory security mode
|
||||
- force unknown acl user
|
||||
- inherit permissions
|
||||
- inherit acls
|
||||
- inherit owner
|
||||
- guest only
|
||||
- only guest
|
||||
- only user
|
||||
- allow hosts
|
||||
- deny hosts
|
||||
- preload modules
|
||||
- use kerberos keytab
|
||||
- syslog
|
||||
- syslog only
|
||||
- max log size
|
||||
- debug timestamp
|
||||
- timestamp logs
|
||||
- debug hires timestamp
|
||||
- debug pid
|
||||
- debug uid
|
||||
- allocation roundup size
|
||||
- aio read size
|
||||
- aio write size
|
||||
- aio write behind
|
||||
- large readwrite
|
||||
- protocol
|
||||
- read bmpx
|
||||
- reset on zero vc
|
||||
- acl compatibility
|
||||
- defer sharing violations
|
||||
- ea support
|
||||
- nt acl support
|
||||
- nt pipe support
|
||||
- profile acls
|
||||
- map acl inherit
|
||||
- afs share
|
||||
- max ttl
|
||||
- client use spnego
|
||||
- enable asu support
|
||||
- svcctl list
|
||||
- block size
|
||||
- change notify timeout
|
||||
- deadtime
|
||||
- getwd cache
|
||||
- keepalive
|
||||
- kernel change notify
|
||||
- lpq cache time
|
||||
- max smbd processes
|
||||
- max disk size
|
||||
- max open files
|
||||
- min print space
|
||||
- strict allocate
|
||||
- sync always
|
||||
- use mmap
|
||||
- use sendfile
|
||||
- hostname lookups
|
||||
- write cache size
|
||||
- name cache timeout
|
||||
- max reported print jobs
|
||||
- load printers
|
||||
- printcap cache time
|
||||
- printcap name
|
||||
- printcap
|
||||
- printing
|
||||
- cups options
|
||||
- cups server
|
||||
- iprint server
|
||||
- print command
|
||||
- disable spoolss
|
||||
- enable spoolss
|
||||
- lpq command
|
||||
- lprm command
|
||||
- lppause command
|
||||
- lpresume command
|
||||
- queuepause command
|
||||
- queueresume command
|
||||
- enumports command
|
||||
- addprinter command
|
||||
- deleteprinter command
|
||||
- show add printer wizard
|
||||
- os2 driver map
|
||||
- use client driver
|
||||
- default devmode
|
||||
- force printername
|
||||
- mangling method
|
||||
- mangle prefix
|
||||
- default case
|
||||
- case sensitive
|
||||
- casesignames
|
||||
- preserve case
|
||||
- short preserve case
|
||||
- mangling char
|
||||
- hide dot files
|
||||
- hide special files
|
||||
- hide unreadable
|
||||
- hide unwriteable files
|
||||
- delete veto files
|
||||
- veto files
|
||||
- hide files
|
||||
- veto oplock files
|
||||
- map readonly
|
||||
- mangled names
|
||||
- mangled map
|
||||
- max stat cache size
|
||||
- stat cache
|
||||
- store dos attributes
|
||||
- machine password timeout
|
||||
- add user script
|
||||
- rename user script
|
||||
- delete user script
|
||||
- add group script
|
||||
- delete group script
|
||||
- add user to group script
|
||||
- delete user from group script
|
||||
- set primary group script
|
||||
- add machine script
|
||||
- shutdown script
|
||||
- abort shutdown script
|
||||
- username map script
|
||||
- logon script
|
||||
- logon path
|
||||
- logon drive
|
||||
- logon home
|
||||
- domain logons
|
||||
- os level
|
||||
- lm announce
|
||||
- lm interval
|
||||
- domain master
|
||||
- browse list
|
||||
- enhanced browsing
|
||||
- wins proxy
|
||||
- blocking locks
|
||||
- fake oplocks
|
||||
- kernel oplocks
|
||||
- locking
|
||||
- lock spin count
|
||||
- lock spin time
|
||||
- oplocks
|
||||
- level2 oplocks
|
||||
- oplock break wait time
|
||||
- oplock contention limit
|
||||
- posix locking
|
||||
- share modes
|
||||
- add share command
|
||||
- change share command
|
||||
- delete share command
|
||||
- eventlog list
|
||||
- utmp directory
|
||||
- wtmp directory
|
||||
- utmp
|
||||
- default service
|
||||
- default
|
||||
- message command
|
||||
- dfree cache time
|
||||
- dfree command
|
||||
- get quota command
|
||||
- set quota command
|
||||
- remote announce
|
||||
- remote browse sync
|
||||
- homedir map
|
||||
- afs username map
|
||||
- afs token lifetime
|
||||
- log nt token command
|
||||
- time offset
|
||||
- NIS homedir
|
||||
- preexec
|
||||
- exec
|
||||
- preexec close
|
||||
- postexec
|
||||
- root preexec
|
||||
- root preexec close
|
||||
- root postexec
|
||||
- set directory
|
||||
- wide links
|
||||
- follow symlinks
|
||||
- dont descend
|
||||
- magic script
|
||||
- magic output
|
||||
- delete readonly
|
||||
- dos filemode
|
||||
- dos filetimes
|
||||
- dos filetime resolution
|
||||
- fake directory create times
|
||||
- panic action
|
||||
- vfs objects
|
||||
- vfs object
|
||||
- msdfs root
|
||||
- msdfs proxy
|
||||
- host msdfs
|
||||
- enable rid algorithm
|
||||
- passdb expand explicit
|
||||
- idmap backend
|
||||
- idmap uid
|
||||
- winbind uid
|
||||
- idmap gid
|
||||
- winbind gid
|
||||
- template homedir
|
||||
- template shell
|
||||
- winbind separator
|
||||
- winbind cache time
|
||||
- winbind enum users
|
||||
- winbind enum groups
|
||||
- winbind use default domain
|
||||
- winbind trusted domains only
|
||||
- winbind nested groups
|
||||
- winbind max idle children
|
||||
- winbind nss info
|
||||
@@ -0,0 +1,171 @@
|
||||
This file contains a history of changes since the first Samba 4 Technology
|
||||
Preview. For a general introduction to Samba 4, see the README file in this
|
||||
directory. The NEWS file contains a list of differences between
|
||||
Samba 3 and Samba 4.
|
||||
|
||||
========================================
|
||||
Changes in Samba4-TP2
|
||||
Release date: 22 March 2006
|
||||
========================================
|
||||
|
||||
* Make ldb async internally (idra)
|
||||
|
||||
* Use HDB-LDB as the keytab (abartlet)
|
||||
|
||||
* Call the wins hook script again (metze)
|
||||
|
||||
* Make sure no more than 25 records are added in the WINS database (metze)
|
||||
|
||||
* Documentation updates (jelmer)
|
||||
|
||||
* Fix termination issue in winreg server (metze)
|
||||
|
||||
* AES fix for Samba 4 <-> Samba4 (abartlet)
|
||||
|
||||
* Better conformance to FHS (abartlet, jelmer)
|
||||
|
||||
* Improve internal API and code quality in smbclient (jelmer)
|
||||
|
||||
* Add testsuite for smbclient (jelmer)
|
||||
|
||||
* Remove support for password as an optional second parameter in
|
||||
smbclient (jelmer)
|
||||
|
||||
* Various warning fixes (metze)
|
||||
|
||||
* Several clarifications of comments (abartlet)
|
||||
|
||||
* Remove use of pstring in some places (jelmer)
|
||||
|
||||
* Re-add the global -k option to enable kerberos (abartlet)
|
||||
|
||||
* Various memory allocation fixes (abartlet)
|
||||
|
||||
* Add new cifsdd client (jpeach)
|
||||
|
||||
* Add tests for even more insane delete-on-close semantics (jra, tridge)
|
||||
|
||||
* Initial work on BASE-DELETE test passing (tridge)
|
||||
|
||||
* Optimizations in tdb (tridge)
|
||||
|
||||
* Improvements to ldb documentation (idra, Brad Hards)
|
||||
|
||||
* Check attribute names to obey rfc2251 (idra)
|
||||
|
||||
* Allow WINS replication with NT4SP6A (metze)
|
||||
|
||||
* Add ManageDSAIT control (Pete Rowley, idra)
|
||||
|
||||
* Add tests for LDB controls (idra)
|
||||
|
||||
* Various LDB crash fixes (idra)
|
||||
|
||||
* Initial work on vlv LDB control (idra)
|
||||
|
||||
* Add -p option to smbtorture (jpeach)
|
||||
|
||||
* Several improvements to the SMB URL and UNC parsing (jpeach)
|
||||
|
||||
* Make DCE/RPC connect functions work async (rafal)
|
||||
|
||||
* Fix invalid steal on supportedControls (closes: #3525) (abartlet)
|
||||
|
||||
* Start parsing saslauthd requests (metze)
|
||||
|
||||
* Split the NBT-WINSREPLICATION test into multiple tests (metze)
|
||||
|
||||
* Add new ACB-bits as seen in acct_flags in the PAC info3 (gd)
|
||||
|
||||
* Move header files out of include/ (jelmer)
|
||||
|
||||
* Create separate library for generic utility functions (jelmer)
|
||||
|
||||
* Add highestCommittedUSN, uSNChanged and uSNCreated support to LDB (tridge)
|
||||
|
||||
* Allow more control over the the winbindd socket location (abartlet)
|
||||
|
||||
* Allow messaging without a server messaging context (abartlet)
|
||||
|
||||
* Make GSSAPI SASL mech work (abartlet)
|
||||
|
||||
* Write out Samba4 version when provisioning (idra)
|
||||
|
||||
* Allow servers to bind to non-broadcast interfaces (tridge, abartlet)
|
||||
|
||||
* Initialize some ASN.1 elements that are optional (metze)
|
||||
|
||||
* Various improvements to RPC-SCHANNEL (abartlet)
|
||||
|
||||
* Make Samba4 pass some of the newer schannel tests (abartlet)
|
||||
|
||||
* Better handling of connections without SPNEGO (abartlet)
|
||||
|
||||
* Generate seperate headers for RPC client functions (jelmer)
|
||||
|
||||
* Improve NTLMSSP tests (abartlet, vl)
|
||||
|
||||
* Support any size pointers in pidl (tridge)
|
||||
|
||||
* Large overhaul of the opendb code to pass BASE-DELETE (tridge)
|
||||
|
||||
* Use doxygen for documenting lib/util and lib/registry (jelmer)
|
||||
|
||||
* Add registration mechanism for modules and backends in ldb (idra, jelmer)
|
||||
|
||||
* Support building shared libraries in the build system (metze, jelmer)
|
||||
|
||||
* Install headers in a sane location (jelmer)
|
||||
|
||||
* Fix BASE-NEGNOWAIT (tridge)
|
||||
|
||||
* Add prefixes to most of the SMB-related functions (metze)
|
||||
|
||||
* Get rid of proto.h (jelmer)
|
||||
|
||||
* Reduce number of headers included in includes.h (jelmer)
|
||||
|
||||
* Support header dependencies (jelmer)
|
||||
|
||||
* Add RAW-NOTIFY (tridge, metze)
|
||||
|
||||
* Fix 'your password has expired' on every login (abartlet)
|
||||
|
||||
* Improvements to RPC-SAMSYNC (abartlet)
|
||||
|
||||
* Work on supporting change notify (tridge, metze)
|
||||
|
||||
* Reopen log files after SIGHUP (metze)
|
||||
|
||||
* Add BUGS.txt (#3523) (jelmer)
|
||||
|
||||
* Add summary to configure (#3442) (metze, jelmer)
|
||||
|
||||
* Swig fixes (idra)
|
||||
|
||||
* Improve NBT-WINSREPLICATION-OWNED test (metze)
|
||||
|
||||
* Fix a lot of compiler warnings (metze)
|
||||
|
||||
* Several code improvements found by static code checker (tridge, metze)
|
||||
|
||||
* Force correct alignment when in ASCII mode (#2921) (tridge)
|
||||
|
||||
* Fix coverity bug #127 (vl)
|
||||
|
||||
* Add support for changing process titles (metze)
|
||||
|
||||
* Support raw NTLMSSP (abartlet)
|
||||
|
||||
* Fix debug levels in several places (abartlet)
|
||||
|
||||
* Work to unify the ntvfs structures for smb and smb2 (metze, tridge)
|
||||
|
||||
* Initial work on asynchronous libnet (rafal)
|
||||
|
||||
* Improvements to the wide character set functions (tridge)
|
||||
|
||||
* Several heimdal build improvements (abartlet, jelmer)
|
||||
|
||||
* A lot of small cleanups and typo fixes
|
||||
(metze, abartlet, idra, jpeach, tridge, jelmer)
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
Samba4 developer howto
|
||||
----------------------
|
||||
|
||||
tridge@samba.org, December 2004
|
||||
|
||||
|
||||
This is a very basic document on how to setup a simple Samba4
|
||||
server. This is aimed at developers who are already familiar with
|
||||
Samba3 and wish to participate in Samba4 development. This is not
|
||||
aimed at production use of Samba4.
|
||||
|
||||
|
||||
Step 1: download Samba4
|
||||
-----------------------
|
||||
|
||||
There are 2 methods of doing this:
|
||||
|
||||
method 1: "rsync -avz samba.org::ftp/unpacked/samba4 ."
|
||||
|
||||
method 2: "svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0 samba4"
|
||||
|
||||
both methods will create a directory called "samba4" in the current
|
||||
directory. If you don't have rsync or svn then install one of them.
|
||||
|
||||
Since only released versions of Samba contain a pregenerated configure script,
|
||||
you will have to generate it by hand:
|
||||
|
||||
$ cd samba4/source
|
||||
$ ./autogen.sh
|
||||
|
||||
Note that the above rsync command will give you a checked out svn
|
||||
repository. So if you also have svn you can update it to the latest
|
||||
version at some future date using:
|
||||
|
||||
$ cd samba4
|
||||
$ svn up
|
||||
|
||||
Step 2: compile Samba4
|
||||
----------------------
|
||||
|
||||
Recommended optional development libraries:
|
||||
- acl and xattr development libraries
|
||||
- gnutls
|
||||
- readline
|
||||
|
||||
Run this:
|
||||
|
||||
$ cd samba4/source
|
||||
$ ./configure
|
||||
$ make proto all
|
||||
|
||||
If you have gcc 3.4 or newer, then substitute "pch" for "proto" to
|
||||
greatly speed up the compile process (about 5x faster).
|
||||
|
||||
Step 3: install Samba4
|
||||
----------------------
|
||||
|
||||
Run this as a user who have permission to write to the install
|
||||
directory (defaults to /usr/local/samba). Use --prefix option to
|
||||
configure above to change this.
|
||||
|
||||
# make install
|
||||
|
||||
|
||||
Step 4: provision Samba4
|
||||
------------------------
|
||||
|
||||
The "provision" step sets up a basic user database. Make sure your smbscript
|
||||
binary is installed in a directory listed in your PATH environment variable.
|
||||
It is presumed it's available just like any other commands from your shell.
|
||||
Must be run as a user with permission to write to the install directory.
|
||||
|
||||
# cd source
|
||||
# ./setup/provision --realm=YOUR.REALM --domain=YOURDOM --adminpass=SOMEPASSWORD
|
||||
|
||||
REMINDER: Add the "bin" directory of the path you installed to
|
||||
(e.g. /usr/local/samba/bin) to your path, or the provision command
|
||||
will not work.
|
||||
|
||||
'YOURDOM' is the NT4 style domain name. 'YOUR.REALM' is your kerberos
|
||||
realm, which is typically your DNS domain name.
|
||||
|
||||
Step 5: Create a simple smb.conf
|
||||
--------------------------------
|
||||
|
||||
The provisioning will create a very simple smb.conf with no shares by
|
||||
default. You will need to update it to add at least one share. For
|
||||
example:
|
||||
|
||||
[test]
|
||||
path = /data/test
|
||||
read only = no
|
||||
|
||||
|
||||
Step 6: starting Samba4
|
||||
-----------------------
|
||||
|
||||
The simplest is to just run "smbd", but as a developer you may find
|
||||
the following more useful:
|
||||
|
||||
# smbd -i -M single
|
||||
|
||||
that means "start smbd without messages in stdout, and running a
|
||||
single process. That mode of operation makes debugging smbd with gdb
|
||||
particularly easy.
|
||||
|
||||
Note that now it is no longer necessary to have an instance of nmbd
|
||||
from Samba 3 running. If you are running any smbd or nmbd processes
|
||||
they need to be stopped before starting smbd from Samba 4.
|
||||
|
||||
Make sure you put the bin and sbin directories from your new install
|
||||
in your $PATH. Make sure you run the right version!
|
||||
|
||||
|
||||
Step 7: testing Samba4
|
||||
----------------------
|
||||
|
||||
try these commands:
|
||||
|
||||
$ smbclient //localhost/test -Uadministrator%SOMEPASSWORD
|
||||
or
|
||||
$ ./script/tests/test_posix.sh //localhost/test administrator SOMEPASSWORD
|
||||
|
||||
|
||||
NOTE about filesystem support
|
||||
-----------------------------
|
||||
|
||||
To use the advanced features of Samba4 you need a filesystem that
|
||||
supports both the "user" and "system" xattr namespaces.
|
||||
|
||||
If you run Linux with a 2.6 kernel and ext3 this means you need to
|
||||
include the option "user_xattr" in your /etc/fstab. For example:
|
||||
|
||||
/dev/hda3 /home ext3 user_xattr 1 1
|
||||
|
||||
You also need to compile your kernel with the XATTR and SECURITY
|
||||
options for your filesystem. For ext3 that means you need:
|
||||
|
||||
CONFIG_EXT3_FS_XATTR=y
|
||||
CONFIG_EXT3_FS_SECURITY=y
|
||||
|
||||
If you are running a Linux 2.6 kernel with CONFIG_IKCONFIG_PROC
|
||||
defined you can check this with the following command:
|
||||
|
||||
$ zgrep CONFIG_EXT3_FS /proc/config.gz
|
||||
|
||||
If you don't have a filesystem with xattr support, then you can
|
||||
simulate it by using the option:
|
||||
|
||||
posix:eadb = /usr/local/samba/eadb.tdb
|
||||
|
||||
that will place all extra file attributes (NT ACLs, DOS EAs, streams
|
||||
etc), in that tdb. It is not efficient, and doesn't scale well, but at
|
||||
least it gives you a choice when you don't have a modern filesystem.
|
||||
|
||||
Testing your filesystem
|
||||
-----------------------
|
||||
|
||||
To test your filesystem support, install the 'attr' package and run
|
||||
the following 4 commands as root:
|
||||
|
||||
# touch test.txt
|
||||
# setfattr -n user.test -v test test.txt
|
||||
# setfattr -n security.test -v test2 test.txt
|
||||
# getfattr -d test.txt
|
||||
# getfattr -n security.test -d test.txt
|
||||
|
||||
You should see output like this:
|
||||
|
||||
# file: test.txt
|
||||
user.test="test"
|
||||
|
||||
# file: test.txt
|
||||
security.test="test2"
|
||||
|
||||
If you get any "Operation not supported" errors then it means your
|
||||
kernel is not configured correctly, or your filesystem is not mounted
|
||||
with the right options.
|
||||
|
||||
If you get any "Operation not permitted" errors then it probably means
|
||||
you didn't try the test as root.
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
The Debian packaging for Samba 4 is maintained in the SVN
|
||||
repository of the Debian Samba packaging team.
|
||||
|
||||
(parts of this file are from their README.building)
|
||||
|
||||
To build:
|
||||
|
||||
: Check out the official Debian packaging:
|
||||
|
||||
svn co svn://svn.debian.org/pkg-samba/branches/samba4 samba4/debian
|
||||
|
||||
: Samba is not a native Debian package, so you will need to create
|
||||
: an .orig.tar.gz tarball. Do something along the lines of:
|
||||
|
||||
VER=$( dpkg-parsechangelog -lsamba4/debian/changelog | sed -n 's/^Version: \(.*:\|\)//p' | sed 's/-[0-9.]\+$//' )
|
||||
svn export samba4 samba-$VER
|
||||
( cd samba-$VER/source && ./autogen.sh )
|
||||
( cd samba-$VER/source && ./script/mkversion.sh VERSION include/version.h ../../samba4/source/ )
|
||||
tar zcf samba_$VER.orig.tar.gz samba-$VER
|
||||
|
||||
: With the .orig.tar.gz in place, you can now also export the debian/
|
||||
: directory:
|
||||
|
||||
svn export samba4/debian samba-$VER/debian
|
||||
|
||||
: Finally, build the package using whatever method you prefer:
|
||||
|
||||
cd samba-$VER
|
||||
debuild
|
||||
|
||||
@@ -0,0 +1,788 @@
|
||||
|
||||
|
||||
THIS IS INCOMPLETE! I'M ONLY COMMITING IT IN ORDER TO SOLICIT COMMENTS
|
||||
FROM A FEW PEOPLE. DON'T TAKE THIS AS THE FINAL VERSION YET.
|
||||
|
||||
|
||||
Samba4 Programming Guide
|
||||
------------------------
|
||||
|
||||
The internals of Samba4 are quite different from previous versions of
|
||||
Samba, so even if you are an experienced Samba developer please take
|
||||
the time to read through this document.
|
||||
|
||||
This document will explain both the broad structure of Samba4, and
|
||||
some of the common coding elements such as memory management and
|
||||
dealing with macros.
|
||||
|
||||
|
||||
Coding Style
|
||||
------------
|
||||
|
||||
In past versions of Samba we have basically let each programmer choose
|
||||
their own programming style. Unfortunately the result has often been
|
||||
that code that other members of the team find difficult to read. For
|
||||
Samba version 4 I would like to standardise on a common coding style
|
||||
to make the whole tree more readable. For those of you who are
|
||||
horrified at the idea of having to learn a new style, I can assure you
|
||||
that it isn't as painful as you might think. I was forced to adopt a
|
||||
new style when I started working on the Linux kernel, and after some
|
||||
initial pain found it quite easy.
|
||||
|
||||
That said, I don't want to invent a new style, instead I would like to
|
||||
adopt the style used by the Linux kernel. It is a widely used style
|
||||
with plenty of support tools available. See Documentation/CodingStyle
|
||||
in the Linux source tree. This is the style that I have used to write
|
||||
all of the core infrastructure for Samba4 and I think that we should
|
||||
continue with that style.
|
||||
|
||||
I also think that we should most definately *not* adopt an automatic
|
||||
reformatting system in cvs (or whatever other source code system we
|
||||
end up using in the future). Such automatic formatters are, in my
|
||||
experience, incredibly error prone and don't understand the necessary
|
||||
exceptions. I don't mind if people use automated tools to reformat
|
||||
their own code before they commit it, but please do not run such
|
||||
automated tools on large slabs of existing code without being willing
|
||||
to spend a *lot* of time hand checking the results.
|
||||
|
||||
Finally, I think that for code that is parsing or formatting protocol
|
||||
packets the code layout should strongly reflect the packet
|
||||
format. That means ordring the code so that it parses in the same
|
||||
order as the packet is stored on the wire (where possible) and using
|
||||
white space to align packet offsets so that a reader can immediately
|
||||
map any line of the code to the corresponding place in the packet.
|
||||
|
||||
|
||||
Static and Global Data
|
||||
----------------------
|
||||
|
||||
The basic rule is "avoid static and global data like the plague". What
|
||||
do I mean by static data? The way to tell if you have static data in a
|
||||
file is to use the "size" utility in Linux. For example if we run:
|
||||
|
||||
size libcli/raw/*.o
|
||||
|
||||
in Samba4 then you get the following:
|
||||
|
||||
text data bss dec hex filename
|
||||
2015 0 0 2015 7df libcli/raw/clikrb5.o
|
||||
202 0 0 202 ca libcli/raw/clioplock.o
|
||||
35 0 0 35 23 libcli/raw/clirewrite.o
|
||||
3891 0 0 3891 f33 libcli/raw/clisession.o
|
||||
869 0 0 869 365 libcli/raw/clisocket.o
|
||||
4962 0 0 4962 1362 libcli/raw/clispnego.o
|
||||
1223 0 0 1223 4c7 libcli/raw/clitransport.o
|
||||
2294 0 0 2294 8f6 libcli/raw/clitree.o
|
||||
1081 0 0 1081 439 libcli/raw/raweas.o
|
||||
6765 0 0 6765 1a6d libcli/raw/rawfile.o
|
||||
6824 0 0 6824 1aa8 libcli/raw/rawfileinfo.o
|
||||
2944 0 0 2944 b80 libcli/raw/rawfsinfo.o
|
||||
541 0 0 541 21d libcli/raw/rawioctl.o
|
||||
1728 0 0 1728 6c0 libcli/raw/rawnegotiate.o
|
||||
723 0 0 723 2d3 libcli/raw/rawnotify.o
|
||||
3779 0 0 3779 ec3 libcli/raw/rawreadwrite.o
|
||||
6597 0 0 6597 19c5 libcli/raw/rawrequest.o
|
||||
5580 0 0 5580 15cc libcli/raw/rawsearch.o
|
||||
3034 0 0 3034 bda libcli/raw/rawsetfileinfo.o
|
||||
5187 0 0 5187 1443 libcli/raw/rawtrans.o
|
||||
2033 0 0 2033 7f1 libcli/raw/smb_signing.o
|
||||
|
||||
notice that the "data" and "bss" columns are all zero? That is
|
||||
good. If there are any non-zero values in data or bss then that
|
||||
indicates static data and is bad (as a rule of thumb).
|
||||
|
||||
Lets compare that result to the equivalent in Samba3:
|
||||
|
||||
text data bss dec hex filename
|
||||
3978 0 0 3978 f8a libsmb/asn1.o
|
||||
18963 0 288 19251 4b33 libsmb/cliconnect.o
|
||||
2815 0 1024 3839 eff libsmb/clidgram.o
|
||||
4038 0 0 4038 fc6 libsmb/clientgen.o
|
||||
3337 664 256 4257 10a1 libsmb/clierror.o
|
||||
10043 0 0 10043 273b libsmb/clifile.o
|
||||
332 0 0 332 14c libsmb/clifsinfo.o
|
||||
166 0 0 166 a6 libsmb/clikrb5.o
|
||||
5212 0 0 5212 145c libsmb/clilist.o
|
||||
1367 0 0 1367 557 libsmb/climessage.o
|
||||
259 0 0 259 103 libsmb/clioplock.o
|
||||
1584 0 0 1584 630 libsmb/cliprint.o
|
||||
7565 0 256 7821 1e8d libsmb/cliquota.o
|
||||
7694 0 0 7694 1e0e libsmb/clirap.o
|
||||
27440 0 0 27440 6b30 libsmb/clirap2.o
|
||||
2905 0 0 2905 b59 libsmb/clireadwrite.o
|
||||
1698 0 0 1698 6a2 libsmb/clisecdesc.o
|
||||
5517 0 0 5517 158d libsmb/clispnego.o
|
||||
485 0 0 485 1e5 libsmb/clistr.o
|
||||
8449 0 0 8449 2101 libsmb/clitrans.o
|
||||
2053 0 4 2057 809 libsmb/conncache.o
|
||||
3041 0 256 3297 ce1 libsmb/credentials.o
|
||||
1261 0 1024 2285 8ed libsmb/doserr.o
|
||||
14560 0 0 14560 38e0 libsmb/errormap.o
|
||||
3645 0 0 3645 e3d libsmb/namecache.o
|
||||
16815 0 8 16823 41b7 libsmb/namequery.o
|
||||
1626 0 0 1626 65a libsmb/namequery_dc.o
|
||||
14301 0 1076 15377 3c11 libsmb/nmblib.o
|
||||
24516 0 2048 26564 67c4 libsmb/nterr.o
|
||||
8661 0 8 8669 21dd libsmb/ntlmssp.o
|
||||
3188 0 0 3188 c74 libsmb/ntlmssp_parse.o
|
||||
4945 0 0 4945 1351 libsmb/ntlmssp_sign.o
|
||||
1303 0 0 1303 517 libsmb/passchange.o
|
||||
1221 0 0 1221 4c5 libsmb/pwd_cache.o
|
||||
2475 0 4 2479 9af libsmb/samlogon_cache.o
|
||||
10768 32 0 10800 2a30 libsmb/smb_signing.o
|
||||
4524 0 16 4540 11bc libsmb/smbdes.o
|
||||
5708 0 0 5708 164c libsmb/smbencrypt.o
|
||||
7049 0 3072 10121 2789 libsmb/smberr.o
|
||||
2995 0 0 2995 bb3 libsmb/spnego.o
|
||||
3186 0 0 3186 c72 libsmb/trustdom_cache.o
|
||||
1742 0 0 1742 6ce libsmb/trusts_util.o
|
||||
918 0 28 946 3b2 libsmb/unexpected.o
|
||||
|
||||
notice all of the non-zero data and bss elements? Every bit of that
|
||||
data is a bug waiting to happen.
|
||||
|
||||
Static data is evil as it has the following consequences:
|
||||
- it makes code much less likely to be thread-safe
|
||||
- it makes code much less likely to be recursion-safe
|
||||
- it leads to subtle side effects when the same code is called from
|
||||
multiple places
|
||||
|
||||
Static data is particularly evil in library code (such as our internal
|
||||
smb and rpc libraries). If you can get rid of all static data in
|
||||
libraries then you can make some fairly strong guarantees about the
|
||||
behaviour of functions in that library, which really helps.
|
||||
|
||||
Of course, it is possible to write code that uses static data and is
|
||||
safe, it's just much harder to do that than just avoid static data in
|
||||
the first place. We have been tripped up countless times by subtle
|
||||
bugs in Samba due to the use of static data, so I think it is time to
|
||||
start avoiding it in new code. Much of the core infrastructure of
|
||||
Samba4 was specifically written to avoid static data, so I'm going to
|
||||
be really annoyed if everyone starts adding lots of static data back
|
||||
in.
|
||||
|
||||
So, how do we avoid static data? The basic method is to use context
|
||||
pointers. When reading the Samba4 code you will notice that just about
|
||||
every function takes a pointer to a context structure as its first
|
||||
argument. Any data that the function needs that isn't an explicit
|
||||
argument to the function can be found by traversing that context.
|
||||
|
||||
Note that this includes all of the little caches that we have lying
|
||||
all over the code in Samba3. I'm referring to the ones that generally
|
||||
have a "static int initialised" and then some static string or integer
|
||||
that remembers the last return value of the function. Get rid of them!
|
||||
If you are *REALLY* absolutely completely certain that your personal
|
||||
favourite mini-cache is needed then you should do it properly by
|
||||
putting it into the appropriate context rather than doing it the lazy
|
||||
way by putting it inside the target function. I would suggest however
|
||||
that the vast majority of those little caches are useless - don't
|
||||
stick it in unless you have really firm benchmarking results that show
|
||||
that it is needed and helps by a significant amount.
|
||||
|
||||
Note that Samba4 is not yet completely clean of static data like
|
||||
this. I've gotten the smbd/ directory down to 24 bytes of static data,
|
||||
and libcli/raw/ down to zero. I've also gotten the ntvfs layer and all
|
||||
backends down to just 8 bytes in ntvfs_base.c. The rest still needs
|
||||
some more work.
|
||||
|
||||
Also note that truly constant data is OK, and will not in fact show up
|
||||
in the data and bss columns in "size" anyway (it will be included in
|
||||
"text"). So you can have constant tables of protocol data.
|
||||
|
||||
|
||||
How to use talloc
|
||||
-----------------
|
||||
|
||||
Please see the separate document, source/lib/talloc/talloc_guide.txt
|
||||
You _must_ read this if you want to program in Samba4.
|
||||
|
||||
|
||||
Interface Structures
|
||||
--------------------
|
||||
|
||||
One of the biggest changes in Samba4 is the universal use of interface
|
||||
structures. Go take a look through include/smb_interfaces.h now to get
|
||||
an idea of what I am talking about.
|
||||
|
||||
In Samba3 many of the core wire structures in the SMB protocol were
|
||||
never explicitly defined in Samba. Instead, our parse and generation
|
||||
functions just worked directly with wire buffers. The biggest problem
|
||||
with this is that is tied our parse code with our "business logic"
|
||||
much too closely, which meant the code got extremely confusing to
|
||||
read.
|
||||
|
||||
In Samba4 we have explicitly defined interface structures for
|
||||
everything in the protocol. When we receive a buffer we always parse
|
||||
it completely into one of these structures, then we pass a pointer to
|
||||
that structure to a backend handler. What we must *not* do is make any
|
||||
decisions about the data inside the parse functions. That is critical
|
||||
as different backends will need different portions of the data. This
|
||||
leads to a golden rule for Samba4:
|
||||
|
||||
"don't design interfaces that lose information"
|
||||
|
||||
In Samba3 our backends often received "condensed" versions of the
|
||||
information sent from clients, but this inevitably meant that some
|
||||
backends could not get at the data they needed to do what they wanted,
|
||||
so from now on we should expose the backends to all of the available
|
||||
information and let them choose which bits they want.
|
||||
|
||||
Ok, so now some of you will be thinking "this sounds just like our
|
||||
msrpc code from Samba3", and while to some extent this is true there
|
||||
are extremely important differences in the approach that are worth
|
||||
pointing out.
|
||||
|
||||
In the Samba3 msrpc code we used explicit parse structures for all
|
||||
msrpc functions. The problem is that we didn't just put all of the
|
||||
real variables in these structures, we also put in all the artifacts
|
||||
as well. A good example is the security descriptor strucrure that
|
||||
looks like this in Samba3:
|
||||
|
||||
typedef struct security_descriptor_info
|
||||
{
|
||||
uint16 revision;
|
||||
uint16 type;
|
||||
|
||||
uint32 off_owner_sid;
|
||||
uint32 off_grp_sid;
|
||||
uint32 off_sacl;
|
||||
uint32 off_dacl;
|
||||
|
||||
SEC_ACL *dacl;
|
||||
SEC_ACL *sacl;
|
||||
DOM_SID *owner_sid;
|
||||
DOM_SID *grp_sid;
|
||||
} SEC_DESC;
|
||||
|
||||
The problem with this structure is all the off_* variables. Those are
|
||||
not part of the interface, and do not appear in any real descriptions
|
||||
of Microsoft security descriptors. They are parsing artifacts
|
||||
generated by the IDL compiler that Microsoft use. That doesn't mean
|
||||
they aren't needed on the wire - indeed they are as they tell the
|
||||
parser where to find the following four variables, but they should
|
||||
*NOT* be in the interface structure.
|
||||
|
||||
In Samba3 there were unwritten rules about which variables in a
|
||||
structure a high level caller has to fill in and which ones are filled
|
||||
in by the marshalling code. In Samba4 those rules are gone, because
|
||||
the redundent artifact variables are gone. The high level caller just
|
||||
sets up the real variables and the marshalling code worries about
|
||||
generating the right offsets.
|
||||
|
||||
The same rule applies to strings. In many places in the SMB and MSRPC
|
||||
protocols complex strings are used on the wire, with complex rules
|
||||
about padding, format, alighment, termination etc. None of that
|
||||
information is useful to a high level calling routine or to a backend
|
||||
- its all just so much wire fluff. So, in Samba4 these strings are
|
||||
just "char *" and are always in our internal multi-byte format (which
|
||||
is usually UTF8). It is up to the parse functions to worry about
|
||||
translating the format and getting the padding right.
|
||||
|
||||
The one exception to this is the use of the WIRE_STRING type, but that
|
||||
has a very good justification in terms of regression testing. Go and
|
||||
read the comment in smb_interfaces.h about that now.
|
||||
|
||||
So, here is another rule to code by. When writing an interface
|
||||
structure think carefully about what variables in the structure can be
|
||||
left out as they are redundent. If some length is effectively defined
|
||||
twice on the wire then only put it once in the packet. If a length can
|
||||
be inferred from a null termination then do that and leave the length
|
||||
out of the structure completely. Don't put redundent stuff in
|
||||
structures!
|
||||
|
||||
|
||||
Async Design
|
||||
------------
|
||||
|
||||
Samba4 has an asynchronous design. That affects *lots* of the code,
|
||||
and the implications of the asynchronous design needs to be considered
|
||||
just about everywhere.
|
||||
|
||||
The first aspect of the async design to look at is the SMB client
|
||||
library. Lets take a look at the following three functions in
|
||||
libcli/raw/rawfile.c:
|
||||
|
||||
struct cli_request *smb_raw_seek_send(struct cli_tree *tree, struct smb_seek *parms);
|
||||
NTSTATUS smb_raw_seek_recv(struct cli_request *req, struct smb_seek *parms);
|
||||
NTSTATUS smb_raw_seek(struct cli_tree *tree, struct smb_seek *parms);
|
||||
|
||||
Go and read them now then come back.
|
||||
|
||||
Ok, first notice there there are 3 separate functions, whereas the
|
||||
equivalent code in Samba3 had just one. Also note that the 3rd
|
||||
function is extremely simple - its just a wrapper around calling the
|
||||
first two in order.
|
||||
|
||||
The three separate functions are needed because we need to be able to
|
||||
generate SMB calls asynchronously. The first call, which for smb calls
|
||||
is always called smb_raw_XXXX_send(), constructs and sends a SMB
|
||||
request and returns a "struct cli_request" which acts as a handle for
|
||||
the request. The caller is then free to do lots of other calls if it
|
||||
wants to, then when it is ready it can call the smb_raw_XXX_recv()
|
||||
function to receive the reply.
|
||||
|
||||
If all you want is a synchronous call then call the 3rd interface, the
|
||||
one called smb_raw_XXXX(). That just calls the first two in order, and
|
||||
blocks waiting for the reply.
|
||||
|
||||
But what if you want to be called when the reply comes in? Yes, thats
|
||||
possible. You can do things like this:
|
||||
|
||||
struct cli_request *req;
|
||||
|
||||
req = smb_raw_XXX_send(tree, params);
|
||||
|
||||
req->async.fn = my_callback;
|
||||
req->async.private = my_private_data;
|
||||
|
||||
then in your callback function you can call the smb_raw_XXXX_recv()
|
||||
function to receive the reply. Your callback will receive the "req"
|
||||
pointer, which you can use to retrieve your private data from
|
||||
req->async.private.
|
||||
|
||||
Then all you need to do is ensure that the main loop in the client
|
||||
library gets called. You can either do that by polling the connection
|
||||
using cli_transport_pending() and cli_request_receive_next() or you
|
||||
can use transport->idle.func to setup an idle function handler to call
|
||||
back to your main code. Either way, you can build a fully async
|
||||
application.
|
||||
|
||||
In order to support all of this we have to make sure that when we
|
||||
write a piece of library code (SMB, MSRPC etc) that we build the
|
||||
separate _send() and _recv() functions. It really is worth the effort.
|
||||
|
||||
Now about async in smbd, a much more complex topic.
|
||||
|
||||
The SMB protocol is inherently async. Some functions (such as change
|
||||
notify) often don't return for hours, while hundreds of other
|
||||
functions pass through the socket. Take a look at the RAW-MUX test in
|
||||
the Samba4 smbtorture to see some really extreme examples of the sort
|
||||
of async operations that Windows supports. I particularly like the
|
||||
open/open/close sequence where the 2nd open (which conflicts with the
|
||||
first) succeeds because the subsequent close is answered out of order.
|
||||
|
||||
In Samba3 we handled this stuff very badly. We had awful "pending
|
||||
request" queues that allocated full 128k packet buffers, and even with
|
||||
all that crap we got the semantics wrong. In Samba4 I intend to make
|
||||
sure we get this stuff right.
|
||||
|
||||
So, how do we do this? We now have an async interface between smbd and
|
||||
the NTVFS backends. Whenever smbd calls into a backend the backend has
|
||||
an option of answer the request in a synchronous fashion if it wants
|
||||
to just like in Samba3, but it also has the option of answering the
|
||||
request asynchronously. The only backend that currently does this is
|
||||
the CIFS backend, but I hope the other backends will soon do this to.
|
||||
|
||||
To make this work you need to do things like this in the backend:
|
||||
|
||||
req->control_flags |= REQ_CONTROL_ASYNC;
|
||||
|
||||
that tells smbd that the backend has elected to reply later rather
|
||||
than replying immediately. The backend must *only* do this if
|
||||
req->async.send_fn is not NULL. If send_fn is NULL then it means that
|
||||
the smbd front end cannot handle this function being replied to in an
|
||||
async fashion.
|
||||
|
||||
If the backend does this then it is up to the backend to call
|
||||
req->async.send_fn() when it is ready to reply. It the meantime smbd
|
||||
puts the call on hold and goes back to answering other requests on the
|
||||
socket.
|
||||
|
||||
Inside smbd you will find that there is code to support this. The most
|
||||
obvious change is that smbd splits each SMB reply function into two
|
||||
parts - just like the client library has a _send() and _recv()
|
||||
function, so smbd has a _send() function and the parse function for
|
||||
each SMB.
|
||||
|
||||
As an example go and have a look at reply_getatr_send() and
|
||||
reply_getatr() in smb_server/reply.c. Read them? Good.
|
||||
|
||||
Notice that reply_getatr() sets up the req->async structure to contain
|
||||
the send function. Thats how the backend gets to do an async reply, it
|
||||
calls this function when it is ready. Also notice that reply_getatr()
|
||||
only does the parsing of the request, and does not do the reply
|
||||
generation. That is done by the _send() function.
|
||||
|
||||
The only missing piece in the Samba4 right now that prevents it being
|
||||
fully async is that it currently does the low level socket calls (read
|
||||
and write on sockets) in a blocking fashion. It does use select() to
|
||||
make it somewhat async, but if a client were to send a partial packet
|
||||
then delay before sending the rest then smbd would be stuck waiting
|
||||
for the second half of the packet.
|
||||
|
||||
To fix this I plan on making the socket calls async as well, which
|
||||
luckily will not involve any API changes in the core of smbd or the
|
||||
library. It just involves a little bit of extra code in clitransport.c
|
||||
and smbd/request.c. As a side effect I hope that this will also reduce
|
||||
the average number of system calls required to answer a request, so we
|
||||
may see a performance improvement.
|
||||
|
||||
|
||||
NTVFS
|
||||
-----
|
||||
|
||||
One of the most noticeable changes in Samba4 is the introduction of
|
||||
the NTVFS layer. This provided the initial motivation for the design
|
||||
of Samba4 and in many ways lies at the heart of the design.
|
||||
|
||||
In Samba3 the main file serving process (smbd) combined the handling
|
||||
of the SMB protocol with the mapping to POSIX semantics in the same
|
||||
code. If you look in smbd/reply.c in Samba3 you see numerous places
|
||||
where POSIX assumptions are mixed tightly with SMB parsing code. We
|
||||
did have a VFS layer in Samba3, but it was a POSIX-like VFS layer, so
|
||||
no matter how you wrote a plugin you could not bypass the POSIX
|
||||
mapping decisions that had already been made before the VFS layer was
|
||||
called.
|
||||
|
||||
In Samba4 things are quite different. All SMB parsing is performed in
|
||||
the smbd front end, then fully parsed requests are passed to the NTVFS
|
||||
backend. That backend makes any semantic mapping decisions and fills
|
||||
in the 'out' portion of the request. The front end is then responsible
|
||||
for putting those results into wire format and sending them to the
|
||||
client.
|
||||
|
||||
Lets have a look at one of those request structures. Go and read the
|
||||
definition of "union smb_write" and "enum write_level" in
|
||||
include/smb_interfaces.h. (no, don't just skip reading it, really go
|
||||
and read it. Yes, that means you!).
|
||||
|
||||
Notice the union? That's how Samba4 allows a single NTVFS backend
|
||||
interface to handle the several different ways of doing a write
|
||||
operation in the SMB protocol. Now lets look at one section of that
|
||||
union:
|
||||
|
||||
/* SMBwriteX interface */
|
||||
struct {
|
||||
enum write_level level;
|
||||
|
||||
struct {
|
||||
uint16 fnum;
|
||||
SMB_BIG_UINT offset;
|
||||
uint16 wmode;
|
||||
uint16 remaining;
|
||||
uint32 count;
|
||||
const char *data;
|
||||
} in;
|
||||
struct {
|
||||
uint32 nwritten;
|
||||
uint16 remaining;
|
||||
} out;
|
||||
} writex;
|
||||
|
||||
see the "in" and "out" sections? The "in" section is for parameters
|
||||
that the SMB client sends on the wire as part of the request. The smbd
|
||||
front end parse code parses the wire request and fills in all those
|
||||
parameters. It then calls the NTVFS interface which looks like this:
|
||||
|
||||
NTSTATUS (*write)(struct request_context *req, union smb_write *io);
|
||||
|
||||
and the NTVFS backend does the write request. The backend then fills
|
||||
in the "out" section of the writex structure and gives the union back
|
||||
to the front end (either by returning, or if done in an async fashion
|
||||
then by calling the async send function. See the async discussion
|
||||
elsewhere in this document).
|
||||
|
||||
The NTVFS backend knows which particular function is being requested
|
||||
by looking at io->generic.level. Notice that this enum is also
|
||||
repeated inside each of the sub-structures in the union, so the
|
||||
backend could just as easily look at io->writex.level and would get
|
||||
the same variable.
|
||||
|
||||
Notice also that some levels (such as splwrite) don't have an "out"
|
||||
section. This happens because there is no return value apart from a
|
||||
status code from those SMB calls.
|
||||
|
||||
So what about status codes? The status code is returned directly by
|
||||
the backend NTVFS interface when the call is performed
|
||||
synchronously. When performed asynchronously then the status code is
|
||||
put into req->async.status before the req->async.send_fn() callback is
|
||||
called.
|
||||
|
||||
Currently the most complete NTVFS backend is the CIFS backend. I don't
|
||||
expect this backend will be used much in production, but it does
|
||||
provide the ideal test case for our NTVFS design. As it offers the
|
||||
full capabilities that are possible with a CIFS server we can be sure
|
||||
that we don't have any gaping holes in our APIs, and that the front
|
||||
end code is flexible enough to handle any advances in the NT style
|
||||
feature sets of Unix filesystems that make come along.
|
||||
|
||||
|
||||
Process Models
|
||||
--------------
|
||||
|
||||
In Samba3 we supported just one process model. It just so happens that
|
||||
the process model that Samba3 supported is the "right" one for most
|
||||
users, but there are situations where this model wasn't ideal.
|
||||
|
||||
In Samba4 you can choose the smbd process model on the smbd command
|
||||
line.
|
||||
|
||||
|
||||
DCERPC binding strings
|
||||
----------------------
|
||||
|
||||
When connecting to a dcerpc service you need to specify a binding
|
||||
string.
|
||||
|
||||
The format is:
|
||||
|
||||
TRANSPORT:host[flags]
|
||||
|
||||
where TRANSPORT is either ncacn_np for SMB or ncacn_ip_tcp for RPC/TCP
|
||||
|
||||
"host" is an IP or hostname or netbios name. If the binding string
|
||||
identifies the server side of an endpoint, "host" may be an empty
|
||||
string.
|
||||
|
||||
"flags" can include a SMB pipe name if using the ncacn_np transport or
|
||||
a TCP port number if using the ncacn_ip_tcp transport, otherwise they
|
||||
will be auto-determined.
|
||||
|
||||
other recognised flags are:
|
||||
|
||||
sign : enable ntlmssp signing
|
||||
seal : enable ntlmssp sealing
|
||||
spnego : use SPNEGO instead of NTLMSSP authentication
|
||||
krb5 : use KRB5 instead of NTLMSSP authentication
|
||||
connect : enable rpc connect level auth (auth, but no sign or seal)
|
||||
validate : enable the NDR validator
|
||||
print : enable debugging of the packets
|
||||
bigendian : use bigendian RPC
|
||||
padcheck : check reply data for non-zero pad bytes
|
||||
|
||||
|
||||
Here are some examples:
|
||||
|
||||
ncacn_np:myserver
|
||||
ncacn_np:myserver[samr]
|
||||
ncacn_np:myserver[\pipe\samr]
|
||||
ncacn_np:myserver[/pipe/samr]
|
||||
ncacn_np:myserver[samr,sign,print]
|
||||
ncacn_np:myserver[sign,spnego]
|
||||
ncacn_np:myserver[\pipe\samr,sign,seal,bigendian]
|
||||
ncacn_np:myserver[/pipe/samr,seal,validate]
|
||||
ncacn_np:
|
||||
ncacn_np:[/pipe/samr]
|
||||
ncacn_ip_tcp:myserver
|
||||
ncacn_ip_tcp:myserver[1024]
|
||||
ncacn_ip_tcp:myserver[sign,seal]
|
||||
ncacn_ip_tcp:myserver[spnego,seal]
|
||||
|
||||
|
||||
IDEA: Maybe extend UNC names like this?
|
||||
|
||||
smbclient //server/share
|
||||
smbclient //server/share[sign,seal,spnego]
|
||||
|
||||
DCERPC Handles
|
||||
--------------
|
||||
The various handles that are used in the RPC servers should be created and
|
||||
fetch using the dcesrv_handle_* functions.
|
||||
|
||||
Use dcesrv_handle_new(struct dcesrv_connection *, uint8 handle_type) to obtain
|
||||
a new handle of the specified type. Handle types are unique within each
|
||||
pipe.
|
||||
|
||||
The handle can later be fetched again using
|
||||
struct dcesrv_handle *dcesrv_handle_fetch(struct dcesrv_connection *dce_conn, struct policy_handle *p, uint8 handle_type)
|
||||
and destroyed by dcesrv_handle_destroy(struct dcesrv_handle *).
|
||||
|
||||
User data should be stored in the 'data' member of the dcesrv_handle struct.
|
||||
|
||||
|
||||
MSRPC
|
||||
-----
|
||||
|
||||
|
||||
|
||||
- ntvfs
|
||||
- testing
|
||||
- command line handling
|
||||
- libcli structure
|
||||
- posix reliance
|
||||
- uid/gid handling
|
||||
- process models
|
||||
- static data
|
||||
- msrpc
|
||||
|
||||
|
||||
don't zero structures! avoid ZERO_STRUCT() and talloc_zero()
|
||||
|
||||
|
||||
GMT vs TZ in printout of QFILEINFO timezones
|
||||
|
||||
put in full UNC path in tconx
|
||||
|
||||
test timezone handling by using a server in different zone from client
|
||||
|
||||
do {} while (0) system
|
||||
|
||||
NT_STATUS_IS_OK() is NOT the opposite of NT_STATUS_IS_ERR()
|
||||
|
||||
need to implement secondary parts of trans2 and nttrans in server and
|
||||
client
|
||||
|
||||
document access_mask in openx reply
|
||||
|
||||
check all capabilities and flag1, flag2 fields (eg. EAs)
|
||||
|
||||
large files -> pass thru levels
|
||||
|
||||
setpathinfo is very fussy about null termination of the file name
|
||||
|
||||
the overwrite flag doesn't seem to work on setpathinfo RENAME_INFORMATION
|
||||
|
||||
END_OF_FILE_INFORMATION and ALLOCATION_INFORMATION don't seem to work
|
||||
via setpathinfo
|
||||
|
||||
on w2k3 setpathinfo DISPOSITION_INFORMATION fails, but does have an
|
||||
effect. It leaves the file with SHARING_VIOLATION.
|
||||
|
||||
on w2k3 trans2 setpathinfo with any invalid low numbered level causes
|
||||
the file to get into a state where DELETE_PENDING is reported, and the
|
||||
file cannot be deleted until you reboot
|
||||
|
||||
trans2 qpathinfo doesn't see the delete_pending flag correctly, but
|
||||
qfileinfo does!
|
||||
|
||||
get rid of pstring, fstring, strtok
|
||||
|
||||
add programming documentation note about lp_set_cmdline()
|
||||
|
||||
need to add a wct checking function in all client parsing code,
|
||||
similar to REQ_CHECK_WCT()
|
||||
|
||||
need to make sure that NTTIME is a round number of seconds when
|
||||
converted from time_t
|
||||
|
||||
not using a zero next offset in SMB_FILE_STREAM_INFORMATION for last
|
||||
entry causes explorer exception under win2000
|
||||
|
||||
|
||||
if the server sets the session key the same for a second SMB socket as
|
||||
an initial socket then the client will not re-authenticate, it will go
|
||||
straight to a tconx, skipping session setup and will use all the
|
||||
existing parameters! This allows two sockets with the same keys!?
|
||||
|
||||
|
||||
removed blocking lock code, we now queue the whole request the same as
|
||||
we queue any other pending request. This allows for things like a
|
||||
close() while a pending blocking lock is being processed to operate
|
||||
sanely.
|
||||
|
||||
disabled change notify code
|
||||
|
||||
disabled oplock code
|
||||
|
||||
|
||||
|
||||
MILESTONES
|
||||
==========
|
||||
|
||||
|
||||
client library and test code
|
||||
----------------------------
|
||||
|
||||
convert client library to new structure
|
||||
get smbtorture working
|
||||
get smbclient working
|
||||
expand client library for all requests
|
||||
write per-request test suite
|
||||
gentest randomised test suite
|
||||
separate client code as a library for non-Samba use
|
||||
|
||||
server code
|
||||
-----------
|
||||
add remaining core SMB requests
|
||||
add IPC layer
|
||||
add nttrans layer
|
||||
add rpc layer
|
||||
fix auth models (share, server, rpc)
|
||||
get net command working
|
||||
connect CIFS backend to server level auth
|
||||
get nmbd working
|
||||
get winbindd working
|
||||
reconnect printing code
|
||||
restore removed smbd options
|
||||
add smb.conf macro substitution code
|
||||
add async backend notification
|
||||
add generic timer event mechanism
|
||||
|
||||
clustering code
|
||||
---------------
|
||||
|
||||
write CIFS backend
|
||||
new server models (break 1-1)
|
||||
test clustered models
|
||||
add fulcrum statistics gathering
|
||||
|
||||
docs
|
||||
----
|
||||
|
||||
conference paper
|
||||
developer docs
|
||||
|
||||
svn instructions
|
||||
|
||||
Ideas
|
||||
-----
|
||||
|
||||
- store all config in config.ldb
|
||||
|
||||
- load from smb.conf if modtime changes
|
||||
|
||||
- dump full system config with ldbsearch
|
||||
|
||||
- will need the ability to form a ldif difference file
|
||||
|
||||
- advanced web admin via a web ldb editor
|
||||
|
||||
- normal web admin via web forms -> ldif
|
||||
|
||||
- config.ldb will replace smb.conf, secrets.tdb, shares.tdb etc
|
||||
|
||||
- subsystems in smbd will load config parameters for a share
|
||||
using ldbsearch at tconx time
|
||||
|
||||
- need a loadparm equivalent module that provides parameter defaults
|
||||
|
||||
- start smbd like this: "smbd -C tdb://etc/samba/config.ldb" or
|
||||
"smbd -C ldapi://var/run/ldapi"
|
||||
|
||||
- write a tool that generates a template ldap schema from an existing
|
||||
ldb+tdb file
|
||||
|
||||
- no need to HUP smbd to reload config
|
||||
|
||||
- how to handle configuration comments? same problem as SWAT
|
||||
|
||||
|
||||
BUGS:
|
||||
add a test case for last_entry_offset in trans2 find interfaces
|
||||
conn refused
|
||||
connect -> errno
|
||||
no 137 resolution not possible
|
||||
should not fallback to anon when pass supplied
|
||||
should check pass-thu cap bit, and skip lots of tests
|
||||
possibly allow the test suite to say "allow oversized replies" for
|
||||
trans2 and other calls
|
||||
handle servers that don't have the setattre call in torture
|
||||
add max file coponent length test and max path len test
|
||||
check for alloc failure in all core reply.c and trans2.c code where
|
||||
allocation size depends on client parameter
|
||||
|
||||
case-insenstive idea:
|
||||
all filenames on disk lowercase
|
||||
real case in extended attribute
|
||||
keep cache of what dirs are all lowercase
|
||||
when searching for name, don't search if dir is definately all lowercase
|
||||
when creating file, use dnotify to tell if someone else creates at
|
||||
same time
|
||||
|
||||
solve del *.* idea:
|
||||
make mangle cache dynamic size
|
||||
fill during a dir scan
|
||||
setup a timer
|
||||
destroy cache after 30 sec
|
||||
destroy if a 2nd dir scan happens on same dir
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
<%
|
||||
|
||||
/*
|
||||
* Copyright:
|
||||
* (C) 2006 by Derrell Lipman
|
||||
* All rights reserved
|
||||
*
|
||||
* License:
|
||||
* LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module provides a JSON encoder.
|
||||
*/
|
||||
|
||||
|
||||
/* escape a string as required by json */
|
||||
function _escape(s)
|
||||
{
|
||||
var i;
|
||||
var arr = new Array();
|
||||
|
||||
for (i = 0; i < strlen(s); i++)
|
||||
{
|
||||
var c = substr(s, i, 1);
|
||||
if (c == '\x00')
|
||||
{
|
||||
arr[i] = '\\u0000';
|
||||
}
|
||||
if (Json._internal.convert[c] != undefined)
|
||||
{
|
||||
arr[i] = Json._internal.convert[c];
|
||||
}
|
||||
else
|
||||
{
|
||||
arr[i] = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (arr.length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return join("", arr);
|
||||
}
|
||||
|
||||
/* encode an arbitrary object. called recursively, for object and array */
|
||||
function _encode(o)
|
||||
{
|
||||
var type = nativeTypeOf(o);
|
||||
|
||||
if (type == "undefined")
|
||||
{
|
||||
return "null"; /* you really shouldn't count on this! */
|
||||
}
|
||||
else if (type == "null")
|
||||
{
|
||||
return "null";
|
||||
}
|
||||
else if (type == "boolean")
|
||||
{
|
||||
if (o)
|
||||
{
|
||||
return "true";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "false";
|
||||
}
|
||||
}
|
||||
else if (type == "c_function" ||
|
||||
type == "js_function" ||
|
||||
type == "string_c_function")
|
||||
{
|
||||
/* no output */
|
||||
}
|
||||
else if (type == "float" ||
|
||||
type == "integer" ||
|
||||
type == "integer64")
|
||||
{
|
||||
return o + 0;
|
||||
}
|
||||
else if (type == "pointer")
|
||||
{
|
||||
var x = "" + o;
|
||||
return '"' + substr(x, 16, strlen(x) - 16 - 1) + '"';
|
||||
}
|
||||
else if (type == "object")
|
||||
{
|
||||
var buf;
|
||||
|
||||
/* Is this an array or an ordinary object? */
|
||||
if (o["length"] != undefined)
|
||||
{
|
||||
var i;
|
||||
|
||||
/* Assume it's an array if there's a length field */
|
||||
buf = "[";
|
||||
for (i = 0; i < o.length; i++)
|
||||
{
|
||||
/*
|
||||
* NOTE: We don't support sparse arrays nor associative
|
||||
* arrays. Should we later want to do either, we're supposed
|
||||
* to send it as an object rather than as an array.
|
||||
*/
|
||||
if (i > 0)
|
||||
{
|
||||
buf = buf + ",";
|
||||
}
|
||||
buf = buf + this.encode(o[i]);
|
||||
}
|
||||
buf = buf + "]";
|
||||
}
|
||||
else if (o["__type"] == "_JSON_Date")
|
||||
{
|
||||
buf = "" + o.encoding();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No length field, so it must be an ordinary object */
|
||||
var key;
|
||||
var first = true;
|
||||
|
||||
buf = "{";
|
||||
for (key in o)
|
||||
{
|
||||
if (! first)
|
||||
{
|
||||
buf = buf + ",";
|
||||
}
|
||||
buf = buf + '"' + key + '":' + this.encode(o[key]);
|
||||
first = false;
|
||||
}
|
||||
buf = buf + "}";
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
else if (type == "string")
|
||||
{
|
||||
return '"' + this._internal.escape(o) + '"';
|
||||
}
|
||||
else
|
||||
{
|
||||
return '{ "unknown_object":"' + type + '"}';
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate the public Json access object */
|
||||
Json = new Object();
|
||||
|
||||
/* Json.encode(): encode an arbitrary object */
|
||||
Json.encode = _encode;
|
||||
_encode = null;
|
||||
|
||||
/* Json.decode(): decode a string into its object form */
|
||||
Json.decode = literal_to_var;
|
||||
|
||||
/* Internal stuff, not for external access */
|
||||
Json._internal = new Object();
|
||||
|
||||
Json._internal.escape = _escape;
|
||||
_escape = null;
|
||||
|
||||
Json._internal.convert = new Object();
|
||||
Json._internal.convert['\b'] = '\\b';
|
||||
Json._internal.convert['\t'] = '\\t';
|
||||
Json._internal.convert['\n'] = '\\n';
|
||||
Json._internal.convert['\f'] = '\\f';
|
||||
Json._internal.convert['\r'] = '\\r';
|
||||
Json._internal.convert['"'] = '\\"';
|
||||
Json._internal.convert['\\'] = '\\\\';
|
||||
Json._internal.convert['\x01'] = '\\u0001';
|
||||
Json._internal.convert['\x02'] = '\\u0002';
|
||||
Json._internal.convert['\x03'] = '\\u0003';
|
||||
Json._internal.convert['\x04'] = '\\u0004';
|
||||
Json._internal.convert['\x05'] = '\\u0005';
|
||||
Json._internal.convert['\x06'] = '\\u0006';
|
||||
Json._internal.convert['\x07'] = '\\u0007';
|
||||
Json._internal.convert['\x08'] = '\\u0008';
|
||||
Json._internal.convert['\x09'] = '\\u0009';
|
||||
Json._internal.convert['\x0a'] = '\\u000a';
|
||||
Json._internal.convert['\x0b'] = '\\u000b';
|
||||
Json._internal.convert['\x0c'] = '\\u000c';
|
||||
Json._internal.convert['\x0d'] = '\\u000d';
|
||||
Json._internal.convert['\x0e'] = '\\u000e';
|
||||
Json._internal.convert['\x0f'] = '\\u000f';
|
||||
Json._internal.convert['\x10'] = '\\u0010';
|
||||
Json._internal.convert['\x11'] = '\\u0011';
|
||||
Json._internal.convert['\x12'] = '\\u0012';
|
||||
Json._internal.convert['\x13'] = '\\u0013';
|
||||
Json._internal.convert['\x14'] = '\\u0014';
|
||||
Json._internal.convert['\x15'] = '\\u0015';
|
||||
Json._internal.convert['\x16'] = '\\u0016';
|
||||
Json._internal.convert['\x17'] = '\\u0017';
|
||||
Json._internal.convert['\x18'] = '\\u0018';
|
||||
Json._internal.convert['\x19'] = '\\u0019';
|
||||
Json._internal.convert['\x1a'] = '\\u001a';
|
||||
Json._internal.convert['\x1b'] = '\\u001b';
|
||||
Json._internal.convert['\x1c'] = '\\u001c';
|
||||
Json._internal.convert['\x1d'] = '\\u001d';
|
||||
Json._internal.convert['\x1e'] = '\\u001e';
|
||||
Json._internal.convert['\x1f'] = '\\u001f';
|
||||
/*
|
||||
* At some point, we probably want to add \x80-\xff as well, and it's then
|
||||
* probably more efficient to generate these strings dynamically. (Even now
|
||||
* it may be, but this was the the way I started, and so it remains.)
|
||||
*/
|
||||
|
||||
|
||||
/* Test it */
|
||||
/*
|
||||
libinclude("base.js");
|
||||
function testFormat()
|
||||
{
|
||||
var test = new Object();
|
||||
test.int = 23;
|
||||
test.str = "hello world";
|
||||
test.float = 223.1;
|
||||
test.bool = true;
|
||||
test.array = new Array();
|
||||
test.array[0] = "hello";
|
||||
test.array[1] = "world";
|
||||
test.obj = new Object();
|
||||
test.obj.int = 1000;
|
||||
test.obj.array = new Array();
|
||||
test.obj.array[0] = 42;
|
||||
test.obj.array[1] = 223;
|
||||
printf("%s\n", Json.encode(test));
|
||||
}
|
||||
testFormat();
|
||||
*/
|
||||
|
||||
/*
|
||||
libinclude("base.js");
|
||||
function testParse()
|
||||
{
|
||||
var s;
|
||||
|
||||
s = '{ "x" : 23 }';
|
||||
obj = Json.decode(s);
|
||||
printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj));
|
||||
|
||||
s = '{ "x" : [ 23, 42] }';
|
||||
obj = Json.decode(s);
|
||||
printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj));
|
||||
|
||||
s = '[ 13, 19, { "x" : [ 23, 42] }, 223 ]';
|
||||
obj = Json.decode(s);
|
||||
printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj));
|
||||
|
||||
s = '{ "x" : [ "hi" ] }';
|
||||
obj = Json.decode(s);
|
||||
printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj));
|
||||
|
||||
s = '[ 13, 19, { "x" : [ 23, 42, { "y":{"a":"hello", "b":"world", "c":[1,2,3]}}] }, 223 ]';
|
||||
obj = Json.decode(s);
|
||||
printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj));
|
||||
}
|
||||
testParse();
|
||||
*/
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: c
|
||||
* End:
|
||||
*/
|
||||
%>
|
||||
@@ -0,0 +1,13 @@
|
||||
<%
|
||||
/* Return true to allow access; false otherwise */
|
||||
function json_authenticate(serviceComponents, method)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: c
|
||||
* End:
|
||||
*/
|
||||
%>
|
||||
@@ -0,0 +1,200 @@
|
||||
<%
|
||||
/*
|
||||
* Copyright:
|
||||
* (C) 2006 by Derrell Lipman
|
||||
* All rights reserved
|
||||
*
|
||||
* License:
|
||||
* LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
|
||||
*/
|
||||
|
||||
/*
|
||||
* Date class for JSON-RPC
|
||||
*/
|
||||
|
||||
|
||||
function _JSON_Date_create(secondsSinceEpoch)
|
||||
{
|
||||
var o = new Object();
|
||||
o.__type = "_JSON_Date";
|
||||
|
||||
function _setUtcDateTimeFields(year, month, day, hour, minute, second, millisecond)
|
||||
{
|
||||
this.year = year + 0;
|
||||
this.month = month + 0;
|
||||
this.day = day + 0;
|
||||
this.hour = hour + 0;
|
||||
this.minute = minute + 0;
|
||||
this.second = second + 0;
|
||||
this.millisecond = millisecond + 0;
|
||||
}
|
||||
|
||||
o.setUtcYear = _setUtcDateTimeFields;
|
||||
|
||||
function _setUtcYear(year)
|
||||
{
|
||||
this.year = year + 0;
|
||||
}
|
||||
o.setUtcYear = _setUtcYear;
|
||||
|
||||
function _setUtcMonth(month)
|
||||
{
|
||||
this.month = month + 0;
|
||||
}
|
||||
o.setUtcMonth = _setUtcMonth;
|
||||
|
||||
function _setUtcDay(day)
|
||||
{
|
||||
this.day = day + 0;
|
||||
}
|
||||
o.setUtcDay = _setUtcDay;
|
||||
|
||||
function _setUtcHour(hour)
|
||||
{
|
||||
this.hour = hour + 0;
|
||||
}
|
||||
o.setUtcHour = _setUtcHour;
|
||||
|
||||
function _setUtcMinute(minute)
|
||||
{
|
||||
this.minute = minute + 0;
|
||||
}
|
||||
o.setUtcMinute = _setUtcMinute;
|
||||
|
||||
function _setUtcSecond(second)
|
||||
{
|
||||
this.second = second + 0;
|
||||
}
|
||||
o.setUtcSecond = _setUtcSecond;
|
||||
|
||||
function _setUtcMillisecond(millisecond)
|
||||
{
|
||||
this.millisecond = millisecond + 0;
|
||||
}
|
||||
o.setUtcMillisecond = _setUtcMillisecond;
|
||||
|
||||
function _setEpochTime(secondsSinceEpoch)
|
||||
{
|
||||
var microseconds = 0;
|
||||
|
||||
if (typeof(secondsSinceEpoch) != "number")
|
||||
{
|
||||
var currentTime = gettimeofday();
|
||||
secondsSinceEpoch = currentTime.sec;
|
||||
microseconds = currentTime.usec;
|
||||
}
|
||||
|
||||
var tm = gmtime(secondsSinceEpoch);
|
||||
|
||||
this.year = 1900 + tm.tm_year;
|
||||
this.month = tm.tm_mon;
|
||||
this.day = tm.tm_mday;
|
||||
this.hour = tm.tm_hour;
|
||||
this.minute = tm.tm_min;
|
||||
this.second = tm.tm_sec;
|
||||
this.millisecond = 0;
|
||||
}
|
||||
o.setEpochTime = _setEpochTime;
|
||||
|
||||
function _getUtcYear()
|
||||
{
|
||||
return this.year;
|
||||
}
|
||||
o.getUtcYear = _getUtcYear;
|
||||
|
||||
function _getUtcMonth()
|
||||
{
|
||||
return this.month;
|
||||
}
|
||||
o.getUtcMonth = _getUtcMonth;
|
||||
|
||||
function _getUtcDay()
|
||||
{
|
||||
return this.day;
|
||||
}
|
||||
o.getUtcDay = _getUtcDay;
|
||||
|
||||
function _getUtcHour()
|
||||
{
|
||||
return this.hour;
|
||||
}
|
||||
o.getUtcHour = _getUtcHour;
|
||||
|
||||
function _getUtcMinute()
|
||||
{
|
||||
return this.minute;
|
||||
}
|
||||
o.getUtcMinute = _getUtcMinute;
|
||||
|
||||
function _getUtcSecond()
|
||||
{
|
||||
return this.second;
|
||||
}
|
||||
o.getUtcSecond = _getUtcSecond;
|
||||
|
||||
function _getUtcMillisecond()
|
||||
{
|
||||
return this.millisecond;
|
||||
}
|
||||
o.getUtcMillisecond = _getUtcMillisecond;
|
||||
|
||||
function _getEpochTime()
|
||||
{
|
||||
var tm = new Object();
|
||||
tm.tm_sec = this.second;
|
||||
tm.tm_min = this.minute;
|
||||
tm.tm_hour = this.hour;
|
||||
tm.tm_mday = -1;
|
||||
tm.tm_mon = this.month;
|
||||
tm.tm_year = this.year;
|
||||
tm.tm_wday = -1;
|
||||
tm.tm_yday = -1;
|
||||
tm.isdst = 0;
|
||||
return gmmktime(tm);
|
||||
}
|
||||
o.getEpochTime = _getEpochTime;
|
||||
|
||||
function _encoding()
|
||||
{
|
||||
/* Encode the date in a well-documented fashion */
|
||||
return sprintf("new Date(Date.UTC(%d,%d,%d,%d,%d,%d,%d))",
|
||||
this.year,
|
||||
this.month,
|
||||
this.day,
|
||||
this.hour,
|
||||
this.minute,
|
||||
this.second,
|
||||
this.millisecond);
|
||||
}
|
||||
o.encoding = _encoding;
|
||||
|
||||
if (! secondsSinceEpoch)
|
||||
{
|
||||
var now = gettimeofday();
|
||||
o.setEpochTime(now.sec);
|
||||
}
|
||||
else
|
||||
{
|
||||
o.setEpochTime(secondsSinceEpoch);
|
||||
}
|
||||
o.year = 0;
|
||||
o.month = 0;
|
||||
o.day = 0;
|
||||
o.hour = 0;
|
||||
o.minute = 0;
|
||||
o.second = 0;
|
||||
o.millisecond = 0;
|
||||
return o;
|
||||
}
|
||||
|
||||
JSON_Date = new Object();
|
||||
JSON_Date.create = _JSON_Date_create;
|
||||
_JSON_Date_create = null;
|
||||
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: c
|
||||
* End:
|
||||
*/
|
||||
%>
|
||||
@@ -0,0 +1,236 @@
|
||||
<%
|
||||
/*
|
||||
* Copyright:
|
||||
* (C) 2006 by Derrell Lipman
|
||||
* All rights reserved
|
||||
*
|
||||
* License:
|
||||
* LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the standard qooxdoo test class. There are tests for each of the
|
||||
* primitive types here, along with standard named tests "echo", "sink" and
|
||||
* "sleep".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Echo the (one and only) parameter.
|
||||
*
|
||||
* @param params
|
||||
* An array containing the parameters to this method
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: The object containing the result of the method;
|
||||
* Failure: null
|
||||
*/
|
||||
function _echo(params, error)
|
||||
{
|
||||
if (params.length != 1)
|
||||
{
|
||||
error.setError(JsonRpcError_ParameterMismatch,
|
||||
"Expected 1 parameter; got " + params.length);
|
||||
return error;
|
||||
}
|
||||
return "Client said: [" + params[0] + "]";
|
||||
}
|
||||
jsonrpc.method.echo = _echo;
|
||||
|
||||
/**
|
||||
* Sink all data and never return.
|
||||
*
|
||||
* @param params
|
||||
* An array containing the parameters to this method (none expected)
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* "Never"
|
||||
*/
|
||||
function _sink(params, error)
|
||||
{
|
||||
/* We're never supposed to return. Just sleep for a very long time. */
|
||||
sleep(240);
|
||||
}
|
||||
jsonrpc.method.sink = _sink;
|
||||
|
||||
/**
|
||||
* Sleep for the number of seconds specified by the parameter.
|
||||
*
|
||||
* @param params
|
||||
* An array containing the parameters to this method (one expected)
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: The object containing the result of the method;
|
||||
* Failure: null
|
||||
*/
|
||||
function _sleep(params, error)
|
||||
{
|
||||
if (params.length != 1)
|
||||
{
|
||||
error.setError(JsonRpcError_ParameterMismatch,
|
||||
"Expected 1 parameter; got " + params.length);
|
||||
return error;
|
||||
}
|
||||
|
||||
sleep(params[0]);
|
||||
return params[0];
|
||||
}
|
||||
jsonrpc.method.sleep = _sleep;
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/*
|
||||
* The remainder of the functions test each individual primitive type, and
|
||||
* test echoing arbitrary types. Hopefully the name is self-explanatory.
|
||||
*/
|
||||
|
||||
function _getInteger(params, error)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
jsonrpc.method.getInteger = _getInteger;
|
||||
|
||||
function _getFloat(params, error)
|
||||
{
|
||||
return 1/3;
|
||||
}
|
||||
jsonrpc.method.getFloat = _getFloat;
|
||||
|
||||
function _getString(params, error)
|
||||
{
|
||||
return "Hello world";
|
||||
}
|
||||
jsonrpc.method.getString = _getString;
|
||||
|
||||
function _getBadString(params, error)
|
||||
{
|
||||
return "<!DOCTYPE HTML \"-//IETF//DTD HTML 2.0//EN\">";
|
||||
}
|
||||
jsonrpc.method.getBadString = _getBadString;
|
||||
|
||||
function _getArrayInteger(params, error)
|
||||
{
|
||||
return new Array(1, 2, 3, 4);
|
||||
}
|
||||
jsonrpc.method.getArrayInteger = _getArrayInteger;
|
||||
|
||||
function _getArrayString(params, error)
|
||||
{
|
||||
return new Array("one", "two", "three", "four");
|
||||
}
|
||||
jsonrpc.method.getArrayString = _getArrayString;
|
||||
|
||||
function _getObject(params, error)
|
||||
{
|
||||
o = new Object(); // some arbitrary object
|
||||
o.something = 23;
|
||||
o.garbage = 'lkasjdff;lajsdfkl;sadf';
|
||||
return o;
|
||||
}
|
||||
jsonrpc.method.getObject = _getObject;
|
||||
|
||||
function _getTrue(params, error)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
jsonrpc.method.getTrue = _getTrue;
|
||||
|
||||
function _getFalse(params, error)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
jsonrpc.method.getFalse = _getFalse;
|
||||
|
||||
function _getNull(params, error)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
jsonrpc.method.getNull = _getNull;
|
||||
|
||||
function _isInteger(params, error)
|
||||
{
|
||||
var type = nativeTypeOf(params[0]);
|
||||
return type == "integer" || type == "integer64";
|
||||
}
|
||||
jsonrpc.method.isInteger = _isInteger;
|
||||
|
||||
function _isFloat(params, error)
|
||||
{
|
||||
return nativeTypeOf(params[0]) == "float";
|
||||
}
|
||||
jsonrpc.method.isFloat = _isFloat;
|
||||
|
||||
function _isString(params, error)
|
||||
{
|
||||
return nativeTypeOf(params[0]) == "string";
|
||||
}
|
||||
jsonrpc.method.isString = _isString;
|
||||
|
||||
function _isBoolean(params, error)
|
||||
{
|
||||
return nativeTypeOf(params[0]) == "boolean";
|
||||
}
|
||||
jsonrpc.method.isBoolean = _isBoolean;
|
||||
|
||||
function _isArray(params, error)
|
||||
{
|
||||
return nativeTypeOf(params[0]) == "object" && params.length != undefined;
|
||||
}
|
||||
jsonrpc.method.isArray = _isArray;
|
||||
|
||||
function _isObject(params, error)
|
||||
{
|
||||
return nativeTypeOf(params[0]) == "object";
|
||||
}
|
||||
jsonrpc.method.isObject = _isObject;
|
||||
|
||||
function _isNull(params, error)
|
||||
{
|
||||
return nativeTypeOf(params[0]) == "null";
|
||||
}
|
||||
jsonrpc.method.isNull = _isNull;
|
||||
|
||||
function _getParams(params, error)
|
||||
{
|
||||
return params;
|
||||
}
|
||||
jsonrpc.method.getParams = _getParams;
|
||||
|
||||
function _getParam(params, error)
|
||||
{
|
||||
return params[0];
|
||||
}
|
||||
jsonrpc.method.getParam = _getParam;
|
||||
|
||||
function _getCurrentTimestamp()
|
||||
{
|
||||
now = gettimeofday();
|
||||
obj = new Object();
|
||||
obj.now = now.sec;
|
||||
obj.json = JSON_Date.create(now);
|
||||
return obj;
|
||||
}
|
||||
jsonrpc.method.getCurrentTimestamp = _getCurrentTimestamp;
|
||||
|
||||
function _getError(params, error)
|
||||
{
|
||||
error.setError(23, "This is an application-provided error");
|
||||
return error;
|
||||
}
|
||||
jsonrpc.method.getError = _getError;
|
||||
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: c
|
||||
* End:
|
||||
*/
|
||||
%>
|
||||
@@ -0,0 +1,492 @@
|
||||
<%
|
||||
|
||||
/*
|
||||
* Copyright:
|
||||
* (C) 2006 by Derrell Lipman
|
||||
* All rights reserved
|
||||
*
|
||||
* License:
|
||||
* LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a simple JSON-RPC server.
|
||||
*/
|
||||
|
||||
|
||||
/* Bring in the json format/parse functions */
|
||||
jsonrpc_include("json.esp");
|
||||
|
||||
/* Bring in the date class */
|
||||
jsonrpc_include("jsondate.esp");
|
||||
|
||||
/* Load the authentication script */
|
||||
jsonrpc_include("json_auth.esp");
|
||||
|
||||
|
||||
/* bring the string functions into the global frame */
|
||||
string_init(global);
|
||||
|
||||
/* Bring the system functions into the global frame */
|
||||
sys_init(global);
|
||||
|
||||
function printf()
|
||||
{
|
||||
print(vsprintf(arguments));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* All of our manipulation of JSON RPC methods will be through this object.
|
||||
* Each class of methods will assign to here, and all of the constants will
|
||||
* also be in this object.
|
||||
*/
|
||||
jsonrpc = new Object();
|
||||
jsonrpc.Constant = new Object();
|
||||
jsonrpc.Constant.ErrorOrigin = new Object(); /* error origins */
|
||||
jsonrpc.Constant.ErrorCode = new Object(); /* server-generated error codes */
|
||||
jsonrpc.method = new Object(); /* methods available in requested class */
|
||||
|
||||
/*
|
||||
* ScriptTransport constants
|
||||
*/
|
||||
jsonrpc.Constant.ScriptTransport = new Object();
|
||||
jsonrpc.Constant.ScriptTransport.NotInUse = -1;
|
||||
|
||||
|
||||
/*
|
||||
* JSON-RPC error origin constants
|
||||
*/
|
||||
jsonrpc.Constant.ErrorOrigin.Server = 1;
|
||||
jsonrpc.Constant.ErrorOrigin.Application = 2;
|
||||
jsonrpc.Constant.ErrorOrigin.Transport = 3;
|
||||
jsonrpc.Constant.ErrorOrigin.Client = 4;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* JSON-RPC server-generated error code constants
|
||||
*/
|
||||
|
||||
/**
|
||||
* Error code, value 0: Unknown Error
|
||||
*
|
||||
* The default error code, used only when no specific error code is passed to
|
||||
* the JsonRpcError constructor. This code should generally not be used.
|
||||
*/
|
||||
jsonrpc.Constant.ErrorCode.Unknown = 0;
|
||||
|
||||
/**
|
||||
* Error code, value 1: Illegal Service
|
||||
*
|
||||
* The service name contains illegal characters or is otherwise deemed
|
||||
* unacceptable to the JSON-RPC server.
|
||||
*/
|
||||
jsonrpc.Constant.ErrorCode.IllegalService = 1;
|
||||
|
||||
/**
|
||||
* Error code, value 2: Service Not Found
|
||||
*
|
||||
* The requested service does not exist at the JSON-RPC server.
|
||||
*/
|
||||
jsonrpc.Constant.ErrorCode.ServiceNotFound = 2;
|
||||
|
||||
/**
|
||||
* Error code, value 3: Class Not Found
|
||||
*
|
||||
* If the JSON-RPC server divides service methods into subsets (classes), this
|
||||
* indicates that the specified class was not found. This is slightly more
|
||||
* detailed than "Method Not Found", but that error would always also be legal
|
||||
* (and true) whenever this one is returned. (Not used in this implementation)
|
||||
*/
|
||||
jsonrpc.Constant.ErrorCode.ClassNotFound = 3; // not used in this implementation
|
||||
|
||||
/**
|
||||
* Error code, value 4: Method Not Found
|
||||
*
|
||||
* The method specified in the request is not found in the requested service.
|
||||
*/
|
||||
jsonrpc.Constant.ErrorCode.MethodNotFound = 4;
|
||||
|
||||
/*
|
||||
* Error code, value 5: Parameter Mismatch
|
||||
*
|
||||
* If a method discovers that the parameters (arguments) provided to it do not
|
||||
* match the requisite types for the method's parameters, it should return
|
||||
* this error code to indicate so to the caller.
|
||||
*
|
||||
* This error is also used to indicate an illegal parameter value, in server
|
||||
* scripts.
|
||||
*/
|
||||
jsonrpc.Constant.ErrorCode.ParameterMismatch = 5;
|
||||
|
||||
/**
|
||||
* Error code, value 6: Permission Denied
|
||||
*
|
||||
* A JSON-RPC service provider can require authentication, and that
|
||||
* authentication can be implemented such the method takes authentication
|
||||
* parameters, or such that a method or class of methods requires prior
|
||||
* authentication. If the caller has not properly authenticated to use the
|
||||
* requested method, this error code is returned.
|
||||
*/
|
||||
jsonrpc.Constant.ErrorCode.PermissionDenied = 6;
|
||||
|
||||
/*
|
||||
* Error code, value 7: Unexpected Output
|
||||
*
|
||||
* The called method illegally generated output to the browser, which would
|
||||
* have preceeded the JSON-RPC data.
|
||||
*/
|
||||
jsonrpc.Constant.ErrorCode.UnexpectedOutput = 7;
|
||||
|
||||
/*
|
||||
* Error code, value 8: Resource Error
|
||||
*
|
||||
* Too many resources were requested, a system limitation on the total number
|
||||
* of resources has been reached, or a resource or resource id was misused.
|
||||
*/
|
||||
jsonrpc.Constant.ErrorCode.ResourceError = 8;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function sendReply(reply, scriptTransportId)
|
||||
{
|
||||
/* If not using ScriptTransport... */
|
||||
if (scriptTransportId == jsonrpc.Constant.ScriptTransport.NotInUse)
|
||||
{
|
||||
/* ... then just output the reply. */
|
||||
write(reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, we need to add a call to a qooxdoo-specific function */
|
||||
reply =
|
||||
"qx.io.remote.ScriptTransport._requestFinished(" +
|
||||
scriptTransportId + ", " + reply +
|
||||
");";
|
||||
write(reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _jsonValidRequest(req)
|
||||
{
|
||||
if (req == undefined)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof(req) != "object")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req["id"] == undefined)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req["service"] == undefined)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req["method"] == undefined)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req["params"] == undefined)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
jsonrpc.validRequest = _jsonValidRequest;
|
||||
_jsonValidRequest = null;
|
||||
|
||||
/*
|
||||
* class JsonRpcError
|
||||
*
|
||||
* This class allows service methods to easily provide error information for
|
||||
* return via JSON-RPC.
|
||||
*/
|
||||
function _JsonRpcError_create(origin, code, message)
|
||||
{
|
||||
var o = new Object();
|
||||
|
||||
o.data = new Object();
|
||||
o.data.origin = origin;
|
||||
o.data.code = code;
|
||||
o.data.message = message;
|
||||
o.scriptTransportId = jsonrpc.Constant.ScriptTransport.NotInUse;
|
||||
o.__type = "_JsonRpcError";
|
||||
|
||||
function _origin(origin)
|
||||
{
|
||||
this.data.origin = origin;
|
||||
}
|
||||
o.setOrigin = _origin;
|
||||
|
||||
function _setError(code, message)
|
||||
{
|
||||
this.data.code = code;
|
||||
this.data.message = message;
|
||||
}
|
||||
o.setError = _setError;
|
||||
|
||||
function _setId(id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
o.setId = _setId;
|
||||
|
||||
function _setScriptTransportId(id)
|
||||
{
|
||||
this.scriptTransportId = id;
|
||||
}
|
||||
o.setScriptTransportId = _setScriptTransportId;
|
||||
|
||||
function _Send()
|
||||
{
|
||||
var error = this;
|
||||
var id = this.id;
|
||||
var ret = new Object();
|
||||
ret.error = this.data;
|
||||
ret.id = this.id;
|
||||
sendReply(Json.encode(ret), this.scriptTransportId);
|
||||
}
|
||||
o.Send = _Send;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
jsonrpc.createError = _JsonRpcError_create;
|
||||
_JsonRpcError_create = null;
|
||||
|
||||
/*
|
||||
* 'input' is the user-provided json-encoded request
|
||||
* 'jsonInput' is that request, decoded into its object form
|
||||
*/
|
||||
var input;
|
||||
var jsonInput = null;
|
||||
|
||||
/* Allocate a generic error object */
|
||||
error = jsonrpc.createError(jsonrpc.Constant.ErrorOrigin.Server,
|
||||
jsonrpc.Constant.ErrorCode.Unknown,
|
||||
"Unknown error");
|
||||
|
||||
/* Assume (default) we're not using ScriptTransport */
|
||||
scriptTransportId = jsonrpc.Constant.ScriptTransport.NotInUse;
|
||||
|
||||
/* What type of request did we receive? */
|
||||
if (request["REQUEST_METHOD"] == "POST" &&
|
||||
request["CONTENT_TYPE"] == "text/json")
|
||||
{
|
||||
/* We found literal POSTed json-rpc data (we hope) */
|
||||
input = request["POST_DATA"];
|
||||
jsonInput = Json.decode(input);
|
||||
}
|
||||
else if (request["REQUEST_METHOD"] == "GET" &&
|
||||
form["_ScriptTransport_id"] != undefined &&
|
||||
form["_ScriptTransport_data"] != undefined)
|
||||
{
|
||||
/* We have what looks like a valid ScriptTransport request */
|
||||
scriptTransportId = form["_ScriptTransport_id"];
|
||||
error.setScriptTransportId(scriptTransportId);
|
||||
input = form["_ScriptTransport_data"];
|
||||
jsonInput = Json.decode(input);
|
||||
}
|
||||
|
||||
/* Ensure that this was a JSON-RPC service request */
|
||||
if (! jsonrpc.validRequest(jsonInput))
|
||||
{
|
||||
/*
|
||||
* This request was not issued with JSON-RPC so echo the error rather than
|
||||
* issuing a JsonRpcError response.
|
||||
*/
|
||||
write("JSON-RPC request expected; service, method or params missing<br>");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, it looks like JSON-RPC, so we'll return an Error object if we encounter
|
||||
* errors from here on out.
|
||||
*/
|
||||
error.setId(jsonInput.id);
|
||||
|
||||
/* Service and method names may contain these characters */
|
||||
var nameChars =
|
||||
"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
|
||||
/* The first letter of service and method names must be a letter */
|
||||
var nameFirstLetter =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
/*
|
||||
* Ensure the method name is kosher. A meethod name should be:
|
||||
*
|
||||
* - first character is in [a-zA-Z]
|
||||
* - other characters are in [_a-zA-Z0-9]
|
||||
*/
|
||||
|
||||
/* First check for legal characters */
|
||||
if (strspn(jsonInput.method, nameChars) != strlen(jsonInput.method))
|
||||
{
|
||||
/* There's some illegal character in the service name */
|
||||
error.setError(jsonrpc.Constant.ErrorCode.MethodNotFound,
|
||||
"Illegal character found in method name.");
|
||||
error.Send();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now ensure that it begins with a letter */
|
||||
if (strspn(substr(jsonInput.method, 0, 1), nameFirstLetter) != 1)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.MethodNotFound,
|
||||
"The method name does not begin with a letter");
|
||||
error.Send();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure the requested service name is kosher. A service name should be:
|
||||
*
|
||||
* - a dot-separated sequences of strings; no adjacent dots
|
||||
* - first character of each string is in [a-zA-Z]
|
||||
* - other characters are in [_a-zA-Z0-9]
|
||||
*/
|
||||
|
||||
/* First check for legal characters */
|
||||
if (strspn(jsonInput.service, "." + nameChars) != strlen(jsonInput.service))
|
||||
{
|
||||
/* There's some illegal character in the service name */
|
||||
error.setError(jsonrpc.Constant.ErrorCode.IllegalService,
|
||||
"Illegal character found in service name.");
|
||||
error.Send();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now ensure there are no double dots.
|
||||
*
|
||||
* Frustration with ejs. Result must be NULL, but we can't use the ===
|
||||
* operator: strstr() === null so we have to use typeof. If the result isn't
|
||||
* null, then it'll be a number and therefore not type "pointer".
|
||||
*/
|
||||
if (typeof(strstr(jsonInput.service, "..")) != "pointer")
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.IllegalService,
|
||||
"Illegal use of two consecutive dots in service name");
|
||||
error.Send();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Explode the service name into its dot-separated parts */
|
||||
var serviceComponents = split(".", jsonInput.service);
|
||||
|
||||
/* Ensure that each component begins with a letter */
|
||||
for (var i = 0; i < serviceComponents.length; i++)
|
||||
{
|
||||
if (strspn(substr(serviceComponents[i], 0, 1), nameFirstLetter) != 1)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.IllegalService,
|
||||
"A service name component does not begin with a letter");
|
||||
error.Send();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now replace all dots with slashes so we can locate the service script. We
|
||||
* also retain the split components of the path, as the class name of the
|
||||
* service is the last component of the path.
|
||||
*/
|
||||
var servicePath = join("/", serviceComponents) + ".esp";
|
||||
|
||||
/* Load the requested class */
|
||||
if (jsonrpc_include(servicePath))
|
||||
{
|
||||
/* Couldn't find the requested service */
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ServiceNotFound,
|
||||
"Service class `" + servicePath + "` does not exist.");
|
||||
error.Send();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the requested method.
|
||||
*
|
||||
* What we really want to do here, and could do in any reasonable language,
|
||||
* is:
|
||||
*
|
||||
* method = jsonrpc.method[jsonInput.method];
|
||||
* if (method && typeof(method) == "function") ...
|
||||
*
|
||||
* The following completely unreasonable sequence of commands is because:
|
||||
*
|
||||
* (a) ejs evaluates all OR'ed expressions even if an early one is false, and
|
||||
* barfs on the typeof(method) call if method is undefined
|
||||
*
|
||||
* (b) ejs does not allow comparing against the string "function"!!! What
|
||||
* the hell is special about that particular string???
|
||||
*
|
||||
* E-gad. What a mess.
|
||||
*/
|
||||
var method = jsonrpc.method[jsonInput.method];
|
||||
var valid = (method != undefined);
|
||||
if (valid)
|
||||
{
|
||||
var type = typeof(method);
|
||||
if (substr(type, 0, 1) != 'f' || substr(type, 1) != "unction")
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (! valid)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.MethodNotFound,
|
||||
"Method `" + method + "` not found.");
|
||||
error.Send();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure the logged-in user is allowed to issue the requested method */
|
||||
if (! json_authenticate(serviceComponents, jsonInput.method))
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.PermissionDenied,
|
||||
"Permission denied");
|
||||
error.Send();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Most errors from here on out will be Application-generated */
|
||||
error.setOrigin(jsonrpc.Constant.ErrorOrigin.Application);
|
||||
|
||||
/* Call the requested method passing it the provided params */
|
||||
var retval = method(jsonInput.params, error);
|
||||
|
||||
/* See if the result of the function was actually an error object */
|
||||
if (retval["__type"] == "_JsonRpcError")
|
||||
{
|
||||
/* Yup, it was. Return the error */
|
||||
retval.Send();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Give 'em what they came for! */
|
||||
var ret = new Object();
|
||||
ret.result = retval;
|
||||
ret.id = jsonInput.id;
|
||||
sendReply(Json.encode(ret), scriptTransportId);
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: c
|
||||
* End:
|
||||
*/
|
||||
%>
|
||||
@@ -0,0 +1,170 @@
|
||||
<%
|
||||
|
||||
/*
|
||||
* Various JSON-RPC calls will want to maintain open resources within a
|
||||
* session, across multiple calls. We'll provide a standardized way to
|
||||
* maintain those open resources here, with some protection against rogue
|
||||
* scripts.
|
||||
*/
|
||||
|
||||
function _resourcesCreate()
|
||||
{
|
||||
/* The being-created resources object */
|
||||
var o = new Object();
|
||||
|
||||
/*
|
||||
* The maximum number of resources available to a single session. This
|
||||
* should be more than is ever needed (even by reasonable recursive
|
||||
* functions) but limits rogue scripts ability to generate DOS attacks.
|
||||
*/
|
||||
o.RESOURCE_LIMIT = 100;
|
||||
|
||||
/* List of current resources */
|
||||
o.resourceList = new Object();
|
||||
|
||||
/* Resource id values will be constantly incrementing; never reset. */
|
||||
o.resourceList.id = 0;
|
||||
|
||||
/* We'll maintain our own count of the number of open resources */
|
||||
o.resourceList.count = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Set a new saved resource.
|
||||
*/
|
||||
function _set(resource, type, error)
|
||||
{
|
||||
/* Do they already have the maximum number of resources allocated? */
|
||||
if (this.resourceList.count >= this.RESOURCE_LIMIT)
|
||||
{
|
||||
/* Yup. */
|
||||
error.setOrigin(jsonrpc.Constant.ErrorOrigin.Server);
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ResourceError,
|
||||
"Session limit on resources (" +
|
||||
RESOURCE_LIMIT +
|
||||
") exceeded.");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Allocate an object to hold the new resource and its type */
|
||||
var r = new Object();
|
||||
|
||||
/* Save the resource and its type */
|
||||
r.resource = resource;
|
||||
r.type = type;
|
||||
|
||||
/* Add this resource to the list */
|
||||
this.resourceList[this.resourceList.id] = r;
|
||||
|
||||
/* There's a new resource in the list! */
|
||||
this.resourceList.count++;
|
||||
|
||||
/*
|
||||
* Return the index of the resource, its resource id, and advance to
|
||||
* the next resource id for next time.
|
||||
*/
|
||||
var id = this.resourceList.id;
|
||||
this.resourceList.id++;
|
||||
return id;
|
||||
}
|
||||
o.set = _set;
|
||||
|
||||
/*
|
||||
* Get a previously-saved resource
|
||||
*/
|
||||
function _get(resourceId, error)
|
||||
{
|
||||
/* Does the specified resource id exist? */
|
||||
if (! this.resourceList[resourceId])
|
||||
{
|
||||
/* Nope. */
|
||||
error.setOrigin(jsonrpc.Constant.ErrorOrigin.Server);
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ResourceError,
|
||||
"Resource not found.");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Retrieve the resource */
|
||||
var r = this.resourceList[resourceId];
|
||||
|
||||
/* Give 'em what they came for! */
|
||||
return r.resource;
|
||||
}
|
||||
o.get = _get;
|
||||
|
||||
/*
|
||||
* Find a previously-saved resource
|
||||
*/
|
||||
function _find(type, error)
|
||||
{
|
||||
/* Does the specified resource id exist? */
|
||||
for (var resourceId in this.resourceList)
|
||||
{
|
||||
/* Retrieve the resource */
|
||||
var r = this.resourceList[resourceId];
|
||||
|
||||
/* Ignore "id" and "count" integer fields */
|
||||
if (typeof(r) == "object")
|
||||
{
|
||||
/* Is the specified resource the correct type? */
|
||||
if (r.type == type)
|
||||
{
|
||||
/* Yup, this is the one they want. */
|
||||
return resourceId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* It wasn't found. */
|
||||
return undefined;
|
||||
}
|
||||
o.find = _find;
|
||||
|
||||
/*
|
||||
* Release a previously-saved resource, allowing it to be freed
|
||||
*/
|
||||
function _release(resourceId, error)
|
||||
{
|
||||
/* Does the specified resource id exist? */
|
||||
if (! this.resourceList[resourceId])
|
||||
{
|
||||
/* Nope. */
|
||||
error.setOrigin(jsonrpc.Constant.ErrorOrigin.Server);
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ResourceError,
|
||||
"Resource not found.");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* It exists. Delete it. */
|
||||
delete this.resourceList[resourceId];
|
||||
|
||||
/* There's now one fewer resources in the list */
|
||||
this.resourceList.count--;
|
||||
}
|
||||
o.release = _release;
|
||||
|
||||
/*
|
||||
* Retrieve the list of resources (for debugging) */
|
||||
*/
|
||||
function _getList(error)
|
||||
{
|
||||
return this.resourceList;
|
||||
}
|
||||
o.getList = _getList;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
/* singleton: create session resources list */
|
||||
if (! session.resources)
|
||||
{
|
||||
session.resources = _resourcesCreate();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: c
|
||||
* End:
|
||||
*/
|
||||
%>
|
||||
@@ -0,0 +1,632 @@
|
||||
<%
|
||||
/*
|
||||
* Copyright:
|
||||
* (C) 2006 by Derrell Lipman
|
||||
* All rights reserved
|
||||
*
|
||||
* License:
|
||||
* LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
|
||||
*/
|
||||
|
||||
/*
|
||||
* JSON-RPC mappings to the ldb ejs functions
|
||||
*/
|
||||
|
||||
/* We'll be saving resources in the session */
|
||||
jsonrpc_include("resources.esp");
|
||||
|
||||
|
||||
/**
|
||||
* Local function to determine if the requested database is one which we allow
|
||||
* access to.
|
||||
*
|
||||
* @param dbRequested
|
||||
* Name of the database which is being requested to be opened
|
||||
*
|
||||
* @return
|
||||
* true if access is allowed; false otherwise.
|
||||
*/
|
||||
function accessAllowed(dbRequested)
|
||||
{
|
||||
/* Databases allowed to connect to */
|
||||
dbAllowed = new Array();
|
||||
dbAllowed[dbAllowed.length] = "sam.ldb";
|
||||
|
||||
for (var i = 0; i < dbAllowed.length; i++)
|
||||
{
|
||||
if (dbRequested == dbAllowed[i])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connect to a database
|
||||
*
|
||||
* @param params[0]
|
||||
* Database name
|
||||
*
|
||||
* @param params[1..n]
|
||||
* Option (e.g. "modules:modlist")
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: The resource id to be used for future access to the database
|
||||
* Failure: error event
|
||||
*
|
||||
* @note
|
||||
* Credentials or session_info may be set up first.
|
||||
*/
|
||||
function _connect(params, error)
|
||||
{
|
||||
if (params.length < 1)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <db_name> [<option> ...]");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* First, see if this database was already opened */
|
||||
var resourceId = session.resources.find("ldb:" + params[0], error);
|
||||
if (resourceId != undefined)
|
||||
{
|
||||
/* It was. Give 'em the resource id */
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
/* Ensure that the database name is one that is allowed to be opened */
|
||||
if (! accessAllowed(params[0]))
|
||||
{
|
||||
error.setError(-1, "Invalid or disallowed database name");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Get access to loadparm functions */
|
||||
var lp = loadparm_init();
|
||||
|
||||
/* Determine the private directory */
|
||||
var private_dir = lp.get("private dir");
|
||||
|
||||
/* Database was not previously opened. Connect to it. */
|
||||
ldb = ldb_init();
|
||||
var ret = ldb.connect(private_dir + "/" + params[0]);
|
||||
if (ret && ldb.db)
|
||||
{
|
||||
return session.resources.set(ldb, "ldb:" + params[0], error);
|
||||
}
|
||||
else
|
||||
{
|
||||
error.setError(-1, "ldb.connect failed");
|
||||
return error;
|
||||
}
|
||||
}
|
||||
jsonrpc.method.connect = _connect;
|
||||
|
||||
|
||||
/**
|
||||
* Close a database
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: True
|
||||
* Failure: Will only fail with invalid parameters, and throws an error
|
||||
*/
|
||||
function _close(params, error)
|
||||
{
|
||||
if (params.length != 1)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
var ret = ldb.close();
|
||||
|
||||
/* If close succeeded, release the stored resource */
|
||||
if (ret)
|
||||
{
|
||||
session.resources.release(params[0], error);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
jsonrpc.method.close = _close;
|
||||
|
||||
|
||||
/**
|
||||
* Begin a transaction
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: True
|
||||
* Failure: False
|
||||
*/
|
||||
function _transaction_start(params, error)
|
||||
{
|
||||
if (params.length != 1)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.transaction_start();
|
||||
}
|
||||
jsonrpc.method.transaction_start = _transaction_start;
|
||||
|
||||
|
||||
/**
|
||||
* Cancel a transaction
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: True
|
||||
* Failure: False
|
||||
*/
|
||||
function _transaction_cancel(params, error)
|
||||
{
|
||||
if (params.length != 1)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.transaction_cancel();
|
||||
}
|
||||
jsonrpc.method.transaction_cancel = _transaction_cancel;
|
||||
|
||||
|
||||
/**
|
||||
* Commit a transaction
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: True
|
||||
* Failure: False
|
||||
*/
|
||||
function _transaction_commit(params, error)
|
||||
{
|
||||
if (params.length != 1)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.transaction_commit();
|
||||
}
|
||||
jsonrpc.method.transaction_commit = _transaction_commit;
|
||||
|
||||
|
||||
/**
|
||||
* Issue a Search request
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param params[1]
|
||||
* Search expression
|
||||
*
|
||||
* @param params[2]
|
||||
* Base DN
|
||||
*
|
||||
* @param params[3]
|
||||
* Scope: "default", "base", "one" or "subtree"
|
||||
*
|
||||
* @param params[4]
|
||||
* Attributes: an array object
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: found object
|
||||
* Failure: `undefined`
|
||||
*
|
||||
* @note
|
||||
* If params[4] is missing, assume no attributes
|
||||
* If params[3..4] are missing, also assume "default" scope
|
||||
* If params[2..4] are missing, also assume null base DN
|
||||
*/
|
||||
function _search(params, error)
|
||||
{
|
||||
if (params.length < 2 || params.length > 5)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: " +
|
||||
"<resource_id> <expr> [<baseDN> [<scope> [<attrs>]]]");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
/* Retrieve parameters */
|
||||
var expr = params[1];
|
||||
var baseDN = params[2];
|
||||
var scope = params[3];
|
||||
var attrs = params[4];
|
||||
|
||||
/* Fill in optional parameters */
|
||||
if (params.length < 3) baseDN = null;
|
||||
if (params.length < 4) scope = "one";
|
||||
if (params.length < 5) attrs = null;
|
||||
|
||||
/* Determine scope value */
|
||||
if (scope == "base")
|
||||
{
|
||||
scope = ldb.SCOPE_BASE;
|
||||
}
|
||||
else if (scope == "one")
|
||||
{
|
||||
scope = ldb.SCOPE_ONE;
|
||||
}
|
||||
else if (scope == "subtree")
|
||||
{
|
||||
scope = ldb.SCOPE_SUBTREE;
|
||||
}
|
||||
else if (scope == "default")
|
||||
{
|
||||
scope = ldb.SCOPE_DEFAULT;
|
||||
}
|
||||
else
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"invalid scope: " + scope);
|
||||
return error;
|
||||
}
|
||||
|
||||
return ldb.search(expr, baseDN, scope, attrs);
|
||||
}
|
||||
jsonrpc.method.search = _search;
|
||||
|
||||
|
||||
/**
|
||||
* Add data to the database
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param params[1]
|
||||
* An LDIF string representing the data to be added
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: True
|
||||
* Failure: False
|
||||
*/
|
||||
function _add(params, error)
|
||||
{
|
||||
if (params.length != 2)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id> <ldif>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.add(params[1]);
|
||||
}
|
||||
jsonrpc.method.add = _add;
|
||||
|
||||
|
||||
/**
|
||||
* Modify data in the database
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param params[1]
|
||||
* An LDIF string representing the data to be modified
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: True
|
||||
* Failure: False
|
||||
*/
|
||||
function _modify(params, error)
|
||||
{
|
||||
if (params.length != 2)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id> <ldif>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.modify(params[1]);
|
||||
}
|
||||
jsonrpc.method.modify = _modify;
|
||||
|
||||
|
||||
/**
|
||||
* Delete data from the database
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param params[1]
|
||||
* The DN to be located and deleted
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: True
|
||||
* Failure: False
|
||||
*/
|
||||
function _del(params, error)
|
||||
{
|
||||
if (params.length != 2)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id> <dn>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.del(params[1]);
|
||||
}
|
||||
jsonrpc.method.del = _del;
|
||||
|
||||
|
||||
/**
|
||||
* Rename data in the database
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param params[1]
|
||||
* The DN to be renamed
|
||||
*
|
||||
* @param params[2]
|
||||
* The new name for the DN being renamed
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: True
|
||||
* Failure: False
|
||||
*/
|
||||
function _rename(params, error)
|
||||
{
|
||||
if (params.length != 3)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id> <old_dn> <new_dn>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.rename(params[1], params[2]);
|
||||
}
|
||||
jsonrpc.method.rename = _rename;
|
||||
|
||||
|
||||
/**
|
||||
* Base64-encode a string
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param params[1]
|
||||
* The string to be base64 encoded
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: encoded string
|
||||
* Failure: `undefined`
|
||||
*/
|
||||
function _base64encode(params, error)
|
||||
{
|
||||
if (params.length != 2)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id> <string_to_be_encoded>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.base64encode(params[1]);
|
||||
}
|
||||
jsonrpc.method.base64encode = _base64encode;
|
||||
|
||||
|
||||
/**
|
||||
* Base64-decode a string
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param params[1]
|
||||
* The string to be base64 decoded
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: decoded string
|
||||
* Failure: `undefined`
|
||||
*/
|
||||
function _base64decode(params, error)
|
||||
{
|
||||
if (params.length != 2)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id> <string_to_be_decoded>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.base64decode(params[1]);
|
||||
}
|
||||
jsonrpc.method.base64decode = _base64decode;
|
||||
|
||||
|
||||
/**
|
||||
* escape a DN
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param params[1]
|
||||
* The DN to be escaped
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* Success: escaped string
|
||||
* Failure: undefined
|
||||
*/
|
||||
function _base64decode(params, error)
|
||||
{
|
||||
if (params.length != 2)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id> <string_to_be_decoded>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.base64decode(params[1]);
|
||||
}
|
||||
jsonrpc.method.base64decode = _base64decode;
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a description of the most recent error
|
||||
*
|
||||
* @param params[0]
|
||||
* The resource id of the open database, previously returned by connect()
|
||||
*
|
||||
* @param error
|
||||
* An object of class JsonRpcError.
|
||||
*
|
||||
* @return
|
||||
* The most recent error string for the ldb specified by the resource id
|
||||
*/
|
||||
function _errstring(params, error)
|
||||
{
|
||||
if (params.length != 1)
|
||||
{
|
||||
error.setError(jsonrpc.Constant.ErrorCode.ParameterMismatch,
|
||||
"usage: <resource_id>");
|
||||
return error;
|
||||
}
|
||||
|
||||
ldb = session.resources.get(params[0], error);
|
||||
if (ldb["__type"] == "_JsonRpcError")
|
||||
{
|
||||
return ldb;
|
||||
}
|
||||
|
||||
return ldb.errstring();
|
||||
}
|
||||
jsonrpc.method.errstring = _errstring;
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: c
|
||||
* End:
|
||||
*/
|
||||
%>
|
||||
@@ -0,0 +1,34 @@
|
||||
<%
|
||||
/*
|
||||
* Copyright:
|
||||
* (C) 2006 by Derrell Lipman
|
||||
* All rights reserved
|
||||
*
|
||||
* License:
|
||||
* LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
|
||||
*/
|
||||
|
||||
/*
|
||||
* JSON-RPC mappings to system facilities
|
||||
*/
|
||||
|
||||
/* We'll be accessing session resources */
|
||||
jsonrpc_include("resources.esp");
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the list of open resources (for debugging)
|
||||
*/
|
||||
function _get_open_resources(params, error)
|
||||
{
|
||||
return session.resources.getList(error);
|
||||
}
|
||||
jsonrpc.method.get_open_resources = _get_open_resources;
|
||||
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: c
|
||||
* End:
|
||||
*/
|
||||
%>
|
||||
@@ -0,0 +1,85 @@
|
||||
# add valgrind suppressions for the build farm here. Get the format
|
||||
# from the build farm log
|
||||
|
||||
{
|
||||
samba_dlopen1
|
||||
Memcheck:Cond
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen2
|
||||
Memcheck:Cond
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen3
|
||||
Memcheck:Addr4
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen4
|
||||
Memcheck:Cond
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen5
|
||||
Memcheck:Addr4
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen6
|
||||
Memcheck:Cond
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_dlopen7
|
||||
Memcheck:Addr4
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:_dl_open
|
||||
}
|
||||
|
||||
{
|
||||
samba_libc_dlsym1
|
||||
Memcheck:Addr4
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
obj:/lib/tls/libc-2.3.6.so
|
||||
obj:/lib/ld-2.3.6.so
|
||||
fun:__libc_dlsym
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
########################################################
|
||||
# SAMBA Version #
|
||||
# #
|
||||
# Samba versions are as follows #
|
||||
# 3.0.x New production series #
|
||||
# 3.0.x{tp,pre,rc}y Preview/Testing & RC #
|
||||
# 3.0.x[a-z] Patch releases #
|
||||
# 3.0.x[a-z]-VENDOR-z Vendor patch releases #
|
||||
# #
|
||||
# script/mkversion.sh #
|
||||
# will use this file to create #
|
||||
# include/version.h #
|
||||
# #
|
||||
########################################################
|
||||
|
||||
########################################################
|
||||
# This are the main SAMBA version numbers #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE> #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_MAJOR=3 #
|
||||
# SAMBA_VERSION_MINOR=0 #
|
||||
# SAMBA_VERSION_RELEASE=0 #
|
||||
# -> "3.0.0" #
|
||||
########################################################
|
||||
SAMBA_VERSION_MAJOR=4
|
||||
SAMBA_VERSION_MINOR=0
|
||||
SAMBA_VERSION_RELEASE=0
|
||||
|
||||
########################################################
|
||||
# If a official release has a serious bug #
|
||||
# a security release will have 'a' sufffix #
|
||||
# #
|
||||
# so SAMBA's version will be #
|
||||
# <MAJOR>.<MINOR>.<RELEASE><REVISION> #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_REVISION=a #
|
||||
# -> "2.2.8a" #
|
||||
########################################################
|
||||
SAMBA_VERSION_REVISION=
|
||||
|
||||
########################################################
|
||||
# For 'tp' releases the version will be #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE>tp<TP_RELEASE> #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_TP_RELEASE=1 #
|
||||
# -> "4.0.0tp1" #
|
||||
########################################################
|
||||
SAMBA_VERSION_TP_RELEASE=4
|
||||
|
||||
########################################################
|
||||
# For 'pre' releases the version will be #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE>pre<PRE_RELEASE> #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_PRE_RELEASE=1 #
|
||||
# -> "2.2.9pre1" #
|
||||
########################################################
|
||||
SAMBA_VERSION_PRE_RELEASE=
|
||||
|
||||
########################################################
|
||||
# For 'rc' releases the version will be #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE>rc<RC_RELEASE> #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_RC_RELEASE=1 #
|
||||
# -> "3.0.0rc1" #
|
||||
########################################################
|
||||
SAMBA_VERSION_RC_RELEASE=
|
||||
|
||||
########################################################
|
||||
# To mark SVN snapshots this should be set to 'yes' #
|
||||
# in the development BRANCH, and set to 'no' only in #
|
||||
# the SAMBA_X_X_RELEASE BRANCH #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE>[...]-SVN-build-xxx #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_IS_SVN_SNAPSHOT=yes #
|
||||
# -> "3.0.0-SVN-build-199" #
|
||||
########################################################
|
||||
SAMBA_VERSION_IS_SVN_SNAPSHOT=yes
|
||||
|
||||
########################################################
|
||||
# This is for specifying a release nickname #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_RELEASE_NICKNAME=Nicky Nickname #
|
||||
# smbd --version will then give: #
|
||||
# -> "4.0.0-tp1-VendorVersion (Nicky Nickname)" #
|
||||
########################################################
|
||||
SAMBA_VERSION_RELEASE_NICKNAME=
|
||||
|
||||
########################################################
|
||||
# This can be set by vendors if they want... #
|
||||
# This can be a string constant or a function which #
|
||||
# returns a string (const char *) #
|
||||
# #
|
||||
# <MAJOR>.<MINOR>.<RELEASE>[...]-<VENDOR_SUFFIX> #
|
||||
# #
|
||||
# Note the '-' is automaticaly added #
|
||||
# #
|
||||
# e.g. SAMBA_VERSION_VENDOR_SUFFIX=VendorVersion #
|
||||
# -> "3.0.0rc2-VendorVersion" #
|
||||
# #
|
||||
########################################################
|
||||
SAMBA_VERSION_VENDOR_SUFFIX=
|
||||
SAMBA_VERSION_VENDOR_PATCH=
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
|
||||
dnl Copied from libtool.m4
|
||||
AC_DEFUN(AC_PROG_LD_GNU,
|
||||
[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], ac_cv_prog_gnu_ld,
|
||||
[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
|
||||
if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
|
||||
ac_cv_prog_gnu_ld=yes
|
||||
else
|
||||
ac_cv_prog_gnu_ld=no
|
||||
fi])
|
||||
])
|
||||
|
||||
dnl Removes -I/usr/include/? from given variable
|
||||
AC_DEFUN(CFLAGS_REMOVE_USR_INCLUDE,[
|
||||
ac_new_flags=""
|
||||
for i in [$]$1; do
|
||||
case [$]i in
|
||||
-I/usr/include|-I/usr/include/) ;;
|
||||
*) ac_new_flags="[$]ac_new_flags [$]i" ;;
|
||||
esac
|
||||
done
|
||||
$1=[$]ac_new_flags
|
||||
])
|
||||
|
||||
dnl Removes '-L/usr/lib[/]', '-Wl,-rpath,/usr/lib[/]'
|
||||
dnl and '-Wl,-rpath -Wl,/usr/lib[/]' from given variable
|
||||
AC_DEFUN(LIB_REMOVE_USR_LIB,[
|
||||
ac_new_flags=""
|
||||
l=""
|
||||
for i in [$]$1; do
|
||||
case [$]l[$]i in
|
||||
-L/usr/lib) ;;
|
||||
-L/usr/lib/) ;;
|
||||
-Wl,-rpath,/usr/lib) ;;
|
||||
-Wl,-rpath,/usr/lib/) ;;
|
||||
-Wl,-rpath) l=[$]i;;
|
||||
-Wl,-rpath-Wl,/usr/lib) l="";;
|
||||
-Wl,-rpath-Wl,/usr/lib/) l="";;
|
||||
*)
|
||||
s=" "
|
||||
if test x"[$]ac_new_flags" = x""; then
|
||||
s="";
|
||||
fi
|
||||
if test x"[$]l" = x""; then
|
||||
ac_new_flags="[$]ac_new_flags[$]s[$]i";
|
||||
else
|
||||
ac_new_flags="[$]ac_new_flags[$]s[$]l [$]i";
|
||||
fi
|
||||
l=""
|
||||
;;
|
||||
esac
|
||||
done
|
||||
$1=[$]ac_new_flags
|
||||
])
|
||||
|
||||
m4_include(lib/replace/libreplace.m4)
|
||||
m4_include(build/m4/ax_cflags_gcc_option.m4)
|
||||
m4_include(build/m4/ax_cflags_irix_option.m4)
|
||||
m4_include(build/m4/public.m4)
|
||||
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Bartlett 2001-2002
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "auth/auth.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "build.h"
|
||||
|
||||
/***************************************************************************
|
||||
Set a fixed challenge
|
||||
***************************************************************************/
|
||||
NTSTATUS auth_context_set_challenge(struct auth_context *auth_ctx, const uint8_t chal[8], const char *set_by)
|
||||
{
|
||||
auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
|
||||
NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
|
||||
|
||||
auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
|
||||
NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
Set a fixed challenge
|
||||
***************************************************************************/
|
||||
BOOL auth_challenge_may_be_modified(struct auth_context *auth_ctx)
|
||||
{
|
||||
return auth_ctx->challenge.may_be_modified;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Try to get a challenge out of the various authentication modules.
|
||||
Returns a const char of length 8 bytes.
|
||||
****************************************************************************/
|
||||
_PUBLIC_ NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_t **_chal)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_method_context *method;
|
||||
|
||||
if (auth_ctx->challenge.data.length) {
|
||||
DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
|
||||
auth_ctx->challenge.set_by));
|
||||
*_chal = auth_ctx->challenge.data.data;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
for (method = auth_ctx->methods; method; method = method->next) {
|
||||
DATA_BLOB challenge = data_blob(NULL,0);
|
||||
|
||||
nt_status = method->ops->get_challenge(method, auth_ctx, &challenge);
|
||||
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
if (challenge.length != 8) {
|
||||
DEBUG(0, ("auth_get_challenge: invalid challenge (length %u) by mothod [%s]\n",
|
||||
(unsigned)challenge.length, method->ops->name));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
auth_ctx->challenge.data = challenge;
|
||||
auth_ctx->challenge.set_by = method->ops->name;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!auth_ctx->challenge.set_by) {
|
||||
uint8_t chal[8];
|
||||
generate_random_buffer(chal, 8);
|
||||
|
||||
auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
|
||||
NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
|
||||
auth_ctx->challenge.set_by = "random";
|
||||
|
||||
auth_ctx->challenge.may_be_modified = True;
|
||||
}
|
||||
|
||||
DEBUG(10,("auth_get_challenge: challenge set by %s\n",
|
||||
auth_ctx->challenge.set_by));
|
||||
|
||||
*_chal = auth_ctx->challenge.data.data;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
struct auth_check_password_sync_state {
|
||||
BOOL finished;
|
||||
NTSTATUS status;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
};
|
||||
|
||||
static void auth_check_password_sync_callback(struct auth_check_password_request *req,
|
||||
void *private_data)
|
||||
{
|
||||
struct auth_check_password_sync_state *s = talloc_get_type(private_data,
|
||||
struct auth_check_password_sync_state);
|
||||
|
||||
s->finished = True;
|
||||
s->status = auth_check_password_recv(req, s, &s->server_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a user's Plaintext, LM or NTLM password.
|
||||
* (sync version)
|
||||
*
|
||||
* Check a user's password, as given in the user_info struct and return various
|
||||
* interesting details in the server_info struct.
|
||||
*
|
||||
* The return value takes precedence over the contents of the server_info
|
||||
* struct. When the return is other than NT_STATUS_OK the contents
|
||||
* of that structure is undefined.
|
||||
*
|
||||
* @param auth_ctx Supplies the challenges and some other data.
|
||||
* Must be created with auth_context_create(), and the challenges should be
|
||||
* filled in, either at creation or by calling the challenge geneation
|
||||
* function auth_get_challenge().
|
||||
*
|
||||
* @param user_info Contains the user supplied components, including the passwords.
|
||||
*
|
||||
* @param mem_ctx The parent memory context for the server_info structure
|
||||
*
|
||||
* @param server_info If successful, contains information about the authentication,
|
||||
* including a SAM_ACCOUNT struct describing the user.
|
||||
*
|
||||
* @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
|
||||
*
|
||||
**/
|
||||
|
||||
NTSTATUS auth_check_password(struct auth_context *auth_ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
struct auth_check_password_sync_state *sync_state;
|
||||
NTSTATUS status;
|
||||
|
||||
sync_state = talloc_zero(auth_ctx, struct auth_check_password_sync_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(sync_state);
|
||||
|
||||
auth_check_password_send(auth_ctx, user_info, auth_check_password_sync_callback, sync_state);
|
||||
|
||||
while (!sync_state->finished) {
|
||||
event_loop_once(auth_ctx->event_ctx);
|
||||
}
|
||||
|
||||
status = sync_state->status;
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
*server_info = talloc_steal(mem_ctx, sync_state->server_info);
|
||||
}
|
||||
|
||||
talloc_free(sync_state);
|
||||
return status;
|
||||
}
|
||||
|
||||
struct auth_check_password_request {
|
||||
struct auth_context *auth_ctx;
|
||||
const struct auth_usersupplied_info *user_info;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
struct auth_method_context *method;
|
||||
NTSTATUS status;
|
||||
struct {
|
||||
void (*fn)(struct auth_check_password_request *req, void *private_data);
|
||||
void *private_data;
|
||||
} callback;
|
||||
};
|
||||
|
||||
static void auth_check_password_async_timed_handler(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *ptr)
|
||||
{
|
||||
struct auth_check_password_request *req = talloc_get_type(ptr, struct auth_check_password_request);
|
||||
req->status = req->method->ops->check_password(req->method, req, req->user_info, &req->server_info);
|
||||
req->callback.fn(req, req->callback.private_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a user's Plaintext, LM or NTLM password.
|
||||
* async send hook
|
||||
*
|
||||
* Check a user's password, as given in the user_info struct and return various
|
||||
* interesting details in the server_info struct.
|
||||
*
|
||||
* The return value takes precedence over the contents of the server_info
|
||||
* struct. When the return is other than NT_STATUS_OK the contents
|
||||
* of that structure is undefined.
|
||||
*
|
||||
* @param auth_ctx Supplies the challenges and some other data.
|
||||
* Must be created with make_auth_context(), and the challenges should be
|
||||
* filled in, either at creation or by calling the challenge geneation
|
||||
* function auth_get_challenge().
|
||||
*
|
||||
* @param user_info Contains the user supplied components, including the passwords.
|
||||
*
|
||||
* @param callback A callback function which will be called when the operation is finished.
|
||||
* The callback function needs to call auth_check_password_recv() to get the return values
|
||||
*
|
||||
* @param private_data A private pointer which will ba passed to the callback function
|
||||
*
|
||||
**/
|
||||
|
||||
void auth_check_password_send(struct auth_context *auth_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
void (*callback)(struct auth_check_password_request *req, void *private_data),
|
||||
void *private_data)
|
||||
{
|
||||
/* if all the modules say 'not for me' this is reasonable */
|
||||
NTSTATUS nt_status;
|
||||
struct auth_method_context *method;
|
||||
const uint8_t *challenge;
|
||||
struct auth_usersupplied_info *user_info_tmp;
|
||||
struct auth_check_password_request *req = NULL;
|
||||
|
||||
DEBUG(3, ("auth_check_password_send: Checking password for unmapped user [%s]\\[%s]@[%s]\n",
|
||||
user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
|
||||
|
||||
req = talloc_zero(auth_ctx, struct auth_check_password_request);
|
||||
if (!req) {
|
||||
callback(NULL, private_data);
|
||||
return;
|
||||
}
|
||||
req->auth_ctx = auth_ctx;
|
||||
req->user_info = user_info;
|
||||
req->callback.fn = callback;
|
||||
req->callback.private_data = private_data;
|
||||
|
||||
if (!user_info->mapped_state) {
|
||||
nt_status = map_user_info(req, user_info, &user_info_tmp);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) goto failed;
|
||||
user_info = user_info_tmp;
|
||||
req->user_info = user_info_tmp;
|
||||
}
|
||||
|
||||
DEBUGADD(3,("auth_check_password_send: mapped user is: [%s]\\[%s]@[%s]\n",
|
||||
user_info->mapped.domain_name, user_info->mapped.account_name, user_info->workstation_name));
|
||||
|
||||
nt_status = auth_get_challenge(auth_ctx, &challenge);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(0, ("auth_check_password_send: Invalid challenge (length %u) stored for this auth context set_by %s - cannot continue: %s\n",
|
||||
(unsigned)auth_ctx->challenge.data.length, auth_ctx->challenge.set_by, nt_errstr(nt_status)));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (auth_ctx->challenge.set_by) {
|
||||
DEBUG(10, ("auth_check_password_send: auth_context challenge created by %s\n",
|
||||
auth_ctx->challenge.set_by));
|
||||
}
|
||||
|
||||
DEBUG(10, ("auth_check_password_send: challenge is: \n"));
|
||||
dump_data(5, auth_ctx->challenge.data.data, auth_ctx->challenge.data.length);
|
||||
|
||||
nt_status = NT_STATUS_NO_SUCH_USER; /* If all the modules say 'not for me', then this is reasonable */
|
||||
for (method = auth_ctx->methods; method; method = method->next) {
|
||||
NTSTATUS result;
|
||||
struct timed_event *te = NULL;
|
||||
|
||||
/* check if the module wants to chek the password */
|
||||
result = method->ops->want_check(method, req, user_info);
|
||||
if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
|
||||
DEBUG(11,("auth_check_password_send: %s had nothing to say\n", method->ops->name));
|
||||
continue;
|
||||
}
|
||||
|
||||
nt_status = result;
|
||||
req->method = method;
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) break;
|
||||
|
||||
te = event_add_timed(auth_ctx->event_ctx, req,
|
||||
timeval_zero(),
|
||||
auth_check_password_async_timed_handler, req);
|
||||
if (!te) {
|
||||
nt_status = NT_STATUS_NO_MEMORY;
|
||||
goto failed;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
failed:
|
||||
req->status = nt_status;
|
||||
req->callback.fn(req, req->callback.private_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a user's Plaintext, LM or NTLM password.
|
||||
* async receive function
|
||||
*
|
||||
* The return value takes precedence over the contents of the server_info
|
||||
* struct. When the return is other than NT_STATUS_OK the contents
|
||||
* of that structure is undefined.
|
||||
*
|
||||
*
|
||||
* @param req The async auth_check_password state, passes to the callers callback function
|
||||
*
|
||||
* @param mem_ctx The parent memory context for the server_info structure
|
||||
*
|
||||
* @param server_info If successful, contains information about the authentication,
|
||||
* including a SAM_ACCOUNT struct describing the user.
|
||||
*
|
||||
* @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
|
||||
*
|
||||
**/
|
||||
|
||||
NTSTATUS auth_check_password_recv(struct auth_check_password_request *req,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
if (NT_STATUS_IS_OK(req->status)) {
|
||||
DEBUG(5,("auth_check_password_recv: %s authentication for user [%s\\%s] succeeded\n",
|
||||
req->method->ops->name, req->server_info->domain_name, req->server_info->account_name));
|
||||
|
||||
*server_info = talloc_steal(mem_ctx, req->server_info);
|
||||
} else {
|
||||
DEBUG(2,("auth_check_password_recv: %s authentication for user [%s\\%s] FAILED with error %s\n",
|
||||
(req->method ? req->method->ops->name : "NO_METHOD"),
|
||||
req->user_info->mapped.domain_name,
|
||||
req->user_info->mapped.account_name,
|
||||
nt_errstr(req->status)));
|
||||
}
|
||||
|
||||
status = req->status;
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
Make a auth_info struct for the auth subsystem
|
||||
***************************************************************************/
|
||||
NTSTATUS auth_context_create(TALLOC_CTX *mem_ctx, const char **methods,
|
||||
struct event_context *ev,
|
||||
struct messaging_context *msg,
|
||||
struct auth_context **auth_ctx)
|
||||
{
|
||||
int i;
|
||||
struct auth_context *ctx;
|
||||
|
||||
if (!methods) {
|
||||
DEBUG(0,("auth_context_create: No auth method list!?\n"));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!ev) {
|
||||
DEBUG(0,("auth_context_create: called with out event context\n"));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (!msg) {
|
||||
DEBUG(0,("auth_context_create: called with out messaging context\n"));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ctx = talloc(mem_ctx, struct auth_context);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ctx);
|
||||
ctx->challenge.set_by = NULL;
|
||||
ctx->challenge.may_be_modified = False;
|
||||
ctx->challenge.data = data_blob(NULL, 0);
|
||||
ctx->methods = NULL;
|
||||
ctx->event_ctx = ev;
|
||||
ctx->msg_ctx = msg;
|
||||
|
||||
for (i=0; methods[i] ; i++) {
|
||||
struct auth_method_context *method;
|
||||
|
||||
method = talloc(ctx, struct auth_method_context);
|
||||
NT_STATUS_HAVE_NO_MEMORY(method);
|
||||
|
||||
method->ops = auth_backend_byname(methods[i]);
|
||||
if (!method->ops) {
|
||||
DEBUG(1,("auth_context_create: failed to find method=%s\n",
|
||||
methods[i]));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
method->auth_ctx = ctx;
|
||||
method->depth = i;
|
||||
DLIST_ADD_END(ctx->methods, method, struct auth_method_context *);
|
||||
}
|
||||
|
||||
if (!ctx->methods) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
*auth_ctx = ctx;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* the list of currently registered AUTH backends */
|
||||
static struct auth_backend {
|
||||
const struct auth_operations *ops;
|
||||
} *backends = NULL;
|
||||
static int num_backends;
|
||||
|
||||
/*
|
||||
register a AUTH backend.
|
||||
|
||||
The 'name' can be later used by other backends to find the operations
|
||||
structure for this backend.
|
||||
*/
|
||||
NTSTATUS auth_register(const void *_ops)
|
||||
{
|
||||
const struct auth_operations *ops = _ops;
|
||||
struct auth_operations *new_ops;
|
||||
|
||||
if (auth_backend_byname(ops->name) != NULL) {
|
||||
/* its already registered! */
|
||||
DEBUG(0,("AUTH backend '%s' already registered\n",
|
||||
ops->name));
|
||||
return NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
|
||||
backends = realloc_p(backends, struct auth_backend, num_backends+1);
|
||||
if (!backends) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
new_ops = smb_xmemdup(ops, sizeof(*ops));
|
||||
new_ops->name = smb_xstrdup(ops->name);
|
||||
|
||||
backends[num_backends].ops = new_ops;
|
||||
|
||||
num_backends++;
|
||||
|
||||
DEBUG(3,("AUTH backend '%s' registered\n",
|
||||
ops->name));
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
return the operations structure for a named backend of the specified type
|
||||
*/
|
||||
const struct auth_operations *auth_backend_byname(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0;i<num_backends;i++) {
|
||||
if (strcmp(backends[i].ops->name, name) == 0) {
|
||||
return backends[i].ops;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
return the AUTH interface version, and the size of some critical types
|
||||
This can be used by backends to either detect compilation errors, or provide
|
||||
multiple implementations for different smbd compilation options in one module
|
||||
*/
|
||||
const struct auth_critical_sizes *auth_interface_version(void)
|
||||
{
|
||||
static const struct auth_critical_sizes critical_sizes = {
|
||||
AUTH_INTERFACE_VERSION,
|
||||
sizeof(struct auth_operations),
|
||||
sizeof(struct auth_method_context),
|
||||
sizeof(struct auth_context),
|
||||
sizeof(struct auth_usersupplied_info),
|
||||
sizeof(struct auth_serversupplied_info)
|
||||
};
|
||||
|
||||
return &critical_sizes;
|
||||
}
|
||||
|
||||
NTSTATUS auth_init(void)
|
||||
{
|
||||
static BOOL initialized = False;
|
||||
|
||||
init_module_fn static_init[] = STATIC_auth_MODULES;
|
||||
init_module_fn *shared_init;
|
||||
|
||||
if (initialized) return NT_STATUS_OK;
|
||||
initialized = True;
|
||||
|
||||
shared_init = load_samba_modules(NULL, "auth");
|
||||
|
||||
run_init_functions(static_init);
|
||||
run_init_functions(shared_init);
|
||||
|
||||
talloc_free(shared_init);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS server_service_auth_init(void)
|
||||
{
|
||||
return auth_init();
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Standardised Authentication types
|
||||
Copyright (C) Andrew Bartlett 2001
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _SAMBA_AUTH_H
|
||||
#define _SAMBA_AUTH_H
|
||||
|
||||
union netr_Validation;
|
||||
struct netr_SamBaseInfo;
|
||||
struct netr_SamInfo3;
|
||||
|
||||
/* modules can use the following to determine if the interface has changed
|
||||
* please increment the version number after each interface change
|
||||
* with a comment and maybe update struct auth_critical_sizes.
|
||||
*/
|
||||
/* version 1 - version from samba 3.0 - metze */
|
||||
/* version 2 - initial samba4 version - metze */
|
||||
/* version 3 - subsequent samba4 version - abartlet */
|
||||
/* version 4 - subsequent samba4 version - metze */
|
||||
/* version 0 - till samba4 is stable - metze */
|
||||
#define AUTH_INTERFACE_VERSION 0
|
||||
|
||||
#define USER_INFO_CASE_INSENSITIVE_USERNAME 0x01 /* username may be in any case */
|
||||
#define USER_INFO_CASE_INSENSITIVE_PASSWORD 0x02 /* password may be in any case */
|
||||
#define USER_INFO_DONT_CHECK_UNIX_ACCOUNT 0x04 /* dont check unix account status */
|
||||
#define USER_INFO_INTERACTIVE_LOGON 0x08 /* dont check unix account status */
|
||||
|
||||
enum auth_password_state {
|
||||
AUTH_PASSWORD_RESPONSE,
|
||||
AUTH_PASSWORD_HASH,
|
||||
AUTH_PASSWORD_PLAIN
|
||||
};
|
||||
|
||||
struct auth_usersupplied_info
|
||||
{
|
||||
const char *workstation_name;
|
||||
struct socket_address *remote_host;
|
||||
|
||||
uint32_t logon_parameters;
|
||||
|
||||
BOOL mapped_state;
|
||||
/* the values the client gives us */
|
||||
struct {
|
||||
const char *account_name;
|
||||
const char *domain_name;
|
||||
} client, mapped;
|
||||
|
||||
enum auth_password_state password_state;
|
||||
|
||||
union {
|
||||
struct {
|
||||
DATA_BLOB lanman;
|
||||
DATA_BLOB nt;
|
||||
} response;
|
||||
struct {
|
||||
struct samr_Password *lanman;
|
||||
struct samr_Password *nt;
|
||||
} hash;
|
||||
|
||||
char *plaintext;
|
||||
} password;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct auth_serversupplied_info
|
||||
{
|
||||
struct dom_sid *account_sid;
|
||||
struct dom_sid *primary_group_sid;
|
||||
|
||||
size_t n_domain_groups;
|
||||
struct dom_sid **domain_groups;
|
||||
|
||||
DATA_BLOB user_session_key;
|
||||
DATA_BLOB lm_session_key;
|
||||
|
||||
const char *account_name;
|
||||
const char *domain_name;
|
||||
|
||||
const char *full_name;
|
||||
const char *logon_script;
|
||||
const char *profile_path;
|
||||
const char *home_directory;
|
||||
const char *home_drive;
|
||||
const char *logon_server;
|
||||
|
||||
NTTIME last_logon;
|
||||
NTTIME last_logoff;
|
||||
NTTIME acct_expiry;
|
||||
NTTIME last_password_change;
|
||||
NTTIME allow_password_change;
|
||||
NTTIME force_password_change;
|
||||
|
||||
uint16_t logon_count;
|
||||
uint16_t bad_password_count;
|
||||
|
||||
uint32_t acct_flags;
|
||||
|
||||
BOOL authenticated;
|
||||
};
|
||||
|
||||
struct auth_session_info {
|
||||
struct security_token *security_token;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
DATA_BLOB session_key;
|
||||
struct cli_credentials *credentials;
|
||||
};
|
||||
|
||||
struct auth_method_context;
|
||||
struct auth_check_password_request;
|
||||
|
||||
struct auth_operations {
|
||||
const char *name;
|
||||
|
||||
/* If you are using this interface, then you are probably
|
||||
* getting something wrong. This interface is only for
|
||||
* security=server, and makes a number of compromises to allow
|
||||
* that. It is not compatible with being a PDC. */
|
||||
|
||||
NTSTATUS (*get_challenge)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge);
|
||||
|
||||
/* Given the user supplied info, check if this backend want to handle the password checking */
|
||||
|
||||
NTSTATUS (*want_check)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info);
|
||||
|
||||
/* Given the user supplied info, check a password */
|
||||
|
||||
NTSTATUS (*check_password)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info);
|
||||
};
|
||||
|
||||
struct auth_method_context {
|
||||
struct auth_method_context *prev, *next;
|
||||
struct auth_context *auth_ctx;
|
||||
const struct auth_operations *ops;
|
||||
int depth;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
struct auth_context {
|
||||
struct {
|
||||
/* Who set this up in the first place? */
|
||||
const char *set_by;
|
||||
|
||||
BOOL may_be_modified;
|
||||
|
||||
DATA_BLOB data;
|
||||
} challenge;
|
||||
|
||||
/* methods, in the order they should be called */
|
||||
struct auth_method_context *methods;
|
||||
|
||||
/* the event context to use for calls that can block */
|
||||
struct event_context *event_ctx;
|
||||
|
||||
/* the messaging context which can be used by backends */
|
||||
struct messaging_context *msg_ctx;
|
||||
};
|
||||
|
||||
/* this structure is used by backends to determine the size of some critical types */
|
||||
struct auth_critical_sizes {
|
||||
int interface_version;
|
||||
int sizeof_auth_operations;
|
||||
int sizeof_auth_methods;
|
||||
int sizeof_auth_context;
|
||||
int sizeof_auth_usersupplied_info;
|
||||
int sizeof_auth_serversupplied_info;
|
||||
};
|
||||
|
||||
NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
|
||||
enum auth_password_state to_state,
|
||||
const struct auth_usersupplied_info *user_info_in,
|
||||
const struct auth_usersupplied_info **user_info_encrypted);
|
||||
|
||||
#include "auth/auth_proto.h"
|
||||
|
||||
#endif /* _SMBAUTH_H_ */
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Anonymous Authentification
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
/**
|
||||
* Return a anonymous logon for anonymous users (username = "")
|
||||
*
|
||||
* Typically used as the first module in the auth chain, this allows
|
||||
* anonymou logons to be dealt with in one place. Non-anonymou logons 'fail'
|
||||
* and pass onto the next module.
|
||||
**/
|
||||
static NTSTATUS anonymous_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
if (user_info->client.account_name && *user_info->client.account_name) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a anonymous logon for anonymous users (username = "")
|
||||
*
|
||||
* Typically used as the first module in the auth chain, this allows
|
||||
* anonymou logons to be dealt with in one place. Non-anonymou logons 'fail'
|
||||
* and pass onto the next module.
|
||||
**/
|
||||
static NTSTATUS anonymous_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
return auth_anonymous_server_info(mem_ctx, _server_info);
|
||||
}
|
||||
|
||||
static struct auth_operations anonymous_auth_ops = {
|
||||
.name = "anonymous",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = anonymous_want_check,
|
||||
.check_password = anonymous_check_password
|
||||
};
|
||||
|
||||
NTSTATUS auth_anonymous_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
ret = auth_register(&anonymous_auth_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'anonymous' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Generic authentication types
|
||||
Copyright (C) Andrew Bartlett 2001-2002
|
||||
Copyright (C) Jelmer Vernooij 2002
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "librpc/gen_ndr/ndr_samr.h"
|
||||
|
||||
static NTSTATUS name_to_ntstatus_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an error based on username
|
||||
*
|
||||
* This function allows the testing of obsure errors, as well as the generation
|
||||
* of NT_STATUS -> DOS error mapping tables.
|
||||
*
|
||||
* This module is of no value to end-users.
|
||||
*
|
||||
* The password is ignored.
|
||||
*
|
||||
* @return An NTSTATUS value based on the username
|
||||
**/
|
||||
|
||||
static NTSTATUS name_to_ntstatus_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
uint32_t error_num;
|
||||
const char *user;
|
||||
|
||||
user = user_info->client.account_name;
|
||||
|
||||
if (strncasecmp("NT_STATUS", user, strlen("NT_STATUS")) == 0) {
|
||||
nt_status = nt_status_string_to_code(user);
|
||||
} else {
|
||||
error_num = strtoul(user, NULL, 16);
|
||||
DEBUG(5,("name_to_ntstatus_check_password: Error for user %s was 0x%08X\n", user, error_num));
|
||||
nt_status = NT_STATUS(error_num);
|
||||
}
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
|
||||
|
||||
/* is this correct? */
|
||||
server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
|
||||
|
||||
server_info->n_domain_groups = 0;
|
||||
server_info->domain_groups = NULL;
|
||||
|
||||
/* annoying, but the Anonymous really does have a session key,
|
||||
and it is all zeros! */
|
||||
server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
|
||||
|
||||
server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
|
||||
|
||||
data_blob_clear(&server_info->user_session_key);
|
||||
data_blob_clear(&server_info->lm_session_key);
|
||||
|
||||
server_info->account_name = talloc_asprintf(server_info, "NAME TO NTSTATUS %s ANONYMOUS LOGON", user);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
|
||||
server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
|
||||
|
||||
server_info->full_name = talloc_asprintf(server_info, "NAME TO NTSTATUS %s Anonymous Logon", user);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
|
||||
|
||||
server_info->logon_script = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
|
||||
|
||||
server_info->profile_path = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
|
||||
|
||||
server_info->home_directory = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
|
||||
|
||||
server_info->home_drive = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
|
||||
|
||||
server_info->last_logon = 0;
|
||||
server_info->last_logoff = 0;
|
||||
server_info->acct_expiry = 0;
|
||||
server_info->last_password_change = 0;
|
||||
server_info->allow_password_change = 0;
|
||||
server_info->force_password_change = 0;
|
||||
|
||||
server_info->logon_count = 0;
|
||||
server_info->bad_password_count = 0;
|
||||
|
||||
server_info->acct_flags = ACB_NORMAL;
|
||||
|
||||
server_info->authenticated = False;
|
||||
|
||||
*_server_info = server_info;
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
static struct auth_operations name_to_ntstatus_auth_ops = {
|
||||
.name = "name_to_ntstatus",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = name_to_ntstatus_want_check,
|
||||
.check_password = name_to_ntstatus_check_password
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a 'fixed' challenge instead of a variable one.
|
||||
*
|
||||
* The idea of this function is to make packet snifs consistant
|
||||
* with a fixed challenge, so as to aid debugging.
|
||||
*
|
||||
* This module is of no value to end-users.
|
||||
*
|
||||
* This module does not actually authenticate the user, but
|
||||
* just pretenteds to need a specified challenge.
|
||||
* This module removes *all* security from the challenge-response system
|
||||
*
|
||||
* @return NT_STATUS_UNSUCCESSFUL
|
||||
**/
|
||||
static NTSTATUS fixed_challenge_get_challenge(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
const char *challenge = "I am a teapot";
|
||||
|
||||
blob = data_blob_talloc(mem_ctx, challenge, 8);
|
||||
NT_STATUS_HAVE_NO_MEMORY(blob.data);
|
||||
|
||||
*_blob = blob;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS fixed_challenge_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
/* don't handle any users */
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static NTSTATUS fixed_challenge_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
/* don't handle any users */
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
static struct auth_operations fixed_challenge_auth_ops = {
|
||||
.name = "fixed_challenge",
|
||||
.get_challenge = fixed_challenge_get_challenge,
|
||||
.want_check = fixed_challenge_want_check,
|
||||
.check_password = fixed_challenge_check_password
|
||||
};
|
||||
|
||||
NTSTATUS auth_developer_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
ret = auth_register(&name_to_ntstatus_auth_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'name_to_ntstatus' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = auth_register(&fixed_challenge_auth_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'fixed_challenge' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
|
||||
Copyright (C) Gerald Carter 2003
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon.h"
|
||||
#include "system/time.h"
|
||||
#include "db_wrap.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/auth_sam.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
|
||||
extern const char *user_attrs[];
|
||||
extern const char *domain_ref_attrs[];
|
||||
|
||||
/****************************************************************************
|
||||
Look for the specified user in the sam, return ldb result structures
|
||||
****************************************************************************/
|
||||
|
||||
static NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
|
||||
const char *account_name,
|
||||
const char *domain_name,
|
||||
struct ldb_message ***ret_msgs,
|
||||
struct ldb_message ***ret_msgs_domain_ref)
|
||||
{
|
||||
struct ldb_message **msgs_tmp;
|
||||
struct ldb_message **msgs;
|
||||
struct ldb_message **msgs_domain_ref;
|
||||
struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
|
||||
|
||||
int ret;
|
||||
int ret_domain;
|
||||
|
||||
struct ldb_dn *domain_dn = NULL;
|
||||
|
||||
if (domain_name) {
|
||||
char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name);
|
||||
/* find the domain's DN */
|
||||
ret_domain = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &msgs_domain_ref, domain_ref_attrs,
|
||||
"(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
|
||||
escaped_domain, escaped_domain);
|
||||
if (ret_domain == -1) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (ret_domain == 0) {
|
||||
DEBUG(3,("sam_search_user: Couldn't find domain [%s] in samdb.\n",
|
||||
domain_name));
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
if (ret_domain > 1) {
|
||||
DEBUG(0,("Found %d records matching domain [%s]\n",
|
||||
ret_domain, domain_name));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msgs_domain_ref[0], "nCName", NULL);
|
||||
}
|
||||
|
||||
/* pull the user attributes */
|
||||
ret = gendb_search(sam_ctx, mem_ctx, domain_dn, &msgs, user_attrs,
|
||||
"(&(sAMAccountName=%s)(objectclass=user))",
|
||||
ldb_binary_encode_string(mem_ctx, account_name));
|
||||
if (ret == -1) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
DEBUG(3,("sam_search_user: Couldn't find user [%s\\%s] in samdb, under %s\n",
|
||||
domain_name, account_name, ldb_dn_get_linearized(domain_dn)));
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
if (ret > 1) {
|
||||
DEBUG(0,("Found %d records matching user [%s]\n", ret, account_name));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (!domain_name) {
|
||||
struct dom_sid *domain_sid;
|
||||
|
||||
domain_sid = samdb_result_sid_prefix(mem_ctx, msgs[0], "objectSid");
|
||||
if (!domain_sid) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
/* find the domain's DN */
|
||||
ret = gendb_search(sam_ctx, mem_ctx, NULL, &msgs_tmp, NULL,
|
||||
"(&(objectSid=%s)(objectClass=domain))",
|
||||
ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
|
||||
if (ret == -1) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
DEBUG(3,("check_sam_security: Couldn't find domain_sid [%s] in passdb file.\n",
|
||||
dom_sid_string(mem_ctx, domain_sid)));
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
if (ret > 1) {
|
||||
DEBUG(0,("Found %d records matching domain_sid [%s]\n",
|
||||
ret, dom_sid_string(mem_ctx, domain_sid)));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
ret_domain = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &msgs_domain_ref, domain_ref_attrs,
|
||||
"(nCName=%s)", ldb_dn_alloc_linearized(msgs_tmp, msgs_tmp[0]->dn));
|
||||
|
||||
if (ret_domain == -1) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
if (ret_domain == 0) {
|
||||
DEBUG(3,("check_sam_security: Couldn't find domain [%s] in passdb file.\n",
|
||||
ldb_dn_get_linearized(msgs_tmp[0]->dn)));
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
if (ret_domain > 1) {
|
||||
DEBUG(0,("Found %d records matching domain [%s]\n",
|
||||
ret_domain, ldb_dn_get_linearized(msgs_tmp[0]->dn)));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*ret_msgs = msgs;
|
||||
*ret_msgs_domain_ref = msgs_domain_ref;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Do a specific test for an smb password being correct, given a smb_password and
|
||||
the lanman and NT responses.
|
||||
****************************************************************************/
|
||||
static NTSTATUS authsam_password_ok(struct auth_context *auth_context,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
uint16_t acct_flags,
|
||||
const struct samr_Password *lm_pwd,
|
||||
const struct samr_Password *nt_pwd,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
DATA_BLOB *user_sess_key,
|
||||
DATA_BLOB *lm_sess_key)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (acct_flags & ACB_PWNOTREQ) {
|
||||
if (lp_null_passwords()) {
|
||||
DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n",
|
||||
user_info->mapped.account_name));
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n",
|
||||
user_info->mapped.account_name));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
switch (user_info->password_state) {
|
||||
case AUTH_PASSWORD_PLAIN:
|
||||
{
|
||||
const struct auth_usersupplied_info *user_info_temp;
|
||||
status = encrypt_user_info(mem_ctx, auth_context,
|
||||
AUTH_PASSWORD_HASH,
|
||||
user_info, &user_info_temp);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("Failed to convert plaintext password to password HASH: %s\n", nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
user_info = user_info_temp;
|
||||
|
||||
/*fall through*/
|
||||
}
|
||||
case AUTH_PASSWORD_HASH:
|
||||
*lm_sess_key = data_blob(NULL, 0);
|
||||
*user_sess_key = data_blob(NULL, 0);
|
||||
status = hash_password_check(mem_ctx,
|
||||
user_info->password.hash.lanman,
|
||||
user_info->password.hash.nt,
|
||||
user_info->mapped.account_name,
|
||||
lm_pwd, nt_pwd);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
break;
|
||||
|
||||
case AUTH_PASSWORD_RESPONSE:
|
||||
status = ntlm_password_check(mem_ctx, user_info->logon_parameters,
|
||||
&auth_context->challenge.data,
|
||||
&user_info->password.response.lanman,
|
||||
&user_info->password.response.nt,
|
||||
user_info->mapped.account_name,
|
||||
user_info->client.account_name,
|
||||
user_info->client.domain_name,
|
||||
lm_pwd, nt_pwd,
|
||||
user_sess_key, lm_sess_key);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
break;
|
||||
}
|
||||
|
||||
if (user_sess_key && user_sess_key->data) {
|
||||
talloc_steal(auth_context, user_sess_key->data);
|
||||
}
|
||||
if (lm_sess_key && lm_sess_key->data) {
|
||||
talloc_steal(auth_context, lm_sess_key->data);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static NTSTATUS authsam_authenticate(struct auth_context *auth_context,
|
||||
TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
|
||||
struct ldb_message **msgs,
|
||||
struct ldb_message **msgs_domain_ref,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key)
|
||||
{
|
||||
struct samr_Password *lm_pwd, *nt_pwd;
|
||||
NTSTATUS nt_status;
|
||||
uint16_t acct_flags = samdb_result_acct_flags(msgs[0], "userAccountControl");
|
||||
|
||||
/* Quit if the account was locked out. */
|
||||
if (acct_flags & ACB_AUTOLOCK) {
|
||||
DEBUG(3,("check_sam_security: Account for user %s was locked out.\n",
|
||||
user_info->mapped.account_name));
|
||||
return NT_STATUS_ACCOUNT_LOCKED_OUT;
|
||||
}
|
||||
|
||||
/* You can only do an interactive login to normal accounts */
|
||||
if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
|
||||
if (!(acct_flags & ACB_NORMAL)) {
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
}
|
||||
|
||||
nt_status = samdb_result_passwords(mem_ctx, msgs[0], &lm_pwd, &nt_pwd);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
nt_status = authsam_password_ok(auth_context, mem_ctx,
|
||||
acct_flags, lm_pwd, nt_pwd,
|
||||
user_info, user_sess_key, lm_sess_key);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
nt_status = authsam_account_ok(mem_ctx, sam_ctx,
|
||||
user_info->logon_parameters,
|
||||
msgs[0],
|
||||
msgs_domain_ref[0],
|
||||
user_info->workstation_name,
|
||||
user_info->mapped.account_name);
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static NTSTATUS authsam_check_password_internals(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char *domain,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
const char *account_name = user_info->mapped.account_name;
|
||||
struct ldb_message **msgs;
|
||||
struct ldb_message **domain_ref_msgs;
|
||||
struct ldb_context *sam_ctx;
|
||||
DATA_BLOB user_sess_key, lm_sess_key;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
|
||||
if (!account_name || !*account_name) {
|
||||
/* 'not for me' */
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
tmp_ctx = talloc_new(mem_ctx);
|
||||
if (!tmp_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
sam_ctx = samdb_connect(tmp_ctx, system_session(mem_ctx));
|
||||
if (sam_ctx == NULL) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INVALID_SYSTEM_SERVICE;
|
||||
}
|
||||
|
||||
nt_status = authsam_search_account(tmp_ctx, sam_ctx, account_name, domain, &msgs, &domain_ref_msgs);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = authsam_authenticate(ctx->auth_ctx, tmp_ctx, sam_ctx, msgs, domain_ref_msgs, user_info,
|
||||
&user_sess_key, &lm_sess_key);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, msgs[0], domain_ref_msgs[0],
|
||||
user_sess_key, lm_sess_key,
|
||||
server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
talloc_steal(mem_ctx, *server_info);
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS authsam_ignoredomain_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS authsam_ignoredomain_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
return authsam_check_password_internals(ctx, mem_ctx, NULL, user_info, server_info);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Check SAM security (above) but with a few extra checks.
|
||||
****************************************************************************/
|
||||
static NTSTATUS authsam_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
BOOL is_local_name, is_my_domain;
|
||||
|
||||
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
is_local_name = is_myname(user_info->mapped.domain_name);
|
||||
is_my_domain = strequal(user_info->mapped.domain_name, lp_workgroup());
|
||||
|
||||
/* check whether or not we service this domain/workgroup name */
|
||||
switch (lp_server_role()) {
|
||||
case ROLE_STANDALONE:
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case ROLE_DOMAIN_MEMBER:
|
||||
if (!is_local_name) {
|
||||
DEBUG(6,("authsam_check_password: %s is not one of my local names (DOMAIN_MEMBER)\n",
|
||||
user_info->mapped.domain_name));
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case ROLE_DOMAIN_PDC:
|
||||
case ROLE_DOMAIN_BDC:
|
||||
if (!is_local_name && !is_my_domain) {
|
||||
DEBUG(6,("authsam_check_password: %s is not one of my local names or domain name (DC)\n",
|
||||
user_info->mapped.domain_name));
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(6,("authsam_check_password: lp_server_role() has an undefined value\n"));
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Check SAM security (above) but with a few extra checks.
|
||||
****************************************************************************/
|
||||
static NTSTATUS authsam_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
const char *domain;
|
||||
|
||||
/* check whether or not we service this domain/workgroup name */
|
||||
switch (lp_server_role()) {
|
||||
case ROLE_STANDALONE:
|
||||
case ROLE_DOMAIN_MEMBER:
|
||||
domain = lp_netbios_name();
|
||||
break;
|
||||
|
||||
case ROLE_DOMAIN_PDC:
|
||||
case ROLE_DOMAIN_BDC:
|
||||
domain = lp_workgroup();
|
||||
break;
|
||||
|
||||
default:
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
return authsam_check_password_internals(ctx, mem_ctx, domain, user_info, server_info);
|
||||
}
|
||||
|
||||
static const struct auth_operations sam_ignoredomain_ops = {
|
||||
.name = "sam_ignoredomain",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = authsam_ignoredomain_want_check,
|
||||
.check_password = authsam_ignoredomain_check_password
|
||||
};
|
||||
|
||||
static const struct auth_operations sam_ops = {
|
||||
.name = "sam",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = authsam_want_check,
|
||||
.check_password = authsam_check_password
|
||||
};
|
||||
|
||||
NTSTATUS auth_sam_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
ret = auth_register(&sam_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'sam' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = auth_register(&sam_ignoredomain_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'sam_ignoredomain' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Convert a server info struct into the form for PAC and NETLOGON replies
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
|
||||
Copyright (C) Stefan Metzmacher <metze@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon.h"
|
||||
|
||||
NTSTATUS auth_convert_server_info_sambaseinfo(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
struct netr_SamBaseInfo **_sam)
|
||||
{
|
||||
struct netr_SamBaseInfo *sam = talloc_zero(mem_ctx, struct netr_SamBaseInfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(sam);
|
||||
|
||||
sam->domain_sid = dom_sid_dup(mem_ctx, server_info->account_sid);
|
||||
NT_STATUS_HAVE_NO_MEMORY(sam->domain_sid);
|
||||
sam->domain_sid->num_auths--;
|
||||
|
||||
sam->last_logon = server_info->last_logon;
|
||||
sam->last_logoff = server_info->last_logoff;
|
||||
sam->acct_expiry = server_info->acct_expiry;
|
||||
sam->last_password_change = server_info->last_password_change;
|
||||
sam->allow_password_change = server_info->allow_password_change;
|
||||
sam->force_password_change = server_info->force_password_change;
|
||||
|
||||
sam->account_name.string = server_info->account_name;
|
||||
sam->full_name.string = server_info->full_name;
|
||||
sam->logon_script.string = server_info->logon_script;
|
||||
sam->profile_path.string = server_info->profile_path;
|
||||
sam->home_directory.string = server_info->home_directory;
|
||||
sam->home_drive.string = server_info->home_drive;
|
||||
|
||||
sam->logon_count = server_info->logon_count;
|
||||
sam->bad_password_count = sam->bad_password_count;
|
||||
sam->rid = server_info->account_sid->sub_auths[server_info->account_sid->num_auths-1];
|
||||
sam->primary_gid = server_info->primary_group_sid->sub_auths[server_info->primary_group_sid->num_auths-1];
|
||||
|
||||
sam->groups.count = 0;
|
||||
sam->groups.rids = NULL;
|
||||
|
||||
if (server_info->n_domain_groups > 0) {
|
||||
int i;
|
||||
sam->groups.rids = talloc_array(sam, struct samr_RidWithAttribute,
|
||||
server_info->n_domain_groups);
|
||||
|
||||
if (sam->groups.rids == NULL)
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
|
||||
for (i=0; i<server_info->n_domain_groups; i++) {
|
||||
struct dom_sid *group_sid = server_info->domain_groups[i];
|
||||
if (!dom_sid_in_domain(sam->domain_sid, group_sid)) {
|
||||
/* We handle this elsewhere */
|
||||
continue;
|
||||
}
|
||||
sam->groups.rids[sam->groups.count].rid =
|
||||
group_sid->sub_auths[group_sid->num_auths-1];
|
||||
|
||||
sam->groups.rids[sam->groups.count].attributes =
|
||||
SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
|
||||
sam->groups.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
sam->user_flags = 0; /* TODO: w2k3 uses 0x120. We know 0x20
|
||||
* as extra sids (PAC doc) but what is
|
||||
* 0x100? */
|
||||
sam->acct_flags = server_info->acct_flags;
|
||||
sam->logon_server.string = server_info->logon_server;
|
||||
sam->domain.string = server_info->domain_name;
|
||||
|
||||
ZERO_STRUCT(sam->unknown);
|
||||
|
||||
ZERO_STRUCT(sam->key);
|
||||
if (server_info->user_session_key.length == sizeof(sam->key.key)) {
|
||||
memcpy(sam->key.key, server_info->user_session_key.data, sizeof(sam->key.key));
|
||||
}
|
||||
|
||||
ZERO_STRUCT(sam->LMSessKey);
|
||||
if (server_info->lm_session_key.length == sizeof(sam->LMSessKey.key)) {
|
||||
memcpy(sam->LMSessKey.key, server_info->lm_session_key.data,
|
||||
sizeof(sam->LMSessKey.key));
|
||||
}
|
||||
|
||||
*_sam = sam;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS auth_convert_server_info_saminfo3(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
struct netr_SamInfo3 **_sam3)
|
||||
{
|
||||
struct netr_SamBaseInfo *sam;
|
||||
struct netr_SamInfo3 *sam3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
|
||||
NTSTATUS status;
|
||||
int i;
|
||||
NT_STATUS_HAVE_NO_MEMORY(sam3);
|
||||
|
||||
status = auth_convert_server_info_sambaseinfo(mem_ctx, server_info, &sam);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
sam3->base = *sam;
|
||||
sam3->sidcount = 0;
|
||||
sam3->sids = NULL;
|
||||
|
||||
|
||||
sam3->sids = talloc_array(sam, struct netr_SidAttr,
|
||||
server_info->n_domain_groups);
|
||||
NT_STATUS_HAVE_NO_MEMORY(sam3->sids);
|
||||
|
||||
for (i=0; i<server_info->n_domain_groups; i++) {
|
||||
if (dom_sid_in_domain(sam->domain_sid, server_info->domain_groups[i])) {
|
||||
continue;
|
||||
}
|
||||
sam3->sids[sam3->sidcount].sid = talloc_reference(sam3->sids,server_info->domain_groups[i]);
|
||||
sam3->sids[sam3->sidcount].attribute =
|
||||
SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
|
||||
sam3->sidcount += 1;
|
||||
}
|
||||
if (sam3->sidcount) {
|
||||
sam3->base.user_flags |= NETLOGON_EXTRA_SIDS;
|
||||
} else {
|
||||
sam3->sids = NULL;
|
||||
}
|
||||
*_sam3 = sam3;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Authenticate to a remote server
|
||||
Copyright (C) Andrew Tridgell 1992-1998
|
||||
Copyright (C) Andrew Bartlett 2001
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/****************************************************************************
|
||||
Support for server level security.
|
||||
****************************************************************************/
|
||||
|
||||
static struct smbcli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct smbcli_state *cli = NULL;
|
||||
fstring desthost;
|
||||
struct ipv4_addr dest_ip;
|
||||
const char *p;
|
||||
char *pserver;
|
||||
BOOL connected_ok = False;
|
||||
|
||||
if (!(cli = smbcli_initialise(cli)))
|
||||
return NULL;
|
||||
|
||||
/* security = server just can't function with spnego */
|
||||
cli->use_spnego = False;
|
||||
|
||||
pserver = talloc_strdup(mem_ctx, lp_passwordserver());
|
||||
p = pserver;
|
||||
|
||||
while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
|
||||
strupper(desthost);
|
||||
|
||||
if(!resolve_name( desthost, &dest_ip, 0x20)) {
|
||||
DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ismyip(dest_ip)) {
|
||||
DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we use a mutex to prevent two connections at once - when a
|
||||
Win2k PDC get two connections where one hasn't completed a
|
||||
session setup yet it will send a TCP reset to the first
|
||||
connection (tridge) */
|
||||
|
||||
if (!grab_server_mutex(desthost)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (smbcli_connect(cli, desthost, &dest_ip)) {
|
||||
DEBUG(3,("connected to password server %s\n",desthost));
|
||||
connected_ok = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!connected_ok) {
|
||||
release_server_mutex();
|
||||
DEBUG(0,("password server not available\n"));
|
||||
talloc_free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!attempt_netbios_session_request(cli, lp_netbios_name(),
|
||||
desthost, &dest_ip)) {
|
||||
release_server_mutex();
|
||||
DEBUG(1,("password server fails session request\n"));
|
||||
talloc_free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strequal(desthost,myhostname(mem_ctx))) {
|
||||
exit_server("Password server loop!");
|
||||
}
|
||||
|
||||
DEBUG(3,("got session\n"));
|
||||
|
||||
if (!smbcli_negprot(cli)) {
|
||||
DEBUG(1,("%s rejected the negprot\n",desthost));
|
||||
release_server_mutex();
|
||||
talloc_free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cli->protocol < PROTOCOL_LANMAN2 ||
|
||||
!(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
|
||||
DEBUG(1,("%s isn't in user level security mode\n",desthost));
|
||||
release_server_mutex();
|
||||
talloc_free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the first session setup done quickly, to avoid silly
|
||||
Win2k bugs. (The next connection to the server will kill
|
||||
this one...
|
||||
*/
|
||||
|
||||
if (!smbcli_session_setup(cli, "", "", 0, "", 0,
|
||||
"")) {
|
||||
DEBUG(0,("%s rejected the initial session setup (%s)\n",
|
||||
desthost, smbcli_errstr(cli)));
|
||||
release_server_mutex();
|
||||
talloc_free(cli);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
release_server_mutex();
|
||||
|
||||
DEBUG(3,("password server OK\n"));
|
||||
|
||||
return cli;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Clean up our allocated cli.
|
||||
****************************************************************************/
|
||||
|
||||
static void free_server_private_data(void **private_data_pointer)
|
||||
{
|
||||
struct smbcli_state **cli = (struct smbcli_state **)private_data_pointer;
|
||||
if (*cli && (*cli)->initialised) {
|
||||
talloc_free(*cli);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Get the challenge out of a password server.
|
||||
****************************************************************************/
|
||||
|
||||
static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_context,
|
||||
void **my_private_data,
|
||||
TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct smbcli_state *cli = server_cryptkey(mem_ctx);
|
||||
|
||||
if (cli) {
|
||||
DEBUG(3,("using password server validation\n"));
|
||||
|
||||
if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
|
||||
/* We can't work with unencrypted password servers
|
||||
unless 'encrypt passwords = no' */
|
||||
DEBUG(5,("make_auth_info_server: Server is unencrypted, no challenge available..\n"));
|
||||
|
||||
/* However, it is still a perfectly fine connection
|
||||
to pass that unencrypted password over */
|
||||
*my_private_data = (void *)cli;
|
||||
return data_blob(NULL, 0);
|
||||
|
||||
} else if (cli->secblob.length < 8) {
|
||||
/* We can't do much if we don't get a full challenge */
|
||||
DEBUG(2,("make_auth_info_server: Didn't receive a full challenge from server\n"));
|
||||
talloc_free(cli);
|
||||
return data_blob(NULL, 0);
|
||||
}
|
||||
|
||||
*my_private_data = (void *)cli;
|
||||
|
||||
/* The return must be allocated on the caller's mem_ctx, as our own will be
|
||||
destoyed just after the call. */
|
||||
return data_blob_talloc(auth_context->mem_ctx, cli->secblob.data,8);
|
||||
} else {
|
||||
return data_blob(NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Check for a valid username and password in security=server mode.
|
||||
- Validate a password with the password server.
|
||||
****************************************************************************/
|
||||
|
||||
static NTSTATUS check_smbserver_security(const struct auth_context *auth_context,
|
||||
void *my_private_data,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const auth_usersupplied_info *user_info,
|
||||
auth_serversupplied_info **server_info)
|
||||
{
|
||||
struct smbcli_state *cli;
|
||||
static uint8_t badpass[24];
|
||||
static fstring baduser;
|
||||
static BOOL tested_password_server = False;
|
||||
static BOOL bad_password_server = False;
|
||||
NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
|
||||
BOOL locally_made_cli = False;
|
||||
|
||||
/*
|
||||
* Check that the requested domain is not our own machine name.
|
||||
* If it is, we should never check the PDC here, we use our own local
|
||||
* password file.
|
||||
*/
|
||||
|
||||
if(is_myname(user_info->domain.str)) {
|
||||
DEBUG(3,("check_smbserver_security: Requested domain was for this machine.\n"));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
|
||||
cli = my_private_data;
|
||||
|
||||
if (cli) {
|
||||
} else {
|
||||
cli = server_cryptkey(mem_ctx);
|
||||
locally_made_cli = True;
|
||||
}
|
||||
|
||||
if (!cli || !cli->initialised) {
|
||||
DEBUG(1,("password server is not connected (cli not initilised)\n"));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
|
||||
if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
|
||||
if (user_info->encrypted) {
|
||||
DEBUG(1,("password server %s is plaintext, but we are encrypted. This just can't work :-(\n", cli->desthost));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
} else {
|
||||
if (memcmp(cli->secblob.data, auth_context->challenge.data, 8) != 0) {
|
||||
DEBUG(1,("the challenge that the password server (%s) supplied us is not the one we gave our client. This just can't work :-(\n", cli->desthost));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if(badpass[0] == 0)
|
||||
memset(badpass, 0x1f, sizeof(badpass));
|
||||
|
||||
if((user_info->nt_resp.length == sizeof(badpass)) &&
|
||||
!memcmp(badpass, user_info->nt_resp.data, sizeof(badpass))) {
|
||||
/*
|
||||
* Very unlikely, our random bad password is the same as the users
|
||||
* password.
|
||||
*/
|
||||
memset(badpass, badpass[0]+1, sizeof(badpass));
|
||||
}
|
||||
|
||||
if(baduser[0] == 0) {
|
||||
fstrcpy(baduser, INVALID_USER_PREFIX);
|
||||
fstrcat(baduser, lp_netbios_name());
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt a session setup with a totally incorrect password.
|
||||
* If this succeeds with the guest bit *NOT* set then the password
|
||||
* server is broken and is not correctly setting the guest bit. We
|
||||
* need to detect this as some versions of NT4.x are broken. JRA.
|
||||
*/
|
||||
|
||||
/* I sure as hell hope that there aren't servers out there that take
|
||||
* NTLMv2 and have this bug, as we don't test for that...
|
||||
* - abartlet@samba.org
|
||||
*/
|
||||
|
||||
if ((!tested_password_server) && (lp_paranoid_server_security())) {
|
||||
if (smbcli_session_setup(cli, baduser, (char *)badpass, sizeof(badpass),
|
||||
(char *)badpass, sizeof(badpass), user_info->domain.str)) {
|
||||
|
||||
/*
|
||||
* We connected to the password server so we
|
||||
* can say we've tested it.
|
||||
*/
|
||||
tested_password_server = True;
|
||||
|
||||
if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) {
|
||||
DEBUG(0,("server_validate: password server %s allows users as non-guest \
|
||||
with a bad password.\n", cli->desthost));
|
||||
DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \
|
||||
use this machine as the password server.\n"));
|
||||
smbcli_ulogoff(cli);
|
||||
|
||||
/*
|
||||
* Password server has the bug.
|
||||
*/
|
||||
bad_password_server = True;
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
smbcli_ulogoff(cli);
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
* We have already tested the password server.
|
||||
* Fail immediately if it has the bug.
|
||||
*/
|
||||
|
||||
if(bad_password_server) {
|
||||
DEBUG(0,("server_validate: [1] password server %s allows users as non-guest \
|
||||
with a bad password.\n", cli->desthost));
|
||||
DEBUG(0,("server_validate: [1] This is broken (and insecure) behaviour. Please do not \
|
||||
use this machine as the password server.\n"));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we know the password server will correctly set the guest bit, or is
|
||||
* not guest enabled, we can try with the real password.
|
||||
*/
|
||||
|
||||
if (!user_info->encrypted) {
|
||||
/* Plaintext available */
|
||||
if (!smbcli_session_setup(cli, user_info->smb_name.str,
|
||||
(char *)user_info->plaintext_password.data,
|
||||
user_info->plaintext_password.length,
|
||||
NULL, 0,
|
||||
user_info->domain.str)) {
|
||||
DEBUG(1,("password server %s rejected the password\n", cli->desthost));
|
||||
/* Make this smbcli_nt_error() when the conversion is in */
|
||||
nt_status = smbcli_nt_error(cli);
|
||||
} else {
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
} else {
|
||||
if (!smbcli_session_setup(cli, user_info->smb_name.str,
|
||||
(char *)user_info->lm_resp.data,
|
||||
user_info->lm_resp.length,
|
||||
(char *)user_info->nt_resp.data,
|
||||
user_info->nt_resp.length,
|
||||
user_info->domain.str)) {
|
||||
DEBUG(1,("password server %s rejected the password\n", cli->desthost));
|
||||
/* Make this smbcli_nt_error() when the conversion is in */
|
||||
nt_status = smbcli_nt_error(cli);
|
||||
} else {
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* if logged in as guest then reject */
|
||||
if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) {
|
||||
DEBUG(1,("password server %s gave us guest only\n", cli->desthost));
|
||||
nt_status = NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
|
||||
smbcli_ulogoff(cli);
|
||||
|
||||
if NT_STATUS_IS_OK(nt_status) {
|
||||
struct passwd *pass = Get_Pwnam(user_info->internal_username.str);
|
||||
if (pass) {
|
||||
nt_status = make_server_info_pw(auth_context, server_info, pass);
|
||||
} else {
|
||||
nt_status = NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
}
|
||||
|
||||
if (locally_made_cli) {
|
||||
talloc_free(cli);
|
||||
}
|
||||
|
||||
return(nt_status);
|
||||
}
|
||||
|
||||
NTSTATUS auth_init_smbserver(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
|
||||
{
|
||||
if (!make_auth_methods(auth_context, auth_method)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
(*auth_method)->name = "smbserver";
|
||||
(*auth_method)->auth = check_smbserver_security;
|
||||
(*auth_method)->get_chal = auth_get_challenge_server;
|
||||
(*auth_method)->send_keepalive = send_server_keepalive;
|
||||
(*auth_method)->free_private_data = free_server_private_data;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
auth functions
|
||||
|
||||
Copyright (C) Simo Sorce 2005
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Andrew Bartlett 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "lib/events/events.h"
|
||||
|
||||
_PUBLIC_ NTSTATUS authenticate_username_pw(TALLOC_CTX *mem_ctx,
|
||||
struct event_context *ev,
|
||||
struct messaging_context *msg,
|
||||
const char *nt4_domain,
|
||||
const char *nt4_username,
|
||||
const char *password,
|
||||
struct auth_session_info **session_info)
|
||||
{
|
||||
struct auth_context *auth_context;
|
||||
struct auth_usersupplied_info *user_info;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
NTSTATUS nt_status;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
|
||||
|
||||
if (!tmp_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
nt_status = auth_context_create(tmp_ctx, lp_auth_methods(),
|
||||
ev, msg,
|
||||
&auth_context);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
user_info = talloc(tmp_ctx, struct auth_usersupplied_info);
|
||||
if (!user_info) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
user_info->mapped_state = True;
|
||||
user_info->client.account_name = nt4_username;
|
||||
user_info->mapped.account_name = nt4_username;
|
||||
user_info->client.domain_name = nt4_domain;
|
||||
user_info->mapped.domain_name = nt4_domain;
|
||||
|
||||
user_info->workstation_name = NULL;
|
||||
|
||||
user_info->remote_host = NULL;
|
||||
|
||||
user_info->password_state = AUTH_PASSWORD_PLAIN;
|
||||
user_info->password.plaintext = talloc_strdup(user_info, password);
|
||||
|
||||
user_info->flags = USER_INFO_CASE_INSENSITIVE_USERNAME |
|
||||
USER_INFO_DONT_CHECK_UNIX_ACCOUNT;
|
||||
|
||||
user_info->logon_parameters = 0;
|
||||
|
||||
nt_status = auth_check_password(auth_context, tmp_ctx, user_info, &server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = auth_generate_session_info(tmp_ctx, server_info, session_info);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_steal(mem_ctx, *session_info);
|
||||
}
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,839 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Bartlett 2001
|
||||
Copyright (C) Jeremy Allison 2001
|
||||
Copyright (C) Simo Sorce 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "system/passwd.h" /* needed by some systems for struct passwd */
|
||||
#include "lib/socket/socket.h"
|
||||
#include "auth/pam_errors.h"
|
||||
|
||||
/* TODO: look at how to best fill in parms retrieveing a struct passwd info
|
||||
* except in case USER_INFO_DONT_CHECK_UNIX_ACCOUNT is set
|
||||
*/
|
||||
static NTSTATUS authunix_make_server_info(TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct passwd *pwd,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
struct auth_serversupplied_info *server_info;
|
||||
NTSTATUS status;
|
||||
|
||||
/* This is a real, real hack */
|
||||
if (pwd->pw_uid == 0) {
|
||||
status = auth_system_server_info(mem_ctx, &server_info);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
server_info->account_name = talloc_steal(server_info, pwd->pw_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
|
||||
server_info->domain_name = talloc_strdup(server_info, "unix");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
|
||||
} else {
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
server_info->authenticated = True;
|
||||
|
||||
server_info->account_name = talloc_steal(server_info, pwd->pw_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
|
||||
server_info->domain_name = talloc_strdup(server_info, "unix");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
|
||||
|
||||
/* This isn't in any way correct.. */
|
||||
server_info->account_sid = NULL;
|
||||
server_info->primary_group_sid = NULL;
|
||||
server_info->n_domain_groups = 0;
|
||||
server_info->domain_groups = NULL;
|
||||
}
|
||||
server_info->user_session_key = data_blob(NULL,0);
|
||||
server_info->lm_session_key = data_blob(NULL,0);
|
||||
|
||||
server_info->full_name = talloc_steal(server_info, pwd->pw_gecos);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
|
||||
server_info->logon_script = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
|
||||
server_info->profile_path = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
|
||||
server_info->home_directory = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
|
||||
server_info->home_drive = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
|
||||
|
||||
server_info->last_logon = 0;
|
||||
server_info->last_logoff = 0;
|
||||
server_info->acct_expiry = 0;
|
||||
server_info->last_password_change = 0;
|
||||
server_info->allow_password_change = 0;
|
||||
server_info->force_password_change = 0;
|
||||
server_info->logon_count = 0;
|
||||
server_info->bad_password_count = 0;
|
||||
server_info->acct_flags = 0;
|
||||
|
||||
*_server_info = server_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS talloc_getpwnam(TALLOC_CTX *ctx, const char *username, struct passwd **pws)
|
||||
{
|
||||
struct passwd *ret;
|
||||
struct passwd *from;
|
||||
|
||||
*pws = NULL;
|
||||
|
||||
ret = talloc(ctx, struct passwd);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret);
|
||||
|
||||
from = getpwnam(username);
|
||||
if (!from) {
|
||||
return NT_STATUS_NO_SUCH_USER;
|
||||
}
|
||||
|
||||
ret->pw_name = talloc_strdup(ctx, from->pw_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret->pw_name);
|
||||
|
||||
ret->pw_passwd = talloc_strdup(ctx, from->pw_passwd);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret->pw_passwd);
|
||||
|
||||
ret->pw_uid = from->pw_uid;
|
||||
ret->pw_gid = from->pw_gid;
|
||||
ret->pw_gecos = talloc_strdup(ctx, from->pw_gecos);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret->pw_gecos);
|
||||
|
||||
ret->pw_dir = talloc_strdup(ctx, from->pw_dir);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret->pw_dir);
|
||||
|
||||
ret->pw_shell = talloc_strdup(ctx, from->pw_shell);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ret->pw_shell);
|
||||
|
||||
*pws = ret;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_SECURITY_PAM_APPL_H
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
struct smb_pam_user_info {
|
||||
const char *account_name;
|
||||
const char *plaintext_password;
|
||||
};
|
||||
|
||||
#define COPY_STRING(s) (s) ? strdup(s) : NULL
|
||||
|
||||
/*
|
||||
* Check user password
|
||||
* Currently it uses PAM only and fails on systems without PAM
|
||||
* Samba3 code located in pass_check.c is to ugly to be used directly it will
|
||||
* need major rework that's why pass_check.c is still there.
|
||||
*/
|
||||
|
||||
static int smb_pam_conv(int num_msg, const struct pam_message **msg,
|
||||
struct pam_response **reply, void *appdata_ptr)
|
||||
{
|
||||
struct smb_pam_user_info *info = (struct smb_pam_user_info *)appdata_ptr;
|
||||
int num;
|
||||
|
||||
if (num_msg <= 0) {
|
||||
*reply = NULL;
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apparantly HPUX has a buggy PAM that doesn't support the
|
||||
* data pointer. Fail if this is the case. JRA.
|
||||
*/
|
||||
|
||||
if (info == NULL) {
|
||||
*reply = NULL;
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM frees memory in reply messages by itself
|
||||
* so use malloc instead of talloc here.
|
||||
*/
|
||||
*reply = malloc_array_p(struct pam_response, num_msg);
|
||||
if (*reply == NULL) {
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
|
||||
for (num = 0; num < num_msg; num++) {
|
||||
switch (msg[num]->msg_style) {
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
(*reply)[num].resp_retcode = PAM_SUCCESS;
|
||||
(*reply)[num].resp = COPY_STRING(info->account_name);
|
||||
break;
|
||||
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
(*reply)[num].resp_retcode = PAM_SUCCESS;
|
||||
(*reply)[num].resp = COPY_STRING(info->plaintext_password);
|
||||
break;
|
||||
|
||||
case PAM_TEXT_INFO:
|
||||
(*reply)[num].resp_retcode = PAM_SUCCESS;
|
||||
(*reply)[num].resp = NULL;
|
||||
DEBUG(4,("PAM Info message in conversation function: %s\n", (msg[num]->msg)));
|
||||
break;
|
||||
|
||||
case PAM_ERROR_MSG:
|
||||
(*reply)[num].resp_retcode = PAM_SUCCESS;
|
||||
(*reply)[num].resp = NULL;
|
||||
DEBUG(4,("PAM Error message in conversation function: %s\n", (msg[num]->msg)));
|
||||
break;
|
||||
|
||||
default:
|
||||
while (num > 0) {
|
||||
SAFE_FREE((*reply)[num-1].resp);
|
||||
num--;
|
||||
}
|
||||
SAFE_FREE(*reply);
|
||||
*reply = NULL;
|
||||
DEBUG(1,("Error: PAM subsystme sent an UNKNOWN message type to the conversation function!\n"));
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start PAM authentication for specified account
|
||||
*/
|
||||
|
||||
static NTSTATUS smb_pam_start(pam_handle_t **pamh, const char *account_name, const char *remote_host, struct pam_conv *pconv)
|
||||
{
|
||||
int pam_error;
|
||||
|
||||
if (account_name == NULL || remote_host == NULL) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", account_name));
|
||||
|
||||
pam_error = pam_start("samba", account_name, pconv, pamh);
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
/* no vaild pamh here, can we reliably call pam_strerror ? */
|
||||
DEBUG(4,("smb_pam_start: pam_start failed!\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
#ifdef PAM_RHOST
|
||||
DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", remote_host));
|
||||
pam_error = pam_set_item(*pamh, PAM_RHOST, remote_host);
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
NTSTATUS nt_status;
|
||||
|
||||
DEBUG(4,("smb_pam_start: setting rhost failed with error: %s\n",
|
||||
pam_strerror(*pamh, pam_error)));
|
||||
nt_status = pam_to_nt_status(pam_error);
|
||||
|
||||
pam_error = pam_end(*pamh, 0);
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
/* no vaild pamh here, can we reliably call pam_strerror ? */
|
||||
DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
|
||||
pam_error));
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
#endif
|
||||
#ifdef PAM_TTY
|
||||
DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
|
||||
pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
NTSTATUS nt_status;
|
||||
|
||||
DEBUG(4,("smb_pam_start: setting tty failed with error: %s\n",
|
||||
pam_strerror(*pamh, pam_error)));
|
||||
nt_status = pam_to_nt_status(pam_error);
|
||||
|
||||
pam_error = pam_end(*pamh, 0);
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
/* no vaild pamh here, can we reliably call pam_strerror ? */
|
||||
DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
|
||||
pam_error));
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
#endif
|
||||
DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", account_name));
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS smb_pam_end(pam_handle_t *pamh)
|
||||
{
|
||||
int pam_error;
|
||||
|
||||
if (pamh != NULL) {
|
||||
pam_error = pam_end(pamh, 0);
|
||||
if (pam_error != PAM_SUCCESS) {
|
||||
/* no vaild pamh here, can we reliably call pam_strerror ? */
|
||||
DEBUG(4,("smb_pam_end: clean up failed, pam_end gave error %d.\n",
|
||||
pam_error));
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(2,("smb_pam_end: pamh is NULL, PAM not initialized ?\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM Authentication Handler
|
||||
*/
|
||||
static NTSTATUS smb_pam_auth(pam_handle_t *pamh, const char *user)
|
||||
{
|
||||
int pam_error;
|
||||
|
||||
/*
|
||||
* To enable debugging set in /etc/pam.d/samba:
|
||||
* auth required /lib/security/pam_pwdb.so nullok shadow audit
|
||||
*/
|
||||
|
||||
DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user));
|
||||
|
||||
pam_error = pam_authenticate(pamh, PAM_SILENT | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK);
|
||||
switch( pam_error ){
|
||||
case PAM_AUTH_ERR:
|
||||
DEBUG(2, ("smb_pam_auth: PAM: Authentication Error for user %s\n", user));
|
||||
break;
|
||||
case PAM_CRED_INSUFFICIENT:
|
||||
DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
|
||||
break;
|
||||
case PAM_AUTHINFO_UNAVAIL:
|
||||
DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
|
||||
break;
|
||||
case PAM_USER_UNKNOWN:
|
||||
DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
|
||||
break;
|
||||
case PAM_MAXTRIES:
|
||||
DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
|
||||
break;
|
||||
case PAM_ABORT:
|
||||
DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
|
||||
break;
|
||||
case PAM_SUCCESS:
|
||||
DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
|
||||
break;
|
||||
default:
|
||||
DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
|
||||
break;
|
||||
}
|
||||
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM Account Handler
|
||||
*/
|
||||
static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
|
||||
{
|
||||
int pam_error;
|
||||
|
||||
DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user));
|
||||
|
||||
pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
|
||||
switch( pam_error ) {
|
||||
case PAM_AUTHTOK_EXPIRED:
|
||||
DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user));
|
||||
break;
|
||||
case PAM_ACCT_EXPIRED:
|
||||
DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user));
|
||||
break;
|
||||
case PAM_AUTH_ERR:
|
||||
DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user));
|
||||
break;
|
||||
case PAM_PERM_DENIED:
|
||||
DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user));
|
||||
break;
|
||||
case PAM_USER_UNKNOWN:
|
||||
DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user));
|
||||
break;
|
||||
case PAM_SUCCESS:
|
||||
DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user));
|
||||
break;
|
||||
default:
|
||||
DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user));
|
||||
break;
|
||||
}
|
||||
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM Credential Setting
|
||||
*/
|
||||
|
||||
static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
|
||||
{
|
||||
int pam_error;
|
||||
|
||||
/*
|
||||
* This will allow samba to aquire a kerberos token. And, when
|
||||
* exporting an AFS cell, be able to /write/ to this cell.
|
||||
*/
|
||||
|
||||
DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
|
||||
|
||||
pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT));
|
||||
switch( pam_error ) {
|
||||
case PAM_CRED_UNAVAIL:
|
||||
DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user ));
|
||||
break;
|
||||
case PAM_CRED_EXPIRED:
|
||||
DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user ));
|
||||
break;
|
||||
case PAM_USER_UNKNOWN:
|
||||
DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user ));
|
||||
break;
|
||||
case PAM_CRED_ERR:
|
||||
DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user ));
|
||||
break;
|
||||
case PAM_SUCCESS:
|
||||
DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user));
|
||||
break;
|
||||
default:
|
||||
DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user));
|
||||
break;
|
||||
}
|
||||
|
||||
return pam_to_nt_status(pam_error);
|
||||
}
|
||||
|
||||
static NTSTATUS check_unix_password(TALLOC_CTX *ctx, const struct auth_usersupplied_info *user_info, struct passwd **pws)
|
||||
{
|
||||
struct smb_pam_user_info *info;
|
||||
struct pam_conv *pamconv;
|
||||
pam_handle_t *pamh;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
info = talloc(ctx, struct smb_pam_user_info);
|
||||
if (info == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
info->account_name = user_info->mapped.account_name;
|
||||
info->plaintext_password = user_info->password.plaintext;
|
||||
|
||||
pamconv = talloc(ctx, struct pam_conv);
|
||||
if (pamconv == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
pamconv->conv = smb_pam_conv;
|
||||
pamconv->appdata_ptr = (void *)info;
|
||||
|
||||
/* TODO:
|
||||
* check for user_info->flags & USER_INFO_CASE_INSENSITIVE_USERNAME
|
||||
* if true set up a crack name routine.
|
||||
*/
|
||||
|
||||
nt_status = smb_pam_start(&pamh, user_info->mapped.account_name, user_info->remote_host ? user_info->remote_host->addr : NULL, pamconv);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = smb_pam_auth(pamh, user_info->mapped.account_name);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
smb_pam_end(pamh);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if ( ! (user_info->flags & USER_INFO_DONT_CHECK_UNIX_ACCOUNT)) {
|
||||
|
||||
nt_status = smb_pam_account(pamh, user_info->mapped.account_name);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
smb_pam_end(pamh);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = smb_pam_setcred(pamh, user_info->mapped.account_name);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
smb_pam_end(pamh);
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
smb_pam_end(pamh);
|
||||
|
||||
nt_status = talloc_getpwnam(ctx, user_info->mapped.account_name, pws);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/****************************************************************************
|
||||
core of password checking routine
|
||||
****************************************************************************/
|
||||
static NTSTATUS password_check(const char *username, const char *password,
|
||||
const char *crypted, const char *salt)
|
||||
{
|
||||
BOOL ret;
|
||||
|
||||
#ifdef WITH_AFS
|
||||
if (afs_auth(username, password))
|
||||
return NT_STATUS_OK;
|
||||
#endif /* WITH_AFS */
|
||||
|
||||
#ifdef WITH_DFS
|
||||
if (dfs_auth(username, password))
|
||||
return NT_STATUS_OK;
|
||||
#endif /* WITH_DFS */
|
||||
|
||||
#ifdef OSF1_ENH_SEC
|
||||
|
||||
ret = (strcmp(osf1_bigcrypt(password, salt), crypted) == 0);
|
||||
|
||||
if (!ret) {
|
||||
DEBUG(2,
|
||||
("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
|
||||
ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
|
||||
}
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
#endif /* OSF1_ENH_SEC */
|
||||
|
||||
#ifdef ULTRIX_AUTH
|
||||
ret = (strcmp((char *)crypt16(password, salt), crypted) == 0);
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
#endif /* ULTRIX_AUTH */
|
||||
|
||||
#ifdef LINUX_BIGCRYPT
|
||||
ret = (linux_bigcrypt(password, salt, crypted));
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
#endif /* LINUX_BIGCRYPT */
|
||||
|
||||
#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
|
||||
|
||||
/*
|
||||
* Some systems have bigcrypt in the C library but might not
|
||||
* actually use it for the password hashes (HPUX 10.20) is
|
||||
* a noteable example. So we try bigcrypt first, followed
|
||||
* by crypt.
|
||||
*/
|
||||
|
||||
if (strcmp(bigcrypt(password, salt), crypted) == 0)
|
||||
return NT_STATUS_OK;
|
||||
else
|
||||
ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
|
||||
|
||||
#ifdef HAVE_BIGCRYPT
|
||||
ret = (strcmp(bigcrypt(password, salt), crypted) == 0);
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
#endif /* HAVE_BIGCRYPT */
|
||||
|
||||
#ifndef HAVE_CRYPT
|
||||
DEBUG(1, ("Warning - no crypt available\n"));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
#else /* HAVE_CRYPT */
|
||||
ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
|
||||
if (ret) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
#endif /* HAVE_CRYPT */
|
||||
#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
|
||||
}
|
||||
|
||||
static NTSTATUS check_unix_password(TALLOC_CTX *ctx, const struct auth_usersupplied_info *user_info, struct passwd **ret_passwd)
|
||||
{
|
||||
char *username;
|
||||
char *password;
|
||||
char *pwcopy;
|
||||
char *salt;
|
||||
char *crypted;
|
||||
struct passwd *pws;
|
||||
NTSTATUS nt_status;
|
||||
int level = lp_passwordlevel();
|
||||
|
||||
*ret_passwd = NULL;
|
||||
|
||||
username = talloc_strdup(ctx, user_info->mapped.account_name);
|
||||
password = talloc_strdup(ctx, user_info->password.plaintext);
|
||||
|
||||
nt_status = talloc_getpwnam(ctx, username, &pws);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
crypted = pws->pw_passwd;
|
||||
salt = pws->pw_passwd;
|
||||
|
||||
#ifdef HAVE_GETSPNAM
|
||||
{
|
||||
struct spwd *spass;
|
||||
|
||||
/* many shadow systems require you to be root to get
|
||||
the password, in most cases this should already be
|
||||
the case when this function is called, except
|
||||
perhaps for IPC password changing requests */
|
||||
|
||||
spass = getspnam(pws->pw_name);
|
||||
if (spass && spass->sp_pwdp) {
|
||||
crypted = talloc_strdup(ctx, spass->sp_pwdp);
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
salt = talloc_strdup(ctx, spass->sp_pwdp);
|
||||
NT_STATUS_HAVE_NO_MEMORY(salt);
|
||||
}
|
||||
}
|
||||
#elif defined(IA_UINFO)
|
||||
{
|
||||
char *ia_password;
|
||||
/* Need to get password with SVR4.2's ia_ functions
|
||||
instead of get{sp,pw}ent functions. Required by
|
||||
UnixWare 2.x, tested on version
|
||||
2.1. (tangent@cyberport.com) */
|
||||
uinfo_t uinfo;
|
||||
if (ia_openinfo(pws->pw_name, &uinfo) != -1) {
|
||||
ia_get_logpwd(uinfo, &ia_password);
|
||||
crypted = talloc_strdup(ctx, ia_password);
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETPRPWNAM
|
||||
{
|
||||
struct pr_passwd *pr_pw = getprpwnam(pws->pw_name);
|
||||
if (pr_pw && pr_pw->ufld.fd_encrypt) {
|
||||
crypted = talloc_strdup(ctx, pr_pw->ufld.fd_encrypt);
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETPWANAM
|
||||
{
|
||||
struct passwd_adjunct *pwret;
|
||||
pwret = getpwanam(s);
|
||||
if (pwret && pwret->pwa_passwd) {
|
||||
crypted = talloc_strdup(ctx, pwret->pwa_passwd);
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OSF1_ENH_SEC
|
||||
{
|
||||
struct pr_passwd *mypasswd;
|
||||
DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n", username));
|
||||
mypasswd = getprpwnam(username);
|
||||
if (mypasswd) {
|
||||
username = talloc_strdup(ctx, mypasswd->ufld.fd_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(username);
|
||||
crypted = talloc_strdup(ctx, mypasswd->ufld.fd_encrypt);
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
} else {
|
||||
DEBUG(5,("OSF1_ENH_SEC: No entry for user %s in protected database !\n", username));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ULTRIX_AUTH
|
||||
{
|
||||
AUTHORIZATION *ap = getauthuid(pws->pw_uid);
|
||||
if (ap) {
|
||||
crypted = talloc_strdup(ctx, ap->a_password);
|
||||
endauthent();
|
||||
NT_STATUS_HAVE_NO_MEMORY(crypted);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_TRUNCATED_SALT)
|
||||
/* crypt on some platforms (HPUX in particular)
|
||||
won't work with more than 2 salt characters. */
|
||||
salt[2] = 0;
|
||||
#endif
|
||||
|
||||
if (crypted[0] == '\0') {
|
||||
if (!lp_null_passwords()) {
|
||||
DEBUG(2, ("Disallowing %s with null password\n", username));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
if (password == NULL) {
|
||||
DEBUG(3, ("Allowing access to %s with null password\n", username));
|
||||
*ret_passwd = pws;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* try it as it came to us */
|
||||
nt_status = password_check(username, password, crypted, salt);
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
*ret_passwd = pws;
|
||||
return nt_status;
|
||||
}
|
||||
else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
|
||||
/* No point continuing if its not the password thats to blame (ie PAM disabled). */
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if ( user_info->flags | USER_INFO_CASE_INSENSITIVE_PASSWORD) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* if the password was given to us with mixed case then we don't
|
||||
* need to proceed as we know it hasn't been case modified by the
|
||||
* client */
|
||||
if (strhasupper(password) && strhaslower(password)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* make a copy of it */
|
||||
pwcopy = talloc_strdup(ctx, password);
|
||||
if (!pwcopy)
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
|
||||
/* try all lowercase if it's currently all uppercase */
|
||||
if (strhasupper(pwcopy)) {
|
||||
strlower(pwcopy);
|
||||
nt_status = password_check(username, pwcopy, crypted, salt);
|
||||
if NT_STATUS_IS_OK(nt_status) {
|
||||
*ret_passwd = pws;
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
/* give up? */
|
||||
if (level < 1) {
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
/* last chance - all combinations of up to level chars upper! */
|
||||
strlower(pwcopy);
|
||||
|
||||
#if 0
|
||||
if (NT_STATUS_IS_OK(nt_status = string_combinations(pwcopy, password_check, level))) {
|
||||
*ret_passwd = pws;
|
||||
return nt_status;
|
||||
}
|
||||
#endif
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** Check a plaintext username/password
|
||||
*
|
||||
**/
|
||||
|
||||
static NTSTATUS authunix_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS authunix_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
TALLOC_CTX *check_ctx;
|
||||
NTSTATUS nt_status;
|
||||
struct passwd *pwd;
|
||||
|
||||
if (user_info->password_state != AUTH_PASSWORD_PLAIN) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
check_ctx = talloc_named_const(mem_ctx, 0, "check_unix_password");
|
||||
if (check_ctx == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
nt_status = check_unix_password(check_ctx, user_info, &pwd);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(check_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = authunix_make_server_info(mem_ctx, user_info, pwd, server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(check_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
talloc_free(check_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static const struct auth_operations unix_ops = {
|
||||
.name = "unix",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = authunix_want_check,
|
||||
.check_password = authunix_check_password
|
||||
};
|
||||
|
||||
NTSTATUS auth_unix_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
ret = auth_register(&unix_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register unix auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,669 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Authentication utility functions
|
||||
Copyright (C) Andrew Tridgell 1992-1998
|
||||
Copyright (C) Andrew Bartlett 2001
|
||||
Copyright (C) Jeremy Allison 2000-2001
|
||||
Copyright (C) Rafal Szczesniak 2002
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
/* this default function can be used by mostly all backends
|
||||
* which don't want to set a challenge
|
||||
*/
|
||||
NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge)
|
||||
{
|
||||
/* we don't want to set a challenge */
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Create an auth_usersupplied_data structure after appropriate mapping.
|
||||
****************************************************************************/
|
||||
|
||||
NTSTATUS map_user_info(TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_usersupplied_info **user_info_mapped)
|
||||
{
|
||||
const char *domain;
|
||||
char *account_name;
|
||||
char *d;
|
||||
DEBUG(5,("map_user_info: Mapping user [%s]\\[%s] from workstation [%s]\n",
|
||||
user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
|
||||
|
||||
account_name = talloc_strdup(mem_ctx, user_info->client.account_name);
|
||||
if (!account_name) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* don't allow "" as a domain, fixes a Win9X bug
|
||||
where it doens't supply a domain for logon script
|
||||
'net use' commands. */
|
||||
|
||||
/* Split user@realm names into user and realm components. This is TODO to fix with proper userprincipalname support */
|
||||
if (user_info->client.domain_name && *user_info->client.domain_name) {
|
||||
domain = user_info->client.domain_name;
|
||||
} else if (strchr_m(user_info->client.account_name, '@')) {
|
||||
d = strchr_m(account_name, '@');
|
||||
if (!d) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
d[0] = '\0';
|
||||
d++;
|
||||
domain = d;
|
||||
} else {
|
||||
domain = lp_workgroup();
|
||||
}
|
||||
|
||||
*user_info_mapped = talloc(mem_ctx, struct auth_usersupplied_info);
|
||||
if (!*user_info_mapped) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
talloc_reference(*user_info_mapped, user_info);
|
||||
**user_info_mapped = *user_info;
|
||||
(*user_info_mapped)->mapped_state = True;
|
||||
(*user_info_mapped)->mapped.domain_name = talloc_strdup(*user_info_mapped, domain);
|
||||
(*user_info_mapped)->mapped.account_name = talloc_strdup(*user_info_mapped, account_name);
|
||||
talloc_free(account_name);
|
||||
if (!(*user_info_mapped)->mapped.domain_name
|
||||
|| !(*user_info_mapped)->mapped.account_name) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Create an auth_usersupplied_data structure after appropriate mapping.
|
||||
****************************************************************************/
|
||||
|
||||
NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
|
||||
enum auth_password_state to_state,
|
||||
const struct auth_usersupplied_info *user_info_in,
|
||||
const struct auth_usersupplied_info **user_info_encrypted)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_usersupplied_info *user_info_temp;
|
||||
switch (to_state) {
|
||||
case AUTH_PASSWORD_RESPONSE:
|
||||
switch (user_info_in->password_state) {
|
||||
case AUTH_PASSWORD_PLAIN:
|
||||
{
|
||||
const struct auth_usersupplied_info *user_info_temp2;
|
||||
nt_status = encrypt_user_info(mem_ctx, auth_context,
|
||||
AUTH_PASSWORD_HASH,
|
||||
user_info_in, &user_info_temp2);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
user_info_in = user_info_temp2;
|
||||
/* fall through */
|
||||
}
|
||||
case AUTH_PASSWORD_HASH:
|
||||
{
|
||||
const uint8_t *challenge;
|
||||
DATA_BLOB chall_blob;
|
||||
user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
|
||||
if (!user_info_temp) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
talloc_reference(user_info_temp, user_info_in);
|
||||
*user_info_temp = *user_info_in;
|
||||
user_info_temp->mapped_state = to_state;
|
||||
|
||||
nt_status = auth_get_challenge(auth_context, &challenge);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
chall_blob = data_blob_talloc(mem_ctx, challenge, 8);
|
||||
if (lp_client_ntlmv2_auth()) {
|
||||
DATA_BLOB names_blob = NTLMv2_generate_names_blob(mem_ctx, lp_netbios_name(), lp_workgroup());
|
||||
DATA_BLOB lmv2_response, ntlmv2_response, lmv2_session_key, ntlmv2_session_key;
|
||||
|
||||
if (!SMBNTLMv2encrypt_hash(user_info_temp,
|
||||
user_info_in->client.account_name,
|
||||
user_info_in->client.domain_name,
|
||||
user_info_in->password.hash.nt->hash, &chall_blob,
|
||||
&names_blob,
|
||||
&lmv2_response, &ntlmv2_response,
|
||||
&lmv2_session_key, &ntlmv2_session_key)) {
|
||||
data_blob_free(&names_blob);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
data_blob_free(&names_blob);
|
||||
user_info_temp->password.response.lanman = lmv2_response;
|
||||
user_info_temp->password.response.nt = ntlmv2_response;
|
||||
|
||||
data_blob_free(&lmv2_session_key);
|
||||
data_blob_free(&ntlmv2_session_key);
|
||||
} else {
|
||||
DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
SMBOWFencrypt(user_info_in->password.hash.nt->hash, challenge, blob.data);
|
||||
|
||||
user_info_temp->password.response.nt = blob;
|
||||
if (lp_client_lanman_auth() && user_info_in->password.hash.lanman) {
|
||||
DATA_BLOB lm_blob = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
SMBOWFencrypt(user_info_in->password.hash.lanman->hash, challenge, blob.data);
|
||||
user_info_temp->password.response.lanman = lm_blob;
|
||||
} else {
|
||||
/* if not sending the LM password, send the NT password twice */
|
||||
user_info_temp->password.response.lanman = user_info_temp->password.response.nt;
|
||||
}
|
||||
}
|
||||
|
||||
user_info_in = user_info_temp;
|
||||
/* fall through */
|
||||
}
|
||||
case AUTH_PASSWORD_RESPONSE:
|
||||
*user_info_encrypted = user_info_in;
|
||||
}
|
||||
break;
|
||||
case AUTH_PASSWORD_HASH:
|
||||
{
|
||||
switch (user_info_in->password_state) {
|
||||
case AUTH_PASSWORD_PLAIN:
|
||||
{
|
||||
struct samr_Password lanman;
|
||||
struct samr_Password nt;
|
||||
|
||||
user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
|
||||
if (!user_info_temp) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
talloc_reference(user_info_temp, user_info_in);
|
||||
*user_info_temp = *user_info_in;
|
||||
user_info_temp->mapped_state = to_state;
|
||||
|
||||
if (E_deshash(user_info_in->password.plaintext, lanman.hash)) {
|
||||
user_info_temp->password.hash.lanman = talloc(user_info_temp,
|
||||
struct samr_Password);
|
||||
*user_info_temp->password.hash.lanman = lanman;
|
||||
} else {
|
||||
user_info_temp->password.hash.lanman = NULL;
|
||||
}
|
||||
|
||||
E_md4hash(user_info_in->password.plaintext, nt.hash);
|
||||
user_info_temp->password.hash.nt = talloc(user_info_temp,
|
||||
struct samr_Password);
|
||||
*user_info_temp->password.hash.nt = nt;
|
||||
|
||||
user_info_in = user_info_temp;
|
||||
/* fall through */
|
||||
}
|
||||
case AUTH_PASSWORD_HASH:
|
||||
*user_info_encrypted = user_info_in;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
Make a server_info struct from the info3 returned by a domain logon
|
||||
***************************************************************************/
|
||||
NTSTATUS make_server_info_netlogon_validation(TALLOC_CTX *mem_ctx,
|
||||
const char *account_name,
|
||||
uint16_t validation_level,
|
||||
union netr_Validation *validation,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
struct auth_serversupplied_info *server_info;
|
||||
struct netr_SamBaseInfo *base = NULL;
|
||||
int i;
|
||||
|
||||
switch (validation_level) {
|
||||
case 2:
|
||||
if (!validation || !validation->sam2) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
base = &validation->sam2->base;
|
||||
break;
|
||||
case 3:
|
||||
if (!validation || !validation->sam3) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
base = &validation->sam3->base;
|
||||
break;
|
||||
case 6:
|
||||
if (!validation || !validation->sam6) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
base = &validation->sam6->base;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
/*
|
||||
Here is where we should check the list of
|
||||
trusted domains, and verify that the SID
|
||||
matches.
|
||||
*/
|
||||
server_info->account_sid = dom_sid_add_rid(server_info, base->domain_sid, base->rid);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
|
||||
|
||||
|
||||
server_info->primary_group_sid = dom_sid_add_rid(server_info, base->domain_sid, base->primary_gid);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
|
||||
|
||||
server_info->n_domain_groups = base->groups.count;
|
||||
if (base->groups.count) {
|
||||
server_info->domain_groups = talloc_array(server_info, struct dom_sid*, base->groups.count);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_groups);
|
||||
} else {
|
||||
server_info->domain_groups = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < base->groups.count; i++) {
|
||||
server_info->domain_groups[i] = dom_sid_add_rid(server_info, base->domain_sid, base->groups.rids[i].rid);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_groups[i]);
|
||||
}
|
||||
|
||||
/* Copy 'other' sids. We need to do sid filtering here to
|
||||
prevent possible elevation of privileges. See:
|
||||
|
||||
http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
|
||||
*/
|
||||
|
||||
if (validation_level == 3) {
|
||||
struct dom_sid **dgrps = server_info->domain_groups;
|
||||
size_t sidcount = server_info->n_domain_groups + validation->sam3->sidcount;
|
||||
size_t n_dgrps = server_info->n_domain_groups;
|
||||
|
||||
if (validation->sam3->sidcount > 0) {
|
||||
dgrps = talloc_realloc(server_info, dgrps, struct dom_sid*, sidcount);
|
||||
NT_STATUS_HAVE_NO_MEMORY(dgrps);
|
||||
|
||||
for (i = 0; i < validation->sam3->sidcount; i++) {
|
||||
dgrps[n_dgrps + i] = talloc_reference(dgrps, validation->sam3->sids[i].sid);
|
||||
}
|
||||
}
|
||||
|
||||
server_info->n_domain_groups = sidcount;
|
||||
server_info->domain_groups = dgrps;
|
||||
|
||||
/* Where are the 'global' sids?... */
|
||||
}
|
||||
|
||||
if (base->account_name.string) {
|
||||
server_info->account_name = talloc_reference(server_info, base->account_name.string);
|
||||
} else {
|
||||
server_info->account_name = talloc_strdup(server_info, account_name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
}
|
||||
|
||||
server_info->domain_name = talloc_reference(server_info, base->domain.string);
|
||||
server_info->full_name = talloc_reference(server_info, base->full_name.string);
|
||||
server_info->logon_script = talloc_reference(server_info, base->logon_script.string);
|
||||
server_info->profile_path = talloc_reference(server_info, base->profile_path.string);
|
||||
server_info->home_directory = talloc_reference(server_info, base->home_directory.string);
|
||||
server_info->home_drive = talloc_reference(server_info, base->home_drive.string);
|
||||
server_info->logon_server = talloc_reference(server_info, base->logon_server.string);
|
||||
server_info->last_logon = base->last_logon;
|
||||
server_info->last_logoff = base->last_logoff;
|
||||
server_info->acct_expiry = base->acct_expiry;
|
||||
server_info->last_password_change = base->last_password_change;
|
||||
server_info->allow_password_change = base->allow_password_change;
|
||||
server_info->force_password_change = base->force_password_change;
|
||||
server_info->logon_count = base->logon_count;
|
||||
server_info->bad_password_count = base->bad_password_count;
|
||||
server_info->acct_flags = base->acct_flags;
|
||||
|
||||
server_info->authenticated = True;
|
||||
|
||||
/* ensure we are never given NULL session keys */
|
||||
|
||||
if (all_zero(base->key.key, sizeof(base->key.key))) {
|
||||
server_info->user_session_key = data_blob(NULL, 0);
|
||||
} else {
|
||||
server_info->user_session_key = data_blob_talloc(server_info, base->key.key, sizeof(base->key.key));
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
|
||||
}
|
||||
|
||||
if (all_zero(base->LMSessKey.key, sizeof(base->LMSessKey.key))) {
|
||||
server_info->lm_session_key = data_blob(NULL, 0);
|
||||
} else {
|
||||
server_info->lm_session_key = data_blob_talloc(server_info, base->LMSessKey.key, sizeof(base->LMSessKey.key));
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
|
||||
}
|
||||
|
||||
*_server_info = server_info;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS auth_anonymous_server_info(TALLOC_CTX *mem_ctx, struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
struct auth_serversupplied_info *server_info;
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
|
||||
|
||||
/* is this correct? */
|
||||
server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
|
||||
|
||||
server_info->n_domain_groups = 0;
|
||||
server_info->domain_groups = NULL;
|
||||
|
||||
/* annoying, but the Anonymous really does have a session key,
|
||||
and it is all zeros! */
|
||||
server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
|
||||
|
||||
server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
|
||||
|
||||
data_blob_clear(&server_info->user_session_key);
|
||||
data_blob_clear(&server_info->lm_session_key);
|
||||
|
||||
server_info->account_name = talloc_strdup(server_info, "ANONYMOUS LOGON");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
|
||||
server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
|
||||
|
||||
server_info->full_name = talloc_strdup(server_info, "Anonymous Logon");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
|
||||
|
||||
server_info->logon_script = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
|
||||
|
||||
server_info->profile_path = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
|
||||
|
||||
server_info->home_directory = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
|
||||
|
||||
server_info->home_drive = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
|
||||
|
||||
server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
|
||||
|
||||
server_info->last_logon = 0;
|
||||
server_info->last_logoff = 0;
|
||||
server_info->acct_expiry = 0;
|
||||
server_info->last_password_change = 0;
|
||||
server_info->allow_password_change = 0;
|
||||
server_info->force_password_change = 0;
|
||||
|
||||
server_info->logon_count = 0;
|
||||
server_info->bad_password_count = 0;
|
||||
|
||||
server_info->acct_flags = ACB_NORMAL;
|
||||
|
||||
server_info->authenticated = False;
|
||||
|
||||
*_server_info = server_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS auth_system_server_info(TALLOC_CTX *mem_ctx, struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
struct auth_serversupplied_info *server_info;
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_SYSTEM);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
|
||||
|
||||
/* is this correct? */
|
||||
server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_ADMINISTRATORS);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
|
||||
|
||||
server_info->n_domain_groups = 0;
|
||||
server_info->domain_groups = NULL;
|
||||
|
||||
/* annoying, but the Anonymous really does have a session key,
|
||||
and it is all zeros! */
|
||||
server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
|
||||
|
||||
server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
|
||||
|
||||
data_blob_clear(&server_info->user_session_key);
|
||||
data_blob_clear(&server_info->lm_session_key);
|
||||
|
||||
server_info->account_name = talloc_strdup(server_info, "SYSTEM");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
|
||||
|
||||
server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
|
||||
|
||||
server_info->full_name = talloc_strdup(server_info, "System");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
|
||||
|
||||
server_info->logon_script = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
|
||||
|
||||
server_info->profile_path = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
|
||||
|
||||
server_info->home_directory = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
|
||||
|
||||
server_info->home_drive = talloc_strdup(server_info, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
|
||||
|
||||
server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
|
||||
|
||||
server_info->last_logon = 0;
|
||||
server_info->last_logoff = 0;
|
||||
server_info->acct_expiry = 0;
|
||||
server_info->last_password_change = 0;
|
||||
server_info->allow_password_change = 0;
|
||||
server_info->force_password_change = 0;
|
||||
|
||||
server_info->logon_count = 0;
|
||||
server_info->bad_password_count = 0;
|
||||
|
||||
server_info->acct_flags = ACB_NORMAL;
|
||||
|
||||
server_info->authenticated = True;
|
||||
|
||||
*_server_info = server_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
struct auth_session_info **_session_info)
|
||||
{
|
||||
struct auth_session_info *session_info;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
session_info = talloc(mem_ctx, struct auth_session_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(session_info);
|
||||
|
||||
session_info->server_info = talloc_reference(session_info, server_info);
|
||||
|
||||
/* unless set otherwise, the session key is the user session
|
||||
* key from the auth subsystem */
|
||||
session_info->session_key = server_info->user_session_key;
|
||||
|
||||
nt_status = security_token_create(session_info,
|
||||
server_info->account_sid,
|
||||
server_info->primary_group_sid,
|
||||
server_info->n_domain_groups,
|
||||
server_info->domain_groups,
|
||||
server_info->authenticated,
|
||||
&session_info->security_token);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
session_info->credentials = NULL;
|
||||
|
||||
*_session_info = session_info;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS auth_anonymous_session_info(TALLOC_CTX *parent_ctx,
|
||||
struct auth_session_info **_session_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_serversupplied_info *server_info = NULL;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
|
||||
nt_status = auth_anonymous_server_info(mem_ctx,
|
||||
&server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* references the server_info into the session_info */
|
||||
nt_status = auth_generate_session_info(parent_ctx, server_info, &session_info);
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
session_info->credentials = cli_credentials_init(session_info);
|
||||
if (!session_info->credentials) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
cli_credentials_set_conf(session_info->credentials);
|
||||
cli_credentials_set_anonymous(session_info->credentials);
|
||||
|
||||
*_session_info = session_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
struct auth_session_info *anonymous_session(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
nt_status = auth_anonymous_session_info(mem_ctx, &session_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return NULL;
|
||||
}
|
||||
return session_info;
|
||||
}
|
||||
|
||||
NTSTATUS auth_system_session_info(TALLOC_CTX *parent_ctx,
|
||||
struct auth_session_info **_session_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_serversupplied_info *server_info = NULL;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
|
||||
nt_status = auth_system_server_info(mem_ctx,
|
||||
&server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* references the server_info into the session_info */
|
||||
nt_status = auth_generate_session_info(parent_ctx, server_info, &session_info);
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
session_info->credentials = cli_credentials_init(session_info);
|
||||
if (!session_info->credentials) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
cli_credentials_set_conf(session_info->credentials);
|
||||
|
||||
if (lp_parm_bool(-1,"system","anonymous", False)) {
|
||||
cli_credentials_set_anonymous(session_info->credentials);
|
||||
} else {
|
||||
cli_credentials_set_machine_account_pending(session_info->credentials);
|
||||
}
|
||||
*_session_info = session_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ struct auth_session_info *system_session(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
nt_status = auth_system_session_info(mem_ctx, &session_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return NULL;
|
||||
}
|
||||
return session_info;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
prints a struct auth_session_info security token to debug output.
|
||||
****************************************************************************/
|
||||
void auth_session_info_debug(int dbg_lev,
|
||||
const struct auth_session_info *session_info)
|
||||
{
|
||||
if (!session_info) {
|
||||
DEBUG(dbg_lev, ("Session Info: (NULL)\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
security_token_debug(dbg_lev, session_info->security_token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Squash an NT_STATUS in line with security requirements.
|
||||
* In an attempt to avoid giving the whole game away when users
|
||||
* are authenticating, NT replaces both NT_STATUS_NO_SUCH_USER and
|
||||
* NT_STATUS_WRONG_PASSWORD with NT_STATUS_LOGON_FAILURE in certain situations
|
||||
* (session setups in particular).
|
||||
*
|
||||
* @param nt_status NTSTATUS input for squashing.
|
||||
* @return the 'squashed' nt_status
|
||||
**/
|
||||
NTSTATUS auth_nt_status_squash(NTSTATUS nt_status)
|
||||
{
|
||||
if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) {
|
||||
/* Match WinXP and don't give the game away */
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
} else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) {
|
||||
/* Match WinXP and don't give the game away */
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Winbind authentication mechnism
|
||||
|
||||
Copyright (C) Tim Potter 2000
|
||||
Copyright (C) Andrew Bartlett 2001 - 2002
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "nsswitch/winbind_client.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon.h"
|
||||
#include "librpc/gen_ndr/ndr_winbind.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
|
||||
static NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct winbindd_response *response, struct netr_SamInfo3 *info3)
|
||||
{
|
||||
size_t len = response->length - sizeof(struct winbindd_response);
|
||||
if (len > 4) {
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
blob.length = len - 4;
|
||||
blob.data = (uint8_t *)(((char *)response->extra_data) + 4);
|
||||
|
||||
status = ndr_pull_struct_blob(&blob, mem_ctx, info3,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_netr_SamInfo3);
|
||||
|
||||
return status;
|
||||
} else {
|
||||
DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS winbind_want_check(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info)
|
||||
{
|
||||
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/* TODO: maybe limit the user scope to remote users only */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Authenticate a user with a challenge/response
|
||||
using the samba3 winbind protocol
|
||||
*/
|
||||
static NTSTATUS winbind_check_password_samba3(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
struct winbindd_request request;
|
||||
struct winbindd_response response;
|
||||
NSS_STATUS result;
|
||||
NTSTATUS nt_status;
|
||||
struct netr_SamInfo3 info3;
|
||||
|
||||
/* Send off request */
|
||||
const struct auth_usersupplied_info *user_info_temp;
|
||||
nt_status = encrypt_user_info(mem_ctx, ctx->auth_ctx,
|
||||
AUTH_PASSWORD_RESPONSE,
|
||||
user_info, &user_info_temp);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
user_info = user_info_temp;
|
||||
|
||||
ZERO_STRUCT(request);
|
||||
ZERO_STRUCT(response);
|
||||
request.flags = WBFLAG_PAM_INFO3_NDR;
|
||||
|
||||
request.data.auth_crap.logon_parameters = user_info->logon_parameters;
|
||||
|
||||
winbind_strcpy(request.data.auth_crap.user,
|
||||
user_info->client.account_name);
|
||||
winbind_strcpy(request.data.auth_crap.domain,
|
||||
user_info->client.domain_name);
|
||||
winbind_strcpy(request.data.auth_crap.workstation,
|
||||
user_info->workstation_name);
|
||||
|
||||
memcpy(request.data.auth_crap.chal, ctx->auth_ctx->challenge.data.data, sizeof(request.data.auth_crap.chal));
|
||||
|
||||
request.data.auth_crap.lm_resp_len = MIN(user_info->password.response.lanman.length,
|
||||
sizeof(request.data.auth_crap.lm_resp));
|
||||
request.data.auth_crap.nt_resp_len = MIN(user_info->password.response.nt.length,
|
||||
sizeof(request.data.auth_crap.nt_resp));
|
||||
|
||||
memcpy(request.data.auth_crap.lm_resp, user_info->password.response.lanman.data,
|
||||
request.data.auth_crap.lm_resp_len);
|
||||
memcpy(request.data.auth_crap.nt_resp, user_info->password.response.nt.data,
|
||||
request.data.auth_crap.nt_resp_len);
|
||||
|
||||
result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
|
||||
|
||||
nt_status = NT_STATUS(response.data.auth.nt_status);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
if (result == NSS_STATUS_SUCCESS && response.extra_data) {
|
||||
union netr_Validation validation;
|
||||
|
||||
nt_status = get_info3_from_ndr(mem_ctx, &response, &info3);
|
||||
SAFE_FREE(response.extra_data);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
validation.sam3 = &info3;
|
||||
nt_status = make_server_info_netlogon_validation(mem_ctx,
|
||||
user_info->client.account_name,
|
||||
3, &validation,
|
||||
server_info);
|
||||
return nt_status;
|
||||
} else if (result == NSS_STATUS_SUCCESS && !response.extra_data) {
|
||||
DEBUG(0, ("Winbindd authenticated the user [%s]\\[%s], "
|
||||
"but did not include the required info3 reply!\n",
|
||||
user_info->client.domain_name, user_info->client.account_name));
|
||||
return NT_STATUS_INSUFFICIENT_LOGON_INFO;
|
||||
} else if (NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("Winbindd authentication for [%s]\\[%s] failed, "
|
||||
"but no error code is available!\n",
|
||||
user_info->client.domain_name, user_info->client.account_name));
|
||||
return NT_STATUS_NO_LOGON_SERVERS;
|
||||
}
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
struct winbind_check_password_state {
|
||||
struct winbind_SamLogon req;
|
||||
};
|
||||
|
||||
/*
|
||||
Authenticate a user with a challenge/response
|
||||
using IRPC to the winbind task
|
||||
*/
|
||||
static NTSTATUS winbind_check_password(struct auth_method_context *ctx,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct auth_usersupplied_info *user_info,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
uint32_t *winbind_servers;
|
||||
struct winbind_check_password_state *s;
|
||||
const struct auth_usersupplied_info *user_info_new;
|
||||
struct netr_IdentityInfo *identity_info;
|
||||
|
||||
winbind_servers = irpc_servers_byname(ctx->auth_ctx->msg_ctx, "winbind_server");
|
||||
if ((winbind_servers == NULL) || (winbind_servers[0] == 0)) {
|
||||
DEBUG(0, ("Winbind authentication for [%s]\\[%s] failed, "
|
||||
"no winbind_server running!\n",
|
||||
user_info->client.domain_name, user_info->client.account_name));
|
||||
return NT_STATUS_NO_LOGON_SERVERS;
|
||||
}
|
||||
|
||||
s = talloc(mem_ctx, struct winbind_check_password_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(s);
|
||||
|
||||
if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
|
||||
struct netr_PasswordInfo *password_info;
|
||||
|
||||
status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_HASH,
|
||||
user_info, &user_info_new);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
user_info = user_info_new;
|
||||
|
||||
password_info = talloc(s, struct netr_PasswordInfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(password_info);
|
||||
|
||||
password_info->lmpassword = *user_info->password.hash.lanman;
|
||||
password_info->ntpassword = *user_info->password.hash.nt;
|
||||
|
||||
identity_info = &password_info->identity_info;
|
||||
s->req.in.logon_level = 1;
|
||||
s->req.in.logon.password= password_info;
|
||||
} else {
|
||||
struct netr_NetworkInfo *network_info;
|
||||
const uint8_t *challenge;
|
||||
|
||||
status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_RESPONSE,
|
||||
user_info, &user_info_new);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
user_info = user_info_new;
|
||||
|
||||
network_info = talloc(s, struct netr_NetworkInfo);
|
||||
NT_STATUS_HAVE_NO_MEMORY(network_info);
|
||||
|
||||
status = auth_get_challenge(ctx->auth_ctx, &challenge);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
memcpy(network_info->challenge, challenge, sizeof(network_info->challenge));
|
||||
|
||||
network_info->nt.length = user_info->password.response.nt.length;
|
||||
network_info->nt.data = user_info->password.response.nt.data;
|
||||
|
||||
network_info->nt.length = user_info->password.response.lanman.length;
|
||||
network_info->nt.data = user_info->password.response.lanman.data;
|
||||
|
||||
identity_info = &network_info->identity_info;
|
||||
s->req.in.logon_level = 2;
|
||||
s->req.in.logon.network = network_info;
|
||||
}
|
||||
|
||||
identity_info->domain_name.string = user_info->client.domain_name;
|
||||
identity_info->parameter_control = user_info->logon_parameters; /* see MSV1_0_* */
|
||||
identity_info->logon_id_low = 0;
|
||||
identity_info->logon_id_high = 0;
|
||||
identity_info->account_name.string = user_info->client.account_name;
|
||||
identity_info->workstation.string = user_info->workstation_name;
|
||||
|
||||
s->req.in.validation_level = 3;
|
||||
status = IRPC_CALL(ctx->auth_ctx->msg_ctx, winbind_servers[0],
|
||||
winbind, WINBIND_SAMLOGON,
|
||||
&s->req, s);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = make_server_info_netlogon_validation(mem_ctx,
|
||||
user_info->client.account_name,
|
||||
s->req.in.validation_level,
|
||||
&s->req.out.validation,
|
||||
server_info);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static const struct auth_operations winbind_samba3_ops = {
|
||||
.name = "winbind_samba3",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = winbind_want_check,
|
||||
.check_password = winbind_check_password_samba3
|
||||
};
|
||||
|
||||
static const struct auth_operations winbind_ops = {
|
||||
.name = "winbind",
|
||||
.get_challenge = auth_get_challenge_not_implemented,
|
||||
.want_check = winbind_want_check,
|
||||
.check_password = winbind_check_password
|
||||
};
|
||||
|
||||
NTSTATUS auth_winbind_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
ret = auth_register(&winbind_samba3_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'winbind_samba3' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = auth_register(&winbind_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register 'winbind' auth backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
###############################
|
||||
# start SMB_EXT_LIB_PAM
|
||||
# check for security/pam_appl.h and -lpam
|
||||
AC_CHECK_HEADERS(security/pam_appl.h)
|
||||
AC_CHECK_LIB_EXT(pam, PAM_LIBS, pam_start)
|
||||
if test x"$ac_cv_header_security_pam_appl_h" = x"yes" -a x"$ac_cv_lib_ext_pam_pam_start" = x"yes";then
|
||||
SMB_ENABLE(PAM,YES)
|
||||
fi
|
||||
SMB_EXT_LIB(PAM, $PAM_LIBS)
|
||||
# end SMB_EXT_LIB_PAM
|
||||
###############################
|
||||
|
||||
################################################
|
||||
# test for where we get crypt() from
|
||||
AC_CHECK_LIB_EXT(crypt, CRYPT_LIBS, crypt)
|
||||
SMB_ENABLE(CRYPT,YES)
|
||||
SMB_EXT_LIB(CRYPT, $CRYPT_LIBS)
|
||||
|
||||
AC_CHECK_FUNCS(crypt16 getauthuid getpwanam)
|
||||
|
||||
AC_CHECK_HEADERS(sasl/sasl.h)
|
||||
AC_CHECK_LIB_EXT(sasl2, SASL_LIBS, sasl_client_init)
|
||||
SMB_EXT_LIB(SASL, $SASL_LIBS)
|
||||
|
||||
if test x"$ac_cv_header_sasl_sasl_h" = x"yes" -a x"$ac_cv_lib_ext_sasl2_sasl_client_init" = x"yes";then
|
||||
SMB_ENABLE(SASL,YES)
|
||||
SMB_ENABLE(cyrus_sasl,YES)
|
||||
else
|
||||
SMB_ENABLE(cyrus_sasl,NO)
|
||||
fi
|
||||
@@ -0,0 +1,79 @@
|
||||
# auth server subsystem
|
||||
include gensec/config.mk
|
||||
include kerberos/config.mk
|
||||
include ntlmssp/config.mk
|
||||
include credentials/config.mk
|
||||
|
||||
[SUBSYSTEM::auth_sam]
|
||||
PRIVATE_PROTO_HEADER = auth_sam.h
|
||||
OBJ_FILES = sam.o auth_sam_reply.o ntlm_check.o
|
||||
PUBLIC_DEPENDENCIES = SAMDB
|
||||
|
||||
#######################
|
||||
# Start MODULE auth_sam
|
||||
[MODULE::auth_sam_module]
|
||||
# gensec_krb5 and gensec_gssapi depend on it
|
||||
INIT_FUNCTION = auth_sam_init
|
||||
SUBSYSTEM = auth
|
||||
OBJ_FILES = auth_sam.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
SAMDB auth_sam
|
||||
# End MODULE auth_sam
|
||||
#######################
|
||||
|
||||
#######################
|
||||
# Start MODULE auth_anonymous
|
||||
[MODULE::auth_anonymous]
|
||||
INIT_FUNCTION = auth_anonymous_init
|
||||
SUBSYSTEM = auth
|
||||
OBJ_FILES = auth_anonymous.o
|
||||
# End MODULE auth_anonymous
|
||||
#######################
|
||||
|
||||
#######################
|
||||
# Start MODULE auth_winbind
|
||||
[MODULE::auth_winbind]
|
||||
INIT_FUNCTION = auth_winbind_init
|
||||
SUBSYSTEM = auth
|
||||
OBJ_FILES = auth_winbind.o
|
||||
PUBLIC_DEPENDENCIES = NDR_WINBIND MESSAGING LIBWINBIND-CLIENT
|
||||
# End MODULE auth_winbind
|
||||
#######################
|
||||
|
||||
#######################
|
||||
# Start MODULE auth_developer
|
||||
[MODULE::auth_developer]
|
||||
INIT_FUNCTION = auth_developer_init
|
||||
SUBSYSTEM = auth
|
||||
OBJ_FILES = auth_developer.o
|
||||
# End MODULE auth_developer
|
||||
#######################
|
||||
|
||||
#######################
|
||||
# Start MODULE auth_unix
|
||||
[MODULE::auth_unix]
|
||||
INIT_FUNCTION = auth_unix_init
|
||||
SUBSYSTEM = auth
|
||||
OBJ_FILES = auth_unix.o
|
||||
PUBLIC_DEPENDENCIES = CRYPT PAM PAM_ERRORS
|
||||
# End MODULE auth_unix
|
||||
#######################
|
||||
|
||||
[SUBSYSTEM::PAM_ERRORS]
|
||||
PRIVATE_PROTO_HEADER = pam_errors.h
|
||||
OBJ_FILES = pam_errors.o
|
||||
|
||||
#######################
|
||||
# Start SUBSYSTEM auth
|
||||
[SUBSYSTEM::auth]
|
||||
#VERSION = 0.0.1
|
||||
#SO_VERSION = 0
|
||||
PUBLIC_HEADERS = auth.h
|
||||
PUBLIC_PROTO_HEADER = auth_proto.h
|
||||
OBJ_FILES = \
|
||||
auth.o \
|
||||
auth_util.o \
|
||||
auth_simple.o
|
||||
PUBLIC_DEPENDENCIES = LIBSECURITY SAMDB CREDENTIALS
|
||||
# End SUBSYSTEM auth
|
||||
#######################
|
||||
@@ -0,0 +1,24 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM CREDENTIALS
|
||||
[SUBSYSTEM::CREDENTIALS]
|
||||
PUBLIC_PROTO_HEADER = credentials_proto.h
|
||||
PUBLIC_HEADERS = credentials.h
|
||||
OBJ_FILES = credentials.o \
|
||||
credentials_files.o \
|
||||
credentials_ntlm.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
LIBCLI_AUTH SECRETS LIBCRYPTO KERBEROS
|
||||
PRIVATE_DEPENDENCIES = CREDENTIALS_KRB5
|
||||
# End SUBSYSTEM CREDENTIALS
|
||||
#################################
|
||||
|
||||
#################################
|
||||
# Start SUBSYSTEM CREDENTIALS
|
||||
[SUBSYSTEM::CREDENTIALS_KRB5]
|
||||
PUBLIC_PROTO_HEADER = credentials_krb5_proto.h
|
||||
PUBLIC_HEADERS = credentials_krb5.h
|
||||
OBJ_FILES = credentials_krb5.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
HEIMDAL_GSSAPI
|
||||
# End SUBSYSTEM CREDENTIALS
|
||||
#################################
|
||||
@@ -0,0 +1,717 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
User credentials handling
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Tim Potter 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
|
||||
/**
|
||||
* Create a new credentials structure
|
||||
* @param mem_ctx TALLOC_CTX parent for credentials structure
|
||||
*/
|
||||
struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials);
|
||||
if (!cred) {
|
||||
return cred;
|
||||
}
|
||||
|
||||
cred->netlogon_creds = NULL;
|
||||
cred->machine_account_pending = False;
|
||||
cred->workstation_obtained = CRED_UNINITIALISED;
|
||||
cred->username_obtained = CRED_UNINITIALISED;
|
||||
cred->password_obtained = CRED_UNINITIALISED;
|
||||
cred->domain_obtained = CRED_UNINITIALISED;
|
||||
cred->realm_obtained = CRED_UNINITIALISED;
|
||||
cred->ccache_obtained = CRED_UNINITIALISED;
|
||||
cred->client_gss_creds_obtained = CRED_UNINITIALISED;
|
||||
cred->server_gss_creds_obtained = CRED_UNINITIALISED;
|
||||
cred->keytab_obtained = CRED_UNINITIALISED;
|
||||
cred->principal_obtained = CRED_UNINITIALISED;
|
||||
|
||||
cred->old_password = NULL;
|
||||
cred->smb_krb5_context = NULL;
|
||||
cred->salt_principal = NULL;
|
||||
cred->machine_account = False;
|
||||
|
||||
cred->bind_dn = NULL;
|
||||
|
||||
cred->tries = 3;
|
||||
cred->callback_running = False;
|
||||
|
||||
cli_credentials_set_kerberos_state(cred, CRED_AUTO_USE_KERBEROS);
|
||||
|
||||
return cred;
|
||||
}
|
||||
|
||||
void cli_credentials_set_kerberos_state(struct cli_credentials *creds,
|
||||
enum credentials_use_kerberos use_kerberos)
|
||||
{
|
||||
creds->use_kerberos = use_kerberos;
|
||||
}
|
||||
|
||||
enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds)
|
||||
{
|
||||
return creds->use_kerberos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the username for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The username set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_username(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->username_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->username = cred->username_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->username_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->username;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_username(struct cli_credentials *cred,
|
||||
const char *val, enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->username_obtained) {
|
||||
cred->username = talloc_strdup(cred, val);
|
||||
cred->username_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_username_callback(struct cli_credentials *cred,
|
||||
const char *(*username_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->username_obtained < CRED_CALLBACK) {
|
||||
cred->username_cb = username_cb;
|
||||
cred->username_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_bind_dn(struct cli_credentials *cred,
|
||||
const char *bind_dn)
|
||||
{
|
||||
cred->bind_dn = talloc_strdup(cred, bind_dn);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the BIND DN for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The username set on this context.
|
||||
* @note Return value will be NULL if not specified explictly
|
||||
*/
|
||||
const char *cli_credentials_get_bind_dn(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->bind_dn;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the client principal for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The username set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->principal_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->principal = cred->principal_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->principal_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
if (cred->principal_obtained < cred->username_obtained) {
|
||||
if (cred->domain_obtained > cred->realm_obtained) {
|
||||
return talloc_asprintf(mem_ctx, "%s@%s",
|
||||
cli_credentials_get_username(cred),
|
||||
cli_credentials_get_domain(cred));
|
||||
} else {
|
||||
return talloc_asprintf(mem_ctx, "%s@%s",
|
||||
cli_credentials_get_username(cred),
|
||||
cli_credentials_get_realm(cred));
|
||||
}
|
||||
}
|
||||
return talloc_reference(mem_ctx, cred->principal);
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_principal(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->principal_obtained) {
|
||||
cred->principal = talloc_strdup(cred, val);
|
||||
cred->principal_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Set a callback to get the principal. This could be a popup dialog,
|
||||
* a terminal prompt or similar. */
|
||||
|
||||
BOOL cli_credentials_set_principal_callback(struct cli_credentials *cred,
|
||||
const char *(*principal_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->principal_obtained < CRED_CALLBACK) {
|
||||
cred->principal_cb = principal_cb;
|
||||
cred->principal_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Some of our tools are 'anonymous by default'. This is a single
|
||||
* function to determine if authentication has been explicitly
|
||||
* requested */
|
||||
|
||||
BOOL cli_credentials_authentication_requested(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->bind_dn) {
|
||||
return True;
|
||||
}
|
||||
|
||||
if (cli_credentials_is_anonymous(cred)){
|
||||
return False;
|
||||
}
|
||||
|
||||
if (cred->principal_obtained >= CRED_SPECIFIED) {
|
||||
return True;
|
||||
}
|
||||
if (cred->username_obtained >= CRED_SPECIFIED) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the password for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval If set, the cleartext password, otherwise NULL
|
||||
*/
|
||||
const char *cli_credentials_get_password(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->password_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->password = cred->password_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->password_obtained = CRED_CALLBACK_RESULT;
|
||||
}
|
||||
|
||||
return cred->password;
|
||||
}
|
||||
|
||||
/* Set a password on the credentials context, including an indication
|
||||
* of 'how' the password was obtained */
|
||||
|
||||
BOOL cli_credentials_set_password(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->password_obtained) {
|
||||
cred->password = talloc_strdup(cred, val);
|
||||
cred->password_obtained = obtained;
|
||||
|
||||
cred->nt_hash = NULL;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_password_callback(struct cli_credentials *cred,
|
||||
const char *(*password_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->password_obtained < CRED_CALLBACK) {
|
||||
cred->password_cb = password_cb;
|
||||
cred->password_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the 'old' password for this credentials context (used for join accounts).
|
||||
* @param cred credentials context
|
||||
* @retval If set, the cleartext password, otherwise NULL
|
||||
*/
|
||||
const char *cli_credentials_get_old_password(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
return cred->old_password;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_old_password(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
cred->old_password = talloc_strdup(cred, val);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the password, in the form MD4(unicode(password)) for this credentials context.
|
||||
*
|
||||
* Sometimes we only have this much of the password, while the rest of
|
||||
* the time this call avoids calling E_md4hash themselves.
|
||||
*
|
||||
* @param cred credentials context
|
||||
* @retval If set, the cleartext password, otherwise NULL
|
||||
*/
|
||||
const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred,
|
||||
TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
const char *password = cli_credentials_get_password(cred);
|
||||
|
||||
if (password) {
|
||||
struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
|
||||
if (!nt_hash) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
E_md4hash(password, nt_hash->hash);
|
||||
|
||||
return nt_hash;
|
||||
} else {
|
||||
return cred->nt_hash;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_nt_hash(struct cli_credentials *cred,
|
||||
const struct samr_Password *nt_hash,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->password_obtained) {
|
||||
cli_credentials_set_password(cred, NULL, obtained);
|
||||
cred->nt_hash = talloc(cred, struct samr_Password);
|
||||
*cred->nt_hash = *nt_hash;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the 'short' or 'NetBIOS' domain for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The domain set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_domain(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->domain_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->domain = cred->domain_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->domain_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->domain;
|
||||
}
|
||||
|
||||
|
||||
BOOL cli_credentials_set_domain(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->domain_obtained) {
|
||||
/* it is important that the domain be in upper case,
|
||||
* particularly for the sensitive NTLMv2
|
||||
* calculations */
|
||||
cred->domain = strupper_talloc(cred, val);
|
||||
cred->domain_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_domain_callback(struct cli_credentials *cred,
|
||||
const char *(*domain_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->domain_obtained < CRED_CALLBACK) {
|
||||
cred->domain_cb = domain_cb;
|
||||
cred->domain_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the Kerberos realm for this credentials context.
|
||||
* @param cred credentials context
|
||||
* @retval The realm set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_realm(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
if (cred->realm_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->realm = cred->realm_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->realm_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->realm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the realm for this credentials context, and force it to
|
||||
* uppercase for the sainity of our local kerberos libraries
|
||||
*/
|
||||
BOOL cli_credentials_set_realm(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->realm_obtained) {
|
||||
cred->realm = strupper_talloc(cred, val);
|
||||
cred->realm_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_realm_callback(struct cli_credentials *cred,
|
||||
const char *(*realm_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->realm_obtained < CRED_CALLBACK) {
|
||||
cred->realm_cb = realm_cb;
|
||||
cred->realm_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
|
||||
*
|
||||
* @param cred credentials context
|
||||
* @retval The workstation name set on this context.
|
||||
* @note Return value will never be NULL except by programmer error.
|
||||
*/
|
||||
const char *cli_credentials_get_workstation(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->workstation_obtained == CRED_CALLBACK &&
|
||||
!cred->callback_running) {
|
||||
cred->callback_running = True;
|
||||
cred->workstation = cred->workstation_cb(cred);
|
||||
cred->callback_running = False;
|
||||
cred->workstation_obtained = CRED_SPECIFIED;
|
||||
}
|
||||
|
||||
return cred->workstation;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_workstation(struct cli_credentials *cred,
|
||||
const char *val,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
if (obtained >= cred->workstation_obtained) {
|
||||
cred->workstation = talloc_strdup(cred, val);
|
||||
cred->workstation_obtained = obtained;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL cli_credentials_set_workstation_callback(struct cli_credentials *cred,
|
||||
const char *(*workstation_cb) (struct cli_credentials *))
|
||||
{
|
||||
if (cred->workstation_obtained < CRED_CALLBACK) {
|
||||
cred->workstation_cb = workstation_cb;
|
||||
cred->workstation_obtained = CRED_CALLBACK;
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
|
||||
*
|
||||
* The format accepted is [domain\\]user[%password] or user[@realm][%password]
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param data the string containing the username, password etc
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
|
||||
{
|
||||
char *uname, *p;
|
||||
|
||||
if (strcmp("%",data) == 0) {
|
||||
cli_credentials_set_anonymous(credentials);
|
||||
return;
|
||||
}
|
||||
|
||||
uname = talloc_strdup(credentials, data);
|
||||
if ((p = strchr_m(uname,'%'))) {
|
||||
*p = 0;
|
||||
cli_credentials_set_password(credentials, p+1, obtained);
|
||||
}
|
||||
|
||||
if ((p = strchr_m(uname,'@'))) {
|
||||
cli_credentials_set_principal(credentials, uname, obtained);
|
||||
*p = 0;
|
||||
cli_credentials_set_realm(credentials, p+1, obtained);
|
||||
return;
|
||||
} else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
|
||||
*p = 0;
|
||||
cli_credentials_set_domain(credentials, uname, obtained);
|
||||
uname = p+1;
|
||||
}
|
||||
cli_credentials_set_username(credentials, uname, obtained);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a a credentials structure, print it as a string
|
||||
*
|
||||
* The format output is [domain\\]user[%password] or user[@realm][%password]
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param mem_ctx The memory context to place the result on
|
||||
*/
|
||||
|
||||
const char *cli_credentials_get_unparsed_name(struct cli_credentials *credentials, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
const char *bind_dn = cli_credentials_get_bind_dn(credentials);
|
||||
const char *domain;
|
||||
const char *username;
|
||||
const char *name;
|
||||
|
||||
if (bind_dn) {
|
||||
name = talloc_reference(mem_ctx, bind_dn);
|
||||
} else {
|
||||
cli_credentials_get_ntlm_username_domain(credentials, mem_ctx, &username, &domain);
|
||||
if (domain && domain[0]) {
|
||||
name = talloc_asprintf(mem_ctx, "%s\\%s",
|
||||
domain, username);
|
||||
} else {
|
||||
name = talloc_asprintf(mem_ctx, "%s",
|
||||
username);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies default values for domain, workstation and realm
|
||||
* from the smb.conf configuration file
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
*/
|
||||
void cli_credentials_set_conf(struct cli_credentials *cred)
|
||||
{
|
||||
cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
|
||||
cli_credentials_set_domain(cred, lp_workgroup(), CRED_UNINITIALISED);
|
||||
cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_UNINITIALISED);
|
||||
cli_credentials_set_realm(cred, lp_realm(), CRED_UNINITIALISED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess defaults for credentials from environment variables,
|
||||
* and from the configuration file
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
*/
|
||||
void cli_credentials_guess(struct cli_credentials *cred)
|
||||
{
|
||||
char *p;
|
||||
|
||||
cli_credentials_set_conf(cred);
|
||||
|
||||
if (getenv("LOGNAME")) {
|
||||
cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESS_ENV);
|
||||
}
|
||||
|
||||
if (getenv("USER")) {
|
||||
cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESS_ENV);
|
||||
if ((p = strchr_m(getenv("USER"),'%'))) {
|
||||
memset(p,0,strlen(cred->password));
|
||||
}
|
||||
}
|
||||
|
||||
if (getenv("DOMAIN")) {
|
||||
cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESS_ENV);
|
||||
}
|
||||
|
||||
if (getenv("PASSWD")) {
|
||||
cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESS_ENV);
|
||||
}
|
||||
|
||||
if (getenv("PASSWD_FD")) {
|
||||
cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESS_FILE);
|
||||
}
|
||||
|
||||
if (getenv("PASSWD_FILE")) {
|
||||
cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESS_FILE);
|
||||
}
|
||||
|
||||
if (cli_credentials_get_kerberos_state(cred) != CRED_DONT_USE_KERBEROS) {
|
||||
cli_credentials_set_ccache(cred, NULL, CRED_GUESS_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach NETLOGON credentials for use with SCHANNEL
|
||||
*/
|
||||
|
||||
void cli_credentials_set_netlogon_creds(struct cli_credentials *cred,
|
||||
struct creds_CredentialState *netlogon_creds)
|
||||
{
|
||||
cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return attached NETLOGON credentials
|
||||
*/
|
||||
|
||||
struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->netlogon_creds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set NETLOGON secure channel type
|
||||
*/
|
||||
|
||||
void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
|
||||
enum netr_SchannelType secure_channel_type)
|
||||
{
|
||||
cred->secure_channel_type = secure_channel_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return NETLOGON secure chanel type
|
||||
*/
|
||||
|
||||
enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->secure_channel_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in a credentials structure as the anonymous user
|
||||
*/
|
||||
void cli_credentials_set_anonymous(struct cli_credentials *cred)
|
||||
{
|
||||
cli_credentials_set_username(cred, "", CRED_SPECIFIED);
|
||||
cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
|
||||
cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe a credentials context as anonymous or authenticated
|
||||
* @retval True if anonymous, False if a username is specified
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_is_anonymous(struct cli_credentials *cred)
|
||||
{
|
||||
const char *username;
|
||||
|
||||
if (cred->machine_account_pending) {
|
||||
cli_credentials_set_machine_account(cred);
|
||||
}
|
||||
|
||||
username = cli_credentials_get_username(cred);
|
||||
|
||||
/* Yes, it is deliberate that we die if we have a NULL pointer
|
||||
* here - anonymous is "", not NULL, which is 'never specified,
|
||||
* never guessed', ie programmer bug */
|
||||
if (!username[0]) {
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the current password for a credentials struct as wrong. This will
|
||||
* cause the password to be prompted again (if a callback is set).
|
||||
*
|
||||
* This will decrement the number of times the password can be tried.
|
||||
*
|
||||
* @retval whether the credentials struct is finished
|
||||
*/
|
||||
BOOL cli_credentials_wrong_password(struct cli_credentials *cred)
|
||||
{
|
||||
if (cred->password_obtained != CRED_CALLBACK_RESULT) {
|
||||
return False;
|
||||
}
|
||||
|
||||
cred->password_obtained = CRED_CALLBACK;
|
||||
|
||||
cred->tries--;
|
||||
|
||||
return (cred->tries > 0);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
samba -- Unix SMB/CIFS implementation.
|
||||
|
||||
Client credentials structure
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2004-2006
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#ifndef __CREDENTIALS_H__
|
||||
#define __CREDENTIALS_H__
|
||||
|
||||
#include "librpc/gen_ndr/misc.h"
|
||||
|
||||
struct ccache_container;
|
||||
|
||||
/* In order of priority */
|
||||
enum credentials_obtained {
|
||||
CRED_UNINITIALISED = 0, /* We don't even have a guess yet */
|
||||
CRED_GUESS_ENV, /* Current value should be used, which was guessed */
|
||||
CRED_CALLBACK, /* Callback should be used to obtain value */
|
||||
CRED_GUESS_FILE, /* A guess from a file (or file pointed at in env variable) */
|
||||
CRED_CALLBACK_RESULT, /* Value was obtained from a callback */
|
||||
CRED_SPECIFIED /* Was explicitly specified on the command-line */
|
||||
};
|
||||
|
||||
enum credentials_use_kerberos {
|
||||
CRED_AUTO_USE_KERBEROS = 0, /* Default, we try kerberos if available */
|
||||
CRED_DONT_USE_KERBEROS, /* Sometimes trying kerberos just does 'bad things', so don't */
|
||||
CRED_MUST_USE_KERBEROS /* Sometimes administrators are parinoid, so always do kerberos */
|
||||
};
|
||||
|
||||
#define CLI_CRED_NTLM2 0x01
|
||||
#define CLI_CRED_NTLMv2_AUTH 0x02
|
||||
#define CLI_CRED_LANMAN_AUTH 0x04
|
||||
#define CLI_CRED_NTLM_AUTH 0x08
|
||||
#define CLI_CRED_CLEAR_AUTH 0x10 /* TODO: Push cleartext auth with this flag */
|
||||
|
||||
struct cli_credentials {
|
||||
enum credentials_obtained workstation_obtained;
|
||||
enum credentials_obtained username_obtained;
|
||||
enum credentials_obtained password_obtained;
|
||||
enum credentials_obtained domain_obtained;
|
||||
enum credentials_obtained realm_obtained;
|
||||
enum credentials_obtained ccache_obtained;
|
||||
enum credentials_obtained client_gss_creds_obtained;
|
||||
enum credentials_obtained principal_obtained;
|
||||
enum credentials_obtained keytab_obtained;
|
||||
enum credentials_obtained server_gss_creds_obtained;
|
||||
|
||||
const char *workstation;
|
||||
const char *username;
|
||||
const char *password;
|
||||
const char *old_password;
|
||||
const char *domain;
|
||||
const char *realm;
|
||||
const char *principal;
|
||||
const char *salt_principal;
|
||||
|
||||
const char *bind_dn;
|
||||
|
||||
struct samr_Password *nt_hash;
|
||||
|
||||
struct ccache_container *ccache;
|
||||
struct gssapi_creds_container *client_gss_creds;
|
||||
struct keytab_container *keytab;
|
||||
struct gssapi_creds_container *server_gss_creds;
|
||||
|
||||
const char *(*workstation_cb) (struct cli_credentials *);
|
||||
const char *(*password_cb) (struct cli_credentials *);
|
||||
const char *(*username_cb) (struct cli_credentials *);
|
||||
const char *(*domain_cb) (struct cli_credentials *);
|
||||
const char *(*realm_cb) (struct cli_credentials *);
|
||||
const char *(*principal_cb) (struct cli_credentials *);
|
||||
|
||||
/* Private handle for the callback routines to use */
|
||||
void *priv_data;
|
||||
|
||||
struct creds_CredentialState *netlogon_creds;
|
||||
enum netr_SchannelType secure_channel_type;
|
||||
int kvno;
|
||||
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
|
||||
/* We are flagged to get machine account details from the
|
||||
* secrets.ldb when we are asked for a username or password */
|
||||
|
||||
BOOL machine_account_pending;
|
||||
|
||||
/* Is this a machine account? */
|
||||
BOOL machine_account;
|
||||
|
||||
/* Should we be trying to use kerberos? */
|
||||
enum credentials_use_kerberos use_kerberos;
|
||||
|
||||
/* Number of retries left before bailing out */
|
||||
int tries;
|
||||
|
||||
/* Whether any callback is currently running */
|
||||
BOOL callback_running;
|
||||
};
|
||||
|
||||
#include "auth/credentials/credentials_proto.h"
|
||||
|
||||
#endif /* __CREDENTIALS_H__ */
|
||||
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
User credentials handling (as regards on-disk files)
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Tim Potter 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
|
||||
#include "param/secrets.h"
|
||||
#include "system/filesys.h"
|
||||
#include "db_wrap.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
/**
|
||||
* Read a file descriptor, and parse it for a password (eg from a file or stdin)
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param fd open file descriptor to read the password from
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_parse_password_fd(struct cli_credentials *credentials,
|
||||
int fd, enum credentials_obtained obtained)
|
||||
{
|
||||
char *p;
|
||||
char pass[128];
|
||||
|
||||
for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
|
||||
p && p - pass < sizeof(pass);) {
|
||||
switch (read(fd, p, 1)) {
|
||||
case 1:
|
||||
if (*p != '\n' && *p != '\0') {
|
||||
*++p = '\0'; /* advance p, and null-terminate pass */
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case 0:
|
||||
if (p - pass) {
|
||||
*p = '\0'; /* null-terminate it, just in case... */
|
||||
p = NULL; /* then force the loop condition to become false */
|
||||
break;
|
||||
} else {
|
||||
fprintf(stderr, "Error reading password from file descriptor %d: %s\n", fd, "empty password\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Error reading password from file descriptor %d: %s\n",
|
||||
fd, strerror(errno));
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
cli_credentials_set_password(credentials, pass, obtained);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a named file, and parse it for a password
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param file a named file to read the password from
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained)
|
||||
{
|
||||
int fd = open(file, O_RDONLY, 0);
|
||||
BOOL ret;
|
||||
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
|
||||
file, strerror(errno));
|
||||
return False;
|
||||
}
|
||||
|
||||
ret = cli_credentials_parse_password_fd(credentials, fd, obtained);
|
||||
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a named file, and parse it for username, domain, realm and password
|
||||
*
|
||||
* @param credentials Credentials structure on which to set the password
|
||||
* @param file a named file to read the details from
|
||||
* @param obtained This enum describes how 'specified' this password is
|
||||
*/
|
||||
|
||||
BOOL cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained)
|
||||
{
|
||||
uint16_t len = 0;
|
||||
char *ptr, *val, *param;
|
||||
char **lines;
|
||||
int i, numlines;
|
||||
|
||||
lines = file_lines_load(file, &numlines, NULL);
|
||||
|
||||
if (lines == NULL)
|
||||
{
|
||||
/* fail if we can't open the credentials file */
|
||||
d_printf("ERROR: Unable to open credentials file!\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
for (i = 0; i < numlines; i++) {
|
||||
len = strlen(lines[i]);
|
||||
|
||||
if (len == 0)
|
||||
continue;
|
||||
|
||||
/* break up the line into parameter & value.
|
||||
* will need to eat a little whitespace possibly */
|
||||
param = lines[i];
|
||||
if (!(ptr = strchr_m (lines[i], '=')))
|
||||
continue;
|
||||
|
||||
val = ptr+1;
|
||||
*ptr = '\0';
|
||||
|
||||
/* eat leading white space */
|
||||
while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
|
||||
val++;
|
||||
|
||||
if (strwicmp("password", param) == 0) {
|
||||
cli_credentials_set_password(cred, val, obtained);
|
||||
} else if (strwicmp("username", param) == 0) {
|
||||
cli_credentials_set_username(cred, val, obtained);
|
||||
} else if (strwicmp("domain", param) == 0) {
|
||||
cli_credentials_set_domain(cred, val, obtained);
|
||||
} else if (strwicmp("realm", param) == 0) {
|
||||
cli_credentials_set_realm(cred, val, obtained);
|
||||
}
|
||||
memset(lines[i], 0, len);
|
||||
}
|
||||
|
||||
talloc_free(lines);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred,
|
||||
const char *base,
|
||||
const char *filter)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
struct ldb_context *ldb;
|
||||
int ldb_ret;
|
||||
struct ldb_message **msgs;
|
||||
const char *attrs[] = {
|
||||
"secret",
|
||||
"priorSecret",
|
||||
"samAccountName",
|
||||
"flatname",
|
||||
"realm",
|
||||
"secureChannelType",
|
||||
"ntPwdHash",
|
||||
"msDS-KeyVersionNumber",
|
||||
"saltPrincipal",
|
||||
"privateKeytab",
|
||||
"krb5Keytab",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *machine_account;
|
||||
const char *password;
|
||||
const char *old_password;
|
||||
const char *domain;
|
||||
const char *realm;
|
||||
enum netr_SchannelType sct;
|
||||
const char *salt_principal;
|
||||
const char *keytab;
|
||||
|
||||
/* ok, we are going to get it now, don't recurse back here */
|
||||
cred->machine_account_pending = False;
|
||||
|
||||
/* some other parts of the system will key off this */
|
||||
cred->machine_account = True;
|
||||
|
||||
mem_ctx = talloc_named(cred, 0, "cli_credentials fetch machine password");
|
||||
|
||||
/* Local secrets are stored in secrets.ldb */
|
||||
ldb = secrets_db_connect(mem_ctx);
|
||||
if (!ldb) {
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
DEBUG(1, ("Could not open secrets.ldb\n"));
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
/* search for the secret record */
|
||||
ldb_ret = gendb_search(ldb,
|
||||
mem_ctx, ldb_dn_new(mem_ctx, ldb, base),
|
||||
&msgs, attrs,
|
||||
"%s", filter);
|
||||
if (ldb_ret == 0) {
|
||||
DEBUG(1, ("Could not find entry to match filter: %s\n",
|
||||
filter));
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
} else if (ldb_ret != 1) {
|
||||
DEBUG(1, ("Found more than one (%d) entry to match filter: %s\n",
|
||||
ldb_ret, filter));
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
password = ldb_msg_find_attr_as_string(msgs[0], "secret", NULL);
|
||||
old_password = ldb_msg_find_attr_as_string(msgs[0], "priorSecret", NULL);
|
||||
|
||||
machine_account = ldb_msg_find_attr_as_string(msgs[0], "samAccountName", NULL);
|
||||
|
||||
if (!machine_account) {
|
||||
DEBUG(1, ("Could not find 'samAccountName' in join record to domain: %s\n",
|
||||
cli_credentials_get_domain(cred)));
|
||||
/* set anonymous as the fallback, if the machine account won't work */
|
||||
cli_credentials_set_anonymous(cred);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
salt_principal = ldb_msg_find_attr_as_string(msgs[0], "saltPrincipal", NULL);
|
||||
cli_credentials_set_salt_principal(cred, salt_principal);
|
||||
|
||||
sct = ldb_msg_find_attr_as_int(msgs[0], "secureChannelType", 0);
|
||||
if (sct) {
|
||||
cli_credentials_set_secure_channel_type(cred, sct);
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
const struct ldb_val *nt_password_hash = ldb_msg_find_ldb_val(msgs[0], "ntPwdHash");
|
||||
struct samr_Password hash;
|
||||
ZERO_STRUCT(hash);
|
||||
if (nt_password_hash) {
|
||||
memcpy(hash.hash, nt_password_hash->data,
|
||||
MIN(nt_password_hash->length, sizeof(hash.hash)));
|
||||
|
||||
cli_credentials_set_nt_hash(cred, &hash, CRED_SPECIFIED);
|
||||
} else {
|
||||
cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
|
||||
}
|
||||
} else {
|
||||
cli_credentials_set_password(cred, password, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
|
||||
domain = ldb_msg_find_attr_as_string(msgs[0], "flatname", NULL);
|
||||
if (domain) {
|
||||
cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
realm = ldb_msg_find_attr_as_string(msgs[0], "realm", NULL);
|
||||
if (realm) {
|
||||
cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
|
||||
}
|
||||
|
||||
cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
|
||||
|
||||
cli_credentials_set_kvno(cred, ldb_msg_find_attr_as_int(msgs[0], "msDS-KeyVersionNumber", 0));
|
||||
|
||||
/* If there was an external keytab specified by reference in
|
||||
* the LDB, then use this. Otherwise we will make one up
|
||||
* (chewing CPU time) from the password */
|
||||
keytab = ldb_msg_find_attr_as_string(msgs[0], "krb5Keytab", NULL);
|
||||
if (keytab) {
|
||||
cli_credentials_set_keytab_name(cred, keytab, CRED_SPECIFIED);
|
||||
} else {
|
||||
keytab = ldb_msg_find_attr_as_string(msgs[0], "privateKeytab", NULL);
|
||||
if (keytab) {
|
||||
keytab = talloc_asprintf(mem_ctx, "FILE:%s", private_path(mem_ctx, keytab));
|
||||
if (keytab) {
|
||||
cli_credentials_set_keytab_name(cred, keytab, CRED_SPECIFIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred)
|
||||
{
|
||||
char *filter;
|
||||
/* Bleh, nasty recursion issues: We are setting a machine
|
||||
* account here, so we don't want the 'pending' flag around
|
||||
* any more */
|
||||
cred->machine_account_pending = False;
|
||||
filter = talloc_asprintf(cred, SECRETS_PRIMARY_DOMAIN_FILTER,
|
||||
cli_credentials_get_domain(cred));
|
||||
return cli_credentials_set_secrets(cred, SECRETS_PRIMARY_DOMAIN_DN,
|
||||
filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_krbtgt(struct cli_credentials *cred)
|
||||
{
|
||||
char *filter;
|
||||
/* Bleh, nasty recursion issues: We are setting a machine
|
||||
* account here, so we don't want the 'pending' flag around
|
||||
* any more */
|
||||
cred->machine_account_pending = False;
|
||||
filter = talloc_asprintf(cred, SECRETS_KRBTGT_SEARCH,
|
||||
cli_credentials_get_realm(cred),
|
||||
cli_credentials_get_domain(cred));
|
||||
return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN,
|
||||
filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in credentials for the machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @retval NTSTATUS error detailing any failure
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred,
|
||||
const char *serviceprincipal)
|
||||
{
|
||||
char *filter;
|
||||
/* Bleh, nasty recursion issues: We are setting a machine
|
||||
* account here, so we don't want the 'pending' flag around
|
||||
* any more */
|
||||
cred->machine_account_pending = False;
|
||||
filter = talloc_asprintf(cred, SECRETS_PRINCIPAL_SEARCH,
|
||||
cli_credentials_get_realm(cred),
|
||||
cli_credentials_get_domain(cred),
|
||||
serviceprincipal);
|
||||
return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN,
|
||||
filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask that when required, the credentials system will be filled with
|
||||
* machine trust account, from the secrets database.
|
||||
*
|
||||
* @param cred Credentials structure to fill in
|
||||
* @note This function is used to call the above function after, rather
|
||||
* than during, popt processing.
|
||||
*
|
||||
*/
|
||||
void cli_credentials_set_machine_account_pending(struct cli_credentials *cred)
|
||||
{
|
||||
cred->machine_account_pending = True;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS cli_credentials_update_all_keytabs(TALLOC_CTX *parent_ctx)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
int ldb_ret;
|
||||
struct ldb_context *ldb;
|
||||
struct ldb_message **msgs;
|
||||
const char *attrs[] = { NULL };
|
||||
struct cli_credentials *creds;
|
||||
const char *filter;
|
||||
NTSTATUS status;
|
||||
int i, ret;
|
||||
|
||||
mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* Local secrets are stored in secrets.ldb */
|
||||
ldb = secrets_db_connect(mem_ctx);
|
||||
if (!ldb) {
|
||||
DEBUG(1, ("Could not open secrets.ldb\n"));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* search for the secret record, but only of things we can
|
||||
* actually update */
|
||||
ldb_ret = gendb_search(ldb,
|
||||
mem_ctx, NULL,
|
||||
&msgs, attrs,
|
||||
"(&(objectClass=kerberosSecret)(|(secret=*)(ntPwdHash=*)))");
|
||||
if (ldb_ret == -1) {
|
||||
DEBUG(1, ("Error looking for kerberos type secrets to push into a keytab:: %s", ldb_errstring(ldb)));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
for (i=0; i < ldb_ret; i++) {
|
||||
/* Make a credentials structure from it */
|
||||
creds = cli_credentials_init(mem_ctx);
|
||||
if (!creds) {
|
||||
DEBUG(1, ("cli_credentials_init failed!"));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
cli_credentials_set_conf(creds);
|
||||
filter = talloc_asprintf(mem_ctx, "dn=%s", ldb_dn_get_linearized(msgs[i]->dn));
|
||||
status = cli_credentials_set_secrets(creds, NULL, filter);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(1, ("Failed to read secrets for keytab update for %s\n",
|
||||
filter));
|
||||
continue;
|
||||
}
|
||||
ret = cli_credentials_update_keytab(creds);
|
||||
if (ret != 0) {
|
||||
DEBUG(1, ("Failed to update keytab for %s\n",
|
||||
filter));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Handle user credentials (as regards krb5)
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Tim Potter 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
int cli_credentials_get_krb5_context(struct cli_credentials *cred,
|
||||
struct smb_krb5_context **smb_krb5_context)
|
||||
{
|
||||
int ret;
|
||||
if (cred->smb_krb5_context) {
|
||||
*smb_krb5_context = cred->smb_krb5_context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = smb_krb5_init_context(cred, &cred->smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
*smb_krb5_context = cred->smb_krb5_context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This needs to be called directly after the cli_credentials_init(),
|
||||
* otherwise we might have problems with the krb5 context already
|
||||
* being here.
|
||||
*/
|
||||
NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
|
||||
struct smb_krb5_context *smb_krb5_context)
|
||||
{
|
||||
if (!talloc_reference(cred, smb_krb5_context)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
cred->smb_krb5_context = smb_krb5_context;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
int cli_credentials_set_from_ccache(struct cli_credentials *cred,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
|
||||
krb5_principal princ;
|
||||
krb5_error_code ret;
|
||||
char *name;
|
||||
char **realm;
|
||||
|
||||
if (cred->ccache_obtained > obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context,
|
||||
cred->ccache->ccache, &princ);
|
||||
|
||||
if (ret) {
|
||||
char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
|
||||
DEBUG(1,("failed to get principal from ccache: %s\n",
|
||||
err_mess));
|
||||
talloc_free(err_mess);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = krb5_unparse_name(cred->ccache->smb_krb5_context->krb5_context, princ, &name);
|
||||
if (ret) {
|
||||
char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
|
||||
DEBUG(1,("failed to unparse principal from ccache: %s\n",
|
||||
err_mess));
|
||||
talloc_free(err_mess);
|
||||
return ret;
|
||||
}
|
||||
|
||||
realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ);
|
||||
|
||||
cli_credentials_set_principal(cred, name, obtained);
|
||||
|
||||
free(name);
|
||||
|
||||
krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ);
|
||||
|
||||
cred->ccache_obtained = obtained;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free a memory ccache */
|
||||
static int free_mccache(struct ccache_container *ccc)
|
||||
{
|
||||
krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Free a disk-based ccache */
|
||||
static int free_dccache(struct ccache_container *ccc) {
|
||||
krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cli_credentials_set_ccache(struct cli_credentials *cred,
|
||||
const char *name,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal princ;
|
||||
struct ccache_container *ccc;
|
||||
if (cred->ccache_obtained > obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ccc = talloc(cred, struct ccache_container);
|
||||
if (!ccc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
|
||||
if (ret) {
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
talloc_reference(ccc, ccc->smb_krb5_context);
|
||||
|
||||
if (name) {
|
||||
ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
|
||||
name,
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
|
||||
if (ret) {
|
||||
DEBUG(3,("failed to read default krb5 ccache: %s\n",
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_set_destructor(ccc, free_dccache);
|
||||
|
||||
ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
|
||||
|
||||
if (ret) {
|
||||
DEBUG(3,("failed to get principal from default ccache: %s\n",
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
|
||||
|
||||
cred->ccache = ccc;
|
||||
talloc_steal(cred, ccc);
|
||||
|
||||
ret = cli_credentials_set_from_ccache(cred, obtained);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *rand_string;
|
||||
struct ccache_container *ccc = talloc(cred, struct ccache_container);
|
||||
char *ccache_name;
|
||||
if (!ccc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
rand_string = generate_random_str(NULL, 16);
|
||||
if (!rand_string) {
|
||||
talloc_free(ccc);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ccache_name = talloc_asprintf(ccc, "MEMORY:%s",
|
||||
rand_string);
|
||||
talloc_free(rand_string);
|
||||
|
||||
if (!ccache_name) {
|
||||
talloc_free(ccc);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
|
||||
if (ret) {
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
talloc_reference(ccc, ccc->smb_krb5_context);
|
||||
|
||||
ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
|
||||
ccache_name,
|
||||
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
|
||||
talloc_free(ccache_name);
|
||||
talloc_free(ccc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_set_destructor(ccc, free_mccache);
|
||||
|
||||
cred->ccache = ccc;
|
||||
talloc_steal(cred, ccc);
|
||||
talloc_free(ccache_name);
|
||||
|
||||
if (_ccc) {
|
||||
*_ccc = ccc;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cli_credentials_get_ccache(struct cli_credentials *cred,
|
||||
struct ccache_container **ccc)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
if (cred->ccache_obtained >= (MAX(cred->principal_obtained,
|
||||
cred->username_obtained))) {
|
||||
*ccc = cred->ccache;
|
||||
return 0;
|
||||
}
|
||||
if (cli_credentials_is_anonymous(cred)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
ret = cli_credentials_new_ccache(cred, NULL);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
*ccc = cred->ccache;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int free_gssapi_creds(struct gssapi_creds_container *gcc)
|
||||
{
|
||||
OM_uint32 min_stat, maj_stat;
|
||||
maj_stat = gss_release_cred(&min_stat, &gcc->creds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
|
||||
struct gssapi_creds_container **_gcc)
|
||||
{
|
||||
int ret = 0;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
struct gssapi_creds_container *gcc;
|
||||
struct ccache_container *ccache;
|
||||
if (cred->client_gss_creds_obtained >= (MAX(cred->ccache_obtained,
|
||||
MAX(cred->principal_obtained,
|
||||
cred->username_obtained)))) {
|
||||
*_gcc = cred->client_gss_creds;
|
||||
return 0;
|
||||
}
|
||||
ret = cli_credentials_get_ccache(cred,
|
||||
&ccache);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
gcc = talloc(cred, struct gssapi_creds_container);
|
||||
if (!gcc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
|
||||
&gcc->creds);
|
||||
if (maj_stat) {
|
||||
if (min_stat) {
|
||||
ret = min_stat;
|
||||
} else {
|
||||
ret = EINVAL;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
cred->client_gss_creds_obtained = cred->ccache_obtained;
|
||||
talloc_set_destructor(gcc, free_gssapi_creds);
|
||||
cred->client_gss_creds = gcc;
|
||||
*_gcc = gcc;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
Set a gssapi cred_id_t into the credentails system. (Client case)
|
||||
|
||||
This grabs the credentials both 'intact' and getting the krb5
|
||||
ccache out of it. This routine can be generalised in future for
|
||||
the case where we deal with GSSAPI mechs other than krb5.
|
||||
|
||||
On sucess, the caller must not free gssapi_cred, as it now belongs
|
||||
to the credentials system.
|
||||
*/
|
||||
|
||||
int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
|
||||
gss_cred_id_t gssapi_cred,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
int ret;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
struct ccache_container *ccc;
|
||||
struct gssapi_creds_container *gcc;
|
||||
if (cred->client_gss_creds_obtained > obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
gcc = talloc(cred, struct gssapi_creds_container);
|
||||
if (!gcc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_new_ccache(cred, &ccc);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
maj_stat = gss_krb5_copy_ccache(&min_stat,
|
||||
gssapi_cred, ccc->ccache);
|
||||
if (maj_stat) {
|
||||
if (min_stat) {
|
||||
ret = min_stat;
|
||||
} else {
|
||||
ret = EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ret = cli_credentials_set_from_ccache(cred, obtained);
|
||||
}
|
||||
if (ret == 0) {
|
||||
gcc->creds = gssapi_cred;
|
||||
talloc_set_destructor(gcc, free_gssapi_creds);
|
||||
|
||||
cred->client_gss_creds_obtained = obtained;
|
||||
cred->client_gss_creds = gcc;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get the keytab (actually, a container containing the krb5_keytab)
|
||||
* attached to this context. If this hasn't been done or set before,
|
||||
* it will be generated from the password.
|
||||
*/
|
||||
int cli_credentials_get_keytab(struct cli_credentials *cred,
|
||||
struct keytab_container **_ktc)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
|
||||
cred->username_obtained))) {
|
||||
*_ktc = cred->keytab;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cli_credentials_is_anonymous(cred)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = smb_krb5_create_memory_keytab(mem_ctx, cred, smb_krb5_context, &ktc);
|
||||
if (ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cred->keytab_obtained = (MAX(cred->principal_obtained,
|
||||
cred->username_obtained));
|
||||
|
||||
talloc_steal(cred, ktc);
|
||||
cred->keytab = ktc;
|
||||
*_ktc = cred->keytab;
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Given the name of a keytab (presumably in the format
|
||||
* FILE:/etc/krb5.keytab), open it and attach it */
|
||||
|
||||
int cli_credentials_set_keytab_name(struct cli_credentials *cred,
|
||||
const char *keytab_name,
|
||||
enum credentials_obtained obtained)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if (cred->keytab_obtained >= obtained) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
|
||||
keytab_name, &ktc);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
cred->keytab_obtained = obtained;
|
||||
|
||||
talloc_steal(cred, ktc);
|
||||
cred->keytab = ktc;
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cli_credentials_update_keytab(struct cli_credentials *cred)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_keytab(cred, &ktc);
|
||||
if (ret != 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, ktc);
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get server gss credentials (in gsskrb5, this means the keytab) */
|
||||
|
||||
int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
|
||||
struct gssapi_creds_container **_gcc)
|
||||
{
|
||||
int ret = 0;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
struct gssapi_creds_container *gcc;
|
||||
struct keytab_container *ktc;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
krb5_principal princ;
|
||||
|
||||
if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
|
||||
MAX(cred->principal_obtained,
|
||||
cred->username_obtained)))) {
|
||||
*_gcc = cred->server_gss_creds;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cli_credentials_get_keytab(cred,
|
||||
&ktc);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(cred);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gcc = talloc(cred, struct gssapi_creds_container);
|
||||
if (!gcc) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* This creates a GSSAPI cred_id_t with the principal and keytab set */
|
||||
maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
|
||||
&gcc->creds);
|
||||
if (maj_stat) {
|
||||
if (min_stat) {
|
||||
ret = min_stat;
|
||||
} else {
|
||||
ret = EINVAL;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
cred->server_gss_creds_obtained = cred->keytab_obtained;
|
||||
talloc_set_destructor(gcc, free_gssapi_creds);
|
||||
cred->server_gss_creds = gcc;
|
||||
*_gcc = gcc;
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Kerberos KVNO
|
||||
*/
|
||||
|
||||
void cli_credentials_set_kvno(struct cli_credentials *cred,
|
||||
int kvno)
|
||||
{
|
||||
cred->kvno = kvno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Kerberos KVNO
|
||||
*/
|
||||
|
||||
int cli_credentials_get_kvno(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->kvno;
|
||||
}
|
||||
|
||||
const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
|
||||
{
|
||||
return cred->salt_principal;
|
||||
}
|
||||
|
||||
void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
|
||||
{
|
||||
cred->salt_principal = talloc_strdup(cred, principal);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
samba -- Unix SMB/CIFS implementation.
|
||||
|
||||
Client credentials structure
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2004-2006
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "heimdal/lib/gssapi/gssapi/gssapi.h"
|
||||
|
||||
struct ccache_container;
|
||||
|
||||
struct gssapi_creds_container {
|
||||
gss_cred_id_t creds;
|
||||
};
|
||||
|
||||
#include "auth/credentials/credentials_krb5_proto.h"
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
User credentials handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
|
||||
void cli_credentials_get_ntlm_username_domain(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
|
||||
const char **username,
|
||||
const char **domain)
|
||||
{
|
||||
if (cred->principal_obtained > cred->username_obtained) {
|
||||
*domain = talloc_strdup(mem_ctx, "");
|
||||
*username = cli_credentials_get_principal(cred, mem_ctx);
|
||||
} else {
|
||||
*domain = cli_credentials_get_domain(cred);
|
||||
*username = cli_credentials_get_username(cred);
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
|
||||
int *flags,
|
||||
DATA_BLOB challenge, DATA_BLOB target_info,
|
||||
DATA_BLOB *_lm_response, DATA_BLOB *_nt_response,
|
||||
DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key)
|
||||
{
|
||||
const char *user, *domain;
|
||||
DATA_BLOB lm_response, nt_response;
|
||||
DATA_BLOB lm_session_key, session_key;
|
||||
const struct samr_Password *nt_hash;
|
||||
lm_session_key = data_blob(NULL, 0);
|
||||
|
||||
nt_hash = cli_credentials_get_nt_hash(cred, mem_ctx);
|
||||
|
||||
cli_credentials_get_ntlm_username_domain(cred, mem_ctx, &user, &domain);
|
||||
|
||||
/* If we are sending a username@realm login (see function
|
||||
* above), then we will not send LM, it will not be
|
||||
* accepted */
|
||||
if (cred->principal_obtained > cred->username_obtained) {
|
||||
*flags = *flags & ~CLI_CRED_LANMAN_AUTH;
|
||||
}
|
||||
|
||||
/* Likewise if we are a machine account (avoid protocol downgrade attacks) */
|
||||
if (cred->machine_account) {
|
||||
*flags = *flags & ~CLI_CRED_LANMAN_AUTH;
|
||||
}
|
||||
|
||||
if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (!nt_hash) {
|
||||
static const uint8_t zeros[16];
|
||||
/* do nothing - blobs are zero length */
|
||||
|
||||
/* session key is all zeros */
|
||||
session_key = data_blob_talloc(mem_ctx, zeros, 16);
|
||||
lm_session_key = data_blob_talloc(mem_ctx, zeros, 16);
|
||||
|
||||
lm_response = data_blob(NULL, 0);
|
||||
nt_response = data_blob(NULL, 0);
|
||||
|
||||
/* not doing NTLM2 without a password */
|
||||
*flags &= ~CLI_CRED_NTLM2;
|
||||
} else if (*flags & CLI_CRED_NTLMv2_AUTH) {
|
||||
|
||||
if (!target_info.length) {
|
||||
/* be lazy, match win2k - we can't do NTLMv2 without it */
|
||||
DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* TODO: if the remote server is standalone, then we should replace 'domain'
|
||||
with the server name as supplied above */
|
||||
|
||||
if (!SMBNTLMv2encrypt_hash(mem_ctx,
|
||||
user,
|
||||
domain,
|
||||
nt_hash->hash, &challenge,
|
||||
&target_info,
|
||||
&lm_response, &nt_response,
|
||||
NULL, &session_key)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
} else if (*flags & CLI_CRED_NTLM2) {
|
||||
struct MD5Context md5_session_nonce_ctx;
|
||||
uint8_t session_nonce[16];
|
||||
uint8_t session_nonce_hash[16];
|
||||
uint8_t user_session_key[16];
|
||||
|
||||
lm_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
generate_random_buffer(lm_response.data, 8);
|
||||
memset(lm_response.data+8, 0, 16);
|
||||
|
||||
memcpy(session_nonce, challenge.data, 8);
|
||||
memcpy(&session_nonce[8], lm_response.data, 8);
|
||||
|
||||
MD5Init(&md5_session_nonce_ctx);
|
||||
MD5Update(&md5_session_nonce_ctx, challenge.data, 8);
|
||||
MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
|
||||
MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
|
||||
|
||||
DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
|
||||
DEBUG(5, ("challenge is: \n"));
|
||||
dump_data(5, session_nonce_hash, 8);
|
||||
|
||||
nt_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
SMBOWFencrypt(nt_hash->hash,
|
||||
session_nonce_hash,
|
||||
nt_response.data);
|
||||
|
||||
session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
|
||||
SMBsesskeygen_ntv1(nt_hash->hash, user_session_key);
|
||||
hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data);
|
||||
dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
} else {
|
||||
uint8_t lm_hash[16];
|
||||
nt_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
SMBOWFencrypt(nt_hash->hash, challenge.data,
|
||||
nt_response.data);
|
||||
|
||||
session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
SMBsesskeygen_ntv1(nt_hash->hash, session_key.data);
|
||||
dump_data_pw("NT session key:\n", session_key.data, session_key.length);
|
||||
|
||||
/* lanman auth is insecure, it may be disabled.
|
||||
We may also not have a password */
|
||||
if (*flags & CLI_CRED_LANMAN_AUTH) {
|
||||
const char *password;
|
||||
password = cli_credentials_get_password(cred);
|
||||
if (!password) {
|
||||
lm_response = nt_response;
|
||||
} else {
|
||||
lm_response = data_blob_talloc(mem_ctx, NULL, 24);
|
||||
if (!SMBencrypt(password,challenge.data,
|
||||
lm_response.data)) {
|
||||
/* If the LM password was too long (and therefore the LM hash being
|
||||
of the first 14 chars only), don't send it.
|
||||
|
||||
We don't have any better options but to send the NT response
|
||||
*/
|
||||
data_blob_free(&lm_response);
|
||||
lm_response = nt_response;
|
||||
/* LM Key is incompatible with 'long' passwords */
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
} else {
|
||||
E_deshash(password, lm_hash);
|
||||
lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
memcpy(lm_session_key.data, lm_hash, 8);
|
||||
memset(&lm_session_key.data[8], '\0', 8);
|
||||
|
||||
if (!(*flags & CLI_CRED_NTLM_AUTH)) {
|
||||
session_key = lm_session_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char *password;
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
lm_response = nt_response;
|
||||
*flags &= ~CLI_CRED_LANMAN_AUTH;
|
||||
|
||||
password = cli_credentials_get_password(cred);
|
||||
if (password) {
|
||||
E_deshash(password, lm_hash);
|
||||
lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
memcpy(lm_session_key.data, lm_hash, 8);
|
||||
memset(&lm_session_key.data[8], '\0', 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_lm_response) {
|
||||
*_lm_response = lm_response;
|
||||
}
|
||||
if (_nt_response) {
|
||||
*_nt_response = nt_response;
|
||||
}
|
||||
if (_lm_session_key) {
|
||||
*_lm_session_key = lm_session_key;
|
||||
}
|
||||
if (_session_key) {
|
||||
*_session_key = session_key;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
SMB_ENABLE(gensec_krb5, $HAVE_KRB5)
|
||||
SMB_ENABLE(gensec_gssapi, $HAVE_KRB5)
|
||||
@@ -0,0 +1,91 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM gensec
|
||||
[LIBRARY::gensec]
|
||||
VERSION = 0.0.1
|
||||
SO_VERSION = 0
|
||||
DESCRIPTION = Generic Security Library
|
||||
PUBLIC_HEADERS = gensec.h spnego.h
|
||||
PUBLIC_PROTO_HEADER = gensec_proto.h
|
||||
OBJ_FILES = gensec.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
CREDENTIALS LIBSAMBA-UTIL LIBCRYPTO ASN1_UTIL
|
||||
# End SUBSYSTEM gensec
|
||||
#################################
|
||||
|
||||
################################################
|
||||
# Start MODULE gensec_krb5
|
||||
[MODULE::gensec_krb5]
|
||||
SUBSYSTEM = gensec
|
||||
INIT_FUNCTION = gensec_krb5_init
|
||||
OBJ_FILES = gensec_krb5.o
|
||||
PUBLIC_DEPENDENCIES = CREDENTIALS_KRB5 KERBEROS auth auth_sam
|
||||
# End MODULE gensec_krb5
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE gensec_gssapi
|
||||
[MODULE::gensec_gssapi]
|
||||
SUBSYSTEM = gensec
|
||||
INIT_FUNCTION = gensec_gssapi_init
|
||||
OBJ_FILES = gensec_gssapi.o
|
||||
PUBLIC_DEPENDENCIES = CREDENTIALS_KRB5 KERBEROS auth HEIMDAL_GSSAPI
|
||||
# End MODULE gensec_gssapi
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE cyrus_sasl
|
||||
[MODULE::cyrus_sasl]
|
||||
SUBSYSTEM = gensec
|
||||
INIT_FUNCTION = gensec_sasl_init
|
||||
OBJ_FILES = cyrus_sasl.o
|
||||
PUBLIC_DEPENDENCIES = CREDENTIALS SASL auth
|
||||
# End MODULE cyrus_sasl
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE gensec_spnego
|
||||
[MODULE::gensec_spnego]
|
||||
SUBSYSTEM = gensec
|
||||
INIT_FUNCTION = gensec_spnego_init
|
||||
PRIVATE_PROTO_HEADER = spnego_proto.h
|
||||
PRIVATE_DEPENDENCIES = ASN1_UTIL GENSEC_SOCKET
|
||||
PUBLIC_DEPENDENCIES = CREDENTIALS
|
||||
OBJ_FILES = spnego.o \
|
||||
spnego_parse.o
|
||||
# End MODULE gensec_spnego
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE gensec_schannel
|
||||
[MODULE::gensec_schannel]
|
||||
SUBSYSTEM = gensec
|
||||
PRIVATE_PROTO_HEADER = schannel_proto.h
|
||||
INIT_FUNCTION = gensec_schannel_init
|
||||
OBJ_FILES = schannel.o \
|
||||
schannel_sign.o
|
||||
PUBLIC_DEPENDENCIES = auth SCHANNELDB NDR_SCHANNEL CREDENTIALS
|
||||
OUTPUT_TYPE = INTEGRATED
|
||||
# End MODULE gensec_schannel
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM SCHANNELDB
|
||||
[SUBSYSTEM::SCHANNELDB]
|
||||
PRIVATE_PROTO_HEADER = schannel_state.h
|
||||
OBJ_FILES = \
|
||||
schannel_state.o
|
||||
#
|
||||
# End SUBSYSTEM SCHANNELDB
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM GENSEC_SOCKET
|
||||
[SUBSYSTEM::GENSEC_SOCKET]
|
||||
OBJ_FILES = \
|
||||
socket.o
|
||||
PUBLIC_DEPENDENCIES = samba-socket LIBPACKET
|
||||
#PUBLIC_DEPENDENCIES = gensec
|
||||
#
|
||||
# End SUBSYSTEM GENSEC_SOCKET
|
||||
################################################
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Connect GENSEC to an external SASL lib
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include <sasl/sasl.h>
|
||||
|
||||
struct gensec_sasl_state {
|
||||
sasl_conn_t *conn;
|
||||
int step;
|
||||
};
|
||||
|
||||
static NTSTATUS sasl_nt_status(int sasl_ret)
|
||||
{
|
||||
switch (sasl_ret) {
|
||||
case SASL_CONTINUE:
|
||||
return NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
case SASL_NOMEM:
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
case SASL_BADPARAM:
|
||||
case SASL_NOMECH:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
case SASL_BADMAC:
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
case SASL_OK:
|
||||
return NT_STATUS_OK;
|
||||
default:
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
}
|
||||
|
||||
static int gensec_sasl_get_user(void *context, int id,
|
||||
const char **result, unsigned *len)
|
||||
{
|
||||
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
|
||||
const char *username = cli_credentials_get_username(gensec_get_credentials(gensec_security));
|
||||
if (id != SASL_CB_USER && id != SASL_CB_AUTHNAME) {
|
||||
return SASL_FAIL;
|
||||
}
|
||||
|
||||
*result = username;
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
static int gensec_sasl_get_realm(void *context, int id,
|
||||
const char **availrealms,
|
||||
const char **result)
|
||||
{
|
||||
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
|
||||
const char *realm = cli_credentials_get_realm(gensec_get_credentials(gensec_security));
|
||||
int i;
|
||||
if (id != SASL_CB_GETREALM) {
|
||||
return SASL_FAIL;
|
||||
}
|
||||
|
||||
for (i=0; availrealms && availrealms[i]; i++) {
|
||||
if (strcasecmp_m(realm, availrealms[i]) == 0) {
|
||||
result[i] = availrealms[i];
|
||||
return SASL_OK;
|
||||
}
|
||||
}
|
||||
/* None of the realms match, so lets not specify one */
|
||||
*result = "";
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
static int gensec_sasl_get_password(sasl_conn_t *conn, void *context, int id,
|
||||
sasl_secret_t **psecret)
|
||||
{
|
||||
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
|
||||
const char *password = cli_credentials_get_password(gensec_get_credentials(gensec_security));
|
||||
|
||||
sasl_secret_t *secret;
|
||||
if (!password) {
|
||||
*psecret = NULL;
|
||||
return SASL_OK;
|
||||
}
|
||||
secret = talloc_size(gensec_security, sizeof(sasl_secret_t)+strlen(password));
|
||||
if (!secret) {
|
||||
return SASL_NOMEM;
|
||||
}
|
||||
secret->len = strlen(password);
|
||||
safe_strcpy(secret->data, password, secret->len+1);
|
||||
*psecret = secret;
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
static int gensec_sasl_dispose(struct gensec_sasl_state *gensec_sasl_state)
|
||||
{
|
||||
sasl_dispose(&gensec_sasl_state->conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_sasl_client_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct gensec_sasl_state *gensec_sasl_state;
|
||||
const char *service = gensec_get_target_service(gensec_security);
|
||||
const char *target_name = gensec_get_target_hostname(gensec_security);
|
||||
struct socket_address *local_socket_addr = gensec_get_my_addr(gensec_security);
|
||||
struct socket_address *remote_socket_addr = gensec_get_peer_addr(gensec_security);
|
||||
char *local_addr = NULL;
|
||||
char *remote_addr = NULL;
|
||||
int sasl_ret;
|
||||
|
||||
sasl_callback_t *callbacks;
|
||||
|
||||
gensec_sasl_state = talloc(gensec_security, struct gensec_sasl_state);
|
||||
if (!gensec_sasl_state) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
callbacks = talloc_array(gensec_sasl_state, sasl_callback_t, 5);
|
||||
callbacks[0].id = SASL_CB_USER;
|
||||
callbacks[0].proc = gensec_sasl_get_user;
|
||||
callbacks[0].context = gensec_security;
|
||||
|
||||
callbacks[1].id = SASL_CB_AUTHNAME;
|
||||
callbacks[1].proc = gensec_sasl_get_user;
|
||||
callbacks[1].context = gensec_security;
|
||||
|
||||
callbacks[2].id = SASL_CB_GETREALM;
|
||||
callbacks[2].proc = gensec_sasl_get_realm;
|
||||
callbacks[2].context = gensec_security;
|
||||
|
||||
callbacks[3].id = SASL_CB_PASS;
|
||||
callbacks[3].proc = gensec_sasl_get_password;
|
||||
callbacks[3].context = gensec_security;
|
||||
|
||||
callbacks[4].id = SASL_CB_LIST_END;
|
||||
callbacks[4].proc = NULL;
|
||||
callbacks[4].context = NULL;
|
||||
|
||||
gensec_security->private_data = gensec_sasl_state;
|
||||
|
||||
if (local_socket_addr) {
|
||||
local_addr = talloc_asprintf(gensec_sasl_state,
|
||||
"%s;%d",
|
||||
local_socket_addr->addr,
|
||||
local_socket_addr->port);
|
||||
}
|
||||
|
||||
if (remote_socket_addr) {
|
||||
remote_addr = talloc_asprintf(gensec_sasl_state,
|
||||
"%s;%d",
|
||||
remote_socket_addr->addr,
|
||||
remote_socket_addr->port);
|
||||
}
|
||||
gensec_sasl_state->step = 0;
|
||||
|
||||
sasl_ret = sasl_client_new(service,
|
||||
target_name,
|
||||
local_addr, remote_addr, callbacks, 0,
|
||||
&gensec_sasl_state->conn);
|
||||
|
||||
if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
|
||||
sasl_security_properties_t props;
|
||||
talloc_set_destructor(gensec_sasl_state, gensec_sasl_dispose);
|
||||
|
||||
ZERO_STRUCT(props);
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
|
||||
props.min_ssf = 1;
|
||||
}
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
|
||||
props.min_ssf = 40;
|
||||
}
|
||||
|
||||
props.max_ssf = UINT_MAX;
|
||||
props.maxbufsize = 65536;
|
||||
sasl_ret = sasl_setprop(gensec_sasl_state->conn, SASL_SEC_PROPS, &props);
|
||||
if (sasl_ret != SASL_OK) {
|
||||
return sasl_nt_status(sasl_ret);
|
||||
}
|
||||
|
||||
} else {
|
||||
DEBUG(1, ("GENSEC SASL: client_new failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
|
||||
}
|
||||
return sasl_nt_status(sasl_ret);
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_sasl_update(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
|
||||
struct gensec_sasl_state);
|
||||
int sasl_ret;
|
||||
const char *out_data;
|
||||
unsigned int out_len;
|
||||
|
||||
if (gensec_sasl_state->step == 0) {
|
||||
const char *mech;
|
||||
sasl_ret = sasl_client_start(gensec_sasl_state->conn, gensec_security->ops->sasl_name,
|
||||
NULL, &out_data, &out_len, &mech);
|
||||
} else {
|
||||
sasl_ret = sasl_client_step(gensec_sasl_state->conn,
|
||||
in.data, in.length, NULL, &out_data, &out_len);
|
||||
}
|
||||
if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
|
||||
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
|
||||
} else {
|
||||
DEBUG(1, ("GENSEC SASL: step %d update failed: %s\n", gensec_sasl_state->step,
|
||||
sasl_errdetail(gensec_sasl_state->conn)));
|
||||
}
|
||||
gensec_sasl_state->step++;
|
||||
return sasl_nt_status(sasl_ret);
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_sasl_unwrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed)
|
||||
{
|
||||
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
|
||||
struct gensec_sasl_state);
|
||||
const char *out_data;
|
||||
unsigned int out_len;
|
||||
|
||||
int sasl_ret = sasl_decode(gensec_sasl_state->conn,
|
||||
in->data, in->length, &out_data, &out_len);
|
||||
if (sasl_ret == SASL_OK) {
|
||||
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
|
||||
*len_processed = in->length;
|
||||
} else {
|
||||
DEBUG(1, ("GENSEC SASL: unwrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
|
||||
}
|
||||
return sasl_nt_status(sasl_ret);
|
||||
|
||||
}
|
||||
static NTSTATUS gensec_sasl_wrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed)
|
||||
{
|
||||
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
|
||||
struct gensec_sasl_state);
|
||||
const char *out_data;
|
||||
unsigned int out_len;
|
||||
|
||||
int sasl_ret = sasl_encode(gensec_sasl_state->conn,
|
||||
in->data, in->length, &out_data, &out_len);
|
||||
if (sasl_ret == SASL_OK) {
|
||||
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
|
||||
*len_processed = in->length;
|
||||
} else {
|
||||
DEBUG(1, ("GENSEC SASL: wrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
|
||||
}
|
||||
return sasl_nt_status(sasl_ret);
|
||||
}
|
||||
|
||||
/* Try to figure out what features we actually got on the connection */
|
||||
static BOOL gensec_sasl_have_feature(struct gensec_security *gensec_security,
|
||||
uint32_t feature)
|
||||
{
|
||||
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
|
||||
struct gensec_sasl_state);
|
||||
sasl_ssf_t ssf;
|
||||
int sasl_ret = sasl_getprop(gensec_sasl_state->conn, SASL_SSF, &ssf);
|
||||
if (sasl_ret != SASL_OK) {
|
||||
return False;
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_SIGN) {
|
||||
if (ssf == 0) {
|
||||
return False;
|
||||
}
|
||||
if (ssf >= 1) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_SEAL) {
|
||||
if (ssf <= 1) {
|
||||
return False;
|
||||
}
|
||||
if (ssf > 1) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/* This could in theory work with any SASL mech */
|
||||
static const struct gensec_security_ops gensec_sasl_security_ops = {
|
||||
.name = "sasl-DIGEST-MD5",
|
||||
.sasl_name = "DIGEST-MD5",
|
||||
.client_start = gensec_sasl_client_start,
|
||||
.update = gensec_sasl_update,
|
||||
.wrap_packets = gensec_sasl_wrap_packets,
|
||||
.unwrap_packets = gensec_sasl_unwrap_packets,
|
||||
.have_feature = gensec_sasl_have_feature,
|
||||
.enabled = True,
|
||||
.priority = GENSEC_SASL
|
||||
};
|
||||
|
||||
int gensec_sasl_log(void *context,
|
||||
int sasl_log_level,
|
||||
const char *message)
|
||||
{
|
||||
int debug_level;
|
||||
switch (sasl_log_level) {
|
||||
case SASL_LOG_NONE:
|
||||
debug_level = 0;
|
||||
break;
|
||||
case SASL_LOG_ERR:
|
||||
debug_level = 1;
|
||||
break;
|
||||
case SASL_LOG_FAIL:
|
||||
debug_level = 2;
|
||||
break;
|
||||
case SASL_LOG_WARN:
|
||||
debug_level = 3;
|
||||
break;
|
||||
case SASL_LOG_NOTE:
|
||||
debug_level = 5;
|
||||
break;
|
||||
case SASL_LOG_DEBUG:
|
||||
debug_level = 10;
|
||||
break;
|
||||
case SASL_LOG_TRACE:
|
||||
debug_level = 11;
|
||||
break;
|
||||
#if DEBUG_PASSWORD
|
||||
case SASL_LOG_PASS:
|
||||
debug_level = 100;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
debug_level = 0;
|
||||
break;
|
||||
}
|
||||
DEBUG(debug_level, ("gensec_sasl: %s\n", message));
|
||||
|
||||
return SASL_OK;
|
||||
}
|
||||
|
||||
NTSTATUS gensec_sasl_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
int sasl_ret, i;
|
||||
const char **sasl_mechs;
|
||||
|
||||
static const sasl_callback_t callbacks[] = {
|
||||
{
|
||||
.id = SASL_CB_LOG,
|
||||
.proc = gensec_sasl_log,
|
||||
.context = NULL,
|
||||
},
|
||||
{
|
||||
.id = SASL_CB_LIST_END,
|
||||
.proc = gensec_sasl_log,
|
||||
.context = NULL,
|
||||
}
|
||||
};
|
||||
sasl_ret = sasl_client_init(callbacks);
|
||||
|
||||
if (sasl_ret == SASL_NOMECH) {
|
||||
/* Nothing to do here */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (sasl_ret != SASL_OK) {
|
||||
return sasl_nt_status(sasl_ret);
|
||||
}
|
||||
|
||||
/* For now, we just register DIGEST-MD5 */
|
||||
#if 1
|
||||
ret = gensec_register(&gensec_sasl_security_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_sasl_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
sasl_mechs = sasl_global_listmech();
|
||||
for (i = 0; sasl_mechs && sasl_mechs[i]; i++) {
|
||||
const struct gensec_security_ops *oldmech;
|
||||
struct gensec_security_ops *newmech;
|
||||
oldmech = gensec_security_by_sasl_name(NULL, sasl_mechs[i]);
|
||||
if (oldmech) {
|
||||
continue;
|
||||
}
|
||||
newmech = talloc(talloc_autofree_context(), struct gensec_security_ops);
|
||||
if (!newmech) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
*newmech = gensec_sasl_security_ops;
|
||||
newmech->sasl_name = talloc_strdup(newmech, sasl_mechs[i]);
|
||||
newmech->name = talloc_asprintf(newmech, "sasl-%s", sasl_mechs[i]);
|
||||
if (!newmech->sasl_name || !newmech->name) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = gensec_register(newmech);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_sasl_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Generic Authentication Interface
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GENSEC_H__
|
||||
#define __GENSEC_H__
|
||||
|
||||
#include "core.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
|
||||
#define GENSEC_OID_NTLMSSP "1 3 6 1 4 1 311 2 2 10"
|
||||
#define GENSEC_OID_SPNEGO "1 3 6 1 5 5 2"
|
||||
#define GENSEC_OID_KERBEROS5 "1 2 840 113554 1 2 2"
|
||||
#define GENSEC_OID_KERBEROS5_OLD "1 2 840 48018 1 2 2"
|
||||
#define GENSEC_OID_KERBEROS5_USER2USER "1 2 840 113554 1 2 2 3"
|
||||
|
||||
enum gensec_priority {
|
||||
GENSEC_SPNEGO = 90,
|
||||
GENSEC_GSSAPI = 80,
|
||||
GENSEC_KRB5 = 70,
|
||||
GENSEC_SCHANNEL = 60,
|
||||
GENSEC_NTLMSSP = 50,
|
||||
GENSEC_SASL = 20,
|
||||
GENSEC_OTHER = 0
|
||||
};
|
||||
|
||||
enum credentials_use_kerberos;
|
||||
|
||||
struct gensec_security;
|
||||
struct gensec_target {
|
||||
const char *principal;
|
||||
const char *hostname;
|
||||
const char *service;
|
||||
};
|
||||
|
||||
#define GENSEC_FEATURE_SESSION_KEY 0x00000001
|
||||
#define GENSEC_FEATURE_SIGN 0x00000002
|
||||
#define GENSEC_FEATURE_SEAL 0x00000004
|
||||
#define GENSEC_FEATURE_DCE_STYLE 0x00000008
|
||||
#define GENSEC_FEATURE_ASYNC_REPLIES 0x00000010
|
||||
#define GENSEC_FEATURE_DATAGRAM_MODE 0x00000020
|
||||
|
||||
/* GENSEC mode */
|
||||
enum gensec_role
|
||||
{
|
||||
GENSEC_SERVER,
|
||||
GENSEC_CLIENT
|
||||
};
|
||||
|
||||
struct auth_session_info;
|
||||
|
||||
struct gensec_update_request {
|
||||
struct gensec_security *gensec_security;
|
||||
void *private_data;
|
||||
DATA_BLOB in;
|
||||
DATA_BLOB out;
|
||||
NTSTATUS status;
|
||||
struct {
|
||||
void (*fn)(struct gensec_update_request *req, void *private_data);
|
||||
void *private_data;
|
||||
} callback;
|
||||
};
|
||||
|
||||
struct gensec_security_ops {
|
||||
const char *name;
|
||||
const char *sasl_name;
|
||||
uint8_t auth_type; /* 0 if not offered on DCE-RPC */
|
||||
const char **oid; /* NULL if not offered by SPNEGO */
|
||||
NTSTATUS (*client_start)(struct gensec_security *gensec_security);
|
||||
NTSTATUS (*server_start)(struct gensec_security *gensec_security);
|
||||
/**
|
||||
Determine if a packet has the right 'magic' for this mechanism
|
||||
*/
|
||||
NTSTATUS (*magic)(struct gensec_security *gensec_security,
|
||||
const DATA_BLOB *first_packet);
|
||||
NTSTATUS (*update)(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out);
|
||||
NTSTATUS (*seal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig);
|
||||
NTSTATUS (*sign_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig);
|
||||
size_t (*sig_size)(struct gensec_security *gensec_security, size_t data_size);
|
||||
size_t (*max_input_size)(struct gensec_security *gensec_security);
|
||||
size_t (*max_wrapped_size)(struct gensec_security *gensec_security);
|
||||
NTSTATUS (*check_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig);
|
||||
NTSTATUS (*unseal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig);
|
||||
NTSTATUS (*wrap)(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out);
|
||||
NTSTATUS (*unwrap)(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out);
|
||||
NTSTATUS (*wrap_packets)(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed);
|
||||
NTSTATUS (*unwrap_packets)(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed);
|
||||
NTSTATUS (*packet_full_request)(struct gensec_security *gensec_security,
|
||||
DATA_BLOB blob, size_t *size);
|
||||
NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key);
|
||||
NTSTATUS (*session_info)(struct gensec_security *gensec_security,
|
||||
struct auth_session_info **session_info);
|
||||
BOOL (*have_feature)(struct gensec_security *gensec_security,
|
||||
uint32_t feature);
|
||||
BOOL enabled;
|
||||
BOOL kerberos;
|
||||
enum gensec_priority priority;
|
||||
};
|
||||
|
||||
struct gensec_security_ops_wrapper {
|
||||
const struct gensec_security_ops *op;
|
||||
const char *oid;
|
||||
};
|
||||
|
||||
#define GENSEC_INTERFACE_VERSION 0
|
||||
|
||||
struct gensec_security {
|
||||
const struct gensec_security_ops *ops;
|
||||
void *private_data;
|
||||
struct cli_credentials *credentials;
|
||||
struct gensec_target target;
|
||||
enum gensec_role gensec_role;
|
||||
BOOL subcontext;
|
||||
uint32_t want_features;
|
||||
struct event_context *event_ctx;
|
||||
struct messaging_context *msg_ctx; /* only valid as server */
|
||||
struct socket_address *my_addr, *peer_addr;
|
||||
};
|
||||
|
||||
/* this structure is used by backends to determine the size of some critical types */
|
||||
struct gensec_critical_sizes {
|
||||
int interface_version;
|
||||
int sizeof_gensec_security_ops;
|
||||
int sizeof_gensec_security;
|
||||
};
|
||||
|
||||
#include "auth/gensec/gensec_proto.h"
|
||||
|
||||
#endif /* __GENSEC_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,792 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Kerberos backend for GENSEC
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
Copyright (C) Stefan Metzmacher 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "librpc/gen_ndr/krb5pac.h"
|
||||
#include "auth/auth.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "auth/auth_sam.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "librpc/rpc/dcerpc.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
enum GENSEC_KRB5_STATE {
|
||||
GENSEC_KRB5_SERVER_START,
|
||||
GENSEC_KRB5_CLIENT_START,
|
||||
GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
|
||||
GENSEC_KRB5_DONE
|
||||
};
|
||||
|
||||
struct gensec_krb5_state {
|
||||
DATA_BLOB session_key;
|
||||
DATA_BLOB pac;
|
||||
enum GENSEC_KRB5_STATE state_position;
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_auth_context auth_context;
|
||||
krb5_data enc_ticket;
|
||||
krb5_keyblock *keyblock;
|
||||
krb5_ticket *ticket;
|
||||
BOOL gssapi;
|
||||
};
|
||||
|
||||
static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
|
||||
{
|
||||
if (!gensec_krb5_state->smb_krb5_context) {
|
||||
/* We can't clean anything else up unless we started up this far */
|
||||
return 0;
|
||||
}
|
||||
if (gensec_krb5_state->enc_ticket.length) {
|
||||
kerberos_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
&gensec_krb5_state->enc_ticket);
|
||||
}
|
||||
|
||||
if (gensec_krb5_state->ticket) {
|
||||
krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->ticket);
|
||||
}
|
||||
|
||||
/* ccache freed in a child destructor */
|
||||
|
||||
krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->keyblock);
|
||||
|
||||
if (gensec_krb5_state->auth_context) {
|
||||
krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->auth_context);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct gensec_krb5_state *gensec_krb5_state;
|
||||
struct cli_credentials *creds;
|
||||
const struct socket_address *my_addr, *peer_addr;
|
||||
krb5_address my_krb5_addr, peer_krb5_addr;
|
||||
|
||||
creds = gensec_get_credentials(gensec_security);
|
||||
if (!creds) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state);
|
||||
if (!gensec_krb5_state) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
gensec_security->private_data = gensec_krb5_state;
|
||||
gensec_krb5_state->smb_krb5_context = NULL;
|
||||
gensec_krb5_state->auth_context = NULL;
|
||||
gensec_krb5_state->ticket = NULL;
|
||||
ZERO_STRUCT(gensec_krb5_state->enc_ticket);
|
||||
gensec_krb5_state->keyblock = NULL;
|
||||
gensec_krb5_state->session_key = data_blob(NULL, 0);
|
||||
gensec_krb5_state->pac = data_blob(NULL, 0);
|
||||
gensec_krb5_state->gssapi = False;
|
||||
|
||||
talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy);
|
||||
|
||||
if (cli_credentials_get_krb5_context(creds, &gensec_krb5_state->smb_krb5_context)) {
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, gensec_krb5_state)));
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->auth_context,
|
||||
KRB5_AUTH_CONTEXT_DO_SEQUENCE);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, gensec_krb5_state)));
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
my_addr = gensec_get_my_addr(gensec_security);
|
||||
if (my_addr && my_addr->sockaddr) {
|
||||
ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
my_addr->sockaddr, &my_krb5_addr);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, gensec_krb5_state)));
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
peer_addr = gensec_get_peer_addr(gensec_security);
|
||||
if (peer_addr && peer_addr->sockaddr) {
|
||||
ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
peer_addr->sockaddr, &peer_krb5_addr);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, gensec_krb5_state)));
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->auth_context,
|
||||
my_addr ? &my_krb5_addr : NULL,
|
||||
peer_addr ? &peer_krb5_addr : NULL);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, gensec_krb5_state)));
|
||||
talloc_free(gensec_krb5_state);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct gensec_krb5_state *gensec_krb5_state;
|
||||
|
||||
nt_status = gensec_krb5_start(gensec_security);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
gensec_krb5_state = gensec_security->private_data;
|
||||
gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS nt_status = gensec_krb5_server_start(gensec_security);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
struct gensec_krb5_state *gensec_krb5_state;
|
||||
gensec_krb5_state = gensec_security->private_data;
|
||||
gensec_krb5_state->gssapi = True;
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state;
|
||||
krb5_error_code ret;
|
||||
NTSTATUS nt_status;
|
||||
struct ccache_container *ccache_container;
|
||||
const char *hostname;
|
||||
krb5_flags ap_req_options = AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED;
|
||||
|
||||
const char *principal;
|
||||
krb5_data in_data;
|
||||
|
||||
hostname = gensec_get_target_hostname(gensec_security);
|
||||
if (!hostname) {
|
||||
DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (is_ipaddress(hostname)) {
|
||||
DEBUG(2, ("Cannot do krb5 to an IP address"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (strcmp(hostname, "localhost") == 0) {
|
||||
DEBUG(2, ("krb5 to 'localhost' does not make sense"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
nt_status = gensec_krb5_start(gensec_security);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
gensec_krb5_state = gensec_security->private_data;
|
||||
gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
|
||||
|
||||
ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), &ccache_container);
|
||||
if (ret) {
|
||||
DEBUG(1,("gensec_krb5_start: cli_credentials_get_ccache failed: %s\n",
|
||||
error_message(ret)));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
in_data.length = 0;
|
||||
|
||||
principal = gensec_get_target_principal(gensec_security);
|
||||
if (principal && lp_client_use_spnego_principal()) {
|
||||
krb5_principal target_principal;
|
||||
ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
|
||||
&target_principal);
|
||||
if (ret == 0) {
|
||||
ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
&gensec_krb5_state->auth_context,
|
||||
ap_req_options,
|
||||
target_principal,
|
||||
&in_data, ccache_container->ccache,
|
||||
&gensec_krb5_state->enc_ticket);
|
||||
krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
target_principal);
|
||||
}
|
||||
} else {
|
||||
ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
&gensec_krb5_state->auth_context,
|
||||
ap_req_options,
|
||||
gensec_get_target_service(gensec_security),
|
||||
hostname,
|
||||
&in_data, ccache_container->ccache,
|
||||
&gensec_krb5_state->enc_ticket);
|
||||
}
|
||||
switch (ret) {
|
||||
case 0:
|
||||
return NT_STATUS_OK;
|
||||
case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
|
||||
DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n",
|
||||
hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
|
||||
return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
|
||||
case KRB5_KDC_UNREACH:
|
||||
DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
|
||||
hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
|
||||
return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
|
||||
case KRB5KDC_ERR_PREAUTH_FAILED:
|
||||
case KRB5KRB_AP_ERR_TKT_EXPIRED:
|
||||
case KRB5_CC_END:
|
||||
/* Too much clock skew - we will need to kinit to re-skew the clock */
|
||||
case KRB5KRB_AP_ERR_SKEW:
|
||||
case KRB5_KDCREP_SKEW:
|
||||
{
|
||||
DEBUG(3, ("kerberos (mk_req) failed: %s\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
|
||||
/*fall through*/
|
||||
}
|
||||
|
||||
/* just don't print a message for these really ordinary messages */
|
||||
case KRB5_FCC_NOFILE:
|
||||
case KRB5_CC_NOTFOUND:
|
||||
case ENOENT:
|
||||
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG(0, ("kerberos: %s\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS nt_status = gensec_krb5_client_start(gensec_security);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
struct gensec_krb5_state *gensec_krb5_state;
|
||||
gensec_krb5_state = gensec_security->private_data;
|
||||
gensec_krb5_state->gssapi = True;
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the packet is one for this mechansim
|
||||
*
|
||||
* @param gensec_security GENSEC state
|
||||
* @param in The request, as a DATA_BLOB
|
||||
* @return Error, INVALID_PARAMETER if it's not a packet for us
|
||||
* or NT_STATUS_OK if the packet is ok.
|
||||
*/
|
||||
|
||||
static NTSTATUS gensec_fake_gssapi_krb5_magic(struct gensec_security *gensec_security,
|
||||
const DATA_BLOB *in)
|
||||
{
|
||||
if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Next state function for the Krb5 GENSEC mechanism
|
||||
*
|
||||
* @param gensec_krb5_state KRB5 State
|
||||
* @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
|
||||
* @param in The request, as a DATA_BLOB
|
||||
* @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
|
||||
* @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
|
||||
* or NT_STATUS_OK if the user is authenticated.
|
||||
*/
|
||||
|
||||
static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
krb5_error_code ret = 0;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
switch (gensec_krb5_state->state_position) {
|
||||
case GENSEC_KRB5_CLIENT_START:
|
||||
{
|
||||
DATA_BLOB unwrapped_out;
|
||||
|
||||
if (gensec_krb5_state->gssapi) {
|
||||
unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
|
||||
|
||||
/* wrap that up in a nice GSS-API wrapping */
|
||||
*out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
|
||||
} else {
|
||||
*out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
|
||||
}
|
||||
gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
|
||||
nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
|
||||
{
|
||||
DATA_BLOB unwrapped_in;
|
||||
krb5_data inbuf;
|
||||
krb5_ap_rep_enc_part *repl = NULL;
|
||||
uint8_t tok_id[2];
|
||||
|
||||
if (gensec_krb5_state->gssapi) {
|
||||
if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
|
||||
DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
|
||||
dump_data_pw("Mutual authentication message:\n", in.data, in.length);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
} else {
|
||||
unwrapped_in = in;
|
||||
}
|
||||
/* TODO: check the tok_id */
|
||||
|
||||
inbuf.data = unwrapped_in.data;
|
||||
inbuf.length = unwrapped_in.length;
|
||||
ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
gensec_krb5_state->auth_context,
|
||||
&inbuf, &repl);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
|
||||
dump_data_pw("Mutual authentication message:\n", inbuf.data, inbuf.length);
|
||||
nt_status = NT_STATUS_ACCESS_DENIED;
|
||||
} else {
|
||||
*out = data_blob(NULL, 0);
|
||||
nt_status = NT_STATUS_OK;
|
||||
gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
|
||||
}
|
||||
if (repl) {
|
||||
krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
case GENSEC_KRB5_SERVER_START:
|
||||
{
|
||||
DATA_BLOB unwrapped_in;
|
||||
DATA_BLOB unwrapped_out = data_blob(NULL, 0);
|
||||
krb5_data inbuf, outbuf;
|
||||
uint8_t tok_id[2];
|
||||
struct keytab_container *keytab;
|
||||
krb5_principal server_in_keytab;
|
||||
|
||||
if (!in.data) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Grab the keytab, however generated */
|
||||
ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security), &keytab);
|
||||
if (ret) {
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
/* This ensures we lookup the correct entry in that keytab */
|
||||
ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security),
|
||||
gensec_krb5_state->smb_krb5_context,
|
||||
&server_in_keytab);
|
||||
|
||||
if (ret) {
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
/* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
|
||||
if (gensec_krb5_state->gssapi
|
||||
&& gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
|
||||
inbuf.data = unwrapped_in.data;
|
||||
inbuf.length = unwrapped_in.length;
|
||||
} else {
|
||||
inbuf.data = in.data;
|
||||
inbuf.length = in.length;
|
||||
}
|
||||
|
||||
ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
&gensec_krb5_state->auth_context,
|
||||
&inbuf, keytab->keytab, server_in_keytab,
|
||||
&outbuf,
|
||||
&gensec_krb5_state->ticket,
|
||||
&gensec_krb5_state->keyblock);
|
||||
|
||||
if (ret) {
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
unwrapped_out.data = outbuf.data;
|
||||
unwrapped_out.length = outbuf.length;
|
||||
gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
|
||||
/* wrap that up in a nice GSS-API wrapping */
|
||||
if (gensec_krb5_state->gssapi) {
|
||||
*out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
|
||||
} else {
|
||||
*out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
|
||||
}
|
||||
krb5_data_free(&outbuf);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
case GENSEC_KRB5_DONE:
|
||||
default:
|
||||
/* Asking too many times... */
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security,
|
||||
DATA_BLOB *session_key)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
|
||||
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
|
||||
krb5_keyblock *skey;
|
||||
krb5_error_code err = -1;
|
||||
|
||||
if (gensec_krb5_state->session_key.data) {
|
||||
*session_key = gensec_krb5_state->session_key;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
switch (gensec_security->gensec_role) {
|
||||
case GENSEC_CLIENT:
|
||||
err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
|
||||
break;
|
||||
case GENSEC_SERVER:
|
||||
err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
|
||||
break;
|
||||
}
|
||||
if (err == 0 && skey != NULL) {
|
||||
DEBUG(10, ("Got KRB5 session key of length %d\n",
|
||||
(int)KRB5_KEY_LENGTH(skey)));
|
||||
gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state,
|
||||
KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
|
||||
*session_key = gensec_krb5_state->session_key;
|
||||
dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
|
||||
|
||||
krb5_free_keyblock(context, skey);
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(10, ("KRB5 error getting session key %d\n", err));
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
|
||||
struct auth_session_info **_session_info)
|
||||
{
|
||||
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
|
||||
struct auth_serversupplied_info *server_info = NULL;
|
||||
struct auth_session_info *session_info = NULL;
|
||||
struct PAC_LOGON_INFO *logon_info;
|
||||
|
||||
krb5_principal client_principal;
|
||||
char *principal_string;
|
||||
|
||||
DATA_BLOB pac;
|
||||
krb5_data pac_data;
|
||||
|
||||
krb5_error_code ret;
|
||||
|
||||
TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
|
||||
if (ret) {
|
||||
DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n",
|
||||
smb_get_krb5_error_message(context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
client_principal, &principal_string);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Unable to parse client principal: %s\n",
|
||||
smb_get_krb5_error_message(context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket,
|
||||
KRB5_AUTHDATA_WIN2K_PAC,
|
||||
&pac_data);
|
||||
|
||||
if (ret && lp_parm_bool(-1, "gensec", "require_pac", False)) {
|
||||
DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access: %s \n",
|
||||
principal_string,
|
||||
smb_get_krb5_error_message(context,
|
||||
ret, mem_ctx)));
|
||||
krb5_free_principal(context, client_principal);
|
||||
free(principal_string);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
} else if (ret) {
|
||||
/* NO pac */
|
||||
DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n",
|
||||
smb_get_krb5_error_message(context,
|
||||
ret, mem_ctx)));
|
||||
nt_status = sam_get_server_info_principal(mem_ctx, principal_string,
|
||||
&server_info);
|
||||
krb5_free_principal(context, client_principal);
|
||||
free(principal_string);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
} else {
|
||||
/* Found pac */
|
||||
union netr_Validation validation;
|
||||
free(principal_string);
|
||||
|
||||
pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length);
|
||||
if (!pac.data) {
|
||||
krb5_free_principal(context, client_principal);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* decode and verify the pac */
|
||||
nt_status = kerberos_pac_logon_info(gensec_krb5_state, &logon_info, pac,
|
||||
gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
NULL, gensec_krb5_state->keyblock,
|
||||
client_principal,
|
||||
gensec_krb5_state->ticket->ticket.authtime, NULL);
|
||||
krb5_free_principal(context, client_principal);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
validation.sam3 = &logon_info->info3;
|
||||
nt_status = make_server_info_netlogon_validation(mem_ctx,
|
||||
NULL,
|
||||
3, &validation,
|
||||
&server_info);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
/* references the server_info into the session_info */
|
||||
nt_status = auth_generate_session_info(mem_ctx, server_info, &session_info);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = gensec_krb5_session_key(gensec_security, &session_info->session_key);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
*_session_info = session_info;
|
||||
|
||||
talloc_steal(gensec_krb5_state, session_info);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
|
||||
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
|
||||
krb5_error_code ret;
|
||||
krb5_data input, output;
|
||||
input.length = in->length;
|
||||
input.data = in->data;
|
||||
|
||||
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
|
||||
ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
|
||||
if (ret) {
|
||||
DEBUG(1, ("krb5_mk_priv failed: %s\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
*out = data_blob_talloc(mem_ctx, output.data, output.length);
|
||||
|
||||
krb5_data_free(&output);
|
||||
} else {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
|
||||
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
|
||||
krb5_error_code ret;
|
||||
krb5_data input, output;
|
||||
krb5_replay_data replay;
|
||||
input.length = in->length;
|
||||
input.data = in->data;
|
||||
|
||||
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
|
||||
ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
|
||||
if (ret) {
|
||||
DEBUG(1, ("krb5_rd_priv failed: %s\n",
|
||||
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
*out = data_blob_talloc(mem_ctx, output.data, output.length);
|
||||
|
||||
krb5_data_free(&output);
|
||||
} else {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static BOOL gensec_krb5_have_feature(struct gensec_security *gensec_security,
|
||||
uint32_t feature)
|
||||
{
|
||||
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
|
||||
if (feature & GENSEC_FEATURE_SESSION_KEY) {
|
||||
return True;
|
||||
}
|
||||
if (!gensec_krb5_state->gssapi &&
|
||||
(feature & GENSEC_FEATURE_SEAL)) {
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
static const char *gensec_krb5_oids[] = {
|
||||
GENSEC_OID_KERBEROS5,
|
||||
GENSEC_OID_KERBEROS5_OLD,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
|
||||
.name = "fake_gssapi_krb5",
|
||||
.auth_type = DCERPC_AUTH_TYPE_KRB5,
|
||||
.oid = gensec_krb5_oids,
|
||||
.client_start = gensec_fake_gssapi_krb5_client_start,
|
||||
.server_start = gensec_fake_gssapi_krb5_server_start,
|
||||
.update = gensec_krb5_update,
|
||||
.magic = gensec_fake_gssapi_krb5_magic,
|
||||
.session_key = gensec_krb5_session_key,
|
||||
.session_info = gensec_krb5_session_info,
|
||||
.have_feature = gensec_krb5_have_feature,
|
||||
.enabled = False,
|
||||
.kerberos = True,
|
||||
.priority = GENSEC_KRB5
|
||||
};
|
||||
|
||||
static const struct gensec_security_ops gensec_krb5_security_ops = {
|
||||
.name = "krb5",
|
||||
.client_start = gensec_krb5_client_start,
|
||||
.server_start = gensec_krb5_server_start,
|
||||
.update = gensec_krb5_update,
|
||||
.session_key = gensec_krb5_session_key,
|
||||
.session_info = gensec_krb5_session_info,
|
||||
.have_feature = gensec_krb5_have_feature,
|
||||
.wrap = gensec_krb5_wrap,
|
||||
.unwrap = gensec_krb5_unwrap,
|
||||
.enabled = True,
|
||||
.kerberos = True,
|
||||
.priority = GENSEC_KRB5
|
||||
};
|
||||
|
||||
NTSTATUS gensec_krb5_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
auth_init();
|
||||
|
||||
ret = gensec_register(&gensec_krb5_security_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_krb5_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gensec_register(&gensec_fake_gssapi_krb5_security_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_krb5_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
dcerpc schannel operations
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/ndr_schannel.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/gensec/schannel.h"
|
||||
#include "auth/gensec/schannel_state.h"
|
||||
#include "auth/gensec/schannel_proto.h"
|
||||
#include "librpc/rpc/dcerpc.h"
|
||||
|
||||
static size_t schannel_sig_size(struct gensec_security *gensec_security, size_t data_size)
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
static NTSTATUS schannel_session_key(struct gensec_security *gensec_security,
|
||||
DATA_BLOB *session_key)
|
||||
{
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct schannel_state *state = gensec_security->private_data;
|
||||
NTSTATUS status;
|
||||
struct schannel_bind bind_schannel;
|
||||
struct schannel_bind_ack bind_schannel_ack;
|
||||
struct creds_CredentialState *creds;
|
||||
|
||||
const char *workstation;
|
||||
const char *domain;
|
||||
*out = data_blob(NULL, 0);
|
||||
|
||||
switch (gensec_security->gensec_role) {
|
||||
case GENSEC_CLIENT:
|
||||
if (state->state != SCHANNEL_STATE_START) {
|
||||
/* we could parse the bind ack, but we don't know what it is yet */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
state->creds = talloc_reference(state, cli_credentials_get_netlogon_creds(gensec_security->credentials));
|
||||
|
||||
bind_schannel.unknown1 = 0;
|
||||
#if 0
|
||||
/* to support this we'd need to have access to the full domain name */
|
||||
bind_schannel.bind_type = 23;
|
||||
bind_schannel.u.info23.domain = cli_credentials_get_domain(gensec_security->credentials);
|
||||
bind_schannel.u.info23.workstation = cli_credentials_get_workstation(gensec_security->credentials);
|
||||
bind_schannel.u.info23.dnsdomain = cli_credentials_get_realm(gensec_security->credentials);
|
||||
/* w2k3 refuses us if we use the full DNS workstation?
|
||||
why? perhaps because we don't fill in the dNSHostName
|
||||
attribute in the machine account? */
|
||||
bind_schannel.u.info23.dnsworkstation = cli_credentials_get_workstation(gensec_security->credentials);
|
||||
#else
|
||||
bind_schannel.bind_type = 3;
|
||||
bind_schannel.u.info3.domain = cli_credentials_get_domain(gensec_security->credentials);
|
||||
bind_schannel.u.info3.workstation = cli_credentials_get_workstation(gensec_security->credentials);
|
||||
#endif
|
||||
|
||||
status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
|
||||
(ndr_push_flags_fn_t)ndr_push_schannel_bind);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(3, ("Could not create schannel bind: %s\n",
|
||||
nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
|
||||
state->state = SCHANNEL_STATE_UPDATE_1;
|
||||
|
||||
return NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
case GENSEC_SERVER:
|
||||
|
||||
if (state->state != SCHANNEL_STATE_START) {
|
||||
/* no third leg on this protocol */
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* parse the schannel startup blob */
|
||||
status = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_schannel_bind);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (bind_schannel.bind_type == 23) {
|
||||
workstation = bind_schannel.u.info23.workstation;
|
||||
domain = bind_schannel.u.info23.domain;
|
||||
} else {
|
||||
workstation = bind_schannel.u.info3.workstation;
|
||||
domain = bind_schannel.u.info3.domain;
|
||||
}
|
||||
|
||||
/* pull the session key for this client */
|
||||
status = schannel_fetch_session_key(out_mem_ctx, workstation,
|
||||
domain, &creds);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n",
|
||||
workstation, nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
|
||||
state->creds = talloc_reference(state, creds);
|
||||
|
||||
bind_schannel_ack.unknown1 = 1;
|
||||
bind_schannel_ack.unknown2 = 0;
|
||||
bind_schannel_ack.unknown3 = 0x6c0000;
|
||||
|
||||
status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack,
|
||||
(ndr_push_flags_fn_t)ndr_push_schannel_bind_ack);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n",
|
||||
workstation, nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
|
||||
state->state = SCHANNEL_STATE_UPDATE_1;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the struct creds_CredentialState.
|
||||
*
|
||||
* Make sure not to call this unless gensec is using schannel...
|
||||
*/
|
||||
|
||||
/* TODO: make this non-public */
|
||||
_PUBLIC_ NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct creds_CredentialState **creds)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
|
||||
*creds = talloc_reference(mem_ctx, state->creds);
|
||||
if (!*creds) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns anonymous credentials for schannel, matching Win2k3.
|
||||
*
|
||||
*/
|
||||
|
||||
static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
|
||||
struct auth_session_info **_session_info)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
return auth_anonymous_session_info(state, _session_info);
|
||||
}
|
||||
|
||||
static NTSTATUS schannel_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct schannel_state *state;
|
||||
|
||||
state = talloc(gensec_security, struct schannel_state);
|
||||
if (!state) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
state->state = SCHANNEL_STATE_START;
|
||||
state->seq_num = 0;
|
||||
gensec_security->private_data = state;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS schannel_server_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct schannel_state *state;
|
||||
|
||||
status = schannel_start(gensec_security);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
state = gensec_security->private_data;
|
||||
state->initiator = False;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS schannel_client_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct schannel_state *state;
|
||||
|
||||
status = schannel_start(gensec_security);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
state = gensec_security->private_data;
|
||||
state->initiator = True;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static BOOL schannel_have_feature(struct gensec_security *gensec_security,
|
||||
uint32_t feature)
|
||||
{
|
||||
if (feature & (GENSEC_FEATURE_SIGN |
|
||||
GENSEC_FEATURE_SEAL)) {
|
||||
return True;
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_DCE_STYLE) {
|
||||
return True;
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
static const struct gensec_security_ops gensec_schannel_security_ops = {
|
||||
.name = "schannel",
|
||||
.auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
|
||||
.client_start = schannel_client_start,
|
||||
.server_start = schannel_server_start,
|
||||
.update = schannel_update,
|
||||
.seal_packet = schannel_seal_packet,
|
||||
.sign_packet = schannel_sign_packet,
|
||||
.check_packet = schannel_check_packet,
|
||||
.unseal_packet = schannel_unseal_packet,
|
||||
.session_key = schannel_session_key,
|
||||
.session_info = schannel_session_info,
|
||||
.sig_size = schannel_sig_size,
|
||||
.have_feature = schannel_have_feature,
|
||||
.enabled = True,
|
||||
.priority = GENSEC_SCHANNEL
|
||||
};
|
||||
|
||||
NTSTATUS gensec_schannel_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
ret = gensec_register(&gensec_schannel_security_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_schannel_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
dcerpc schannel operations
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "libcli/auth/credentials.h"
|
||||
|
||||
enum schannel_position {
|
||||
SCHANNEL_STATE_START = 0,
|
||||
SCHANNEL_STATE_UPDATE_1
|
||||
};
|
||||
|
||||
struct schannel_state {
|
||||
enum schannel_position state;
|
||||
uint32_t seq_num;
|
||||
BOOL initiator;
|
||||
struct creds_CredentialState *creds;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
schannel library code
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/gensec/schannel.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 }
|
||||
#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 }
|
||||
|
||||
/*******************************************************************
|
||||
Encode or Decode the sequence number (which is symmetric)
|
||||
********************************************************************/
|
||||
static void netsec_deal_with_seq_num(struct schannel_state *state,
|
||||
const uint8_t packet_digest[8],
|
||||
uint8_t seq_num[8])
|
||||
{
|
||||
static const uint8_t zeros[4];
|
||||
uint8_t sequence_key[16];
|
||||
uint8_t digest1[16];
|
||||
|
||||
hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1);
|
||||
hmac_md5(digest1, packet_digest, 8, sequence_key);
|
||||
arcfour_crypt(seq_num, sequence_key, 8);
|
||||
|
||||
state->seq_num++;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
Calculate the key with which to encode the data payload
|
||||
********************************************************************/
|
||||
static void netsec_get_sealing_key(const uint8_t session_key[16],
|
||||
const uint8_t seq_num[8],
|
||||
uint8_t sealing_key[16])
|
||||
{
|
||||
static const uint8_t zeros[4];
|
||||
uint8_t digest2[16];
|
||||
uint8_t sess_kf0[16];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
sess_kf0[i] = session_key[i] ^ 0xf0;
|
||||
}
|
||||
|
||||
hmac_md5(sess_kf0, zeros, 4, digest2);
|
||||
hmac_md5(digest2, seq_num, 8, sealing_key);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
Create a digest over the entire packet (including the data), and
|
||||
MD5 it with the session key.
|
||||
********************************************************************/
|
||||
static void schannel_digest(const uint8_t sess_key[16],
|
||||
const uint8_t netsec_sig[8],
|
||||
const uint8_t *confounder,
|
||||
const uint8_t *data, size_t data_len,
|
||||
uint8_t digest_final[16])
|
||||
{
|
||||
uint8_t packet_digest[16];
|
||||
static const uint8_t zeros[4];
|
||||
struct MD5Context ctx;
|
||||
|
||||
MD5Init(&ctx);
|
||||
MD5Update(&ctx, zeros, 4);
|
||||
MD5Update(&ctx, netsec_sig, 8);
|
||||
if (confounder) {
|
||||
MD5Update(&ctx, confounder, 8);
|
||||
}
|
||||
MD5Update(&ctx, data, data_len);
|
||||
MD5Final(packet_digest, &ctx);
|
||||
|
||||
hmac_md5(sess_key, packet_digest, sizeof(packet_digest), digest_final);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
unseal a packet
|
||||
*/
|
||||
NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
|
||||
uint8_t digest_final[16];
|
||||
uint8_t confounder[8];
|
||||
uint8_t seq_num[8];
|
||||
uint8_t sealing_key[16];
|
||||
static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
|
||||
|
||||
if (sig->length != 32) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
memcpy(confounder, sig->data+24, 8);
|
||||
|
||||
RSIVAL(seq_num, 0, state->seq_num);
|
||||
SIVAL(seq_num, 4, state->initiator?0:0x80);
|
||||
|
||||
netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
|
||||
arcfour_crypt(confounder, sealing_key, 8);
|
||||
arcfour_crypt(data, sealing_key, length);
|
||||
|
||||
schannel_digest(state->creds->session_key,
|
||||
netsec_sig, confounder,
|
||||
data, length, digest_final);
|
||||
|
||||
if (memcmp(digest_final, sig->data+16, 8) != 0) {
|
||||
dump_data_pw("calc digest:", digest_final, 8);
|
||||
dump_data_pw("wire digest:", sig->data+16, 8);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
netsec_deal_with_seq_num(state, digest_final, seq_num);
|
||||
|
||||
if (memcmp(seq_num, sig->data+8, 8) != 0) {
|
||||
dump_data_pw("calc seq num:", seq_num, 8);
|
||||
dump_data_pw("wire seq num:", sig->data+8, 8);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
check the signature on a packet
|
||||
*/
|
||||
NTSTATUS schannel_check_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
|
||||
uint8_t digest_final[16];
|
||||
uint8_t seq_num[8];
|
||||
static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
|
||||
|
||||
/* w2k sends just 24 bytes and skip the confounder */
|
||||
if (sig->length != 32 && sig->length != 24) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
RSIVAL(seq_num, 0, state->seq_num);
|
||||
SIVAL(seq_num, 4, state->initiator?0:0x80);
|
||||
|
||||
dump_data_pw("seq_num:\n", seq_num, 8);
|
||||
dump_data_pw("sess_key:\n", state->creds->session_key, 16);
|
||||
|
||||
schannel_digest(state->creds->session_key,
|
||||
netsec_sig, NULL,
|
||||
data, length, digest_final);
|
||||
|
||||
netsec_deal_with_seq_num(state, digest_final, seq_num);
|
||||
|
||||
if (memcmp(seq_num, sig->data+8, 8) != 0) {
|
||||
dump_data_pw("calc seq num:", seq_num, 8);
|
||||
dump_data_pw("wire seq num:", sig->data+8, 8);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (memcmp(digest_final, sig->data+16, 8) != 0) {
|
||||
dump_data_pw("calc digest:", digest_final, 8);
|
||||
dump_data_pw("wire digest:", sig->data+16, 8);
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
seal a packet
|
||||
*/
|
||||
NTSTATUS schannel_seal_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
|
||||
uint8_t digest_final[16];
|
||||
uint8_t confounder[8];
|
||||
uint8_t seq_num[8];
|
||||
uint8_t sealing_key[16];
|
||||
static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
|
||||
|
||||
generate_random_buffer(confounder, 8);
|
||||
|
||||
RSIVAL(seq_num, 0, state->seq_num);
|
||||
SIVAL(seq_num, 4, state->initiator?0x80:0);
|
||||
|
||||
schannel_digest(state->creds->session_key,
|
||||
netsec_sig, confounder,
|
||||
data, length, digest_final);
|
||||
|
||||
netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
|
||||
arcfour_crypt(confounder, sealing_key, 8);
|
||||
arcfour_crypt(data, sealing_key, length);
|
||||
|
||||
netsec_deal_with_seq_num(state, digest_final, seq_num);
|
||||
|
||||
(*sig) = data_blob_talloc(mem_ctx, NULL, 32);
|
||||
|
||||
memcpy(sig->data, netsec_sig, 8);
|
||||
memcpy(sig->data+8, seq_num, 8);
|
||||
memcpy(sig->data+16, digest_final, 8);
|
||||
memcpy(sig->data+24, confounder, 8);
|
||||
|
||||
dump_data_pw("signature:", sig->data+ 0, 8);
|
||||
dump_data_pw("seq_num :", sig->data+ 8, 8);
|
||||
dump_data_pw("digest :", sig->data+16, 8);
|
||||
dump_data_pw("confound :", sig->data+24, 8);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
sign a packet
|
||||
*/
|
||||
NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig)
|
||||
{
|
||||
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
|
||||
|
||||
uint8_t digest_final[16];
|
||||
uint8_t seq_num[8];
|
||||
static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
|
||||
|
||||
RSIVAL(seq_num, 0, state->seq_num);
|
||||
SIVAL(seq_num, 4, state->initiator?0x80:0);
|
||||
|
||||
schannel_digest(state->creds->session_key,
|
||||
netsec_sig, NULL,
|
||||
data, length, digest_final);
|
||||
|
||||
netsec_deal_with_seq_num(state, digest_final, seq_num);
|
||||
|
||||
(*sig) = data_blob_talloc(mem_ctx, NULL, 32);
|
||||
|
||||
memcpy(sig->data, netsec_sig, 8);
|
||||
memcpy(sig->data+8, seq_num, 8);
|
||||
memcpy(sig->data+16, digest_final, 8);
|
||||
memset(sig->data+24, 0, 8);
|
||||
|
||||
dump_data_pw("signature:", sig->data+ 0, 8);
|
||||
dump_data_pw("seq_num :", sig->data+ 8, 8);
|
||||
dump_data_pw("digest :", sig->data+16, 8);
|
||||
dump_data_pw("confound :", sig->data+24, 8);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
module to store/fetch session keys for the schannel server
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "lib/ldb/include/ldb_errors.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "db_wrap.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
/**
|
||||
connect to the schannel ldb
|
||||
*/
|
||||
struct ldb_context *schannel_db_connect(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
char *path;
|
||||
struct ldb_context *ldb;
|
||||
BOOL existed;
|
||||
const char *init_ldif =
|
||||
"dn: @ATTRIBUTES\n" \
|
||||
"computerName: CASE_INSENSITIVE\n" \
|
||||
"flatname: CASE_INSENSITIVE\n";
|
||||
|
||||
path = smbd_tmp_path(mem_ctx, "schannel.ldb");
|
||||
if (!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
existed = file_exist(path);
|
||||
|
||||
ldb = ldb_wrap_connect(mem_ctx, path, system_session(mem_ctx),
|
||||
NULL, LDB_FLG_NOSYNC, NULL);
|
||||
talloc_free(path);
|
||||
if (!ldb) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!existed) {
|
||||
gendb_add_ldif(ldb, init_ldif);
|
||||
}
|
||||
|
||||
return ldb;
|
||||
}
|
||||
|
||||
/*
|
||||
remember an established session key for a netr server authentication
|
||||
use a simple ldb structure
|
||||
*/
|
||||
NTSTATUS schannel_store_session_key_ldb(TALLOC_CTX *mem_ctx,
|
||||
struct ldb_context *ldb,
|
||||
struct creds_CredentialState *creds)
|
||||
{
|
||||
struct ldb_message *msg;
|
||||
struct ldb_val val, seed, client_state, server_state;
|
||||
char *f;
|
||||
char *sct;
|
||||
int ret;
|
||||
|
||||
f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags);
|
||||
|
||||
if (f == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type);
|
||||
|
||||
if (sct == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
msg = ldb_msg_new(ldb);
|
||||
if (msg == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
msg->dn = ldb_dn_new_fmt(msg, ldb, "computerName=%s", creds->computer_name);
|
||||
if ( ! msg->dn) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
val.data = creds->session_key;
|
||||
val.length = sizeof(creds->session_key);
|
||||
|
||||
seed.data = creds->seed.data;
|
||||
seed.length = sizeof(creds->seed.data);
|
||||
|
||||
client_state.data = creds->client.data;
|
||||
client_state.length = sizeof(creds->client.data);
|
||||
server_state.data = creds->server.data;
|
||||
server_state.length = sizeof(creds->server.data);
|
||||
|
||||
ldb_msg_add_string(msg, "objectClass", "schannelState");
|
||||
ldb_msg_add_value(msg, "sessionKey", &val, NULL);
|
||||
ldb_msg_add_value(msg, "seed", &seed, NULL);
|
||||
ldb_msg_add_value(msg, "clientState", &client_state, NULL);
|
||||
ldb_msg_add_value(msg, "serverState", &server_state, NULL);
|
||||
ldb_msg_add_string(msg, "negotiateFlags", f);
|
||||
ldb_msg_add_string(msg, "secureChannelType", sct);
|
||||
ldb_msg_add_string(msg, "accountName", creds->account_name);
|
||||
ldb_msg_add_string(msg, "computerName", creds->computer_name);
|
||||
ldb_msg_add_string(msg, "flatname", creds->domain);
|
||||
samdb_msg_add_dom_sid(ldb, mem_ctx, msg, "objectSid", creds->sid);
|
||||
|
||||
ldb_delete(ldb, msg->dn);
|
||||
|
||||
ret = ldb_add(ldb, msg);
|
||||
|
||||
if (ret != 0) {
|
||||
DEBUG(0,("Unable to add %s to session key db - %s\n",
|
||||
ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb)));
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS schannel_store_session_key(TALLOC_CTX *mem_ctx,
|
||||
struct creds_CredentialState *creds)
|
||||
{
|
||||
struct ldb_context *ldb;
|
||||
NTSTATUS nt_status;
|
||||
int ret;
|
||||
|
||||
ldb = schannel_db_connect(mem_ctx);
|
||||
if (!ldb) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
ret = ldb_transaction_start(ldb);
|
||||
if (ret != 0) {
|
||||
talloc_free(ldb);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
nt_status = schannel_store_session_key_ldb(mem_ctx, ldb, creds);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
ret = ldb_transaction_commit(ldb);
|
||||
} else {
|
||||
ret = ldb_transaction_cancel(ldb);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
DEBUG(0,("Unable to commit adding credentials for %s to schannel key db - %s\n",
|
||||
creds->computer_name, ldb_errstring(ldb)));
|
||||
talloc_free(ldb);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
talloc_free(ldb);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/*
|
||||
read back a credentials back for a computer
|
||||
*/
|
||||
NTSTATUS schannel_fetch_session_key_ldb(TALLOC_CTX *mem_ctx,
|
||||
struct ldb_context *ldb,
|
||||
const char *computer_name,
|
||||
const char *domain,
|
||||
struct creds_CredentialState **creds)
|
||||
{
|
||||
struct ldb_result *res;
|
||||
int ret;
|
||||
const struct ldb_val *val;
|
||||
|
||||
*creds = talloc_zero(mem_ctx, struct creds_CredentialState);
|
||||
if (!*creds) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
ret = ldb_search_exp_fmt(ldb, mem_ctx, &res,
|
||||
NULL, LDB_SCOPE_SUBTREE, NULL,
|
||||
"(&(computerName=%s)(flatname=%s))", computer_name, domain);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
DEBUG(3,("schannel: Failed to find a record for client %s: %s\n", computer_name, ldb_errstring(ldb)));
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
if (res->count != 1) {
|
||||
DEBUG(3,("schannel: Failed to find a record for client: %s (found %d records)\n", computer_name, res->count));
|
||||
talloc_free(res);
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
val = ldb_msg_find_ldb_val(res->msgs[0], "sessionKey");
|
||||
if (val == NULL || val->length != 16) {
|
||||
DEBUG(1,("schannel: record in schannel DB must contain a sessionKey of length 16, when searching for client: %s\n", computer_name));
|
||||
talloc_free(res);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
memcpy((*creds)->session_key, val->data, 16);
|
||||
|
||||
val = ldb_msg_find_ldb_val(res->msgs[0], "seed");
|
||||
if (val == NULL || val->length != 8) {
|
||||
DEBUG(1,("schannel: record in schannel DB must contain a vaid seed of length 8, when searching for client: %s\n", computer_name));
|
||||
talloc_free(res);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
memcpy((*creds)->seed.data, val->data, 8);
|
||||
|
||||
val = ldb_msg_find_ldb_val(res->msgs[0], "clientState");
|
||||
if (val == NULL || val->length != 8) {
|
||||
DEBUG(1,("schannel: record in schannel DB must contain a vaid clientState of length 8, when searching for client: %s\n", computer_name));
|
||||
talloc_free(res);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
memcpy((*creds)->client.data, val->data, 8);
|
||||
|
||||
val = ldb_msg_find_ldb_val(res->msgs[0], "serverState");
|
||||
if (val == NULL || val->length != 8) {
|
||||
DEBUG(1,("schannel: record in schannel DB must contain a vaid serverState of length 8, when searching for client: %s\n", computer_name));
|
||||
talloc_free(res);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
memcpy((*creds)->server.data, val->data, 8);
|
||||
|
||||
(*creds)->negotiate_flags = ldb_msg_find_attr_as_int(res->msgs[0], "negotiateFlags", 0);
|
||||
|
||||
(*creds)->secure_channel_type = ldb_msg_find_attr_as_int(res->msgs[0], "secureChannelType", 0);
|
||||
|
||||
(*creds)->account_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "accountName", NULL));
|
||||
if ((*creds)->account_name == NULL) {
|
||||
talloc_free(res);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*creds)->computer_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "computerName", NULL));
|
||||
if ((*creds)->computer_name == NULL) {
|
||||
talloc_free(res);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*creds)->domain = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "flatname", NULL));
|
||||
if ((*creds)->domain == NULL) {
|
||||
talloc_free(res);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*creds)->sid = samdb_result_dom_sid(*creds, res->msgs[0], "objectSid");
|
||||
|
||||
talloc_free(res);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS schannel_fetch_session_key(TALLOC_CTX *mem_ctx,
|
||||
const char *computer_name,
|
||||
const char *domain,
|
||||
struct creds_CredentialState **creds)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct ldb_context *ldb;
|
||||
|
||||
ldb = schannel_db_connect(mem_ctx);
|
||||
if (!ldb) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
nt_status = schannel_fetch_session_key_ldb(mem_ctx, ldb,
|
||||
computer_name, domain,
|
||||
creds);
|
||||
talloc_free(ldb);
|
||||
return nt_status;
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
GENSEC socket interface
|
||||
|
||||
Copyright (C) Andrew Bartlett 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/stream/packet.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
static const struct socket_ops gensec_socket_ops;
|
||||
|
||||
struct gensec_socket {
|
||||
struct gensec_security *gensec_security;
|
||||
struct socket_context *socket;
|
||||
struct event_context *ev;
|
||||
struct packet_context *packet;
|
||||
DATA_BLOB read_buffer; /* SASL packets are turned into liniarlised data here, for reading */
|
||||
size_t orig_send_len;
|
||||
BOOL eof;
|
||||
NTSTATUS error;
|
||||
BOOL interrupted;
|
||||
void (*recv_handler)(void *, uint16_t);
|
||||
void *recv_private;
|
||||
int in_extra_read;
|
||||
BOOL wrap; /* Should we be wrapping on this socket at all? */
|
||||
};
|
||||
|
||||
static NTSTATUS gensec_socket_init_fn(struct socket_context *sock)
|
||||
{
|
||||
switch (sock->type) {
|
||||
case SOCKET_TYPE_STREAM:
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
sock->backend_name = "gensec";
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
_PUBLIC_ NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed)
|
||||
{
|
||||
if (!gensec_security->ops->wrap_packets) {
|
||||
NTSTATUS nt_status;
|
||||
size_t max_input_size;
|
||||
DATA_BLOB unwrapped, wrapped;
|
||||
max_input_size = gensec_max_input_size(gensec_security);
|
||||
unwrapped = data_blob_const(in->data, MIN(max_input_size, (size_t)in->length));
|
||||
|
||||
nt_status = gensec_wrap(gensec_security,
|
||||
mem_ctx,
|
||||
&unwrapped, &wrapped);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
*out = data_blob_talloc(mem_ctx, NULL, 4);
|
||||
if (!out->data) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
RSIVAL(out->data, 0, wrapped.length);
|
||||
|
||||
nt_status = data_blob_append(mem_ctx, out, wrapped.data, wrapped.length);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
*len_processed = unwrapped.length;
|
||||
return nt_status;
|
||||
}
|
||||
return gensec_security->ops->wrap_packets(gensec_security, mem_ctx, in, out,
|
||||
len_processed);
|
||||
}
|
||||
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed)
|
||||
{
|
||||
if (!gensec_security->ops->unwrap_packets) {
|
||||
DATA_BLOB wrapped;
|
||||
NTSTATUS nt_status;
|
||||
size_t packet_size;
|
||||
if (in->length < 4) {
|
||||
/* Missing the header we already had! */
|
||||
DEBUG(0, ("Asked to unwrap packet of bogus length! How did we get the short packet?!\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
packet_size = RIVAL(in->data, 0);
|
||||
|
||||
wrapped = data_blob_const(in->data + 4, packet_size);
|
||||
|
||||
if (wrapped.length > (in->length - 4)) {
|
||||
DEBUG(0, ("Asked to unwrap packed of bogus length %d > %d! How did we get this?!\n",
|
||||
(int)wrapped.length, (int)(in->length - 4)));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
nt_status = gensec_unwrap(gensec_security,
|
||||
mem_ctx,
|
||||
&wrapped, out);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
*len_processed = packet_size + 4;
|
||||
return nt_status;
|
||||
}
|
||||
return gensec_security->ops->unwrap_packets(gensec_security, mem_ctx, in, out,
|
||||
len_processed);
|
||||
}
|
||||
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
|
||||
DATA_BLOB blob, size_t *size)
|
||||
{
|
||||
if (gensec_security->ops->packet_full_request) {
|
||||
return gensec_security->ops->packet_full_request(gensec_security,
|
||||
blob, size);
|
||||
}
|
||||
if (gensec_security->ops->unwrap_packets) {
|
||||
if (blob.length) {
|
||||
*size = blob.length;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return STATUS_MORE_ENTRIES;
|
||||
}
|
||||
return packet_full_request_u32(NULL, blob, size);
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_socket_full_request(void *private, DATA_BLOB blob, size_t *size)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
struct gensec_security *gensec_security = gensec_socket->gensec_security;
|
||||
return gensec_packet_full_request(gensec_security, blob, size);
|
||||
}
|
||||
|
||||
/* Try to figure out how much data is waiting to be read */
|
||||
static NTSTATUS gensec_socket_pending(struct socket_context *sock, size_t *npending)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
if (!gensec_socket->wrap) {
|
||||
return socket_pending(gensec_socket->socket, npending);
|
||||
}
|
||||
|
||||
if (gensec_socket->read_buffer.length > 0) {
|
||||
*npending = gensec_socket->read_buffer.length;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* This is a lie. We hope the decrypted data will always be
|
||||
* less than this value, so the application just gets a short
|
||||
* read. Without reading and decrypting it, we can't tell.
|
||||
* If the SASL mech does compression, then we just need to
|
||||
* manually trigger read events */
|
||||
return socket_pending(gensec_socket->socket, npending);
|
||||
}
|
||||
|
||||
/* Note if an error occours, so we can return it up the stack */
|
||||
static void gensec_socket_error_handler(void *private, NTSTATUS status)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
|
||||
gensec_socket->eof = True;
|
||||
} else {
|
||||
gensec_socket->error = status;
|
||||
}
|
||||
}
|
||||
|
||||
static void gensec_socket_trigger_read(struct event_context *ev,
|
||||
struct timed_event *te,
|
||||
struct timeval t, void *private)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
|
||||
gensec_socket->in_extra_read++;
|
||||
gensec_socket->recv_handler(gensec_socket->recv_private, EVENT_FD_READ);
|
||||
gensec_socket->in_extra_read--;
|
||||
|
||||
/* It may well be that, having run the recv handler, we still
|
||||
* have even more data waiting for us!
|
||||
*/
|
||||
if (gensec_socket->read_buffer.length && gensec_socket->recv_handler) {
|
||||
/* Schedule this funcion to run again */
|
||||
event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
|
||||
gensec_socket_trigger_read, gensec_socket);
|
||||
}
|
||||
}
|
||||
|
||||
/* These two routines could be changed to use a circular buffer of
|
||||
* some kind, or linked lists, or ... */
|
||||
static NTSTATUS gensec_socket_recv(struct socket_context *sock, void *buf,
|
||||
size_t wantlen, size_t *nread)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
|
||||
if (!gensec_socket->wrap) {
|
||||
return socket_recv(gensec_socket->socket, buf, wantlen, nread);
|
||||
}
|
||||
|
||||
gensec_socket->error = NT_STATUS_OK;
|
||||
|
||||
if (gensec_socket->read_buffer.length == 0) {
|
||||
/* Process any data on the socket, into the read buffer. At
|
||||
* this point, the socket is not available for read any
|
||||
* longer */
|
||||
packet_recv(gensec_socket->packet);
|
||||
|
||||
if (gensec_socket->eof) {
|
||||
*nread = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
|
||||
return gensec_socket->error;
|
||||
}
|
||||
|
||||
if (gensec_socket->read_buffer.length == 0) {
|
||||
/* Clearly we don't have the entire SASL packet yet,
|
||||
* so it has not been written into the buffer */
|
||||
*nread = 0;
|
||||
return STATUS_MORE_ENTRIES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*nread = MIN(wantlen, gensec_socket->read_buffer.length);
|
||||
memcpy(buf, gensec_socket->read_buffer.data, *nread);
|
||||
|
||||
if (gensec_socket->read_buffer.length > *nread) {
|
||||
memmove(gensec_socket->read_buffer.data,
|
||||
gensec_socket->read_buffer.data + *nread,
|
||||
gensec_socket->read_buffer.length - *nread);
|
||||
}
|
||||
|
||||
gensec_socket->read_buffer.length -= *nread;
|
||||
gensec_socket->read_buffer.data = talloc_realloc(gensec_socket,
|
||||
gensec_socket->read_buffer.data,
|
||||
uint8_t,
|
||||
gensec_socket->read_buffer.length);
|
||||
|
||||
if (gensec_socket->read_buffer.length &&
|
||||
gensec_socket->in_extra_read == 0 &&
|
||||
gensec_socket->recv_handler) {
|
||||
/* Manually call a read event, to get this moving
|
||||
* again (as the socket should be dry, so the normal
|
||||
* event handler won't trigger) */
|
||||
event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
|
||||
gensec_socket_trigger_read, gensec_socket);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Completed SASL packet callback. When we have a 'whole' SASL
|
||||
* packet, decrypt it, and add it to the read buffer
|
||||
*
|
||||
* This function (and anything under it) MUST NOT call the event system
|
||||
*/
|
||||
static NTSTATUS gensec_socket_unwrap(void *private, DATA_BLOB blob)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
DATA_BLOB unwrapped;
|
||||
NTSTATUS nt_status;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
size_t packet_size;
|
||||
|
||||
mem_ctx = talloc_new(gensec_socket);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
nt_status = gensec_unwrap_packets(gensec_socket->gensec_security,
|
||||
mem_ctx,
|
||||
&blob, &unwrapped,
|
||||
&packet_size);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (packet_size != blob.length) {
|
||||
DEBUG(0, ("gensec_socket_unwrap: Did not consume entire packet!\n"));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/* We could change this into a linked list, and have
|
||||
* gensec_socket_recv() and gensec_socket_pending() walk the
|
||||
* linked list */
|
||||
|
||||
nt_status = data_blob_append(gensec_socket, &gensec_socket->read_buffer,
|
||||
unwrapped.data, unwrapped.length);
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* when the data is sent, we know we have not been interrupted */
|
||||
static void send_callback(void *private)
|
||||
{
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
|
||||
gensec_socket->interrupted = False;
|
||||
}
|
||||
|
||||
/*
|
||||
send data, but only as much as we allow in one packet.
|
||||
|
||||
If this returns STATUS_MORE_ENTRIES, the caller must retry with
|
||||
exactly the same data, or a NULL blob.
|
||||
*/
|
||||
static NTSTATUS gensec_socket_send(struct socket_context *sock,
|
||||
const DATA_BLOB *blob, size_t *sendlen)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
DATA_BLOB wrapped;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if (!gensec_socket->wrap) {
|
||||
return socket_send(gensec_socket->socket, blob, sendlen);
|
||||
}
|
||||
|
||||
*sendlen = 0;
|
||||
|
||||
/* We have have been interupted, so the caller should be
|
||||
* giving us the same data again. */
|
||||
if (gensec_socket->interrupted) {
|
||||
packet_queue_run(gensec_socket->packet);
|
||||
|
||||
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
|
||||
return gensec_socket->error;
|
||||
} else if (gensec_socket->interrupted) {
|
||||
return STATUS_MORE_ENTRIES;
|
||||
} else {
|
||||
*sendlen = gensec_socket->orig_send_len;
|
||||
gensec_socket->orig_send_len = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(gensec_socket);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
nt_status = gensec_wrap_packets(gensec_socket->gensec_security,
|
||||
mem_ctx,
|
||||
blob, &wrapped,
|
||||
&gensec_socket->orig_send_len);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
gensec_socket->interrupted = True;
|
||||
gensec_socket->error = NT_STATUS_OK;
|
||||
|
||||
nt_status = packet_send_callback(gensec_socket->packet,
|
||||
wrapped,
|
||||
send_callback, gensec_socket);
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
packet_queue_run(gensec_socket->packet);
|
||||
|
||||
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
|
||||
return gensec_socket->error;
|
||||
} else if (gensec_socket->interrupted) {
|
||||
return STATUS_MORE_ENTRIES;
|
||||
} else {
|
||||
*sendlen = gensec_socket->orig_send_len;
|
||||
gensec_socket->orig_send_len = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Turn a normal socket into a potentially GENSEC wrapped socket */
|
||||
|
||||
NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
|
||||
struct socket_context *current_socket,
|
||||
struct event_context *ev,
|
||||
void (*recv_handler)(void *, uint16_t),
|
||||
void *recv_private,
|
||||
struct socket_context **new_socket)
|
||||
{
|
||||
struct gensec_socket *gensec_socket;
|
||||
struct socket_context *new_sock;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
nt_status = socket_create_with_ops(current_socket, &gensec_socket_ops, &new_sock,
|
||||
SOCKET_TYPE_STREAM, current_socket->flags | SOCKET_FLAG_ENCRYPT);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
*new_socket = NULL;
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
new_sock->state = current_socket->state;
|
||||
|
||||
gensec_socket = talloc(new_sock, struct gensec_socket);
|
||||
if (gensec_socket == NULL) {
|
||||
*new_socket = NULL;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
new_sock->private_data = gensec_socket;
|
||||
gensec_socket->socket = current_socket;
|
||||
|
||||
if (talloc_reference(gensec_socket, current_socket) == NULL) {
|
||||
*new_socket = NULL;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* Nothing to do here, if we are not actually wrapping on this socket */
|
||||
if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) &&
|
||||
!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
|
||||
|
||||
gensec_socket->wrap = False;
|
||||
*new_socket = new_sock;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
gensec_socket->gensec_security = gensec_security;
|
||||
|
||||
gensec_socket->wrap = True;
|
||||
gensec_socket->eof = False;
|
||||
gensec_socket->error = NT_STATUS_OK;
|
||||
gensec_socket->interrupted = False;
|
||||
gensec_socket->in_extra_read = 0;
|
||||
|
||||
gensec_socket->read_buffer = data_blob(NULL, 0);
|
||||
|
||||
gensec_socket->recv_handler = recv_handler;
|
||||
gensec_socket->recv_private = recv_private;
|
||||
gensec_socket->ev = ev;
|
||||
|
||||
gensec_socket->packet = packet_init(gensec_socket);
|
||||
if (gensec_socket->packet == NULL) {
|
||||
*new_socket = NULL;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
packet_set_private(gensec_socket->packet, gensec_socket);
|
||||
packet_set_socket(gensec_socket->packet, gensec_socket->socket);
|
||||
packet_set_callback(gensec_socket->packet, gensec_socket_unwrap);
|
||||
packet_set_full_request(gensec_socket->packet, gensec_socket_full_request);
|
||||
packet_set_error_handler(gensec_socket->packet, gensec_socket_error_handler);
|
||||
packet_set_serialise(gensec_socket->packet);
|
||||
|
||||
/* TODO: full-request that knows about maximum packet size */
|
||||
|
||||
*new_socket = new_sock;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static NTSTATUS gensec_socket_set_option(struct socket_context *sock, const char *option, const char *val)
|
||||
{
|
||||
set_socket_options(socket_get_fd(sock), option);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static char *gensec_socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_peer_name(gensec->socket, mem_ctx);
|
||||
}
|
||||
|
||||
static struct socket_address *gensec_socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_peer_addr(gensec->socket, mem_ctx);
|
||||
}
|
||||
|
||||
static struct socket_address *gensec_socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_my_addr(gensec->socket, mem_ctx);
|
||||
}
|
||||
|
||||
static int gensec_socket_get_fd(struct socket_context *sock)
|
||||
{
|
||||
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
|
||||
return socket_get_fd(gensec->socket);
|
||||
}
|
||||
|
||||
static const struct socket_ops gensec_socket_ops = {
|
||||
.name = "gensec",
|
||||
.fn_init = gensec_socket_init_fn,
|
||||
.fn_recv = gensec_socket_recv,
|
||||
.fn_send = gensec_socket_send,
|
||||
.fn_pending = gensec_socket_pending,
|
||||
|
||||
.fn_set_option = gensec_socket_set_option,
|
||||
|
||||
.fn_get_peer_name = gensec_socket_get_peer_name,
|
||||
.fn_get_peer_addr = gensec_socket_get_peer_addr,
|
||||
.fn_get_my_addr = gensec_socket_get_my_addr,
|
||||
.fn_get_fd = gensec_socket_get_fd
|
||||
};
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Generic Authentication Interface (socket wrapper)
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
struct gensec_security;
|
||||
struct socket_context;
|
||||
|
||||
NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
|
||||
struct socket_context *current_socket,
|
||||
struct event_context *ev,
|
||||
void (*recv_handler)(void *, uint16_t),
|
||||
void *recv_private,
|
||||
struct socket_context **new_socket);
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed);
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out,
|
||||
size_t *len_processed);
|
||||
|
||||
/* These functions are for use here only (public because SPNEGO must
|
||||
* use them for recursion) */
|
||||
NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
|
||||
DATA_BLOB blob, size_t *size);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
RFC2478 Compliant SPNEGO implementation
|
||||
|
||||
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#define SPNEGO_DELEG_FLAG 0x01
|
||||
#define SPNEGO_MUTUAL_FLAG 0x02
|
||||
#define SPNEGO_REPLAY_FLAG 0x04
|
||||
#define SPNEGO_SEQUENCE_FLAG 0x08
|
||||
#define SPNEGO_ANON_FLAG 0x10
|
||||
#define SPNEGO_CONF_FLAG 0x20
|
||||
#define SPNEGO_INTEG_FLAG 0x40
|
||||
#define SPNEGO_REQ_FLAG 0x80
|
||||
|
||||
enum spnego_negResult {
|
||||
SPNEGO_ACCEPT_COMPLETED = 0,
|
||||
SPNEGO_ACCEPT_INCOMPLETE = 1,
|
||||
SPNEGO_REJECT = 2,
|
||||
SPNEGO_NONE_RESULT = 3
|
||||
};
|
||||
|
||||
struct spnego_negTokenInit {
|
||||
const char **mechTypes;
|
||||
int reqFlags;
|
||||
DATA_BLOB mechToken;
|
||||
DATA_BLOB mechListMIC;
|
||||
char *targetPrincipal;
|
||||
};
|
||||
|
||||
struct spnego_negTokenTarg {
|
||||
uint8_t negResult;
|
||||
const char *supportedMech;
|
||||
DATA_BLOB responseToken;
|
||||
DATA_BLOB mechListMIC;
|
||||
};
|
||||
|
||||
struct spnego_data {
|
||||
int type;
|
||||
struct spnego_negTokenInit negTokenInit;
|
||||
struct spnego_negTokenTarg negTokenTarg;
|
||||
};
|
||||
|
||||
enum spnego_message_type {
|
||||
SPNEGO_NEG_TOKEN_INIT = 0,
|
||||
SPNEGO_NEG_TOKEN_TARG = 1,
|
||||
};
|
||||
|
||||
#include "auth/gensec/spnego_proto.h"
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
RFC2478 Compliant SPNEGO implementation
|
||||
|
||||
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/gensec/spnego.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "libcli/util/asn_1.h"
|
||||
|
||||
static BOOL read_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
|
||||
{
|
||||
ZERO_STRUCTP(token);
|
||||
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
|
||||
|
||||
while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
|
||||
int i;
|
||||
uint8_t context;
|
||||
if (!asn1_peek_uint8(asn1, &context)) {
|
||||
asn1->has_error = True;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (context) {
|
||||
/* Read mechTypes */
|
||||
case ASN1_CONTEXT(0):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
|
||||
|
||||
token->mechTypes = talloc(NULL, const char *);
|
||||
for (i = 0; !asn1->has_error &&
|
||||
0 < asn1_tag_remaining(asn1); i++) {
|
||||
token->mechTypes = talloc_realloc(NULL,
|
||||
token->mechTypes,
|
||||
const char *, i+2);
|
||||
asn1_read_OID(asn1, token->mechTypes + i);
|
||||
if (token->mechTypes[i]) {
|
||||
talloc_steal(token->mechTypes,
|
||||
token->mechTypes[i]);
|
||||
}
|
||||
}
|
||||
token->mechTypes[i] = NULL;
|
||||
|
||||
asn1_end_tag(asn1);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
/* Read reqFlags */
|
||||
case ASN1_CONTEXT(1):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_read_Integer(asn1, &token->reqFlags);
|
||||
token->reqFlags |= SPNEGO_REQ_FLAG;
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
/* Read mechToken */
|
||||
case ASN1_CONTEXT(2):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(2));
|
||||
asn1_read_OctetString(asn1, &token->mechToken);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
/* Read mecListMIC */
|
||||
case ASN1_CONTEXT(3):
|
||||
{
|
||||
uint8_t type_peek;
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(3));
|
||||
if (!asn1_peek_uint8(asn1, &type_peek)) {
|
||||
asn1->has_error = True;
|
||||
break;
|
||||
}
|
||||
if (type_peek == ASN1_OCTET_STRING) {
|
||||
asn1_read_OctetString(asn1,
|
||||
&token->mechListMIC);
|
||||
} else {
|
||||
/* RFC 2478 says we have an Octet String here,
|
||||
but W2k sends something different... */
|
||||
char *mechListMIC;
|
||||
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_read_GeneralString(asn1, &mechListMIC);
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
|
||||
token->targetPrincipal = mechListMIC;
|
||||
}
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
asn1->has_error = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
asn1_end_tag(asn1);
|
||||
asn1_end_tag(asn1);
|
||||
|
||||
return !asn1->has_error;
|
||||
}
|
||||
|
||||
static BOOL write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
|
||||
{
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
|
||||
|
||||
/* Write mechTypes */
|
||||
if (token->mechTypes && *token->mechTypes) {
|
||||
int i;
|
||||
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
|
||||
for (i = 0; token->mechTypes[i]; i++) {
|
||||
asn1_write_OID(asn1, token->mechTypes[i]);
|
||||
}
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
/* write reqFlags */
|
||||
if (token->reqFlags & SPNEGO_REQ_FLAG) {
|
||||
int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
|
||||
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_write_Integer(asn1, flags);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
/* write mechToken */
|
||||
if (token->mechToken.data) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(2));
|
||||
asn1_write_OctetString(asn1, token->mechToken.data,
|
||||
token->mechToken.length);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
/* write mechListMIC */
|
||||
if (token->mechListMIC.data) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(3));
|
||||
#if 0
|
||||
/* This is what RFC 2478 says ... */
|
||||
asn1_write_OctetString(asn1, token->mechListMIC.data,
|
||||
token->mechListMIC.length);
|
||||
#else
|
||||
/* ... but unfortunately this is what Windows
|
||||
sends/expects */
|
||||
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_push_tag(asn1, ASN1_GENERAL_STRING);
|
||||
asn1_write(asn1, token->mechListMIC.data,
|
||||
token->mechListMIC.length);
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
#endif
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
|
||||
return !asn1->has_error;
|
||||
}
|
||||
|
||||
static BOOL read_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
|
||||
{
|
||||
ZERO_STRUCTP(token);
|
||||
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
|
||||
|
||||
while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
|
||||
uint8_t context;
|
||||
if (!asn1_peek_uint8(asn1, &context)) {
|
||||
asn1->has_error = True;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (context) {
|
||||
case ASN1_CONTEXT(0):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_start_tag(asn1, ASN1_ENUMERATED);
|
||||
asn1_read_uint8(asn1, &token->negResult);
|
||||
asn1_end_tag(asn1);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
case ASN1_CONTEXT(1):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_read_OID(asn1, &token->supportedMech);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
case ASN1_CONTEXT(2):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(2));
|
||||
asn1_read_OctetString(asn1, &token->responseToken);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
case ASN1_CONTEXT(3):
|
||||
asn1_start_tag(asn1, ASN1_CONTEXT(3));
|
||||
asn1_read_OctetString(asn1, &token->mechListMIC);
|
||||
asn1_end_tag(asn1);
|
||||
break;
|
||||
default:
|
||||
asn1->has_error = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
asn1_end_tag(asn1);
|
||||
asn1_end_tag(asn1);
|
||||
|
||||
return !asn1->has_error;
|
||||
}
|
||||
|
||||
static BOOL write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
|
||||
{
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
|
||||
|
||||
if (token->negResult != SPNEGO_NONE_RESULT) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(0));
|
||||
asn1_write_enumerated(asn1, token->negResult);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
if (token->supportedMech) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(1));
|
||||
asn1_write_OID(asn1, token->supportedMech);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
if (token->responseToken.data) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(2));
|
||||
asn1_write_OctetString(asn1, token->responseToken.data,
|
||||
token->responseToken.length);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
if (token->mechListMIC.data) {
|
||||
asn1_push_tag(asn1, ASN1_CONTEXT(3));
|
||||
asn1_write_OctetString(asn1, token->mechListMIC.data,
|
||||
token->mechListMIC.length);
|
||||
asn1_pop_tag(asn1);
|
||||
}
|
||||
|
||||
asn1_pop_tag(asn1);
|
||||
asn1_pop_tag(asn1);
|
||||
|
||||
return !asn1->has_error;
|
||||
}
|
||||
|
||||
ssize_t spnego_read_data(DATA_BLOB data, struct spnego_data *token)
|
||||
{
|
||||
struct asn1_data asn1;
|
||||
ssize_t ret = -1;
|
||||
uint8_t context;
|
||||
|
||||
ZERO_STRUCTP(token);
|
||||
ZERO_STRUCT(asn1);
|
||||
|
||||
if (data.length == 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
asn1_load(&asn1, data);
|
||||
|
||||
if (!asn1_peek_uint8(&asn1, &context)) {
|
||||
asn1.has_error = True;
|
||||
} else {
|
||||
switch (context) {
|
||||
case ASN1_APPLICATION(0):
|
||||
asn1_start_tag(&asn1, ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&asn1, GENSEC_OID_SPNEGO);
|
||||
if (read_negTokenInit(&asn1, &token->negTokenInit)) {
|
||||
token->type = SPNEGO_NEG_TOKEN_INIT;
|
||||
}
|
||||
asn1_end_tag(&asn1);
|
||||
break;
|
||||
case ASN1_CONTEXT(1):
|
||||
if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
|
||||
token->type = SPNEGO_NEG_TOKEN_TARG;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
asn1.has_error = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!asn1.has_error) ret = asn1.ofs;
|
||||
asn1_free(&asn1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
|
||||
{
|
||||
struct asn1_data asn1;
|
||||
ssize_t ret = -1;
|
||||
|
||||
ZERO_STRUCT(asn1);
|
||||
|
||||
switch (spnego->type) {
|
||||
case SPNEGO_NEG_TOKEN_INIT:
|
||||
asn1_push_tag(&asn1, ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&asn1, GENSEC_OID_SPNEGO);
|
||||
write_negTokenInit(&asn1, &spnego->negTokenInit);
|
||||
asn1_pop_tag(&asn1);
|
||||
break;
|
||||
case SPNEGO_NEG_TOKEN_TARG:
|
||||
write_negTokenTarg(&asn1, &spnego->negTokenTarg);
|
||||
break;
|
||||
default:
|
||||
asn1.has_error = True;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!asn1.has_error) {
|
||||
*blob = data_blob_talloc(mem_ctx, asn1.data, asn1.length);
|
||||
ret = asn1.ofs;
|
||||
}
|
||||
asn1_free(&asn1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL spnego_free_data(struct spnego_data *spnego)
|
||||
{
|
||||
BOOL ret = True;
|
||||
|
||||
if (!spnego) goto out;
|
||||
|
||||
switch(spnego->type) {
|
||||
case SPNEGO_NEG_TOKEN_INIT:
|
||||
if (spnego->negTokenInit.mechTypes) {
|
||||
talloc_free(spnego->negTokenInit.mechTypes);
|
||||
}
|
||||
data_blob_free(&spnego->negTokenInit.mechToken);
|
||||
data_blob_free(&spnego->negTokenInit.mechListMIC);
|
||||
talloc_free(spnego->negTokenInit.targetPrincipal);
|
||||
break;
|
||||
case SPNEGO_NEG_TOKEN_TARG:
|
||||
if (spnego->negTokenTarg.supportedMech) {
|
||||
talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
|
||||
}
|
||||
data_blob_free(&spnego->negTokenTarg.responseToken);
|
||||
data_blob_free(&spnego->negTokenTarg.mechListMIC);
|
||||
break;
|
||||
default:
|
||||
ret = False;
|
||||
break;
|
||||
}
|
||||
ZERO_STRUCTP(spnego);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5 routines for active directory
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/network.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "system/time.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
|
||||
#if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
|
||||
krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
|
||||
{
|
||||
return krb5_set_default_in_tkt_etypes(ctx, enc);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
|
||||
/* HEIMDAL */
|
||||
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
|
||||
{
|
||||
pkaddr->addr_type = KRB5_ADDRESS_INET;
|
||||
pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
}
|
||||
#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
|
||||
/* MIT */
|
||||
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
|
||||
{
|
||||
pkaddr->addrtype = ADDRTYPE_INET;
|
||||
pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
|
||||
}
|
||||
#else
|
||||
#error UNKNOWN_ADDRTYPE
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK)
|
||||
int create_kerberos_key_from_string(krb5_context context,
|
||||
krb5_principal host_princ,
|
||||
krb5_data *password,
|
||||
krb5_keyblock *key,
|
||||
krb5_enctype enctype)
|
||||
{
|
||||
int ret;
|
||||
krb5_data salt;
|
||||
krb5_encrypt_block eblock;
|
||||
|
||||
ret = krb5_principal2salt(context, host_princ, &salt);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
krb5_use_enctype(context, &eblock, enctype);
|
||||
ret = krb5_string_to_key(context, &eblock, key, password, &salt);
|
||||
SAFE_FREE(salt.data);
|
||||
return ret;
|
||||
}
|
||||
#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
|
||||
int create_kerberos_key_from_string(krb5_context context,
|
||||
krb5_principal host_princ,
|
||||
krb5_data *password,
|
||||
krb5_keyblock *key,
|
||||
krb5_enctype enctype)
|
||||
{
|
||||
int ret;
|
||||
krb5_salt salt;
|
||||
|
||||
ret = krb5_get_pw_salt(context, host_princ, &salt);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
ret = krb5_string_to_key_salt(context, enctype, password->data,
|
||||
salt, key);
|
||||
krb5_free_salt(context, salt);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#error UNKNOWN_CREATE_KEY_FUNCTIONS
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
|
||||
krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
|
||||
krb5_enctype **enctypes)
|
||||
{
|
||||
return krb5_get_permitted_enctypes(context, enctypes);
|
||||
}
|
||||
#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
|
||||
krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
|
||||
krb5_enctype **enctypes)
|
||||
{
|
||||
return krb5_get_default_in_tkt_etypes(context, enctypes);
|
||||
}
|
||||
#else
|
||||
#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
|
||||
#endif
|
||||
|
||||
void free_kerberos_etypes(krb5_context context,
|
||||
krb5_enctype *enctypes)
|
||||
{
|
||||
#if defined(HAVE_KRB5_FREE_KTYPES)
|
||||
krb5_free_ktypes(context, enctypes);
|
||||
return;
|
||||
#else
|
||||
SAFE_FREE(enctypes);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
|
||||
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
|
||||
krb5_auth_context auth_context,
|
||||
krb5_keyblock *keyblock)
|
||||
{
|
||||
return krb5_auth_con_setkey(context, auth_context, keyblock);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
|
||||
void krb5_free_unparsed_name(krb5_context context, char *val)
|
||||
{
|
||||
SAFE_FREE(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
void kerberos_free_data_contents(krb5_context context, krb5_data *pdata)
|
||||
{
|
||||
#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
|
||||
if (pdata->data) {
|
||||
krb5_free_data_contents(context, pdata);
|
||||
}
|
||||
#else
|
||||
SAFE_FREE(pdata->data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype)
|
||||
{
|
||||
#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS)
|
||||
KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype;
|
||||
#elif defined(HAVE_KRB5_SESSION_IN_CREDS)
|
||||
KRB5_KEY_TYPE((&pcreds->session)) = enctype;
|
||||
#else
|
||||
#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL kerberos_compatible_enctypes(krb5_context context,
|
||||
krb5_enctype enctype1,
|
||||
krb5_enctype enctype2)
|
||||
{
|
||||
#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE)
|
||||
krb5_boolean similar = 0;
|
||||
|
||||
krb5_c_enctype_compare(context, enctype1, enctype2, &similar);
|
||||
return similar ? True : False;
|
||||
#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS)
|
||||
return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False;
|
||||
#endif
|
||||
}
|
||||
|
||||
krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry)
|
||||
{
|
||||
#if defined(HAVE_KRB5_KT_FREE_ENTRY)
|
||||
return krb5_kt_free_entry(context, kt_entry);
|
||||
#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
|
||||
return krb5_free_keytab_entry_contents(context, kt_entry);
|
||||
#else
|
||||
#error UNKNOWN_KT_FREE_FUNCTION
|
||||
#endif
|
||||
}
|
||||
|
||||
char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
#if defined(HAVE_KRB5_GET_ERROR_STRING) && defined(HAVE_KRB5_FREE_ERROR_STRING)
|
||||
char *context_error = krb5_get_error_string(context);
|
||||
if (context_error) {
|
||||
ret = talloc_asprintf(mem_ctx, "%s: %s", error_message(code), context_error);
|
||||
krb5_free_error_string(context, context_error);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
ret = talloc_strdup(mem_ctx, error_message(code));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,540 @@
|
||||
# NOTE! this whole m4 file is disabled in configure.in for now
|
||||
|
||||
#################################################
|
||||
# KRB5 support
|
||||
KRB5_CFLAGS=""
|
||||
KRB5_CPPFLAGS=""
|
||||
KRB5_LDFLAGS=""
|
||||
KRB5_LIBS=""
|
||||
with_krb5_support=auto
|
||||
krb5_withval=auto
|
||||
AC_MSG_CHECKING([for KRB5 support])
|
||||
|
||||
# Do no harm to the values of CFLAGS and LIBS while testing for
|
||||
# Kerberos support.
|
||||
AC_ARG_WITH(krb5,
|
||||
[ --with-krb5=base-dir Locate Kerberos 5 support (default=auto)],
|
||||
[ case "$withval" in
|
||||
no)
|
||||
with_krb5_support=no
|
||||
AC_MSG_RESULT(no)
|
||||
krb5_withval=no
|
||||
;;
|
||||
yes)
|
||||
with_krb5_support=yes
|
||||
AC_MSG_RESULT(yes)
|
||||
krb5_withval=yes
|
||||
;;
|
||||
auto)
|
||||
with_krb5_support=auto
|
||||
AC_MSG_RESULT(auto)
|
||||
krb5_withval=auto
|
||||
;;
|
||||
*)
|
||||
with_krb5_support=yes
|
||||
AC_MSG_RESULT(yes)
|
||||
krb5_withval=$withval
|
||||
KRB5CONFIG="$krb5_withval/bin/krb5-config"
|
||||
;;
|
||||
esac ],
|
||||
AC_MSG_RESULT($with_krb5_support)
|
||||
)
|
||||
|
||||
if test x$with_krb5_support != x"no"; then
|
||||
FOUND_KRB5=no
|
||||
FOUND_KRB5_VIA_CONFIG=no
|
||||
|
||||
#################################################
|
||||
# check for krb5-config from recent MIT and Heimdal kerberos 5
|
||||
AC_MSG_CHECKING(for working specified location for krb5-config)
|
||||
if test x$KRB5CONFIG != "x"; then
|
||||
if test -x "$KRB5CONFIG"; then
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS="";export CFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="";export LDFLAGS
|
||||
KRB5_LIBS="`$KRB5CONFIG --libs gssapi`"
|
||||
KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
CFLAGS=$ac_save_CFLAGS;export CFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
|
||||
FOUND_KRB5=yes
|
||||
FOUND_KRB5_VIA_CONFIG=yes
|
||||
AC_MSG_RESULT(yes. Found $KRB5CONFIG)
|
||||
else
|
||||
AC_MSG_RESULT(no. Fallback to specified directory)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no. Fallback to finding krb5-config in path)
|
||||
#################################################
|
||||
# check for krb5-config from recent MIT and Heimdal kerberos 5
|
||||
AC_PATH_PROG(KRB5CONFIG, krb5-config)
|
||||
AC_MSG_CHECKING(for working krb5-config in path)
|
||||
if test -x "$KRB5CONFIG"; then
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS="";export CFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="";export LDFLAGS
|
||||
KRB5_LIBS="`$KRB5CONFIG --libs gssapi`"
|
||||
KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
|
||||
CFLAGS=$ac_save_CFLAGS;export CFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
|
||||
FOUND_KRB5=yes
|
||||
FOUND_KRB5_VIA_CONFIG=yes
|
||||
AC_MSG_RESULT(yes. Found $KRB5CONFIG)
|
||||
else
|
||||
AC_MSG_RESULT(no. Fallback to previous krb5 detection strategy)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$FOUND_KRB5 != x"yes"; then
|
||||
#################################################
|
||||
# check for location of Kerberos 5 install
|
||||
AC_MSG_CHECKING(for kerberos 5 install path)
|
||||
case "$krb5_withval" in
|
||||
no)
|
||||
AC_MSG_RESULT(no krb5-path given)
|
||||
;;
|
||||
yes)
|
||||
AC_MSG_RESULT(/usr)
|
||||
FOUND_KRB5=yes
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT($krb5_withval)
|
||||
KRB5_CFLAGS="-I$krb5_withval/include"
|
||||
KRB5_CPPFLAGS="-I$krb5_withval/include"
|
||||
KRB5_LDFLAGS="-L$krb5_withval/lib"
|
||||
FOUND_KRB5=yes
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test x$FOUND_KRB5 != x"yes"; then
|
||||
#################################################
|
||||
# see if this box has the SuSE location for the heimdal krb implementation
|
||||
AC_MSG_CHECKING(for /usr/include/heimdal)
|
||||
if test -d /usr/include/heimdal; then
|
||||
if test -f /usr/lib/heimdal/lib/libkrb5.a; then
|
||||
KRB5_CFLAGS="-I/usr/include/heimdal"
|
||||
KRB5_CPPFLAGS="-I/usr/include/heimdal"
|
||||
KRB5_LDFLAGS="-L/usr/lib/heimdal/lib"
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
KRB5_CFLAGS="-I/usr/include/heimdal"
|
||||
KRB5_CPPFLAGS="-I/usr/include/heimdal"
|
||||
AC_MSG_RESULT(yes)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$FOUND_KRB5 != x"yes"; then
|
||||
#################################################
|
||||
# see if this box has the RedHat location for kerberos
|
||||
AC_MSG_CHECKING(for /usr/kerberos)
|
||||
if test -d /usr/kerberos -a -f /usr/kerberos/lib/libkrb5.a; then
|
||||
KRB5_LDFLAGS="-L/usr/kerberos/lib"
|
||||
KRB5_CFLAGS="-I/usr/kerberos/include"
|
||||
KRB5_CPPFLAGS="-I/usr/kerberos/include"
|
||||
AC_MSG_RESULT(yes)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
fi
|
||||
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
ac_save_CPPFLAGS=$CPPFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
|
||||
#MIT needs this, to let us see 'internal' parts of the headers we use
|
||||
KRB5_CFLAGS="${KRB5_CFLAGS} -DKRB5_PRIVATE -DKRB5_DEPRECATED"
|
||||
|
||||
#Heimdal needs this
|
||||
#TODO: we need to parse KRB5_LIBS for -L path
|
||||
# and set -Wl,-rpath -Wl,path
|
||||
|
||||
CFLAGS="$CFLAGS $KRB5_CFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
|
||||
|
||||
KRB5_LIBS="$KRB5_LDFLAGS $KRB5_LIBS"
|
||||
|
||||
# now check for krb5.h. Some systems have the libraries without the headers!
|
||||
# note that this check is done here to allow for different kerberos
|
||||
# include paths
|
||||
AC_CHECK_HEADERS(krb5.h)
|
||||
|
||||
if test x"$ac_cv_header_krb5_h" = x"no"; then
|
||||
# Give a warning if KRB5 support was not explicitly requested,
|
||||
# i.e with_krb5_support = auto, otherwise die with an error.
|
||||
if test x"$with_krb5_support" = x"yes"; then
|
||||
AC_MSG_ERROR([KRB5 cannot be supported without krb5.h])
|
||||
else
|
||||
AC_MSG_WARN([KRB5 cannot be supported without krb5.h])
|
||||
fi
|
||||
# Turn off AD support and restore CFLAGS and LIBS variables
|
||||
with_krb5_support="no"
|
||||
fi
|
||||
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
CPPFLAGS=$ac_save_CPPFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS
|
||||
fi
|
||||
|
||||
# Now we have determined whether we really want KRB5 support
|
||||
|
||||
if test x"$with_krb5_support" != x"no"; then
|
||||
ac_save_CFLAGS=$CFLAGS
|
||||
ac_save_CPPFLAGS=$CPPFLAGS
|
||||
ac_save_LDFLAGS=$LDFLAGS
|
||||
ac_save_LIBS=$LIBS
|
||||
|
||||
CFLAGS="$CFLAGS $KRB5_CFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
|
||||
|
||||
# now check for gssapi headers. This is also done here to allow for
|
||||
# different kerberos include paths
|
||||
AC_CHECK_HEADERS(gssapi.h gssapi_krb5.h gssapi/gssapi.h gssapi/gssapi_generic.h gssapi/gssapi_krb5.h com_err.h)
|
||||
|
||||
|
||||
# Heimdal checks.
|
||||
# But only if we didn't have a krb5-config to tell us this already
|
||||
if test x"$FOUND_KRB5_VIA_CONFIG" != x"yes"; then
|
||||
##################################################################
|
||||
# we might need the k5crypto and com_err libraries on some systems
|
||||
AC_CHECK_LIB_EXT(com_err, KRB5_LIBS, _et_list)
|
||||
AC_CHECK_LIB_EXT(k5crypto, KRB5_LIBS, krb5_encrypt_data)
|
||||
|
||||
AC_CHECK_LIB_EXT(crypto, KRB5_LIBS, des_set_key)
|
||||
AC_CHECK_LIB_EXT(asn1, KRB5_LIBS, copy_Authenticator)
|
||||
AC_CHECK_LIB_EXT(roken, KRB5_LIBS, roken_getaddrinfo_hostspec)
|
||||
fi
|
||||
|
||||
# Heimdal checks. On static Heimdal gssapi must be linked before krb5.
|
||||
AC_CHECK_LIB_EXT(gssapi, KRB5_LIBS, gss_display_status,[],[],
|
||||
AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
|
||||
|
||||
########################################################
|
||||
# now see if we can find the krb5 libs in standard paths
|
||||
# or as specified above
|
||||
AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_mk_req_extended)
|
||||
AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_kt_compare)
|
||||
|
||||
########################################################
|
||||
# now see if we can find the gssapi libs in standard paths
|
||||
if test x"$ac_cv_lib_ext_gssapi_gss_display_status" != x"yes"; then
|
||||
AC_CHECK_LIB_EXT(gssapi_krb5, KRB5_LIBS,gss_display_status,[],[],
|
||||
AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNC_EXT(krb5_set_real_time, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_set_default_in_tkt_etypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_set_default_tgs_ktypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_principal2salt, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_use_enctype, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_string_to_key, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_pw_salt, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_string_to_key_salt, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_auth_con_setkey, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_auth_con_setuseruserkey, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_locate_kdc, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_permitted_enctypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_default_in_tkt_etypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_ktypes, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_data_contents, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_principal_get_comp_string, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_unparsed_name, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_keytab_entry_contents, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_kt_free_entry, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_verify_checksum, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_c_verify_checksum, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_ticket_get_authorization_data_type, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_c_enctype_compare, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_enctypes_compatible_keys, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_get_error_string, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_free_error_string, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_initlog, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_addlog_func, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(krb5_set_warn_dest, $KRB5_LIBS)
|
||||
|
||||
LIBS="$LIBS $KRB5_LIBS"
|
||||
|
||||
AC_CACHE_CHECK([for krb5_log_facility type],
|
||||
samba_cv_HAVE_KRB5_LOG_FACILITY,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_log_facility block;],
|
||||
samba_cv_HAVE_KRB5_LOG_FACILITY=yes,
|
||||
samba_cv_HAVE_KRB5_LOG_FACILITY=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_LOG_FACILITY" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_LOG_FACILITY,1,
|
||||
[Whether the type krb5_log_facility exists])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for krb5_encrypt_block type],
|
||||
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_encrypt_block block;],
|
||||
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=yes,
|
||||
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_ENCRYPT_BLOCK" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_ENCRYPT_BLOCK,1,
|
||||
[Whether the type krb5_encrypt_block exists])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for addrtype in krb5_address],
|
||||
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_address kaddr; kaddr.addrtype = ADDRTYPE_INET;],
|
||||
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=yes,
|
||||
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=no)])
|
||||
if test x"$samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_ADDRTYPE_IN_KRB5_ADDRESS,1,
|
||||
[Whether the krb5_address struct has a addrtype property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for addr_type in krb5_address],
|
||||
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_address kaddr; kaddr.addr_type = KRB5_ADDRESS_INET;],
|
||||
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=yes,
|
||||
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=no)])
|
||||
if test x"$samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,1,
|
||||
[Whether the krb5_address struct has a addr_type property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for enc_part2 in krb5_ticket],
|
||||
samba_cv_HAVE_KRB5_TKT_ENC_PART2,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_ticket tkt; tkt.enc_part2->authorization_data[0]->contents = NULL;],
|
||||
samba_cv_HAVE_KRB5_TKT_ENC_PART2=yes,
|
||||
samba_cv_HAVE_KRB5_TKT_ENC_PART2=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_TKT_ENC_PART2" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_TKT_ENC_PART2,1,
|
||||
[Whether the krb5_ticket struct has a enc_part2 property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for keyblock in krb5_creds],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_creds creds; krb5_keyblock kb; creds.keyblock = kb;],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=yes,
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYBLOCK_IN_CREDS,1,
|
||||
[Whether the krb5_creds struct has a keyblock property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for session in krb5_creds],
|
||||
samba_cv_HAVE_KRB5_SESSION_IN_CREDS,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_creds creds; krb5_keyblock kb; creds.session = kb;],
|
||||
samba_cv_HAVE_KRB5_SESSION_IN_CREDS=yes,
|
||||
samba_cv_HAVE_KRB5_SESSION_IN_CREDS=no)])
|
||||
|
||||
if test x"$samba_cv_HAVE_KRB5_SESSION_IN_CREDS" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_SESSION_IN_CREDS,1,
|
||||
[Whether the krb5_creds struct has a session property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for keyvalue in krb5_keyblock],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keyblock key; key.keyvalue.data = NULL;],
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=yes,
|
||||
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYBLOCK_KEYVALUE,1,
|
||||
[Whether the krb5_keyblock struct has a keyvalue property])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5],
|
||||
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;],
|
||||
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes,
|
||||
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)])
|
||||
AC_CACHE_CHECK([for KEYTYPE_ARCFOUR_56],
|
||||
samba_cv_HAVE_KEYTYPE_ARCFOUR_56,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytype keytype; keytype = KEYTYPE_ARCFOUR_56;],
|
||||
samba_cv_HAVE_KEYTYPE_ARCFOUR_56=yes,
|
||||
samba_cv_HAVE_KEYTYPE_ARCFOUR_56=no)])
|
||||
# Heimdals with KEYTYPE_ARCFOUR but not KEYTYPE_ARCFOUR_56 are broken
|
||||
# w.r.t. arcfour and windows, so we must not enable it here
|
||||
if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes" -a\
|
||||
x"$samba_cv_HAVE_KEYTYPE_ARCFOUR_56" = x"yes"; then
|
||||
AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1,
|
||||
[Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for AP_OPTS_USE_SUBKEY],
|
||||
samba_cv_HAVE_AP_OPTS_USE_SUBKEY,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_flags ap_options; ap_options = AP_OPTS_USE_SUBKEY;],
|
||||
samba_cv_HAVE_AP_OPTS_USE_SUBKEY=yes,
|
||||
samba_cv_HAVE_AP_OPTS_USE_SUBKEY=no)])
|
||||
if test x"$samba_cv_HAVE_AP_OPTS_USE_SUBKEY" = x"yes"; then
|
||||
AC_DEFINE(HAVE_AP_OPTS_USE_SUBKEY,1,
|
||||
[Whether the AP_OPTS_USE_SUBKEY ap option is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for KV5M_KEYTAB],
|
||||
samba_cv_HAVE_KV5M_KEYTAB,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytab_entry entry; entry.magic = KV5M_KEYTAB;],
|
||||
samba_cv_HAVE_KV5M_KEYTAB=yes,
|
||||
samba_cv_HAVE_KV5M_KEYTAB=no)])
|
||||
if test x"$samba_cv_HAVE_KV5M_KEYTAB" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KV5M_KEYTAB,1,
|
||||
[Whether the KV5M_KEYTAB option is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for the krb5_princ_component macro],
|
||||
samba_cv_HAVE_KRB5_PRINC_COMPONENT,[
|
||||
AC_TRY_LINK([#include <krb5.h>],
|
||||
[const krb5_data *pkdata; krb5_context context; krb5_principal principal;
|
||||
pkdata = krb5_princ_component(context, principal, 0);],
|
||||
samba_cv_HAVE_KRB5_PRINC_COMPONENT=yes,
|
||||
samba_cv_HAVE_KRB5_PRINC_COMPONENT=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_PRINC_COMPONENT" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_PRINC_COMPONENT,1,
|
||||
[Whether krb5_princ_component is available])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for key in krb5_keytab_entry],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytab_entry entry; krb5_keyblock e; entry.key = e;],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=yes,
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEY,1,
|
||||
[Whether krb5_keytab_entry has key member])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for keyblock in krb5_keytab_entry],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_keytab_entry entry; entry.keyblock.keytype = 0;],
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=yes,
|
||||
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=no)])
|
||||
if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,1,
|
||||
[Whether krb5_keytab_entry has keyblock member])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for WRFILE: keytab support],
|
||||
samba_cv_HAVE_WRFILE_KEYTAB,[
|
||||
AC_TRY_RUN([
|
||||
#include<krb5.h>
|
||||
main()
|
||||
{
|
||||
krb5_context context;
|
||||
krb5_keytab keytab;
|
||||
krb5_init_context(&context);
|
||||
return krb5_kt_resolve(context, "WRFILE:api", &keytab);
|
||||
}],
|
||||
samba_cv_HAVE_WRFILE_KEYTAB=yes,
|
||||
samba_cv_HAVE_WRFILE_KEYTAB=no)])
|
||||
if test x"$samba_cv_HAVE_WRFILE_KEYTAB" = x"yes"; then
|
||||
AC_DEFINE(HAVE_WRFILE_KEYTAB,1,
|
||||
[Whether the WRFILE:-keytab is supported])
|
||||
fi
|
||||
|
||||
AC_CACHE_CHECK([for krb5_princ_realm returns krb5_realm or krb5_data],
|
||||
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM,[
|
||||
AC_TRY_COMPILE([#include <krb5.h>],
|
||||
[krb5_context context;krb5_principal principal;krb5_realm realm;
|
||||
realm = *krb5_princ_realm(context, principal);],
|
||||
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=yes,
|
||||
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=no)])
|
||||
if test x"$samba_cv_KRB5_PRINC_REALM_RETURNS_REALM" = x"yes"; then
|
||||
AC_DEFINE(KRB5_PRINC_REALM_RETURNS_REALM,1,
|
||||
[Whether krb5_princ_realm returns krb5_realm or krb5_data])
|
||||
fi
|
||||
|
||||
# TODO: check all gssapi headers for this
|
||||
AC_CACHE_CHECK([for GSS_C_DCE_STYLE in gssapi.h],
|
||||
samba_cv_GSS_C_DCE_STYLE,[
|
||||
AC_TRY_COMPILE([#include <gssapi.h>],
|
||||
[int flags = GSS_C_DCE_STYLE;],
|
||||
samba_cv_GSS_C_DCE_STYLE=yes,
|
||||
samba_cv_GSS_C_DCE_STYLE=no)])
|
||||
|
||||
AC_CHECK_FUNC_EXT(gsskrb5_get_initiator_subkey, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(gsskrb5_extract_authz_data_from_sec_context, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(gsskrb5_register_acceptor_identity, $KRB5_LIBS)
|
||||
AC_CHECK_FUNC_EXT(gss_krb5_ccache_name, $KRB5_LIBS)
|
||||
if test x"$ac_cv_lib_ext_krb5_krb5_mk_req_extended" = x"yes"; then
|
||||
AC_DEFINE(HAVE_KRB5,1,[Whether to have KRB5 support])
|
||||
AC_MSG_CHECKING(whether KRB5 support is used)
|
||||
SMB_ENABLE(KRB5,YES)
|
||||
AC_MSG_RESULT(yes)
|
||||
echo "KRB5_CFLAGS: ${KRB5_CFLAGS}"
|
||||
echo "KRB5_CPPFLAGS: ${KRB5_CPPFLAGS}"
|
||||
echo "KRB5_LDFLAGS: ${KRB5_LDFLAGS}"
|
||||
echo "KRB5_LIBS: ${KRB5_LIBS}"
|
||||
else
|
||||
if test x"$with_krb5_support" = x"yes"; then
|
||||
AC_MSG_ERROR(a working krb5 library is needed for KRB5 support)
|
||||
else
|
||||
AC_MSG_WARN(a working krb5 library is needed for KRB5 support)
|
||||
fi
|
||||
KRB5_CFLAGS=""
|
||||
KRB5_CPPFLAGS=""
|
||||
KRB5_LDFLAGS=""
|
||||
KRB5_LIBS=""
|
||||
with_krb5_support=no
|
||||
fi
|
||||
|
||||
# checks if we have access to a libkdc
|
||||
# and can use it for our builtin kdc server_service
|
||||
KDC_CFLAGS=""
|
||||
KDC_CPPFLAGS=""
|
||||
KDC_DLFLAGS=""
|
||||
KDC_LIBS=""
|
||||
AC_CHECK_HEADERS(kdc.h)
|
||||
AC_CHECK_LIB_EXT(kdc, KDC_LIBS, krb5_kdc_default_config)
|
||||
AC_CHECK_LIB_EXT(hdb, KDC_LIBS, hdb_generate_key_set_password)
|
||||
|
||||
AC_MSG_CHECKING(whether libkdc is used)
|
||||
if test x"$ac_cv_header_kdc_h" = x"yes"; then
|
||||
if test x"$ac_cv_lib_ext_kdc_krb5_kdc_default_config" = x"yes"; then
|
||||
if test x"$ac_cv_lib_ext_hdb_hdb_generate_key_set_password" = x"yes"; then
|
||||
SMB_ENABLE(KDC,YES)
|
||||
AC_MSG_RESULT(yes)
|
||||
echo "KDC_LIBS: ${KDC_LIBS}"
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
CFLAGS=$ac_save_CFLAGS
|
||||
CPPFLAGS=$ac_save_CPPFLAGS
|
||||
LDFLAGS=$ac_save_LDFLAGS
|
||||
LIBS="$ac_save_LIBS"
|
||||
|
||||
# as a nasty hack add the krb5 stuff to the global vars,
|
||||
# at some point this should not be needed anymore when the build system
|
||||
# can handle that alone
|
||||
CFLAGS="$CFLAGS $KRB5_CFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
|
||||
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
|
||||
fi
|
||||
|
||||
SMB_EXT_LIB(KRB5,[${KRB5_LIBS}],[${KRB5_CFLAGS}],[${KRB5_CPPFLAGS}],[${KRB5_LDFLAGS}])
|
||||
SMB_EXT_LIB(KDC,[${KDC_LIBS}],[${KDC_CFLAGS}],[${KDC_CPPFLAGS}],[${KDC_LDFLAGS}])
|
||||
@@ -0,0 +1,15 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM KERBEROS
|
||||
[SUBSYSTEM::KERBEROS]
|
||||
PRIVATE_PROTO_HEADER = proto.h
|
||||
OBJ_FILES = kerberos.o \
|
||||
clikrb5.o \
|
||||
kerberos_heimdal.o \
|
||||
kerberos_util.o \
|
||||
kerberos_pac.o \
|
||||
gssapi_parse.o \
|
||||
krb5_init_context.o
|
||||
PUBLIC_DEPENDENCIES = HEIMDAL_KRB5 NDR_KRB5PAC samba-socket LIBCLI_RESOLVE
|
||||
PRIVATE_DEPENDENCIES = ASN1_UTIL HEIMDAL_ROKEN_ADDRINFO auth_sam CREDENTIALS_KRB5
|
||||
# End SUBSYSTEM KERBEROS
|
||||
#################################
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
simple GSSAPI wrappers
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
|
||||
Copyright (C) Luke Howard 2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/util/asn_1.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
/*
|
||||
generate a krb5 GSS-API wrapper packet given a ticket
|
||||
*/
|
||||
DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2])
|
||||
{
|
||||
struct asn1_data data;
|
||||
DATA_BLOB ret = data_blob(NULL,0);
|
||||
|
||||
if (!ticket->data) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(data);
|
||||
|
||||
asn1_push_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&data, GENSEC_OID_KERBEROS5);
|
||||
|
||||
asn1_write(&data, tok_id, 2);
|
||||
asn1_write(&data, ticket->data, ticket->length);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
if (data.has_error) {
|
||||
DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
|
||||
asn1_free(&data);
|
||||
}
|
||||
|
||||
ret = data_blob_talloc(mem_ctx, data.data, data.length);
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
parse a krb5 GSS-API wrapper packet giving a ticket
|
||||
*/
|
||||
BOOL gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2])
|
||||
{
|
||||
BOOL ret;
|
||||
struct asn1_data data;
|
||||
int data_remaining;
|
||||
|
||||
asn1_load(&data, *blob);
|
||||
asn1_start_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&data, GENSEC_OID_KERBEROS5);
|
||||
|
||||
data_remaining = asn1_tag_remaining(&data);
|
||||
|
||||
if (data_remaining < 3) {
|
||||
data.has_error = True;
|
||||
} else {
|
||||
asn1_read(&data, tok_id, 2);
|
||||
data_remaining -= 2;
|
||||
*ticket = data_blob_talloc(mem_ctx, NULL, data_remaining);
|
||||
asn1_read(&data, ticket->data, ticket->length);
|
||||
}
|
||||
|
||||
asn1_end_tag(&data);
|
||||
|
||||
ret = !data.has_error;
|
||||
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
check a GSS-API wrapper packet givin an expected OID
|
||||
*/
|
||||
BOOL gensec_gssapi_check_oid(const DATA_BLOB *blob, const char *oid)
|
||||
{
|
||||
BOOL ret;
|
||||
struct asn1_data data;
|
||||
|
||||
asn1_load(&data, *blob);
|
||||
asn1_start_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&data, GENSEC_OID_KERBEROS5);
|
||||
|
||||
ret = !data.has_error;
|
||||
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
AllowedWorkstationNames and Krb5
|
||||
--------------------------------
|
||||
|
||||
Microsoft uses the clientAddresses *multiple value* field in the krb5
|
||||
protocol (particularly the AS_REQ) to communicate it's netbios name.
|
||||
This is (my guess) to permit the userWorkstations field to work.
|
||||
|
||||
The KDC I imagine checks the netbios address against this value, in
|
||||
the same way that the Samba server does this.
|
||||
|
||||
The checking of this implies a little of the next question:
|
||||
|
||||
Is a DAL the layer we need?
|
||||
---------------------------
|
||||
|
||||
Looking at what we need to pass around, I start to seriously wonder if
|
||||
the DAL is even the right layer - we seem to want to create an account
|
||||
authorization abstraction layer - is this account permitted to login to
|
||||
this computer, at this time?
|
||||
|
||||
This information in AD is much richer than the Heimdal HDB, and it
|
||||
seems to make sense to do AD-specific access control checks in an
|
||||
AD-specific layer, not in the back-end agnostic server.
|
||||
|
||||
Because the DAL only reads in the principalName as the key, it has
|
||||
trouble performing access control decisions on things other than the
|
||||
name.
|
||||
|
||||
I'll be very interested if the DAL really works for eDirectory too.
|
||||
Perhaps all we need to do is add in the same kludges as we have in
|
||||
Samba 3.0 for eDirectory. Hmm...
|
||||
|
||||
That said, the current layer provides us with a very good start, and
|
||||
any redefinition would occour from that basis.
|
||||
|
||||
|
||||
GSSAPI layer requirements
|
||||
-------------------------
|
||||
|
||||
Welcome to the wonderful world of canonicalisation
|
||||
|
||||
The MIT GSSAPI libs do not support kinit returning a different
|
||||
realm to what the client asked for, even just in case differences.
|
||||
|
||||
Heimdal has the same problem, and this applies to the krb5 layer, not
|
||||
just gssapi.
|
||||
|
||||
We need to test if the canonicalisation is controlled by the KDCOption
|
||||
flags, windows always sends the Canonicalize flags
|
||||
|
||||
Old Clients (samba3 and HPUX clients) uses 'selfmade' gssapi/krb5
|
||||
for using it in the CIFS session setup. Because they use krb5_mk_req()
|
||||
they get a chksum field depending on the encryption type, but that's wrong
|
||||
for GSSAPI (see rfc 1964 section 1.1.1). The Cheksum type 8003
|
||||
should be used in the Authenticator of the AP-REQ! That allows the channel bindings,
|
||||
the GCC_C_* req_flags and optional delegation tickets to be passed from the client to the server.
|
||||
Hower windows doesn't seems to care about if the checksum is of the wrong type,
|
||||
for CIFS SessionSetups, it seems that the req_flags are just set to 0.
|
||||
So this can't work for LDAP connections with sign or seal, or for any DCERPC
|
||||
connection.
|
||||
|
||||
So we need to also support old clients!
|
||||
|
||||
Principal Names, long and short names
|
||||
-------------------------------------
|
||||
|
||||
As far as servicePrincipalNames are concerned, these are not
|
||||
canonicalised, except as regards the realm in the reply. That is, the
|
||||
client gets back the principal it asked for, with the realm portion
|
||||
'fixed' to uppercase, long form.
|
||||
|
||||
The short name of the realm seems to be accepted for at least AS_REQ
|
||||
operations, but because the server performs canonicalisation, this
|
||||
causes pain for current client libraries.
|
||||
|
||||
The canonicalisation of names matters not only for the KDC, but also
|
||||
for code that has to deal with keytabs.
|
||||
|
||||
We also need to handle type 10 names (NT-ENTERPRISE), which are a full
|
||||
principal name in the principal field, unrelated to the realm.
|
||||
|
||||
HOST/ Aliases
|
||||
-------------
|
||||
|
||||
There is another post somewhere (ref lost for the moment) that details
|
||||
where in active directory the list of stored aliases for HOST/ is.
|
||||
This should be read, parsed and used to allow any of these requests to
|
||||
use the HOST/ key.
|
||||
|
||||
For example, this is how HTTP/, DNS/ and CIFS/ can use HOST/ without
|
||||
any explicit entry.
|
||||
|
||||
|
||||
Jean-Baptiste.Marchand@hsc.fr reminds me:
|
||||
|
||||
> This is the SPNMappings attribute in Active Directory:
|
||||
|
||||
> http://msdn.microsoft.com/library/en-us/adschema/adschema/a_spnmappings.asp
|
||||
|
||||
We implement this in hdb-ldb.
|
||||
|
||||
Implicit names for Win2000 Accounts
|
||||
-----------------------------------
|
||||
|
||||
Despite not having a DNS name, nor a servicePrincipalName on accounts
|
||||
created by computers running win2000, it appears we are expected to
|
||||
have an implicit mapping from host/computer.full.name and
|
||||
host/computer to it's entry.
|
||||
|
||||
Returned Salt for PreAuthentication
|
||||
-----------------------------------
|
||||
|
||||
When the server replies for pre-authentication, it returns the Salt,
|
||||
which may be in the form of a principalName that is in no way
|
||||
connected with the current names. (ie, even if the userPrincipalName
|
||||
and samAccountName are renamed, the old salt is returned).
|
||||
|
||||
This is probably the kerberos standard salt, kept in the 'Key'. The
|
||||
standard generation rules are found in a Mail from Luke Howard dated
|
||||
10 Nov 2004:
|
||||
|
||||
|
||||
From: Luke Howard <lukeh@padl.com>
|
||||
Message-Id: <200411100231.iAA2VLUW006101@au.padl.com>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=US-ASCII
|
||||
Organization: PADL Software Pty Ltd
|
||||
To: lukeh@padl.com
|
||||
Date: Wed, 10 Nov 2004 13:31:21 +1100
|
||||
Versions: dmail (bsd44) 2.6d/makemail 2.10
|
||||
Cc: huaraz@moeller.plus.com, samba-technical@lists.samba.org
|
||||
Subject: Re: Samba-3.0.7-1.3E Active Directory Issues
|
||||
X-BeenThere: samba-technical@lists.samba.org
|
||||
X-Mailman-Version: 2.1.4
|
||||
Precedence: list
|
||||
Reply-To: lukeh@padl.com
|
||||
|
||||
Did some more testing, it appears the behaviour has another
|
||||
explanation. It appears that the standard Kerberos password salt
|
||||
algorithm is applied in Windows 2003, just that the source principal
|
||||
name is different.
|
||||
|
||||
Here is what I've been able to deduce from creating a bunch of
|
||||
different accounts:
|
||||
|
||||
Type of account Principal for Salting
|
||||
========================================================================
|
||||
Computer Account host/<SAM-Name-Without-$>.realm@REALM
|
||||
User Account Without UPN <SAM-Name>@REALM
|
||||
User Account With UPN <LHS-Of-UPN>@REALM
|
||||
|
||||
Note that if the computer account's SAM account name does not include
|
||||
the trailing '$', then the entire SAM account name is used as input to
|
||||
the salting principal. Setting a UPN for a computer account has no
|
||||
effect.
|
||||
|
||||
It seems to me odd that the RHS of the UPN is not used in the salting
|
||||
principal. For example, a user with UPN foo@mydomain.com in the realm
|
||||
MYREALM.COM would have a salt of MYREALM.COMfoo. Perhaps this is to
|
||||
allow a user's UPN suffix to be changed without changing the salt. And
|
||||
perhaps using the UPN for salting signifies a move away SAM names and
|
||||
their associated constraints.
|
||||
|
||||
For more information on how UPNs relate to the Kerberos protocol,
|
||||
see:
|
||||
|
||||
http://www.ietf.org/proceedings/01dec/I-D/draft-ietf-krb-wg-kerberos-referrals-02.txt
|
||||
|
||||
-- Luke
|
||||
|
||||
--
|
||||
|
||||
|
||||
|
||||
|
||||
Heimdal oddities
|
||||
----------------
|
||||
|
||||
Heimdal is built such that it should be able to serve multiple realms
|
||||
at the same time. This isn't relevant for Samba's use, but it shows
|
||||
up in a lot of generalisations throughout the code.
|
||||
|
||||
Other odd things:
|
||||
- Support for multiple passwords on a client account: we seem to
|
||||
call hdb_next_enctype2key() in the pre-authentication routines to
|
||||
allow multiple passwords per account in krb5. (I think this was
|
||||
intened to allow multiple salts)
|
||||
|
||||
State Machine safety
|
||||
--------------------
|
||||
|
||||
Samba is a giant state machine, and as such have very different
|
||||
requirements to those traditionally expressed for kerberos and GSSAPI
|
||||
libraries.
|
||||
|
||||
Samba requires all of the libraries it uses to be state machine safe in
|
||||
their use of internal data. This does not mean thread safe, and an
|
||||
application could be thread safe, but not state machine safe (if it
|
||||
instead used thread-local variables).
|
||||
|
||||
So, what does it mean for a library to be state machine safe? This is
|
||||
mostly a question of context, and how the library manages whatever
|
||||
internal state machines it has. If the library uses a context
|
||||
variable, passed in by the caller, which contains all the information
|
||||
about the current state of the library, then it is safe. An example
|
||||
of this state is the sequence number and session keys for an ongoing
|
||||
encrypted session).
|
||||
|
||||
The other issue affecting state machines is 'blocking' (waiting for a
|
||||
read on a network socket).
|
||||
|
||||
Heimdal has this 'state machine safety' in parts, and we have modified
|
||||
the lorikeet branch to improve this behviour, when using a new,
|
||||
non-standard API.
|
||||
|
||||
Heimdal uses a per-context variable for the 'krb5_auth_context', which
|
||||
controls the ongoing encrypted connection, but does use global
|
||||
variables for the ubiquitous krb5_context parameter.
|
||||
|
||||
The modification that has added most to 'state machine safety' of
|
||||
GSSAPI is the addition of the gsskrb5_acquire_creds function. This
|
||||
allows the caller to specify a keytab and ccache, for use by the
|
||||
GSSAPI code. Therefore there is no need to use global variables to
|
||||
communicate this information.
|
||||
|
||||
At a more theoritical level (simply counting static and global
|
||||
variables) Heimdal is not state machine safe for the GSSAPI layer.
|
||||
The Krb5 layer alone is much closer, as far as I can tell, blocking
|
||||
excepted. .
|
||||
|
||||
To deal with blocking, we could have a fork()ed child per context,
|
||||
using the 'GSSAPI export context' function to transfer
|
||||
the GSSAPI state back into the main code for the wrap()/unwrap() part
|
||||
of the operation. This will still hit issues of static storage (one
|
||||
gss_krb5_context per process, and multiple GSSAPI encrypted sessions
|
||||
at a time) but these may not matter in practice.
|
||||
|
||||
In the short-term, we deal with blocking by taking over the network
|
||||
send() and recv() functions, therefore making them 'semi-async'. This
|
||||
doens't apply to DNS yet.
|
||||
|
||||
GSSAPI and Kerberos extensions
|
||||
------------------------------
|
||||
|
||||
This is a general list of the other extensions we have made to / need from
|
||||
the kerberos libraries
|
||||
|
||||
- DCE_STYLE
|
||||
|
||||
- gsskrb5_get_initiator_subkey() (return the exact key that Samba3
|
||||
has always asked for. gsskrb5_get_subkey() might do what we need
|
||||
anyway)
|
||||
|
||||
- gsskrb5_acquire_creds() (takes keytab and/or ccache as input
|
||||
parameters, see keytab and state machine discussion)
|
||||
|
||||
- gss_krb5_copy_service_keyblock() (get the key used to actually
|
||||
encrypt the ticket to the server, because the same key is used for
|
||||
the PAC validation).
|
||||
- gsskrb5_extract_authtime_from_sec_context (get authtime from
|
||||
kerberos ticket)
|
||||
- gsskrb5_extract_authz_data_from_sec_context (get authdata from
|
||||
ticket, ie the PAC. Must unwrap the data if in an AD-IFRELEVENT)
|
||||
- gsskrb5_wrap_size (find out how big the wrapped packet will be,
|
||||
given input length).
|
||||
|
||||
Keytab requirements
|
||||
-------------------
|
||||
|
||||
Because windows machine account handling is very different to the
|
||||
tranditional 'MIT' keytab operation. This starts when we look at the
|
||||
basis of the secrets handling:
|
||||
|
||||
Traditional 'MIT' behaviour is to use a keytab, continaing salted key
|
||||
data, extracted from the KDC. (In this modal, there is no 'service
|
||||
password', instead the keys are often simply application of random
|
||||
bytes). Heimdal also implements this behaviour.
|
||||
|
||||
The windows modal is very different - instead of sharing a keytab with
|
||||
each member server, a password is stored for the whole machine. The
|
||||
password is set with non-kerberos mechanisms (particularly SAMR, a
|
||||
DCE-RPC service) and when interacting on a kerberos basis, the
|
||||
password is salted by the client. (That is, no salt infromation
|
||||
appears to be convayed from the KDC to the member).
|
||||
|
||||
In dealing with this modal, we leverage both the traditional file
|
||||
keytab and in-MEMORY keytabs.
|
||||
|
||||
When dealing with a windows KDC, the behaviour regarding case
|
||||
sensitivity and canonacolisation must be accomidated. This means that
|
||||
an incoming request to a member server may have a wide variety of
|
||||
service principal names. These include:
|
||||
|
||||
machine$@REALM (samba clients)
|
||||
HOST/foo.bar@realm (win2k clients)
|
||||
HOST/foo@realm (win2k clients, using netbios)
|
||||
cifs/foo.bar@realm (winxp clients)
|
||||
cifs/foo@realm (winxp clients, using netbios)
|
||||
|
||||
as well as all case variations on the above.
|
||||
|
||||
Because that all got 'too hard' to put into a keytab in the
|
||||
traditional way (with the client to specify the name), we either
|
||||
pre-compute the keys into a traditional keytab or make an in-MEMORY
|
||||
keytab at run time. In both cases we specifiy the principal name to
|
||||
GSSAPI, which avoids the need to store duplicate principals.
|
||||
|
||||
We use a 'private' keytab in our private dir, referenced from the
|
||||
secrets.ldb by default.
|
||||
|
||||
Extra Heimdal functions used
|
||||
----------------------------
|
||||
(an attempt to list some of the Heimdal-specific functions I know we use)
|
||||
|
||||
krb5_free_keyblock_contents()
|
||||
|
||||
also a raft of prinicpal manipulation functions:
|
||||
|
||||
Prncipal Manipulation
|
||||
---------------------
|
||||
|
||||
Samba makes extensive use of the principal manipulation functions in
|
||||
Heimdal, including the known structure behind krb_principal and
|
||||
krb5_realm (a char *).
|
||||
|
||||
Authz data extraction
|
||||
---------------------
|
||||
|
||||
We use krb5_ticket_get_authorization_data_type(), and expect it to
|
||||
return the correct authz data, even if wrapped in an AD-IFRELEVENT container.
|
||||
|
||||
|
||||
KDC/hdb Extensions
|
||||
--------------
|
||||
|
||||
We have modified Heimdal's 'hdb' interface to specify the 'type' of
|
||||
Principal being requested. This allows us to correctly behave with
|
||||
the different 'classes' of Principal name.
|
||||
|
||||
We currently define 2 classes:
|
||||
- client (kinit)
|
||||
- server (tgt)
|
||||
|
||||
I also now specify the kerberos principal as an explict parameter, not
|
||||
an in/out value on the entry itself.
|
||||
|
||||
Inside hdb-ldb, we add krbtgt as a special class of principal, because
|
||||
of particular special-case backend requirements.
|
||||
|
||||
Callbacks:
|
||||
In addition, I have added a new interface hdb_fetch_ex(), which
|
||||
returns a structure including callbacks, which provide the hook for
|
||||
the PAC, as well as a callback into the main access control routines.
|
||||
|
||||
A new callback should be added to increment the bad password counter
|
||||
on failure.
|
||||
|
||||
Another possability for a callback is to obtain the keys. This would
|
||||
allow the plaintext password to only be hashed into the encryption
|
||||
types we need. This idea from the eDirectory/MIT DAL work.
|
||||
|
||||
This probably should be combined with storing the hashed passwords in
|
||||
the supplementalCredentials attribute. If combined with a kvno
|
||||
parameter, this could also allow changing of the krbtgt password
|
||||
(valuable for security).
|
||||
|
||||
libkdc
|
||||
------
|
||||
|
||||
Samba4 needs to be built as a single binary (design requirement), and
|
||||
this should include the KDC. Samba also (and perhaps more
|
||||
importantly) needs to control the configuration environment of the
|
||||
KDC.
|
||||
|
||||
The interface we have defined for libkdc allow for packet injection
|
||||
into the post-socket layer, with a defined krb5_context and
|
||||
kdb5_kdc_configuration structure. These effectively redirect the
|
||||
kerberos warnings, logging and database calls as we require.
|
||||
|
||||
Using our socket lib
|
||||
--------------------
|
||||
|
||||
An important detail in the use of libkdc is that we use our own socket
|
||||
lib. This allows the KDC code to be as portable as the rest of samba
|
||||
(this cuts both ways), but far more importantly it ensures a
|
||||
consistancy in the handling of requests, binding to sockets etc.
|
||||
|
||||
To handle TCP, we use of our socket layer in much the same way as
|
||||
we deal with TCP for CIFS. Tridge created a generic packet handling
|
||||
layer for this.
|
||||
|
||||
For the client, we likewise must take over the socket functions, so
|
||||
that our single thread smbd will not lock up talking to itself. (We
|
||||
allow processing while waiting for packets in our socket routines).
|
||||
|
||||
Kerberos logging support
|
||||
------------------------
|
||||
|
||||
Samba now (optionally in the main code, required for the KDC) uses the
|
||||
krb5_log_facility from Heimdal. This allows us to redirect the
|
||||
warnings and status from the KDC (and client/server kerberos code) to
|
||||
Samba's DEBUG() system.
|
||||
|
||||
Similarly important is the Heimdal-specific krb5_get_error_string()
|
||||
function, which does a lot to reduce the 'administrator pain' level,
|
||||
by providing specific, english text-string error messages instead of
|
||||
just error code translations.
|
||||
|
||||
|
||||
Short name rules
|
||||
----------------
|
||||
|
||||
Samba is highly likely to be misconfigured, in many weird and
|
||||
interesting ways. As such, we have a patch for Heimdal that avoids
|
||||
DNS lookups on names without a . in them. This should avoid some
|
||||
delay and root server load.
|
||||
|
||||
PAC Correctness
|
||||
---------------
|
||||
|
||||
We now put the PAC into the TGT, not just the service ticket.
|
||||
|
||||
Forwarded tickets
|
||||
-----------------
|
||||
|
||||
We extract forwarded tickets from the GSSAPI layer, and put
|
||||
them into the credentials. We can then use them for proxy work.
|
||||
|
||||
|
||||
Kerberos TODO
|
||||
=============
|
||||
|
||||
(Feel free to contribute to any of these tasks, or ask
|
||||
abartlet@samba.org about them).
|
||||
|
||||
Lockout Control
|
||||
--------------
|
||||
|
||||
We need to get (either if PADL publishes their patch, or write our
|
||||
own) access control hooks in the Heimdal KDC. We need to lockout
|
||||
accounts, and perform other controls.
|
||||
|
||||
Gssmonger
|
||||
---------
|
||||
|
||||
Microsoft has released a testsuite called gssmonger, which tests
|
||||
interop. We should compile it against lorikeet-heimdal, MIT and see
|
||||
if we can build a 'Samba4' server for it.
|
||||
|
||||
Kpasswd server
|
||||
--------------
|
||||
|
||||
I have a partial kpasswd server which needs finishing, and a we need a
|
||||
client testsuite written, either via the krb5 API or directly against
|
||||
GENSEC and the ASN.1 routines.
|
||||
|
||||
Currently it only works for Heimdal, not MIT clients. This may be due
|
||||
to call ordering constraints.
|
||||
|
||||
|
||||
Correct TCP support
|
||||
-------------------
|
||||
|
||||
Our current TCP support does not send back 'too large' error messages
|
||||
if the high bit is set. This is needed for a proposed extension
|
||||
mechanism, but is likewise unsupported in both current Heimdal and MIT.
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
kerberos utility library
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Remus Koos 2001
|
||||
Copyright (C) Nalin Dahyabhai 2004.
|
||||
Copyright (C) Jeremy Allison 2004.
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "roken.h"
|
||||
|
||||
#ifdef HAVE_KRB5
|
||||
|
||||
/*
|
||||
simulate a kinit, putting the tgt in the given credentials cache.
|
||||
Orignally by remus@snapserver.com
|
||||
|
||||
This version is built to use a keyblock, rather than needing the
|
||||
original password.
|
||||
*/
|
||||
int kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, krb5_keyblock *keyblock,
|
||||
time_t *expire_time, time_t *kdc_time)
|
||||
{
|
||||
krb5_error_code code = 0;
|
||||
krb5_creds my_creds;
|
||||
krb5_get_init_creds_opt options;
|
||||
|
||||
krb5_get_init_creds_opt_init(&options);
|
||||
|
||||
krb5_get_init_creds_opt_set_default_flags(ctx, NULL, NULL, &options);
|
||||
|
||||
if ((code = krb5_get_init_creds_keyblock(ctx, &my_creds, principal, keyblock,
|
||||
0, NULL, &options))) {
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_initialize(ctx, cc, principal))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if (expire_time) {
|
||||
*expire_time = (time_t) my_creds.times.endtime;
|
||||
}
|
||||
|
||||
if (kdc_time) {
|
||||
*kdc_time = (time_t) my_creds.times.starttime;
|
||||
}
|
||||
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
simulate a kinit, putting the tgt in the given credentials cache.
|
||||
Orignally by remus@snapserver.com
|
||||
*/
|
||||
int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, const char *password,
|
||||
time_t *expire_time, time_t *kdc_time)
|
||||
{
|
||||
krb5_error_code code = 0;
|
||||
krb5_creds my_creds;
|
||||
krb5_get_init_creds_opt options;
|
||||
|
||||
krb5_get_init_creds_opt_init(&options);
|
||||
|
||||
krb5_get_init_creds_opt_set_default_flags(ctx, NULL, NULL, &options);
|
||||
|
||||
if ((code = krb5_get_init_creds_password(ctx, &my_creds, principal, password,
|
||||
NULL,
|
||||
NULL, 0, NULL, &options))) {
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_initialize(ctx, cc, principal))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
return code;
|
||||
}
|
||||
|
||||
if (expire_time) {
|
||||
*expire_time = (time_t) my_creds.times.endtime;
|
||||
}
|
||||
|
||||
if (kdc_time) {
|
||||
*kdc_time = (time_t) my_creds.times.starttime;
|
||||
}
|
||||
|
||||
krb5_free_cred_contents(ctx, &my_creds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5 routines for active directory
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if defined(HAVE_KRB5)
|
||||
|
||||
#include "auth/kerberos/krb5_init_context.h"
|
||||
#include "librpc/gen_ndr/krb5pac.h"
|
||||
|
||||
struct auth_serversupplied_info;
|
||||
struct cli_credentials;
|
||||
|
||||
struct ccache_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_ccache ccache;
|
||||
};
|
||||
|
||||
struct keytab_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_keytab keytab;
|
||||
};
|
||||
|
||||
/* not really ASN.1, but RFC 1964 */
|
||||
#define TOK_ID_KRB_AP_REQ ((const uint8_t *)"\x01\x00")
|
||||
#define TOK_ID_KRB_AP_REP ((const uint8_t *)"\x02\x00")
|
||||
#define TOK_ID_KRB_ERROR ((const uint8_t *)"\x03\x00")
|
||||
#define TOK_ID_GSS_GETMIC ((const uint8_t *)"\x01\x01")
|
||||
#define TOK_ID_GSS_WRAP ((const uint8_t *)"\x02\x01")
|
||||
|
||||
#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
|
||||
#define KRB5_KEY_TYPE(k) ((k)->keytype)
|
||||
#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length)
|
||||
#define KRB5_KEY_DATA(k) ((k)->keyvalue.data)
|
||||
#else
|
||||
#define KRB5_KEY_TYPE(k) ((k)->enctype)
|
||||
#define KRB5_KEY_LENGTH(k) ((k)->length)
|
||||
#define KRB5_KEY_DATA(k) ((k)->contents)
|
||||
#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
|
||||
|
||||
#ifndef HAVE_KRB5_SET_REAL_TIME
|
||||
krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES
|
||||
krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
|
||||
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_KRB5_FREE_UNPARSED_NAME
|
||||
void krb5_free_unparsed_name(krb5_context ctx, char *val);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
|
||||
const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
|
||||
#endif
|
||||
|
||||
/* Samba wrapper function for krb5 functionality. */
|
||||
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
|
||||
int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
|
||||
int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
|
||||
krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
|
||||
krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
|
||||
void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes);
|
||||
BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote);
|
||||
krb5_error_code ads_krb5_mk_req(krb5_context context,
|
||||
krb5_auth_context *auth_context,
|
||||
const krb5_flags ap_req_options,
|
||||
const char *principal,
|
||||
krb5_ccache ccache,
|
||||
krb5_data *outbuf);
|
||||
BOOL get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt);
|
||||
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_auth_context *auth_context,
|
||||
struct cli_credentials *machine_account,
|
||||
const char *service,
|
||||
const DATA_BLOB *enc_ticket,
|
||||
krb5_ticket **tkt,
|
||||
DATA_BLOB *ap_rep,
|
||||
krb5_keyblock **keyblock);
|
||||
int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, const char *password,
|
||||
time_t *expire_time, time_t *kdc_time);
|
||||
int kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
|
||||
krb5_principal principal, krb5_keyblock *keyblock,
|
||||
time_t *expire_time, time_t *kdc_time);
|
||||
krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
|
||||
krb5_principal host_princ,
|
||||
int enctype);
|
||||
void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype);
|
||||
BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2);
|
||||
void kerberos_free_data_contents(krb5_context context, krb5_data *pdata);
|
||||
krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry);
|
||||
char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx);
|
||||
krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_ccache ccache);
|
||||
krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *princ);
|
||||
NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA **pac_data_out,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret);
|
||||
NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_LOGON_INFO **logon_info,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret);
|
||||
krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA *pac_data,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
DATA_BLOB *pac);
|
||||
krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
DATA_BLOB *pac);
|
||||
|
||||
#include "auth/kerberos/proto.h"
|
||||
|
||||
#endif /* HAVE_KRB5 */
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
|
||||
* (Royal Institute of Technology, Stockholm, Sweden).
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of the Institute nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* This file for code taken from the Heimdal code, to preserve licence */
|
||||
/* Modified by Andrew Bartlett <abartlet@samba.org> */
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
|
||||
/* Taken from accept_sec_context.c,v 1.65 */
|
||||
krb5_error_code smb_rd_req_return_stuff(krb5_context context,
|
||||
krb5_auth_context *auth_context,
|
||||
const krb5_data *inbuf,
|
||||
krb5_keytab keytab,
|
||||
krb5_principal acceptor_principal,
|
||||
krb5_data *outbuf,
|
||||
krb5_ticket **ticket,
|
||||
krb5_keyblock **keyblock)
|
||||
{
|
||||
krb5_rd_req_in_ctx in = NULL;
|
||||
krb5_rd_req_out_ctx out = NULL;
|
||||
krb5_error_code kret;
|
||||
|
||||
*keyblock = NULL;
|
||||
*ticket = NULL;
|
||||
outbuf->length = 0;
|
||||
outbuf->data = NULL;
|
||||
|
||||
kret = krb5_rd_req_in_ctx_alloc(context, &in);
|
||||
if (kret == 0)
|
||||
kret = krb5_rd_req_in_set_keytab(context, in, keytab);
|
||||
if (kret) {
|
||||
if (in)
|
||||
krb5_rd_req_in_ctx_free(context, in);
|
||||
return kret;
|
||||
}
|
||||
|
||||
kret = krb5_rd_req_ctx(context,
|
||||
auth_context,
|
||||
inbuf,
|
||||
acceptor_principal,
|
||||
in, &out);
|
||||
krb5_rd_req_in_ctx_free(context, in);
|
||||
if (kret) {
|
||||
return kret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to remember some data on the context_handle.
|
||||
*/
|
||||
kret = krb5_rd_req_out_get_ticket(context, out,
|
||||
ticket);
|
||||
if (kret == 0) {
|
||||
kret = krb5_rd_req_out_get_keyblock(context, out,
|
||||
keyblock);
|
||||
}
|
||||
krb5_rd_req_out_ctx_free(context, out);
|
||||
|
||||
if (kret == 0) {
|
||||
kret = krb5_mk_rep(context, *auth_context, outbuf);
|
||||
}
|
||||
|
||||
if (kret) {
|
||||
krb5_free_ticket(context, *ticket);
|
||||
krb5_free_keyblock(context, *keyblock);
|
||||
krb5_data_free(outbuf);
|
||||
}
|
||||
|
||||
return kret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Create and parse the krb5 PAC
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Luke Howard 2002-2003
|
||||
Copyright (C) Stefan Metzmacher 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "librpc/gen_ndr/ndr_krb5pac.h"
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "auth/auth_sam.h"
|
||||
|
||||
static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB pac_data,
|
||||
struct PAC_SIGNATURE_DATA *sig,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *keyblock)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_crypto crypto;
|
||||
Checksum cksum;
|
||||
|
||||
cksum.cksumtype = (CKSUMTYPE)sig->type;
|
||||
cksum.checksum.length = sig->signature.length;
|
||||
cksum.checksum.data = sig->signature.data;
|
||||
|
||||
ret = krb5_crypto_init(context,
|
||||
keyblock,
|
||||
0,
|
||||
&crypto);
|
||||
if (ret) {
|
||||
DEBUG(0,("krb5_crypto_init() failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
return ret;
|
||||
}
|
||||
ret = krb5_verify_checksum(context,
|
||||
crypto,
|
||||
KRB5_KU_OTHER_CKSUM,
|
||||
pac_data.data,
|
||||
pac_data.length,
|
||||
&cksum);
|
||||
krb5_crypto_destroy(context, crypto);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA **pac_data_out,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
NTSTATUS status;
|
||||
struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
|
||||
struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
|
||||
struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
|
||||
struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
|
||||
struct PAC_LOGON_INFO *logon_info = NULL;
|
||||
struct PAC_LOGON_NAME *logon_name = NULL;
|
||||
struct PAC_DATA *pac_data;
|
||||
struct PAC_DATA_RAW *pac_data_raw;
|
||||
|
||||
DATA_BLOB *srv_sig_blob = NULL;
|
||||
DATA_BLOB *kdc_sig_blob = NULL;
|
||||
|
||||
DATA_BLOB modified_pac_blob;
|
||||
NTTIME tgs_authtime_nttime;
|
||||
krb5_principal client_principal_pac;
|
||||
int i;
|
||||
|
||||
krb5_clear_error_string(context);
|
||||
|
||||
if (k5ret) {
|
||||
*k5ret = KRB5_PARSE_MALFORMED;
|
||||
}
|
||||
|
||||
pac_data = talloc(mem_ctx, struct PAC_DATA);
|
||||
pac_data_raw = talloc(mem_ctx, struct PAC_DATA_RAW);
|
||||
kdc_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
|
||||
srv_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
|
||||
if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
|
||||
if (k5ret) {
|
||||
*k5ret = ENOMEM;
|
||||
}
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = ndr_pull_struct_blob(&blob, pac_data, pac_data,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the PAC\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
if (pac_data->num_buffers < 4) {
|
||||
/* we need logon_ingo, service_key and kdc_key */
|
||||
DEBUG(0,("less than 4 PAC buffers\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
status = ndr_pull_struct_blob(&blob, pac_data_raw, pac_data_raw,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the PAC\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
if (pac_data_raw->num_buffers < 4) {
|
||||
/* we need logon_ingo, service_key and kdc_key */
|
||||
DEBUG(0,("less than 4 PAC buffers\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (pac_data->num_buffers != pac_data_raw->num_buffers) {
|
||||
/* we need logon_ingo, service_key and kdc_key */
|
||||
DEBUG(0,("misparse! PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n",
|
||||
pac_data->num_buffers, pac_data_raw->num_buffers));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) {
|
||||
DEBUG(0,("misparse! PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n",
|
||||
i, pac_data->buffers[i].type, pac_data->buffers[i].type));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
switch (pac_data->buffers[i].type) {
|
||||
case PAC_TYPE_LOGON_INFO:
|
||||
if (!pac_data->buffers[i].info) {
|
||||
break;
|
||||
}
|
||||
logon_info = pac_data->buffers[i].info->logon_info.info;
|
||||
break;
|
||||
case PAC_TYPE_SRV_CHECKSUM:
|
||||
if (!pac_data->buffers[i].info) {
|
||||
break;
|
||||
}
|
||||
srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum;
|
||||
srv_sig_blob = &pac_data_raw->buffers[i].info->remaining;
|
||||
break;
|
||||
case PAC_TYPE_KDC_CHECKSUM:
|
||||
if (!pac_data->buffers[i].info) {
|
||||
break;
|
||||
}
|
||||
kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum;
|
||||
kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining;
|
||||
break;
|
||||
case PAC_TYPE_LOGON_NAME:
|
||||
logon_name = &pac_data->buffers[i].info->logon_name;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!logon_info) {
|
||||
DEBUG(0,("PAC no logon_info\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!logon_name) {
|
||||
DEBUG(0,("PAC no logon_name\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!srv_sig_ptr || !srv_sig_blob) {
|
||||
DEBUG(0,("PAC no srv_key\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!kdc_sig_ptr || !kdc_sig_blob) {
|
||||
DEBUG(0,("PAC no kdc_key\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Find and zero out the signatures, as required by the signing algorithm */
|
||||
|
||||
/* We find the data blobs above, now we parse them to get at the exact portion we should zero */
|
||||
status = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe, kdc_sig_wipe,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the KDC signature\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
status = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe, srv_sig_wipe,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't parse the SRV signature\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Now zero the decoded structure */
|
||||
memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length);
|
||||
memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length);
|
||||
|
||||
/* and reencode, back into the same place it came from */
|
||||
status = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw, kdc_sig_wipe,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't repack the KDC signature\n"));
|
||||
return status;
|
||||
}
|
||||
status = ndr_push_struct_blob(srv_sig_blob, pac_data_raw, srv_sig_wipe,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't repack the SRV signature\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* push out the whole structure, but now with zero'ed signatures */
|
||||
status = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw, pac_data_raw,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("can't repack the RAW PAC\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* verify by service_key */
|
||||
ret = check_pac_checksum(mem_ctx,
|
||||
modified_pac_blob, srv_sig_ptr,
|
||||
context,
|
||||
service_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
if (k5ret) {
|
||||
*k5ret = ret;
|
||||
}
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (krbtgt_keyblock) {
|
||||
ret = check_pac_checksum(mem_ctx,
|
||||
srv_sig_ptr->signature, kdc_sig_ptr,
|
||||
context, krbtgt_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
if (k5ret) {
|
||||
*k5ret = ret;
|
||||
}
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert to NT time, so as not to loose accuracy in comparison */
|
||||
unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
|
||||
|
||||
if (tgs_authtime_nttime != logon_name->logon_time) {
|
||||
DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n"));
|
||||
DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time)));
|
||||
DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime)));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
ret = krb5_parse_name_flags(context, logon_name->account_name, KRB5_PRINCIPAL_PARSE_NO_REALM,
|
||||
&client_principal_pac);
|
||||
if (ret) {
|
||||
DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n",
|
||||
logon_name->account_name,
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
if (k5ret) {
|
||||
*k5ret = ret;
|
||||
}
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) {
|
||||
DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n",
|
||||
logon_name->account_name));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (strcasecmp(logon_info->info3.base.account_name.string,
|
||||
"Administrator")== 0) {
|
||||
file_save("tmp_pac_data-admin.dat",blob.data,blob.length);
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG(3,("Found account name from PAC: %s [%s]\n",
|
||||
logon_info->info3.base.account_name.string,
|
||||
logon_info->info3.base.full_name.string));
|
||||
*pac_data_out = pac_data;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_LOGON_INFO **logon_info,
|
||||
DATA_BLOB blob,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_const_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
krb5_error_code *k5ret)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct PAC_DATA *pac_data;
|
||||
int i;
|
||||
nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
|
||||
blob,
|
||||
context,
|
||||
krbtgt_keyblock,
|
||||
service_keyblock,
|
||||
client_principal,
|
||||
tgs_authtime,
|
||||
k5ret);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
*logon_info = NULL;
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
|
||||
continue;
|
||||
}
|
||||
*logon_info = pac_data->buffers[i].info->logon_info.info;
|
||||
}
|
||||
if (!*logon_info) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *pac_data,
|
||||
struct PAC_SIGNATURE_DATA *sig,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *keyblock)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_crypto crypto;
|
||||
Checksum cksum;
|
||||
|
||||
|
||||
ret = krb5_crypto_init(context,
|
||||
keyblock,
|
||||
0,
|
||||
&crypto);
|
||||
if (ret) {
|
||||
DEBUG(0,("krb5_crypto_init() failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
return ret;
|
||||
}
|
||||
ret = krb5_create_checksum(context,
|
||||
crypto,
|
||||
KRB5_KU_OTHER_CKSUM,
|
||||
0,
|
||||
pac_data->data,
|
||||
pac_data->length,
|
||||
&cksum);
|
||||
if (ret) {
|
||||
DEBUG(2, ("PAC Verification failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
}
|
||||
|
||||
krb5_crypto_destroy(context, crypto);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
sig->type = cksum.cksumtype;
|
||||
sig->signature = data_blob_talloc(mem_ctx, cksum.checksum.data, cksum.checksum.length);
|
||||
free_Checksum(&cksum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
|
||||
struct PAC_DATA *pac_data,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
DATA_BLOB *pac)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
krb5_error_code ret;
|
||||
DATA_BLOB zero_blob = data_blob(NULL, 0);
|
||||
DATA_BLOB tmp_blob = data_blob(NULL, 0);
|
||||
struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
|
||||
struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
|
||||
int i;
|
||||
|
||||
/* First, just get the keytypes filled in (and lengths right, eventually) */
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
|
||||
continue;
|
||||
}
|
||||
kdc_checksum = &pac_data->buffers[i].info->kdc_cksum,
|
||||
ret = make_pac_checksum(mem_ctx, &zero_blob,
|
||||
kdc_checksum,
|
||||
context, krbtgt_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i < pac_data->num_buffers; i++) {
|
||||
if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
|
||||
continue;
|
||||
}
|
||||
srv_checksum = &pac_data->buffers[i].info->srv_cksum;
|
||||
ret = make_pac_checksum(mem_ctx, &zero_blob,
|
||||
srv_checksum,
|
||||
context, service_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(2, ("making service PAC checksum failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kdc_checksum) {
|
||||
DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
|
||||
return EINVAL;
|
||||
}
|
||||
if (!srv_checksum) {
|
||||
DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* But wipe out the actual signatures */
|
||||
memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
|
||||
memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
|
||||
|
||||
nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_DATA);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(pac_data);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Then sign the result of the previous push, where the sig was zero'ed out */
|
||||
ret = make_pac_checksum(mem_ctx, &tmp_blob, srv_checksum,
|
||||
context, service_keyblock);
|
||||
|
||||
/* Then sign Server checksum */
|
||||
ret = make_pac_checksum(mem_ctx, &srv_checksum->signature, kdc_checksum, context, krbtgt_keyblock);
|
||||
if (ret) {
|
||||
DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
|
||||
smb_get_krb5_error_message(context, ret, mem_ctx)));
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* And push it out again, this time to the world. This relies on determanistic pointer values */
|
||||
nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
|
||||
(ndr_push_flags_fn_t)ndr_push_PAC_DATA);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(pac_data);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
*pac = tmp_blob;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
|
||||
struct auth_serversupplied_info *server_info,
|
||||
krb5_context context,
|
||||
const krb5_keyblock *krbtgt_keyblock,
|
||||
const krb5_keyblock *service_keyblock,
|
||||
krb5_principal client_principal,
|
||||
time_t tgs_authtime,
|
||||
DATA_BLOB *pac)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
krb5_error_code ret;
|
||||
struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
|
||||
struct netr_SamInfo3 *sam3;
|
||||
union PAC_INFO *u_LOGON_INFO;
|
||||
struct PAC_LOGON_INFO *LOGON_INFO;
|
||||
union PAC_INFO *u_LOGON_NAME;
|
||||
struct PAC_LOGON_NAME *LOGON_NAME;
|
||||
union PAC_INFO *u_KDC_CHECKSUM;
|
||||
union PAC_INFO *u_SRV_CHECKSUM;
|
||||
|
||||
char *name;
|
||||
|
||||
enum {
|
||||
PAC_BUF_LOGON_INFO = 0,
|
||||
PAC_BUF_LOGON_NAME = 1,
|
||||
PAC_BUF_SRV_CHECKSUM = 2,
|
||||
PAC_BUF_KDC_CHECKSUM = 3,
|
||||
PAC_BUF_NUM_BUFFERS = 4
|
||||
};
|
||||
|
||||
if (!pac_data) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
|
||||
pac_data->version = 0;
|
||||
|
||||
pac_data->buffers = talloc_array(pac_data,
|
||||
struct PAC_BUFFER,
|
||||
pac_data->num_buffers);
|
||||
if (!pac_data->buffers) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* LOGON_INFO */
|
||||
u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_LOGON_INFO) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
|
||||
pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
|
||||
|
||||
/* LOGON_NAME */
|
||||
u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_LOGON_NAME) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
|
||||
pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
|
||||
LOGON_NAME = &u_LOGON_NAME->logon_name;
|
||||
|
||||
/* SRV_CHECKSUM */
|
||||
u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_SRV_CHECKSUM) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
|
||||
pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
|
||||
|
||||
/* KDC_CHECKSUM */
|
||||
u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
|
||||
if (!u_KDC_CHECKSUM) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
|
||||
pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
|
||||
|
||||
/* now the real work begins... */
|
||||
|
||||
LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
|
||||
if (!LOGON_INFO) {
|
||||
talloc_free(pac_data);
|
||||
return ENOMEM;
|
||||
}
|
||||
nt_status = auth_convert_server_info_saminfo3(LOGON_INFO, server_info, &sam3);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
|
||||
talloc_free(pac_data);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
u_LOGON_INFO->logon_info.info = LOGON_INFO;
|
||||
LOGON_INFO->info3 = *sam3;
|
||||
|
||||
ret = krb5_unparse_name_flags(context, client_principal,
|
||||
KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
LOGON_NAME->account_name = talloc_strdup(LOGON_NAME, name);
|
||||
free(name);
|
||||
/*
|
||||
this logon_time field is absolutely critical. This is what
|
||||
caused all our PAC troubles :-)
|
||||
*/
|
||||
unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
|
||||
|
||||
ret = kerberos_encode_pac(mem_ctx,
|
||||
pac_data,
|
||||
context,
|
||||
krbtgt_keyblock,
|
||||
service_keyblock,
|
||||
pac);
|
||||
talloc_free(pac_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Kerberos utility functions for GENSEC
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/credentials/credentials_krb5.h"
|
||||
|
||||
struct principal_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_principal principal;
|
||||
};
|
||||
|
||||
static int free_principal(struct principal_container *pc)
|
||||
{
|
||||
/* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
|
||||
krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *salt_princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *machine_username;
|
||||
char *salt_body;
|
||||
char *lower_realm;
|
||||
const char *salt_principal;
|
||||
struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
salt_principal = cli_credentials_get_salt_principal(machine_account);
|
||||
if (salt_principal) {
|
||||
ret = krb5_parse_name(smb_krb5_context->krb5_context, salt_principal, salt_princ);
|
||||
} else {
|
||||
machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
|
||||
|
||||
if (!machine_username) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (machine_username[strlen(machine_username)-1] == '$') {
|
||||
machine_username[strlen(machine_username)-1] = '\0';
|
||||
}
|
||||
lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
|
||||
if (!lower_realm) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username,
|
||||
lower_realm);
|
||||
if (!salt_body) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ,
|
||||
cli_credentials_get_realm(machine_account),
|
||||
"host", salt_body, NULL);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
/* This song-and-dance effectivly puts the principal
|
||||
* into talloc, so we can't loose it. */
|
||||
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
|
||||
mem_ctx->principal = *salt_princ;
|
||||
talloc_set_destructor(mem_ctx, free_principal);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Obtain the principal set on this context. Requires a
|
||||
* smb_krb5_context because we are doing krb5 principal parsing with
|
||||
* the library routines. The returned princ is placed in the talloc
|
||||
* system by means of a destructor (do *not* free). */
|
||||
|
||||
krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_principal *princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *princ_string;
|
||||
struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
princ_string = cli_credentials_get_principal(credentials, mem_ctx);
|
||||
|
||||
/* A NULL here has meaning, as the gssapi server case will
|
||||
* then use the principal from the client */
|
||||
if (!princ_string) {
|
||||
talloc_free(mem_ctx);
|
||||
princ = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = krb5_parse_name(smb_krb5_context->krb5_context,
|
||||
princ_string, princ);
|
||||
|
||||
if (ret == 0) {
|
||||
/* This song-and-dance effectivly puts the principal
|
||||
* into talloc, so we can't loose it. */
|
||||
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
|
||||
mem_ctx->principal = *princ;
|
||||
talloc_set_destructor(mem_ctx, free_principal);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a freshly allocated ccache (destroyed by destructor on child
|
||||
* of parent_ctx), for a given set of client credentials
|
||||
*/
|
||||
|
||||
krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_ccache ccache)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *password;
|
||||
time_t kdc_time = 0;
|
||||
krb5_principal princ;
|
||||
int tries;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
password = cli_credentials_get_password(credentials);
|
||||
|
||||
tries = 2;
|
||||
while (tries--) {
|
||||
if (password) {
|
||||
ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache,
|
||||
princ,
|
||||
password, NULL, &kdc_time);
|
||||
} else {
|
||||
/* No password available, try to use a keyblock instead */
|
||||
|
||||
krb5_keyblock keyblock;
|
||||
const struct samr_Password *mach_pwd;
|
||||
mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
|
||||
if (!mach_pwd) {
|
||||
talloc_free(mem_ctx);
|
||||
DEBUG(1, ("kinit_to_ccache: No password available for kinit\n"));
|
||||
return EINVAL;
|
||||
}
|
||||
ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
|
||||
ETYPE_ARCFOUR_HMAC_MD5,
|
||||
mach_pwd->hash, sizeof(mach_pwd->hash),
|
||||
&keyblock);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache,
|
||||
princ,
|
||||
&keyblock, NULL, &kdc_time);
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
|
||||
/* Perhaps we have been given an invalid skew, so try again without it */
|
||||
time_t t = time(NULL);
|
||||
krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
|
||||
} else {
|
||||
/* not a skew problem */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
|
||||
DEBUG(1,("kinit for %s failed (%s)\n",
|
||||
cli_credentials_get_principal(credentials, mem_ctx),
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* cope with ticket being in the future due to clock skew */
|
||||
if ((unsigned)kdc_time > time(NULL)) {
|
||||
time_t t = time(NULL);
|
||||
int time_offset =(unsigned)kdc_time-t;
|
||||
DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
|
||||
krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
|
||||
}
|
||||
|
||||
if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
|
||||
ret = kinit_to_ccache(parent_ctx,
|
||||
credentials,
|
||||
smb_krb5_context,
|
||||
ccache);
|
||||
}
|
||||
if (ret) {
|
||||
DEBUG(1,("kinit for %s failed (%s)\n",
|
||||
cli_credentials_get_principal(credentials, mem_ctx),
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int free_keytab(struct keytab_container *ktc)
|
||||
{
|
||||
krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smb_krb5_open_keytab(TALLOC_CTX *mem_ctx,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
const char *keytab_name, struct keytab_container **ktc)
|
||||
{
|
||||
krb5_keytab keytab;
|
||||
int ret;
|
||||
ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
|
||||
if (ret) {
|
||||
DEBUG(1,("failed to open krb5 keytab: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
*ktc = talloc(mem_ctx, struct keytab_container);
|
||||
if (!*ktc) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
(*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
|
||||
(*ktc)->keytab = keytab;
|
||||
talloc_set_destructor(*ktc, free_keytab);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct enctypes_container {
|
||||
struct smb_krb5_context *smb_krb5_context;
|
||||
krb5_enctype *enctypes;
|
||||
};
|
||||
|
||||
static int free_enctypes(struct enctypes_container *etc)
|
||||
{
|
||||
free_kerberos_etypes(etc->smb_krb5_context->krb5_context, etc->enctypes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
|
||||
const char *princ_string,
|
||||
krb5_principal princ,
|
||||
krb5_principal salt_princ,
|
||||
int kvno,
|
||||
const char *password_s,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab keytab)
|
||||
{
|
||||
int i;
|
||||
krb5_error_code ret;
|
||||
krb5_enctype *enctypes;
|
||||
char *enctype_string;
|
||||
struct enctypes_container *etc;
|
||||
krb5_data password;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
etc = talloc(mem_ctx, struct enctypes_container);
|
||||
if (!etc) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context,
|
||||
&enctypes);
|
||||
if (ret != 0) {
|
||||
DEBUG(1,("keytab_add_keys: getting encrption types failed (%s)\n",
|
||||
error_message(ret)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
etc->smb_krb5_context = talloc_reference(etc, smb_krb5_context);
|
||||
etc->enctypes = enctypes;
|
||||
|
||||
talloc_set_destructor(etc, free_enctypes);
|
||||
|
||||
password.data = discard_const_p(char *, password_s);
|
||||
password.length = strlen(password_s);
|
||||
|
||||
for (i=0; enctypes[i]; i++) {
|
||||
krb5_keytab_entry entry;
|
||||
ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context,
|
||||
salt_princ, &password, &entry.keyblock, enctypes[i]);
|
||||
if (ret != 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
entry.principal = princ;
|
||||
entry.vno = kvno;
|
||||
ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
|
||||
enctype_string = NULL;
|
||||
krb5_enctype_to_string(smb_krb5_context->krb5_context, enctypes[i], &enctype_string);
|
||||
if (ret != 0) {
|
||||
DEBUG(1, ("Failed to add %s entry for %s(kvno %d) to keytab: %s\n",
|
||||
enctype_string,
|
||||
princ_string,
|
||||
kvno,
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
free(enctype_string);
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
|
||||
princ_string, kvno,
|
||||
enctype_string));
|
||||
free(enctype_string);
|
||||
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab keytab,
|
||||
BOOL add_old)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const char *password_s;
|
||||
char *enctype_string;
|
||||
const char *old_secret;
|
||||
int kvno;
|
||||
krb5_principal salt_princ;
|
||||
krb5_principal princ;
|
||||
const char *princ_string;
|
||||
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
|
||||
/* Get the principal we will store the new keytab entries under */
|
||||
ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The salt used to generate these entries may be different however, fetch that */
|
||||
ret = salt_principal_from_credentials(mem_ctx, machine_account,
|
||||
smb_krb5_context,
|
||||
&salt_princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Finally, do the dance to get the password to put in the entry */
|
||||
password_s = cli_credentials_get_password(machine_account);
|
||||
if (!password_s) {
|
||||
/* If we don't have the plaintext password, try for
|
||||
* the MD4 password hash */
|
||||
|
||||
krb5_keytab_entry entry;
|
||||
const struct samr_Password *mach_pwd;
|
||||
mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
|
||||
if (!mach_pwd) {
|
||||
DEBUG(1, ("create_keytab: Domain trust informaton for account %s not available\n",
|
||||
cli_credentials_get_principal(machine_account, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return EINVAL;
|
||||
}
|
||||
ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
|
||||
ETYPE_ARCFOUR_HMAC_MD5,
|
||||
mach_pwd->hash, sizeof(mach_pwd->hash),
|
||||
&entry.keyblock);
|
||||
if (ret) {
|
||||
DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
entry.principal = princ;
|
||||
entry.vno = cli_credentials_get_kvno(machine_account);
|
||||
ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
|
||||
if (ret) {
|
||||
DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s",
|
||||
cli_credentials_get_principal(machine_account, mem_ctx),
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_enctype_to_string(smb_krb5_context->krb5_context,
|
||||
ETYPE_ARCFOUR_HMAC_MD5,
|
||||
&enctype_string);
|
||||
DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
|
||||
cli_credentials_get_principal(machine_account, mem_ctx),
|
||||
cli_credentials_get_kvno(machine_account),
|
||||
enctype_string));
|
||||
free(enctype_string);
|
||||
|
||||
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
|
||||
|
||||
/* Can't go any further, we only have this one key */
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
kvno = cli_credentials_get_kvno(machine_account);
|
||||
/* good, we actually have the real plaintext */
|
||||
ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
|
||||
kvno, password_s, smb_krb5_context, keytab);
|
||||
if (!ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!add_old || kvno == 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
old_secret = cli_credentials_get_old_password(machine_account);
|
||||
if (!old_secret) {
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
|
||||
kvno - 1, old_secret, smb_krb5_context, keytab);
|
||||
if (!ret) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Walk the keytab, looking for entries of this principal name, with KVNO other than current kvno -1.
|
||||
*
|
||||
* These entries are now stale, we only keep the current, and previous entries around.
|
||||
*
|
||||
* Inspired by the code in Samba3 for 'use kerberos keytab'.
|
||||
*
|
||||
*/
|
||||
|
||||
static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
krb5_keytab keytab, BOOL *found_previous)
|
||||
{
|
||||
krb5_error_code ret, ret2;
|
||||
krb5_kt_cursor cursor;
|
||||
krb5_principal princ;
|
||||
int kvno;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
const char *princ_string;
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
*found_previous = False;
|
||||
princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
|
||||
|
||||
/* Get the principal we will store the new keytab entries under */
|
||||
ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
|
||||
if (ret) {
|
||||
DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kvno = cli_credentials_get_kvno(machine_account);
|
||||
|
||||
/* for each entry in the keytab */
|
||||
ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case HEIM_ERR_OPNOTSUPP:
|
||||
case ENOENT:
|
||||
case KRB5_KT_END:
|
||||
/* no point enumerating if there isn't anything here */
|
||||
talloc_free(mem_ctx);
|
||||
return 0;
|
||||
default:
|
||||
DEBUG(1,("failed to open keytab for read of old entries: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (!ret) {
|
||||
krb5_keytab_entry entry;
|
||||
ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
/* if it matches our principal */
|
||||
if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
|
||||
/* Free the entry, it wasn't the one we were looking for anyway */
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* delete it, if it is not kvno -1 */
|
||||
if (entry.vno != (kvno - 1 )) {
|
||||
/* Release the enumeration. We are going to
|
||||
* have to start this from the top again,
|
||||
* because deletes during enumeration may not
|
||||
* always be consistant.
|
||||
*
|
||||
* Also, the enumeration locks a FILE: keytab
|
||||
*/
|
||||
|
||||
krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
|
||||
ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
|
||||
/* Deleted: Restart from the top */
|
||||
ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
if (ret2) {
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
DEBUG(1,("failed to restart enumeration of keytab: %s\n",
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return ret2;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
*found_previous = True;
|
||||
}
|
||||
|
||||
/* Free the entry, we don't need it any more */
|
||||
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
|
||||
|
||||
|
||||
}
|
||||
krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case ENOENT:
|
||||
case KRB5_KT_END:
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
|
||||
princ_string,
|
||||
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
|
||||
ret, mem_ctx)));
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
struct keytab_container *keytab_container)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
BOOL found_previous;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = remove_old_entries(mem_ctx, machine_account,
|
||||
smb_krb5_context, keytab_container->keytab, &found_previous);
|
||||
if (ret != 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Create a new keytab. If during the cleanout we found
|
||||
* entires for kvno -1, then don't try and duplicate them.
|
||||
* Otherwise, add kvno, and kvno -1 */
|
||||
|
||||
ret = create_keytab(mem_ctx, machine_account, smb_krb5_context,
|
||||
keytab_container->keytab,
|
||||
found_previous ? False : True);
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
_PUBLIC_ int smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
|
||||
struct cli_credentials *machine_account,
|
||||
struct smb_krb5_context *smb_krb5_context,
|
||||
struct keytab_container **keytab_container)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
|
||||
const char *rand_string;
|
||||
const char *keytab_name;
|
||||
if (!mem_ctx) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
*keytab_container = talloc(mem_ctx, struct keytab_container);
|
||||
|
||||
rand_string = generate_random_str(mem_ctx, 16);
|
||||
if (!rand_string) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s",
|
||||
rand_string);
|
||||
if (!keytab_name) {
|
||||
talloc_free(mem_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, keytab_name, keytab_container);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = smb_krb5_update_keytab(mem_ctx, machine_account, smb_krb5_context, *keytab_container);
|
||||
if (ret == 0) {
|
||||
talloc_steal(parent_ctx, *keytab_container);
|
||||
} else {
|
||||
*keytab_container = NULL;
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Wrapper for krb5_init_context
|
||||
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/kerberos.h"
|
||||
#include "heimdal/lib/krb5/krb5_locl.h"
|
||||
#include "auth/kerberos/kerberos.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "roken.h"
|
||||
|
||||
/*
|
||||
context structure for operations on cldap packets
|
||||
*/
|
||||
struct smb_krb5_socket {
|
||||
struct socket_context *sock;
|
||||
|
||||
/* the fd event */
|
||||
struct fd_event *fde;
|
||||
|
||||
BOOL timeout;
|
||||
NTSTATUS status;
|
||||
DATA_BLOB request, reply, partial;
|
||||
|
||||
size_t partial_read;
|
||||
|
||||
krb5_krbhst_info *hi;
|
||||
};
|
||||
|
||||
static int smb_krb5_context_destroy_1(struct smb_krb5_context *ctx)
|
||||
{
|
||||
krb5_free_context(ctx->krb5_context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smb_krb5_context_destroy_2(struct smb_krb5_context *ctx)
|
||||
{
|
||||
/* Otherwise krb5_free_context will try and close what we have already free()ed */
|
||||
krb5_set_warn_dest(ctx->krb5_context, NULL);
|
||||
krb5_closelog(ctx->krb5_context, ctx->logf);
|
||||
smb_krb5_context_destroy_1(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We never close down the DEBUG system, and no need to unreference the use */
|
||||
static void smb_krb5_debug_close(void *private) {
|
||||
return;
|
||||
}
|
||||
|
||||
static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private)
|
||||
{
|
||||
DEBUG(2, ("Kerberos: %s\n", msg));
|
||||
}
|
||||
|
||||
/*
|
||||
handle recv events on a smb_krb5 socket
|
||||
*/
|
||||
static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
|
||||
{
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
|
||||
DATA_BLOB blob;
|
||||
size_t nread, dsize;
|
||||
|
||||
switch (smb_krb5->hi->proto) {
|
||||
case KRB5_KRBHST_UDP:
|
||||
smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
blob = data_blob_talloc(tmp_ctx, NULL, dsize);
|
||||
if (blob.data == NULL && dsize != 0) {
|
||||
smb_krb5->status = NT_STATUS_NO_MEMORY;
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
blob.length = nread;
|
||||
|
||||
if (nread == 0) {
|
||||
smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG(2,("Received smb_krb5 packet of length %d\n",
|
||||
(int)blob.length));
|
||||
|
||||
talloc_steal(smb_krb5, blob.data);
|
||||
smb_krb5->reply = blob;
|
||||
talloc_free(tmp_ctx);
|
||||
break;
|
||||
case KRB5_KRBHST_TCP:
|
||||
if (smb_krb5->partial.length == 0) {
|
||||
smb_krb5->partial = data_blob_talloc(smb_krb5, NULL, 4);
|
||||
if (!smb_krb5->partial.data) {
|
||||
smb_krb5->status = NT_STATUS_NO_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->partial_read = 0;
|
||||
}
|
||||
|
||||
/* read in the packet length */
|
||||
if (smb_krb5->partial_read < 4) {
|
||||
uint32_t packet_length;
|
||||
|
||||
smb_krb5->status = socket_recv(smb_krb5->sock,
|
||||
smb_krb5->partial.data + smb_krb5->partial_read,
|
||||
4 - smb_krb5->partial_read,
|
||||
&nread);
|
||||
/* todo: this should be converted to the packet_*() routines */
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->partial_read += nread;
|
||||
if (smb_krb5->partial_read != 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
packet_length = RIVAL(smb_krb5->partial.data, 0);
|
||||
|
||||
smb_krb5->partial.data = talloc_realloc(smb_krb5, smb_krb5->partial.data,
|
||||
uint8_t, packet_length + 4);
|
||||
if (!smb_krb5->partial.data) {
|
||||
smb_krb5->status = NT_STATUS_NO_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
smb_krb5->partial.length = packet_length + 4;
|
||||
}
|
||||
|
||||
/* read in the body */
|
||||
smb_krb5->status = socket_recv(smb_krb5->sock,
|
||||
smb_krb5->partial.data + smb_krb5->partial_read,
|
||||
smb_krb5->partial.length - smb_krb5->partial_read,
|
||||
&nread);
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) return;
|
||||
|
||||
smb_krb5->partial_read += nread;
|
||||
|
||||
if (smb_krb5->partial_read != smb_krb5->partial.length) return;
|
||||
|
||||
smb_krb5->reply = data_blob_talloc(smb_krb5, smb_krb5->partial.data + 4, smb_krb5->partial.length - 4);
|
||||
break;
|
||||
case KRB5_KRBHST_HTTP:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
handle request timeouts
|
||||
*/
|
||||
static void smb_krb5_request_timeout(struct event_context *event_ctx,
|
||||
struct timed_event *te, struct timeval t,
|
||||
void *private)
|
||||
{
|
||||
struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
|
||||
DEBUG(5,("Timed out smb_krb5 packet\n"));
|
||||
smb_krb5->timeout = True;
|
||||
}
|
||||
|
||||
/*
|
||||
handle send events on a smb_krb5 socket
|
||||
*/
|
||||
static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
size_t len;
|
||||
|
||||
len = smb_krb5->request.length;
|
||||
status = socket_send(smb_krb5->sock, &smb_krb5->request, &len);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) return;
|
||||
|
||||
EVENT_FD_READABLE(smb_krb5->fde);
|
||||
|
||||
EVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handle fd events on a smb_krb5_socket
|
||||
*/
|
||||
static void smb_krb5_socket_handler(struct event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private)
|
||||
{
|
||||
struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
|
||||
if (flags & EVENT_FD_WRITE) {
|
||||
smb_krb5_socket_send(smb_krb5);
|
||||
}
|
||||
if (flags & EVENT_FD_READ) {
|
||||
smb_krb5_socket_recv(smb_krb5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
|
||||
void *data,
|
||||
krb5_krbhst_info *hi,
|
||||
const krb5_data *send_buf,
|
||||
krb5_data *recv_buf)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
NTSTATUS status;
|
||||
struct socket_address *remote_addr;
|
||||
const char *name;
|
||||
struct addrinfo *ai, *a;
|
||||
struct smb_krb5_socket *smb_krb5;
|
||||
|
||||
struct event_context *ev = talloc_get_type(data, struct event_context);
|
||||
|
||||
DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length);
|
||||
|
||||
ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (a = ai; a; a = ai->ai_next) {
|
||||
smb_krb5 = talloc(NULL, struct smb_krb5_socket);
|
||||
if (!smb_krb5) {
|
||||
return ENOMEM;
|
||||
}
|
||||
smb_krb5->hi = hi;
|
||||
|
||||
switch (a->ai_family) {
|
||||
case PF_INET:
|
||||
name = "ipv4";
|
||||
break;
|
||||
#ifdef HAVE_SOCKET_IPV6
|
||||
case PF_INET6:
|
||||
name = "ipv6";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
switch (hi->proto) {
|
||||
case KRB5_KRBHST_UDP:
|
||||
if (lp_parm_bool(-1, "krb5", "udp", True)) {
|
||||
status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
|
||||
}
|
||||
break;
|
||||
case KRB5_KRBHST_TCP:
|
||||
if (lp_parm_bool(-1, "krb5", "tcp", True)) {
|
||||
status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
|
||||
}
|
||||
break;
|
||||
case KRB5_KRBHST_HTTP:
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
talloc_steal(smb_krb5, smb_krb5->sock);
|
||||
|
||||
remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
|
||||
if (!remote_addr) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
talloc_free(remote_addr);
|
||||
|
||||
smb_krb5->fde = event_add_fd(ev, smb_krb5,
|
||||
socket_get_fd(smb_krb5->sock), 0,
|
||||
smb_krb5_socket_handler, smb_krb5);
|
||||
|
||||
event_add_timed(ev, smb_krb5,
|
||||
timeval_current_ofs(context->kdc_timeout, 0),
|
||||
smb_krb5_request_timeout, smb_krb5);
|
||||
|
||||
EVENT_FD_WRITEABLE(smb_krb5->fde);
|
||||
|
||||
switch (hi->proto) {
|
||||
case KRB5_KRBHST_UDP:
|
||||
smb_krb5->request = send_blob;
|
||||
break;
|
||||
case KRB5_KRBHST_TCP:
|
||||
smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
|
||||
RSIVAL(smb_krb5->request.data, 0, send_blob.length);
|
||||
memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
|
||||
break;
|
||||
case KRB5_KRBHST_HTTP:
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
smb_krb5->timeout = False;
|
||||
smb_krb5->status = NT_STATUS_OK;
|
||||
smb_krb5->reply = data_blob(NULL, 0);
|
||||
smb_krb5->partial = data_blob(NULL, 0);
|
||||
|
||||
while (!smb_krb5->timeout && (NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
|
||||
if (event_loop_once(ev) != 0) {
|
||||
talloc_free(smb_krb5);
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
|
||||
DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status)));
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (smb_krb5->timeout) {
|
||||
talloc_free(smb_krb5);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
|
||||
if (ret) {
|
||||
talloc_free(smb_krb5);
|
||||
return ret;
|
||||
}
|
||||
talloc_free(smb_krb5);
|
||||
|
||||
break;
|
||||
}
|
||||
if (a) {
|
||||
return 0;
|
||||
}
|
||||
return KRB5_KDC_UNREACH;
|
||||
}
|
||||
|
||||
krb5_error_code smb_krb5_init_context(void *parent_ctx,
|
||||
struct smb_krb5_context **smb_krb5_context)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
struct event_context *ev;
|
||||
|
||||
initialize_krb5_error_table();
|
||||
|
||||
tmp_ctx = talloc_new(parent_ctx);
|
||||
*smb_krb5_context = talloc(tmp_ctx, struct smb_krb5_context);
|
||||
|
||||
if (!*smb_krb5_context || !tmp_ctx) {
|
||||
talloc_free(*smb_krb5_context);
|
||||
talloc_free(tmp_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
ret = krb5_init_context(&(*smb_krb5_context)->krb5_context);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_init_context failed (%s)\n",
|
||||
error_message(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_1);
|
||||
|
||||
if (lp_realm() && *lp_realm()) {
|
||||
char *upper_realm = strupper_talloc(tmp_ctx, lp_realm());
|
||||
if (!upper_realm) {
|
||||
DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm()));
|
||||
talloc_free(tmp_ctx);
|
||||
return ENOMEM;
|
||||
}
|
||||
ret = krb5_set_default_realm((*smb_krb5_context)->krb5_context, upper_realm);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_set_default_realm failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Should we have a different name here? */
|
||||
ret = krb5_initlog((*smb_krb5_context)->krb5_context, "Samba", &(*smb_krb5_context)->logf);
|
||||
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_initlog failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_2);
|
||||
|
||||
ret = krb5_addlog_func((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf, 0 /* min */, -1 /* max */,
|
||||
smb_krb5_debug_wrapper, smb_krb5_debug_close, NULL);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_addlog_func failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
krb5_set_warn_dest((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf);
|
||||
|
||||
ev = event_context_find(*smb_krb5_context);
|
||||
/* Set use of our socket lib */
|
||||
ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
|
||||
smb_krb5_send_and_recv_func,
|
||||
ev);
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
|
||||
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*smb_krb5_context)->krb5_context->mem_ctx = *smb_krb5_context;
|
||||
|
||||
talloc_steal(parent_ctx, *smb_krb5_context);
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
/* Set options in kerberos */
|
||||
|
||||
krb5_set_dns_canonicalize_hostname((*smb_krb5_context)->krb5_context, FALSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5 routines for active directory
|
||||
Copyright (C) Andrew Bartlett 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
struct smb_krb5_context {
|
||||
struct krb5_context_data *krb5_context;
|
||||
krb5_log_facility *logf;
|
||||
};
|
||||
|
||||
krb5_error_code smb_krb5_init_context(void *parent_ctx,
|
||||
struct smb_krb5_context **smb_krb5_context);
|
||||
void smb_krb5_free_context(struct smb_krb5_context *smb_krb5_context);
|
||||
|
||||
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
|
||||
void *data,
|
||||
krb5_krbhst_info *hi,
|
||||
const krb5_data *send_buf,
|
||||
krb5_data *recv_buf);
|
||||
@@ -0,0 +1,599 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
|
||||
Copyright (C) Gerald Carter 2003
|
||||
Copyright (C) Luke Kenneth Casson Leighton 1996-2000
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "librpc/gen_ndr/netlogon.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
|
||||
/****************************************************************************
|
||||
Core of smb password checking routine.
|
||||
****************************************************************************/
|
||||
|
||||
static BOOL smb_pwd_check_ntlmv1(TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *nt_response,
|
||||
const uint8_t *part_passwd,
|
||||
const DATA_BLOB *sec_blob,
|
||||
DATA_BLOB *user_sess_key)
|
||||
{
|
||||
/* Finish the encryption of part_passwd. */
|
||||
uint8_t p24[24];
|
||||
|
||||
if (part_passwd == NULL) {
|
||||
DEBUG(10,("No password set - DISALLOWING access\n"));
|
||||
/* No password set - always false ! */
|
||||
return False;
|
||||
}
|
||||
|
||||
if (sec_blob->length != 8) {
|
||||
DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect challenge size (%lu)\n",
|
||||
(unsigned long)sec_blob->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
if (nt_response->length != 24) {
|
||||
DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%lu)\n",
|
||||
(unsigned long)nt_response->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
SMBOWFencrypt(part_passwd, sec_blob->data, p24);
|
||||
|
||||
#if DEBUG_PASSWORD
|
||||
DEBUG(100,("Part password (P16) was |\n"));
|
||||
dump_data(100, part_passwd, 16);
|
||||
DEBUGADD(100,("Password from client was |\n"));
|
||||
dump_data(100, nt_response->data, nt_response->length);
|
||||
DEBUGADD(100,("Given challenge was |\n"));
|
||||
dump_data(100, sec_blob->data, sec_blob->length);
|
||||
DEBUGADD(100,("Value from encryption was |\n"));
|
||||
dump_data(100, p24, 24);
|
||||
#endif
|
||||
if (memcmp(p24, nt_response->data, 24) == 0) {
|
||||
if (user_sess_key != NULL) {
|
||||
*user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
SMBsesskeygen_ntv1(part_passwd, user_sess_key->data);
|
||||
}
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Core of smb password checking routine. (NTLMv2, LMv2)
|
||||
Note: The same code works with both NTLMv2 and LMv2.
|
||||
****************************************************************************/
|
||||
|
||||
static BOOL smb_pwd_check_ntlmv2(TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *ntv2_response,
|
||||
const uint8_t *part_passwd,
|
||||
const DATA_BLOB *sec_blob,
|
||||
const char *user, const char *domain,
|
||||
BOOL upper_case_domain, /* should the domain be transformed into upper case? */
|
||||
DATA_BLOB *user_sess_key)
|
||||
{
|
||||
/* Finish the encryption of part_passwd. */
|
||||
uint8_t kr[16];
|
||||
uint8_t value_from_encryption[16];
|
||||
DATA_BLOB client_key_data;
|
||||
|
||||
if (part_passwd == NULL) {
|
||||
DEBUG(10,("No password set - DISALLOWING access\n"));
|
||||
/* No password set - always False */
|
||||
return False;
|
||||
}
|
||||
|
||||
if (sec_blob->length != 8) {
|
||||
DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect challenge size (%lu)\n",
|
||||
(unsigned long)sec_blob->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
if (ntv2_response->length < 24) {
|
||||
/* We MUST have more than 16 bytes, or the stuff below will go
|
||||
crazy. No known implementation sends less than the 24 bytes
|
||||
for LMv2, let alone NTLMv2. */
|
||||
DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%lu)\n",
|
||||
(unsigned long)ntv2_response->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
|
||||
/*
|
||||
todo: should we be checking this for anything? We can't for LMv2,
|
||||
but for NTLMv2 it is meant to contain the current time etc.
|
||||
*/
|
||||
|
||||
if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
|
||||
|
||||
#if DEBUG_PASSWORD
|
||||
DEBUG(100,("Part password (P16) was |\n"));
|
||||
dump_data(100, part_passwd, 16);
|
||||
DEBUGADD(100,("Password from client was |\n"));
|
||||
dump_data(100, ntv2_response->data, ntv2_response->length);
|
||||
DEBUGADD(100,("Variable data from client was |\n"));
|
||||
dump_data(100, client_key_data.data, client_key_data.length);
|
||||
DEBUGADD(100,("Given challenge was |\n"));
|
||||
dump_data(100, sec_blob->data, sec_blob->length);
|
||||
DEBUGADD(100,("Value from encryption was |\n"));
|
||||
dump_data(100, value_from_encryption, 16);
|
||||
#endif
|
||||
data_blob_clear_free(&client_key_data);
|
||||
if (memcmp(value_from_encryption, ntv2_response->data, 16) == 0) {
|
||||
if (user_sess_key != NULL) {
|
||||
*user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
|
||||
}
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Core of smb password checking routine. (NTLMv2, LMv2)
|
||||
Note: The same code works with both NTLMv2 and LMv2.
|
||||
****************************************************************************/
|
||||
|
||||
static BOOL smb_sess_key_ntlmv2(TALLOC_CTX *mem_ctx,
|
||||
const DATA_BLOB *ntv2_response,
|
||||
const uint8_t *part_passwd,
|
||||
const DATA_BLOB *sec_blob,
|
||||
const char *user, const char *domain,
|
||||
BOOL upper_case_domain, /* should the domain be transformed into upper case? */
|
||||
DATA_BLOB *user_sess_key)
|
||||
{
|
||||
/* Finish the encryption of part_passwd. */
|
||||
uint8_t kr[16];
|
||||
uint8_t value_from_encryption[16];
|
||||
DATA_BLOB client_key_data;
|
||||
|
||||
if (part_passwd == NULL) {
|
||||
DEBUG(10,("No password set - DISALLOWING access\n"));
|
||||
/* No password set - always False */
|
||||
return False;
|
||||
}
|
||||
|
||||
if (sec_blob->length != 8) {
|
||||
DEBUG(0, ("smb_sess_key_ntlmv2: incorrect challenge size (%lu)\n",
|
||||
(unsigned long)sec_blob->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
if (ntv2_response->length < 24) {
|
||||
/* We MUST have more than 16 bytes, or the stuff below will go
|
||||
crazy. No known implementation sends less than the 24 bytes
|
||||
for LMv2, let alone NTLMv2. */
|
||||
DEBUG(0, ("smb_sess_key_ntlmv2: incorrect password length (%lu)\n",
|
||||
(unsigned long)ntv2_response->length));
|
||||
return False;
|
||||
}
|
||||
|
||||
client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
|
||||
|
||||
if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
|
||||
*user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
|
||||
return True;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare password hashes against those from the SAM
|
||||
*
|
||||
* @param mem_ctx talloc context
|
||||
* @param client_lanman LANMAN password hash, as supplied by the client
|
||||
* @param client_nt NT (MD4) password hash, as supplied by the client
|
||||
* @param username internal Samba username, for log messages
|
||||
* @param client_username username the client used
|
||||
* @param client_domain domain name the client used (may be mapped)
|
||||
* @param stored_lanman LANMAN password hash, as stored on the SAM
|
||||
* @param stored_nt NT (MD4) password hash, as stored on the SAM
|
||||
* @param user_sess_key User session key
|
||||
* @param lm_sess_key LM session key (first 8 bytes of the LM hash)
|
||||
*/
|
||||
|
||||
NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
|
||||
const struct samr_Password *client_lanman,
|
||||
const struct samr_Password *client_nt,
|
||||
const char *username,
|
||||
const struct samr_Password *stored_lanman,
|
||||
const struct samr_Password *stored_nt)
|
||||
{
|
||||
if (stored_nt == NULL) {
|
||||
DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
|
||||
username));
|
||||
}
|
||||
|
||||
if (client_nt && stored_nt) {
|
||||
if (memcmp(client_nt->hash, stored_nt->hash, sizeof(stored_nt->hash)) == 0) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(3,("ntlm_password_check: Interactive logon: NT password check failed for user %s\n",
|
||||
username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
} else if (client_lanman && stored_lanman) {
|
||||
if (!lp_lanman_auth()) {
|
||||
DEBUG(3,("ntlm_password_check: Interactive logon: only LANMAN password supplied for user %s, and LM passwords are disabled!\n",
|
||||
username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
if (strchr_m(username, '@')) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (memcmp(client_lanman->hash, stored_lanman->hash, sizeof(stored_lanman->hash)) == 0) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(3,("ntlm_password_check: Interactive logon: LANMAN password check failed for user %s\n",
|
||||
username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
}
|
||||
if (strchr_m(username, '@')) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a challenge-response password against the value of the NT or
|
||||
* LM password hash.
|
||||
*
|
||||
* @param mem_ctx talloc context
|
||||
* @param challenge 8-byte challenge. If all zero, forces plaintext comparison
|
||||
* @param nt_response 'unicode' NT response to the challenge, or unicode password
|
||||
* @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
|
||||
* @param username internal Samba username, for log messages
|
||||
* @param client_username username the client used
|
||||
* @param client_domain domain name the client used (may be mapped)
|
||||
* @param stored_lanman LANMAN ASCII password from our passdb or similar
|
||||
* @param stored_nt MD4 unicode password from our passdb or similar
|
||||
* @param user_sess_key User session key
|
||||
* @param lm_sess_key LM session key (first 8 bytes of the LM hash)
|
||||
*/
|
||||
|
||||
NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
|
||||
uint32_t logon_parameters,
|
||||
const DATA_BLOB *challenge,
|
||||
const DATA_BLOB *lm_response,
|
||||
const DATA_BLOB *nt_response,
|
||||
const char *username,
|
||||
const char *client_username,
|
||||
const char *client_domain,
|
||||
const struct samr_Password *stored_lanman,
|
||||
const struct samr_Password *stored_nt,
|
||||
DATA_BLOB *user_sess_key,
|
||||
DATA_BLOB *lm_sess_key)
|
||||
{
|
||||
static const uint8_t zeros[8];
|
||||
DATA_BLOB tmp_sess_key;
|
||||
|
||||
if (stored_nt == NULL) {
|
||||
DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
|
||||
username));
|
||||
}
|
||||
|
||||
*lm_sess_key = data_blob(NULL, 0);
|
||||
*user_sess_key = data_blob(NULL, 0);
|
||||
|
||||
/* Check for cleartext netlogon. Used by Exchange 5.5. */
|
||||
if ((logon_parameters & MSV1_0_CLEARTEXT_PASSWORD_ALLOWED)
|
||||
&& challenge->length == sizeof(zeros)
|
||||
&& (memcmp(challenge->data, zeros, challenge->length) == 0 )) {
|
||||
struct samr_Password client_nt;
|
||||
struct samr_Password client_lm;
|
||||
char *unix_pw = NULL;
|
||||
BOOL lm_ok;
|
||||
|
||||
DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n",
|
||||
username));
|
||||
mdfour(client_nt.hash, nt_response->data, nt_response->length);
|
||||
|
||||
if (lm_response->length &&
|
||||
(convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX,
|
||||
lm_response->data, lm_response->length,
|
||||
(void **)&unix_pw) != -1)) {
|
||||
if (E_deshash(unix_pw, client_lm.hash)) {
|
||||
lm_ok = True;
|
||||
} else {
|
||||
lm_ok = False;
|
||||
}
|
||||
} else {
|
||||
lm_ok = False;
|
||||
}
|
||||
return hash_password_check(mem_ctx,
|
||||
lm_ok ? &client_lm : NULL,
|
||||
nt_response->length ? &client_nt : NULL,
|
||||
username,
|
||||
stored_lanman, stored_nt);
|
||||
}
|
||||
|
||||
if (nt_response->length != 0 && nt_response->length < 24) {
|
||||
DEBUG(2,("ntlm_password_check: invalid NT password length (%lu) for user %s\n",
|
||||
(unsigned long)nt_response->length, username));
|
||||
}
|
||||
|
||||
if (nt_response->length > 24 && stored_nt) {
|
||||
/* We have the NT MD4 hash challenge available - see if we can
|
||||
use it
|
||||
*/
|
||||
DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with domain [%s]\n", client_domain));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
False,
|
||||
user_sess_key)) {
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with uppercased version of domain [%s]\n", client_domain));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
True,
|
||||
user_sess_key)) {
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(4,("ntlm_password_check: Checking NTLMv2 password without a domain\n"));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
"",
|
||||
False,
|
||||
user_sess_key)) {
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(3,("ntlm_password_check: NTLMv2 password check failed\n"));
|
||||
}
|
||||
} else if (nt_response->length == 24 && stored_nt) {
|
||||
if (lp_ntlm_auth()) {
|
||||
/* We have the NT MD4 hash challenge available - see if we can
|
||||
use it (ie. does it exist in the smbpasswd file).
|
||||
*/
|
||||
DEBUG(4,("ntlm_password_check: Checking NT MD4 password\n"));
|
||||
if (smb_pwd_check_ntlmv1(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
user_sess_key)) {
|
||||
/* The LM session key for this response is not very secure,
|
||||
so use it only if we otherwise allow LM authentication */
|
||||
|
||||
if (lp_lanman_auth() && stored_lanman) {
|
||||
*lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(3,("ntlm_password_check: NT MD4 password check failed for user %s\n",
|
||||
username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
} else {
|
||||
DEBUG(2,("ntlm_password_check: NTLMv1 passwords NOT PERMITTED for user %s\n",
|
||||
username));
|
||||
/* no return, becouse we might pick up LMv2 in the LM field */
|
||||
}
|
||||
}
|
||||
|
||||
if (lm_response->length == 0) {
|
||||
DEBUG(3,("ntlm_password_check: NEITHER LanMan nor NT password supplied for user %s\n",
|
||||
username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
if (lm_response->length < 24) {
|
||||
DEBUG(2,("ntlm_password_check: invalid LanMan password length (%lu) for user %s\n",
|
||||
(unsigned long)nt_response->length, username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
if (!lp_lanman_auth()) {
|
||||
DEBUG(3,("ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n",
|
||||
username));
|
||||
} else if (!stored_lanman) {
|
||||
DEBUG(3,("ntlm_password_check: NO LanMan password set for user %s (and no NT password supplied)\n",
|
||||
username));
|
||||
} else if (strchr_m(username, '@')) {
|
||||
DEBUG(3,("ntlm_password_check: NO LanMan password allowed for username@realm logins (user: %s)\n",
|
||||
username));
|
||||
} else {
|
||||
DEBUG(4,("ntlm_password_check: Checking LM password\n"));
|
||||
if (smb_pwd_check_ntlmv1(mem_ctx,
|
||||
lm_response,
|
||||
stored_lanman->hash, challenge,
|
||||
NULL)) {
|
||||
/* The session key for this response is still very odd.
|
||||
It not very secure, so use it only if we otherwise
|
||||
allow LM authentication */
|
||||
|
||||
if (lp_lanman_auth() && stored_lanman) {
|
||||
uint8_t first_8_lm_hash[16];
|
||||
memcpy(first_8_lm_hash, stored_lanman->hash, 8);
|
||||
memset(first_8_lm_hash + 8, '\0', 8);
|
||||
*user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
|
||||
*lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stored_nt) {
|
||||
DEBUG(4,("ntlm_password_check: LM password check failed for user, no NT password %s\n",username));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
/* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes.
|
||||
- related to Win9X, legacy NAS pass-though authentication
|
||||
*/
|
||||
DEBUG(4,("ntlm_password_check: Checking LMv2 password with domain %s\n", client_domain));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
lm_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
False,
|
||||
&tmp_sess_key)) {
|
||||
if (nt_response->length > 24) {
|
||||
/* If NTLMv2 authentication has preceeded us
|
||||
* (even if it failed), then use the session
|
||||
* key from that. See the RPC-SAMLOGON
|
||||
* torture test */
|
||||
smb_sess_key_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
False,
|
||||
user_sess_key);
|
||||
} else {
|
||||
/* Otherwise, use the LMv2 session key */
|
||||
*user_sess_key = tmp_sess_key;
|
||||
}
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(4,("ntlm_password_check: Checking LMv2 password with upper-cased version of domain %s\n", client_domain));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
lm_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
True,
|
||||
&tmp_sess_key)) {
|
||||
if (nt_response->length > 24) {
|
||||
/* If NTLMv2 authentication has preceeded us
|
||||
* (even if it failed), then use the session
|
||||
* key from that. See the RPC-SAMLOGON
|
||||
* torture test */
|
||||
smb_sess_key_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
client_domain,
|
||||
True,
|
||||
user_sess_key);
|
||||
} else {
|
||||
/* Otherwise, use the LMv2 session key */
|
||||
*user_sess_key = tmp_sess_key;
|
||||
}
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
DEBUG(4,("ntlm_password_check: Checking LMv2 password without a domain\n"));
|
||||
if (smb_pwd_check_ntlmv2(mem_ctx,
|
||||
lm_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
"",
|
||||
False,
|
||||
&tmp_sess_key)) {
|
||||
if (nt_response->length > 24) {
|
||||
/* If NTLMv2 authentication has preceeded us
|
||||
* (even if it failed), then use the session
|
||||
* key from that. See the RPC-SAMLOGON
|
||||
* torture test */
|
||||
smb_sess_key_ntlmv2(mem_ctx,
|
||||
nt_response,
|
||||
stored_nt->hash, challenge,
|
||||
client_username,
|
||||
"",
|
||||
False,
|
||||
user_sess_key);
|
||||
} else {
|
||||
/* Otherwise, use the LMv2 session key */
|
||||
*user_sess_key = tmp_sess_key;
|
||||
}
|
||||
*lm_sess_key = *user_sess_key;
|
||||
if (user_sess_key->length) {
|
||||
lm_sess_key->length = 8;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Apparently NT accepts NT responses in the LM field
|
||||
- I think this is related to Win9X pass-though authentication
|
||||
*/
|
||||
DEBUG(4,("ntlm_password_check: Checking NT MD4 password in LM field\n"));
|
||||
if (lp_ntlm_auth()) {
|
||||
if (smb_pwd_check_ntlmv1(mem_ctx,
|
||||
lm_response,
|
||||
stored_nt->hash, challenge,
|
||||
NULL)) {
|
||||
/* The session key for this response is still very odd.
|
||||
It not very secure, so use it only if we otherwise
|
||||
allow LM authentication */
|
||||
|
||||
if (lp_lanman_auth() && stored_lanman) {
|
||||
uint8_t first_8_lm_hash[16];
|
||||
memcpy(first_8_lm_hash, stored_lanman->hash, 8);
|
||||
memset(first_8_lm_hash + 8, '\0', 8);
|
||||
*user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
|
||||
*lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
DEBUG(3,("ntlm_password_check: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",username));
|
||||
} else {
|
||||
DEBUG(3,("ntlm_password_check: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",username));
|
||||
}
|
||||
|
||||
/* Try and match error codes */
|
||||
if (strchr_m(username, '@')) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
[SUBSYSTEM::MSRPC_PARSE]
|
||||
PRIVATE_PROTO_HEADER = msrpc_parse.h
|
||||
OBJ_FILES = ntlmssp_parse.o
|
||||
|
||||
################################################
|
||||
# Start MODULE gensec_ntlmssp
|
||||
[MODULE::gensec_ntlmssp]
|
||||
SUBSYSTEM = gensec
|
||||
INIT_FUNCTION = gensec_ntlmssp_init
|
||||
PRIVATE_PROTO_HEADER = proto.h
|
||||
OBJ_FILES = ntlmssp.o \
|
||||
ntlmssp_sign.o \
|
||||
ntlmssp_client.o \
|
||||
ntlmssp_server.o
|
||||
PUBLIC_DEPENDENCIES = auth MSRPC_PARSE
|
||||
OUTPUT_TYPE = INTEGRATED
|
||||
# End MODULE gensec_ntlmssp
|
||||
################################################
|
||||
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Version 3.0
|
||||
handle NLTMSSP, client server side parsing
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/ntlmssp/ntlmssp.h"
|
||||
#include "auth/ntlmssp/msrpc_parse.h"
|
||||
#include "librpc/gen_ndr/ndr_dcerpc.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
/**
|
||||
* Callbacks for NTLMSSP - for both client and server operating modes
|
||||
*
|
||||
*/
|
||||
|
||||
static const struct ntlmssp_callbacks {
|
||||
enum ntlmssp_role role;
|
||||
enum ntlmssp_message_type command;
|
||||
NTSTATUS (*sync_fn)(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
DATA_BLOB in, DATA_BLOB *out);
|
||||
} ntlmssp_callbacks[] = {
|
||||
{
|
||||
.role = NTLMSSP_CLIENT,
|
||||
.command = NTLMSSP_INITIAL,
|
||||
.sync_fn = ntlmssp_client_initial,
|
||||
},{
|
||||
.role = NTLMSSP_SERVER,
|
||||
.command = NTLMSSP_NEGOTIATE,
|
||||
.sync_fn = ntlmssp_server_negotiate,
|
||||
},{
|
||||
.role = NTLMSSP_CLIENT,
|
||||
.command = NTLMSSP_CHALLENGE,
|
||||
.sync_fn = ntlmssp_client_challenge,
|
||||
},{
|
||||
.role = NTLMSSP_SERVER,
|
||||
.command = NTLMSSP_AUTH,
|
||||
.sync_fn = ntlmssp_server_auth,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Print out the NTLMSSP flags for debugging
|
||||
* @param neg_flags The flags from the packet
|
||||
*/
|
||||
|
||||
void debug_ntlmssp_flags(uint32_t neg_flags)
|
||||
{
|
||||
DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags));
|
||||
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_OEM)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n"));
|
||||
if (neg_flags & NTLMSSP_REQUEST_TARGET)
|
||||
DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_SIGN)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_SEAL)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM_STYLE)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DATAGRAM_STYLE\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_NTLM)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n"));
|
||||
if (neg_flags & NTLMSSP_CHAL_ACCEPT_RESPONSE)
|
||||
DEBUGADD(4, (" NTLMSSP_CHAL_ACCEPT_RESPONSE\n"));
|
||||
if (neg_flags & NTLMSSP_CHAL_NON_NT_SESSION_KEY)
|
||||
DEBUGADD(4, (" NTLMSSP_CHAL_NON_NT_SESSION_KEY\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n"));
|
||||
if (neg_flags & NTLMSSP_CHAL_TARGET_INFO)
|
||||
DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_128)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_56)
|
||||
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_56\n"));
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_ntlmssp_magic(struct gensec_security *gensec_security,
|
||||
const DATA_BLOB *first_packet)
|
||||
{
|
||||
if (first_packet->length > 8 && memcmp("NTLMSSP\0", first_packet->data, 8) == 0) {
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS gensec_ntlmssp_update_find(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
const DATA_BLOB input, uint32_t *idx)
|
||||
{
|
||||
struct gensec_security *gensec_security = gensec_ntlmssp_state->gensec_security;
|
||||
uint32_t ntlmssp_command;
|
||||
uint32_t i;
|
||||
|
||||
if (gensec_ntlmssp_state->expected_state == NTLMSSP_DONE) {
|
||||
/* We are strict here because other modules, which we
|
||||
* don't fully control (such as GSSAPI) are also
|
||||
* strict, but are tested less often */
|
||||
|
||||
DEBUG(1, ("Called NTLMSSP after state machine was 'done'\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!input.length) {
|
||||
switch (gensec_ntlmssp_state->role) {
|
||||
case NTLMSSP_CLIENT:
|
||||
ntlmssp_command = NTLMSSP_INITIAL;
|
||||
break;
|
||||
case NTLMSSP_SERVER:
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_DATAGRAM_MODE) {
|
||||
/* 'datagram' mode - no neg packet */
|
||||
ntlmssp_command = NTLMSSP_NEGOTIATE;
|
||||
} else {
|
||||
/* This is normal in SPNEGO mech negotiation fallback */
|
||||
DEBUG(2, ("Failed to parse NTLMSSP packet: zero length\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!msrpc_parse(gensec_ntlmssp_state,
|
||||
&input, "Cd",
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command)) {
|
||||
DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n"));
|
||||
dump_data(2, input.data, input.length);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
if (ntlmssp_command != gensec_ntlmssp_state->expected_state) {
|
||||
DEBUG(2, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, gensec_ntlmssp_state->expected_state));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
for (i=0; i < ARRAY_SIZE(ntlmssp_callbacks); i++) {
|
||||
if (ntlmssp_callbacks[i].role == gensec_ntlmssp_state->role &&
|
||||
ntlmssp_callbacks[i].command == ntlmssp_command) {
|
||||
*idx = i;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n",
|
||||
gensec_ntlmssp_state->role, ntlmssp_command));
|
||||
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Next state function for the wrapped NTLMSSP state machine
|
||||
*
|
||||
* @param gensec_security GENSEC state, initialised to NTLMSSP
|
||||
* @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
|
||||
* @param in The request, as a DATA_BLOB
|
||||
* @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
|
||||
* @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
|
||||
* or NT_STATUS_OK if the user is authenticated.
|
||||
*/
|
||||
|
||||
static NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB input, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
NTSTATUS status;
|
||||
uint32_t i;
|
||||
|
||||
*out = data_blob(NULL, 0);
|
||||
|
||||
if (!out_mem_ctx) {
|
||||
/* if the caller doesn't want to manage/own the memory,
|
||||
we can put it on our context */
|
||||
out_mem_ctx = gensec_ntlmssp_state;
|
||||
}
|
||||
|
||||
status = gensec_ntlmssp_update_find(gensec_ntlmssp_state, input, &i);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = ntlmssp_callbacks[i].sync_fn(gensec_security, out_mem_ctx, input, out);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the NTLMSSP master session key
|
||||
*
|
||||
* @param gensec_ntlmssp_state NTLMSSP State
|
||||
*/
|
||||
|
||||
NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security,
|
||||
DATA_BLOB *session_key)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
if (!gensec_ntlmssp_state->session_key.data) {
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
*session_key = gensec_ntlmssp_state->session_key;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
void ntlmssp_handle_neg_flags(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
uint32_t neg_flags, BOOL allow_lm)
|
||||
{
|
||||
if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
|
||||
gensec_ntlmssp_state->unicode = True;
|
||||
} else {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE;
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
|
||||
gensec_ntlmssp_state->unicode = False;
|
||||
}
|
||||
|
||||
if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm && !gensec_ntlmssp_state->use_ntlmv2) {
|
||||
/* other end forcing us to use LM */
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
|
||||
} else {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_SIGN)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_SEAL)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SEAL;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_56)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_56;
|
||||
}
|
||||
|
||||
if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH;
|
||||
}
|
||||
|
||||
/* Woop Woop - unknown flag for Windows compatibility...
|
||||
What does this really do ? JRA. */
|
||||
if (!(neg_flags & NTLMSSP_UNKNOWN_02000000)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_UNKNOWN_02000000;
|
||||
}
|
||||
|
||||
if ((neg_flags & NTLMSSP_REQUEST_TARGET)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Weaken NTLMSSP keys to cope with down-level clients and servers.
|
||||
|
||||
We probably should have some parameters to control this, but as
|
||||
it only occours for LM_KEY connections, and this is controlled
|
||||
by the client lanman auth/lanman auth parameters, it isn't too bad.
|
||||
*/
|
||||
|
||||
DATA_BLOB ntlmssp_weakend_key(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
DATA_BLOB weakened_key = data_blob_talloc(mem_ctx,
|
||||
gensec_ntlmssp_state->session_key.data,
|
||||
gensec_ntlmssp_state->session_key.length);
|
||||
/* Nothing to weaken. We certainly don't want to 'extend' the length... */
|
||||
if (weakened_key.length < 16) {
|
||||
/* perhaps there was no key? */
|
||||
return weakened_key;
|
||||
}
|
||||
|
||||
/* Key weakening not performed on the master key for NTLM2
|
||||
and does not occour for NTLM1. Therefore we only need
|
||||
to do this for the LM_KEY.
|
||||
*/
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
|
||||
/* LM key doesn't support 128 bit crypto, so this is
|
||||
* the best we can do. If you negotiate 128 bit, but
|
||||
* not 56, you end up with 40 bit... */
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
|
||||
weakened_key.data[7] = 0xa0;
|
||||
weakened_key.length = 8;
|
||||
} else { /* forty bits */
|
||||
weakened_key.data[5] = 0xe5;
|
||||
weakened_key.data[6] = 0x38;
|
||||
weakened_key.data[7] = 0xb0;
|
||||
weakened_key.length = 8;
|
||||
}
|
||||
}
|
||||
return weakened_key;
|
||||
}
|
||||
|
||||
static BOOL gensec_ntlmssp_have_feature(struct gensec_security *gensec_security,
|
||||
uint32_t feature)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
if (feature & GENSEC_FEATURE_SIGN) {
|
||||
if (!gensec_ntlmssp_state->session_key.length) {
|
||||
return False;
|
||||
}
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_SEAL) {
|
||||
if (!gensec_ntlmssp_state->session_key.length) {
|
||||
return False;
|
||||
}
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_SESSION_KEY) {
|
||||
if (gensec_ntlmssp_state->session_key.length) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_DCE_STYLE) {
|
||||
return True;
|
||||
}
|
||||
if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state;
|
||||
|
||||
gensec_ntlmssp_state = talloc_zero(gensec_security, struct gensec_ntlmssp_state);
|
||||
if (!gensec_ntlmssp_state) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
gensec_ntlmssp_state->gensec_security = gensec_security;
|
||||
gensec_ntlmssp_state->auth_context = NULL;
|
||||
gensec_ntlmssp_state->server_info = NULL;
|
||||
|
||||
gensec_security->private_data = gensec_ntlmssp_state;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static const char *gensec_ntlmssp_oids[] = {
|
||||
GENSEC_OID_NTLMSSP,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
|
||||
.name = "ntlmssp",
|
||||
.sasl_name = "NTLM",
|
||||
.auth_type = DCERPC_AUTH_TYPE_NTLMSSP,
|
||||
.oid = gensec_ntlmssp_oids,
|
||||
.client_start = gensec_ntlmssp_client_start,
|
||||
.server_start = gensec_ntlmssp_server_start,
|
||||
.magic = gensec_ntlmssp_magic,
|
||||
.update = gensec_ntlmssp_update,
|
||||
.sig_size = gensec_ntlmssp_sig_size,
|
||||
.sign_packet = gensec_ntlmssp_sign_packet,
|
||||
.check_packet = gensec_ntlmssp_check_packet,
|
||||
.seal_packet = gensec_ntlmssp_seal_packet,
|
||||
.unseal_packet = gensec_ntlmssp_unseal_packet,
|
||||
.wrap = gensec_ntlmssp_wrap,
|
||||
.unwrap = gensec_ntlmssp_unwrap,
|
||||
.session_key = gensec_ntlmssp_session_key,
|
||||
.session_info = gensec_ntlmssp_session_info,
|
||||
.have_feature = gensec_ntlmssp_have_feature,
|
||||
.enabled = True,
|
||||
.priority = GENSEC_NTLMSSP
|
||||
};
|
||||
|
||||
|
||||
NTSTATUS gensec_ntlmssp_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
|
||||
auth_init();
|
||||
|
||||
ret = gensec_register(&gensec_ntlmssp_security_ops);
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register '%s' gensec backend!\n",
|
||||
gensec_ntlmssp_security_ops.name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB parameters and setup
|
||||
Copyright (C) Andrew Tridgell 1992-1997
|
||||
Copyright (C) Luke Kenneth Casson Leighton 1996-1997
|
||||
Copyright (C) Paul Ashton 1997
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "librpc/gen_ndr/samr.h"
|
||||
|
||||
/* NTLMSSP mode */
|
||||
enum ntlmssp_role
|
||||
{
|
||||
NTLMSSP_SERVER,
|
||||
NTLMSSP_CLIENT
|
||||
};
|
||||
|
||||
/* NTLMSSP message types */
|
||||
enum ntlmssp_message_type
|
||||
{
|
||||
NTLMSSP_INITIAL = 0 /* samba internal state */,
|
||||
NTLMSSP_NEGOTIATE = 1,
|
||||
NTLMSSP_CHALLENGE = 2,
|
||||
NTLMSSP_AUTH = 3,
|
||||
NTLMSSP_UNKNOWN = 4,
|
||||
NTLMSSP_DONE = 5 /* samba final state */
|
||||
};
|
||||
|
||||
/* NTLMSSP negotiation flags */
|
||||
#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
|
||||
#define NTLMSSP_NEGOTIATE_OEM 0x00000002
|
||||
#define NTLMSSP_REQUEST_TARGET 0x00000004
|
||||
#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* Message integrity */
|
||||
#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* Message confidentiality */
|
||||
#define NTLMSSP_NEGOTIATE_DATAGRAM_STYLE 0x00000040
|
||||
#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
|
||||
#define NTLMSSP_NEGOTIATE_NETWARE 0x00000100
|
||||
#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
|
||||
#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
|
||||
#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
|
||||
#define NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL 0x00004000
|
||||
#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
|
||||
#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000
|
||||
#define NTLMSSP_TARGET_TYPE_SERVER 0x20000
|
||||
#define NTLMSSP_CHAL_INIT_RESPONSE 0x00010000
|
||||
|
||||
#define NTLMSSP_CHAL_ACCEPT_RESPONSE 0x00020000
|
||||
#define NTLMSSP_CHAL_NON_NT_SESSION_KEY 0x00040000
|
||||
#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
|
||||
#define NTLMSSP_CHAL_TARGET_INFO 0x00800000
|
||||
#define NTLMSSP_UNKNOWN_02000000 0x02000000
|
||||
#define NTLMSSP_NEGOTIATE_128 0x20000000 /* 128-bit encryption */
|
||||
#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000
|
||||
#define NTLMSSP_NEGOTIATE_56 0x80000000
|
||||
|
||||
#define NTLMSSP_NAME_TYPE_SERVER 0x01
|
||||
#define NTLMSSP_NAME_TYPE_DOMAIN 0x02
|
||||
#define NTLMSSP_NAME_TYPE_SERVER_DNS 0x03
|
||||
#define NTLMSSP_NAME_TYPE_DOMAIN_DNS 0x04
|
||||
|
||||
#define NTLMSSP_SIGN_VERSION 1
|
||||
|
||||
#define NTLMSSP_SIG_SIZE 16
|
||||
|
||||
struct gensec_ntlmssp_state
|
||||
{
|
||||
struct gensec_security *gensec_security;
|
||||
|
||||
enum ntlmssp_role role;
|
||||
enum samr_Role server_role;
|
||||
uint32_t expected_state;
|
||||
|
||||
BOOL unicode;
|
||||
BOOL use_ntlmv2;
|
||||
BOOL use_nt_response; /* Set to 'False' to debug what happens when the NT response is omited */
|
||||
BOOL allow_lm_key; /* The LM_KEY code is not functional at this point, and it's not
|
||||
very secure anyway */
|
||||
|
||||
BOOL server_multiple_authentications; /* Set to 'True' to allow squid 2.5
|
||||
style 'challenge caching' */
|
||||
|
||||
char *user;
|
||||
char *domain;
|
||||
const char *workstation;
|
||||
char *server_domain;
|
||||
|
||||
DATA_BLOB internal_chal; /* Random challenge as supplied to the client for NTLM authentication */
|
||||
|
||||
DATA_BLOB chal; /* Random challenge as input into the actual NTLM (or NTLM2) authentication */
|
||||
DATA_BLOB lm_resp;
|
||||
DATA_BLOB nt_resp;
|
||||
DATA_BLOB session_key;
|
||||
|
||||
uint32_t neg_flags; /* the current state of negotiation with the NTLMSSP partner */
|
||||
|
||||
/* internal variables used by KEY_EXCH (client-supplied user session key */
|
||||
DATA_BLOB encrypted_session_key;
|
||||
|
||||
/**
|
||||
* Callback to get the 'challenge' used for NTLM authentication.
|
||||
*
|
||||
* @param ntlmssp_state This structure
|
||||
* @return 8 bytes of challenge data, determined by the server to be the challenge for NTLM authentication
|
||||
*
|
||||
*/
|
||||
const uint8_t *(*get_challenge)(const struct gensec_ntlmssp_state *);
|
||||
|
||||
/**
|
||||
* Callback to find if the challenge used by NTLM authentication may be modified
|
||||
*
|
||||
* The NTLM2 authentication scheme modifies the effective challenge, but this is not compatiable with the
|
||||
* current 'security=server' implementation..
|
||||
*
|
||||
* @param ntlmssp_state This structure
|
||||
* @return Can the challenge be set to arbitary values?
|
||||
*
|
||||
*/
|
||||
BOOL (*may_set_challenge)(const struct gensec_ntlmssp_state *);
|
||||
|
||||
/**
|
||||
* Callback to set the 'challenge' used for NTLM authentication.
|
||||
*
|
||||
* The callback may use the void *auth_context to store state information, but the same value is always available
|
||||
* from the DATA_BLOB chal on this structure.
|
||||
*
|
||||
* @param ntlmssp_state This structure
|
||||
* @param challenge 8 bytes of data, agreed by the client and server to be the effective challenge for NTLM2 authentication
|
||||
*
|
||||
*/
|
||||
NTSTATUS (*set_challenge)(struct gensec_ntlmssp_state *, DATA_BLOB *challenge);
|
||||
|
||||
/**
|
||||
* Callback to check the user's password.
|
||||
*
|
||||
* The callback must reads the feilds of this structure for the information it needs on the user
|
||||
* @param ntlmssp_state This structure
|
||||
* @param nt_session_key If an NT session key is returned by the authentication process, return it here
|
||||
* @param lm_session_key If an LM session key is returned by the authentication process, return it here
|
||||
*
|
||||
*/
|
||||
NTSTATUS (*check_password)(struct gensec_ntlmssp_state *,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key);
|
||||
|
||||
const char *server_name;
|
||||
const char *(*get_domain)(void);
|
||||
|
||||
BOOL doing_ntlm2;
|
||||
|
||||
union {
|
||||
/* NTLM */
|
||||
struct {
|
||||
uint32_t seq_num;
|
||||
struct arcfour_state *arcfour_state;
|
||||
} ntlm;
|
||||
|
||||
/* NTLM2 */
|
||||
struct {
|
||||
uint32_t send_seq_num;
|
||||
uint32_t recv_seq_num;
|
||||
DATA_BLOB send_sign_key;
|
||||
DATA_BLOB recv_sign_key;
|
||||
struct arcfour_state *send_seal_arcfour_state;
|
||||
struct arcfour_state *recv_seal_arcfour_state;
|
||||
|
||||
/* internal variables used by NTLM2 */
|
||||
uint8_t session_nonce[16];
|
||||
} ntlm2;
|
||||
} crypt;
|
||||
|
||||
struct auth_context *auth_context;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
};
|
||||
|
||||
struct auth_session_info;
|
||||
#include "auth/ntlmssp/proto.h"
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Version 3.0
|
||||
handle NLTMSSP, client server side parsing
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/ntlmssp/ntlmssp.h"
|
||||
#include "auth/ntlmssp/msrpc_parse.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
/*********************************************************************
|
||||
Client side NTLMSSP
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Next state function for the Initial packet
|
||||
*
|
||||
* @param ntlmssp_state NTLMSSP State
|
||||
* @param out_mem_ctx The DATA_BLOB *out will be allocated on this context
|
||||
* @param in A NULL data blob (input ignored)
|
||||
* @param out The initial negotiate request to the server, as an talloc()ed DATA_BLOB, on out_mem_ctx
|
||||
* @return Errors or NT_STATUS_OK.
|
||||
*/
|
||||
|
||||
NTSTATUS ntlmssp_client_initial(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
if (gensec_ntlmssp_state->unicode) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
|
||||
} else {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
|
||||
}
|
||||
|
||||
if (gensec_ntlmssp_state->use_ntlmv2) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
|
||||
}
|
||||
|
||||
/* generate the ntlmssp negotiate packet */
|
||||
msrpc_gen(out_mem_ctx,
|
||||
out, "CddAA",
|
||||
"NTLMSSP",
|
||||
NTLMSSP_NEGOTIATE,
|
||||
gensec_ntlmssp_state->neg_flags,
|
||||
gensec_ntlmssp_state->get_domain(),
|
||||
cli_credentials_get_workstation(gensec_security->credentials));
|
||||
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_CHALLENGE;
|
||||
|
||||
return NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Next state function for the Challenge Packet. Generate an auth packet.
|
||||
*
|
||||
* @param gensec_security GENSEC state
|
||||
* @param out_mem_ctx Memory context for *out
|
||||
* @param in The server challnege, as a DATA_BLOB. reply.data must be NULL
|
||||
* @param out The next request (auth packet) to the server, as an allocated DATA_BLOB, on the out_mem_ctx context
|
||||
* @return Errors or NT_STATUS_OK.
|
||||
*/
|
||||
|
||||
NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
uint32_t chal_flags, ntlmssp_command, unkn1, unkn2;
|
||||
DATA_BLOB server_domain_blob;
|
||||
DATA_BLOB challenge_blob;
|
||||
DATA_BLOB target_info = data_blob(NULL, 0);
|
||||
char *server_domain;
|
||||
const char *chal_parse_string;
|
||||
const char *auth_gen_string;
|
||||
DATA_BLOB lm_response = data_blob(NULL, 0);
|
||||
DATA_BLOB nt_response = data_blob(NULL, 0);
|
||||
DATA_BLOB session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB lm_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB encrypted_session_key = data_blob(NULL, 0);
|
||||
NTSTATUS nt_status;
|
||||
int flags = 0;
|
||||
const char *user, *domain;
|
||||
|
||||
TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (!msrpc_parse(mem_ctx,
|
||||
&in, "CdBd",
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&server_domain_blob,
|
||||
&chal_flags)) {
|
||||
DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
|
||||
dump_data(2, in.data, in.length);
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
data_blob_free(&server_domain_blob);
|
||||
|
||||
DEBUG(3, ("Got challenge flags:\n"));
|
||||
debug_ntlmssp_flags(chal_flags);
|
||||
|
||||
ntlmssp_handle_neg_flags(gensec_ntlmssp_state, chal_flags, gensec_ntlmssp_state->allow_lm_key);
|
||||
|
||||
if (gensec_ntlmssp_state->unicode) {
|
||||
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
|
||||
chal_parse_string = "CdUdbddB";
|
||||
} else {
|
||||
chal_parse_string = "CdUdbdd";
|
||||
}
|
||||
auth_gen_string = "CdBBUUUBd";
|
||||
} else {
|
||||
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
|
||||
chal_parse_string = "CdAdbddB";
|
||||
} else {
|
||||
chal_parse_string = "CdAdbdd";
|
||||
}
|
||||
|
||||
auth_gen_string = "CdBBAAABd";
|
||||
}
|
||||
|
||||
if (!msrpc_parse(mem_ctx,
|
||||
&in, chal_parse_string,
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&server_domain,
|
||||
&chal_flags,
|
||||
&challenge_blob, 8,
|
||||
&unkn1, &unkn2,
|
||||
&target_info)) {
|
||||
DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));
|
||||
dump_data(2, in.data, in.length);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gensec_ntlmssp_state->server_domain = server_domain;
|
||||
|
||||
if (challenge_blob.length != 8) {
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
cli_credentials_get_ntlm_username_domain(gensec_security->credentials, mem_ctx,
|
||||
&user, &domain);
|
||||
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
flags |= CLI_CRED_NTLM2;
|
||||
}
|
||||
if (gensec_ntlmssp_state->use_ntlmv2) {
|
||||
flags |= CLI_CRED_NTLMv2_AUTH;
|
||||
}
|
||||
if (gensec_ntlmssp_state->use_nt_response) {
|
||||
flags |= CLI_CRED_NTLM_AUTH;
|
||||
}
|
||||
if (lp_client_lanman_auth()) {
|
||||
flags |= CLI_CRED_LANMAN_AUTH;
|
||||
}
|
||||
|
||||
nt_status = cli_credentials_get_ntlm_response(gensec_security->credentials, mem_ctx,
|
||||
&flags, challenge_blob, target_info,
|
||||
&lm_response, &nt_response,
|
||||
&lm_session_key, &session_key);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (!(flags & CLI_CRED_LANMAN_AUTH)) {
|
||||
/* LM Key is still possible, just silly. Fortunetly
|
||||
* we require command line options to end up here */
|
||||
/* gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; */
|
||||
}
|
||||
|
||||
if (!(flags & CLI_CRED_NTLM2)) {
|
||||
/* NTLM2 is incompatible... */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
|
||||
}
|
||||
|
||||
if ((gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
|
||||
&& lp_client_lanman_auth() && lm_session_key.length == 16) {
|
||||
DATA_BLOB new_session_key = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
if (lm_response.length == 24) {
|
||||
SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data,
|
||||
new_session_key.data);
|
||||
} else {
|
||||
static const uint8_t zeros[24];
|
||||
SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros,
|
||||
new_session_key.data);
|
||||
}
|
||||
session_key = new_session_key;
|
||||
dump_data_pw("LM session key\n", session_key.data, session_key.length);
|
||||
}
|
||||
|
||||
|
||||
/* Key exchange encryptes a new client-generated session key with
|
||||
the password-derived key */
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
|
||||
/* Make up a new session key */
|
||||
uint8_t client_session_key[16];
|
||||
generate_random_buffer(client_session_key, sizeof(client_session_key));
|
||||
|
||||
/* Encrypt the new session key with the old one */
|
||||
encrypted_session_key = data_blob_talloc(gensec_ntlmssp_state,
|
||||
client_session_key, sizeof(client_session_key));
|
||||
dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length);
|
||||
arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length);
|
||||
dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);
|
||||
|
||||
/* Mark the new session key as the 'real' session key */
|
||||
session_key = data_blob_talloc(mem_ctx, client_session_key, sizeof(client_session_key));
|
||||
}
|
||||
|
||||
DEBUG(3, ("NTLMSSP: Set final flags:\n"));
|
||||
debug_ntlmssp_flags(gensec_ntlmssp_state->neg_flags);
|
||||
|
||||
/* this generates the actual auth packet */
|
||||
if (!msrpc_gen(mem_ctx,
|
||||
out, auth_gen_string,
|
||||
"NTLMSSP",
|
||||
NTLMSSP_AUTH,
|
||||
lm_response.data, lm_response.length,
|
||||
nt_response.data, nt_response.length,
|
||||
domain,
|
||||
user,
|
||||
cli_credentials_get_workstation(gensec_security->credentials),
|
||||
encrypted_session_key.data, encrypted_session_key.length,
|
||||
gensec_ntlmssp_state->neg_flags)) {
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
gensec_ntlmssp_state->session_key = session_key;
|
||||
talloc_steal(gensec_ntlmssp_state, session_key.data);
|
||||
|
||||
talloc_steal(out_mem_ctx, out->data);
|
||||
|
||||
gensec_ntlmssp_state->chal = challenge_blob;
|
||||
gensec_ntlmssp_state->lm_resp = lm_response;
|
||||
talloc_steal(gensec_ntlmssp_state->lm_resp.data, lm_response.data);
|
||||
gensec_ntlmssp_state->nt_resp = nt_response;
|
||||
talloc_steal(gensec_ntlmssp_state->nt_resp.data, nt_response.data);
|
||||
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_DONE;
|
||||
|
||||
if (gensec_security->want_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) {
|
||||
nt_status = ntlmssp_sign_init(gensec_ntlmssp_state);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n",
|
||||
nt_errstr(nt_status)));
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
nt_status = gensec_ntlmssp_start(gensec_security);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
gensec_ntlmssp_state->role = NTLMSSP_CLIENT;
|
||||
|
||||
gensec_ntlmssp_state->get_domain = lp_workgroup;
|
||||
|
||||
gensec_ntlmssp_state->unicode = lp_parm_bool(-1, "ntlmssp_client", "unicode", True);
|
||||
|
||||
gensec_ntlmssp_state->use_nt_response = lp_parm_bool(-1, "ntlmssp_client", "send_nt_reponse", True);
|
||||
|
||||
gensec_ntlmssp_state->allow_lm_key = (lp_client_lanman_auth()
|
||||
&& (lp_parm_bool(-1, "ntlmssp_client", "allow_lm_key", False)
|
||||
|| lp_parm_bool(-1, "ntlmssp_client", "lm_key", False)));
|
||||
|
||||
gensec_ntlmssp_state->use_ntlmv2 = lp_client_ntlmv2_auth();
|
||||
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_INITIAL;
|
||||
|
||||
gensec_ntlmssp_state->neg_flags =
|
||||
NTLMSSP_NEGOTIATE_NTLM |
|
||||
NTLMSSP_REQUEST_TARGET;
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "128bit", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_128;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "56bit", False)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "lm_key", False)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "keyexchange", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "alwayssign", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_client", "ntlm2", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
|
||||
} else {
|
||||
/* apparently we can't do ntlmv2 if we don't do ntlm2 */
|
||||
gensec_ntlmssp_state->use_ntlmv2 = False;
|
||||
}
|
||||
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
|
||||
/*
|
||||
* We need to set this to allow a later SetPassword
|
||||
* via the SAMR pipe to succeed. Strange.... We could
|
||||
* also add NTLMSSP_NEGOTIATE_SEAL here. JRA.
|
||||
*
|
||||
* Without this, Windows will not create the master key
|
||||
* that it thinks is only used for NTLMSSP signing and
|
||||
* sealing. (It is actually pulled out and used directly)
|
||||
*/
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
|
||||
}
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
|
||||
}
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
|
||||
}
|
||||
|
||||
gensec_security->private_data = gensec_ntlmssp_state;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
simple kerberos5/SPNEGO routines
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
|
||||
Copyright (C) Andrew Bartlett 2002-2003
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "pstring.h"
|
||||
|
||||
/*
|
||||
this is a tiny msrpc packet generator. I am only using this to
|
||||
avoid tying this code to a particular varient of our rpc code. This
|
||||
generator is not general enough for all our rpc needs, its just
|
||||
enough for the spnego/ntlmssp code
|
||||
|
||||
format specifiers are:
|
||||
|
||||
U = unicode string (input is unix string)
|
||||
a = address (input is char *unix_string)
|
||||
(1 byte type, 1 byte length, unicode/ASCII string, all inline)
|
||||
A = ASCII string (input is unix string)
|
||||
B = data blob (pointer + length)
|
||||
b = data blob in header (pointer + length)
|
||||
D
|
||||
d = word (4 bytes)
|
||||
C = constant ascii string
|
||||
*/
|
||||
BOOL msrpc_gen(TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
|
||||
const char *format, ...)
|
||||
{
|
||||
int i;
|
||||
ssize_t n;
|
||||
va_list ap;
|
||||
char *s;
|
||||
uint8_t *b;
|
||||
int head_size=0, data_size=0;
|
||||
int head_ofs, data_ofs;
|
||||
int *intargs;
|
||||
|
||||
DATA_BLOB *pointers;
|
||||
|
||||
pointers = talloc_array(mem_ctx, DATA_BLOB, strlen(format));
|
||||
intargs = talloc_array(pointers, int, strlen(format));
|
||||
|
||||
/* first scan the format to work out the header and body size */
|
||||
va_start(ap, format);
|
||||
for (i=0; format[i]; i++) {
|
||||
switch (format[i]) {
|
||||
case 'U':
|
||||
s = va_arg(ap, char *);
|
||||
head_size += 8;
|
||||
n = push_ucs2_talloc(pointers, (void **)&pointers[i].data, s);
|
||||
if (n == -1) {
|
||||
return False;
|
||||
}
|
||||
pointers[i].length = n;
|
||||
pointers[i].length -= 2;
|
||||
data_size += pointers[i].length;
|
||||
break;
|
||||
case 'A':
|
||||
s = va_arg(ap, char *);
|
||||
head_size += 8;
|
||||
n = push_ascii_talloc(pointers, (char **)&pointers[i].data, s);
|
||||
if (n == -1) {
|
||||
return False;
|
||||
}
|
||||
pointers[i].length = n;
|
||||
pointers[i].length -= 1;
|
||||
data_size += pointers[i].length;
|
||||
break;
|
||||
case 'a':
|
||||
n = va_arg(ap, int);
|
||||
intargs[i] = n;
|
||||
s = va_arg(ap, char *);
|
||||
n = push_ucs2_talloc(pointers, (void **)&pointers[i].data, s);
|
||||
if (n == -1) {
|
||||
return False;
|
||||
}
|
||||
pointers[i].length = n;
|
||||
pointers[i].length -= 2;
|
||||
data_size += pointers[i].length + 4;
|
||||
break;
|
||||
case 'B':
|
||||
b = va_arg(ap, uint8_t *);
|
||||
head_size += 8;
|
||||
pointers[i].data = b;
|
||||
pointers[i].length = va_arg(ap, int);
|
||||
data_size += pointers[i].length;
|
||||
break;
|
||||
case 'b':
|
||||
b = va_arg(ap, uint8_t *);
|
||||
pointers[i].data = b;
|
||||
pointers[i].length = va_arg(ap, int);
|
||||
head_size += pointers[i].length;
|
||||
break;
|
||||
case 'd':
|
||||
n = va_arg(ap, int);
|
||||
intargs[i] = n;
|
||||
head_size += 4;
|
||||
break;
|
||||
case 'C':
|
||||
s = va_arg(ap, char *);
|
||||
pointers[i].data = (uint8_t *)s;
|
||||
pointers[i].length = strlen(s)+1;
|
||||
head_size += pointers[i].length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
/* allocate the space, then scan the format again to fill in the values */
|
||||
*blob = data_blob_talloc(mem_ctx, NULL, head_size + data_size);
|
||||
|
||||
head_ofs = 0;
|
||||
data_ofs = head_size;
|
||||
|
||||
va_start(ap, format);
|
||||
for (i=0; format[i]; i++) {
|
||||
switch (format[i]) {
|
||||
case 'U':
|
||||
case 'A':
|
||||
case 'B':
|
||||
n = pointers[i].length;
|
||||
SSVAL(blob->data, head_ofs, n); head_ofs += 2;
|
||||
SSVAL(blob->data, head_ofs, n); head_ofs += 2;
|
||||
SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
|
||||
if (pointers[i].data && n) /* don't follow null pointers... */
|
||||
memcpy(blob->data+data_ofs, pointers[i].data, n);
|
||||
data_ofs += n;
|
||||
break;
|
||||
case 'a':
|
||||
n = intargs[i];
|
||||
SSVAL(blob->data, data_ofs, n); data_ofs += 2;
|
||||
|
||||
n = pointers[i].length;
|
||||
SSVAL(blob->data, data_ofs, n); data_ofs += 2;
|
||||
if (n >= 0) {
|
||||
memcpy(blob->data+data_ofs, pointers[i].data, n);
|
||||
}
|
||||
data_ofs += n;
|
||||
break;
|
||||
case 'd':
|
||||
n = intargs[i];
|
||||
SIVAL(blob->data, head_ofs, n);
|
||||
head_ofs += 4;
|
||||
break;
|
||||
case 'b':
|
||||
n = pointers[i].length;
|
||||
memcpy(blob->data + head_ofs, pointers[i].data, n);
|
||||
head_ofs += n;
|
||||
break;
|
||||
case 'C':
|
||||
n = pointers[i].length;
|
||||
memcpy(blob->data + head_ofs, pointers[i].data, n);
|
||||
head_ofs += n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
talloc_free(pointers);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/* a helpful macro to avoid running over the end of our blob */
|
||||
#define NEED_DATA(amount) \
|
||||
if ((head_ofs + amount) > blob->length) { \
|
||||
return False; \
|
||||
}
|
||||
|
||||
/*
|
||||
this is a tiny msrpc packet parser. This the the partner of msrpc_gen
|
||||
|
||||
format specifiers are:
|
||||
|
||||
U = unicode string (output is unix string)
|
||||
A = ascii string
|
||||
B = data blob
|
||||
b = data blob in header
|
||||
d = word (4 bytes)
|
||||
C = constant ascii string
|
||||
*/
|
||||
|
||||
BOOL msrpc_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob,
|
||||
const char *format, ...)
|
||||
{
|
||||
int i;
|
||||
va_list ap;
|
||||
const char **ps, *s;
|
||||
DATA_BLOB *b;
|
||||
size_t head_ofs = 0;
|
||||
uint16_t len1, len2;
|
||||
uint32_t ptr;
|
||||
uint32_t *v;
|
||||
pstring p;
|
||||
|
||||
va_start(ap, format);
|
||||
for (i=0; format[i]; i++) {
|
||||
switch (format[i]) {
|
||||
case 'U':
|
||||
NEED_DATA(8);
|
||||
len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
|
||||
|
||||
ps = (const char **)va_arg(ap, char **);
|
||||
if (len1 == 0 && len2 == 0) {
|
||||
*ps = "";
|
||||
} else {
|
||||
/* make sure its in the right format - be strict */
|
||||
if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
|
||||
return False;
|
||||
}
|
||||
if (len1 & 1) {
|
||||
/* if odd length and unicode */
|
||||
return False;
|
||||
}
|
||||
if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data)
|
||||
return False;
|
||||
|
||||
if (0 < len1) {
|
||||
pull_string(p, blob->data + ptr, sizeof(p),
|
||||
len1, STR_UNICODE|STR_NOALIGN);
|
||||
(*ps) = talloc_strdup(mem_ctx, p);
|
||||
if (!(*ps)) {
|
||||
return False;
|
||||
}
|
||||
} else {
|
||||
(*ps) = "";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'A':
|
||||
NEED_DATA(8);
|
||||
len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
|
||||
|
||||
ps = (const char **)va_arg(ap, char **);
|
||||
/* make sure its in the right format - be strict */
|
||||
if (len1 == 0 && len2 == 0) {
|
||||
*ps = "";
|
||||
} else {
|
||||
if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data)
|
||||
return False;
|
||||
|
||||
if (0 < len1) {
|
||||
pull_string(p, blob->data + ptr, sizeof(p),
|
||||
len1, STR_ASCII|STR_NOALIGN);
|
||||
(*ps) = talloc_strdup(mem_ctx, p);
|
||||
if (!(*ps)) {
|
||||
return False;
|
||||
}
|
||||
} else {
|
||||
(*ps) = "";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
NEED_DATA(8);
|
||||
len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
|
||||
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
|
||||
|
||||
b = (DATA_BLOB *)va_arg(ap, void *);
|
||||
if (len1 == 0 && len2 == 0) {
|
||||
*b = data_blob_talloc(mem_ctx, NULL, 0);
|
||||
} else {
|
||||
/* make sure its in the right format - be strict */
|
||||
if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data)
|
||||
return False;
|
||||
|
||||
*b = data_blob_talloc(mem_ctx, blob->data + ptr, len1);
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
b = (DATA_BLOB *)va_arg(ap, void *);
|
||||
len1 = va_arg(ap, uint_t);
|
||||
/* make sure its in the right format - be strict */
|
||||
NEED_DATA(len1);
|
||||
if (blob->data + head_ofs < (uint8_t *)head_ofs || blob->data + head_ofs < blob->data)
|
||||
return False;
|
||||
|
||||
*b = data_blob_talloc(mem_ctx, blob->data + head_ofs, len1);
|
||||
head_ofs += len1;
|
||||
break;
|
||||
case 'd':
|
||||
v = va_arg(ap, uint32_t *);
|
||||
NEED_DATA(4);
|
||||
*v = IVAL(blob->data, head_ofs); head_ofs += 4;
|
||||
break;
|
||||
case 'C':
|
||||
s = va_arg(ap, char *);
|
||||
|
||||
if (blob->data + head_ofs < (uint8_t *)head_ofs || blob->data + head_ofs < blob->data)
|
||||
return False;
|
||||
|
||||
head_ofs += pull_string(p, blob->data+head_ofs, sizeof(p),
|
||||
blob->length - head_ofs,
|
||||
STR_ASCII|STR_TERMINATE);
|
||||
if (strcmp(s, p) != 0) {
|
||||
return False;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return True;
|
||||
}
|
||||
@@ -0,0 +1,852 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Version 3.0
|
||||
handle NLTMSSP, client server side parsing
|
||||
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/ntlmssp/ntlmssp.h"
|
||||
#include "auth/ntlmssp/msrpc_parse.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "pstring.h"
|
||||
#include "system/filesys.h"
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/auth.h"
|
||||
|
||||
/**
|
||||
* Set a username on an NTLMSSP context - ensures it is talloc()ed
|
||||
*
|
||||
*/
|
||||
|
||||
static NTSTATUS ntlmssp_set_username(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *user)
|
||||
{
|
||||
if (!user) {
|
||||
/* it should be at least "" */
|
||||
DEBUG(1, ("NTLMSSP failed to set username - cannot accept NULL username\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
gensec_ntlmssp_state->user = talloc_strdup(gensec_ntlmssp_state, user);
|
||||
if (!gensec_ntlmssp_state->user) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a domain on an NTLMSSP context - ensures it is talloc()ed
|
||||
*
|
||||
*/
|
||||
static NTSTATUS ntlmssp_set_domain(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *domain)
|
||||
{
|
||||
gensec_ntlmssp_state->domain = talloc_strdup(gensec_ntlmssp_state, domain);
|
||||
if (!gensec_ntlmssp_state->domain) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a workstation on an NTLMSSP context - ensures it is talloc()ed
|
||||
*
|
||||
*/
|
||||
static NTSTATUS ntlmssp_set_workstation(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *workstation)
|
||||
{
|
||||
gensec_ntlmssp_state->workstation = talloc_strdup(gensec_ntlmssp_state, workstation);
|
||||
if (!gensec_ntlmssp_state->workstation) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine correct target name flags for reply, given server role
|
||||
* and negotiated flags
|
||||
*
|
||||
* @param gensec_ntlmssp_state NTLMSSP State
|
||||
* @param neg_flags The flags from the packet
|
||||
* @param chal_flags The flags to be set in the reply packet
|
||||
* @return The 'target name' string.
|
||||
*/
|
||||
|
||||
static const char *ntlmssp_target_name(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
uint32_t neg_flags, uint32_t *chal_flags)
|
||||
{
|
||||
if (neg_flags & NTLMSSP_REQUEST_TARGET) {
|
||||
*chal_flags |= NTLMSSP_CHAL_TARGET_INFO;
|
||||
*chal_flags |= NTLMSSP_REQUEST_TARGET;
|
||||
if (gensec_ntlmssp_state->server_role == ROLE_STANDALONE) {
|
||||
*chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
|
||||
return gensec_ntlmssp_state->server_name;
|
||||
} else {
|
||||
*chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
|
||||
return gensec_ntlmssp_state->get_domain();
|
||||
};
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Andrew, please remove these totally bogus calls when you get time
|
||||
*/
|
||||
static BOOL get_myfullname(char *my_name)
|
||||
{
|
||||
pstring hostname;
|
||||
|
||||
*hostname = 0;
|
||||
|
||||
/* get my host name */
|
||||
if (gethostname(hostname, sizeof(hostname)) == -1) {
|
||||
DEBUG(0,("gethostname failed\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Ensure null termination. */
|
||||
hostname[sizeof(hostname)-1] = '\0';
|
||||
|
||||
if (my_name)
|
||||
fstrcpy(my_name, hostname);
|
||||
return True;
|
||||
}
|
||||
|
||||
static BOOL get_mydomname(char *my_domname)
|
||||
{
|
||||
pstring hostname;
|
||||
char *p;
|
||||
|
||||
/* arrgh! relies on full name in system */
|
||||
|
||||
*hostname = 0;
|
||||
/* get my host name */
|
||||
if (gethostname(hostname, sizeof(hostname)) == -1) {
|
||||
DEBUG(0,("gethostname failed\n"));
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Ensure null termination. */
|
||||
hostname[sizeof(hostname)-1] = '\0';
|
||||
|
||||
p = strchr_m(hostname, '.');
|
||||
|
||||
if (!p)
|
||||
return False;
|
||||
|
||||
p++;
|
||||
|
||||
if (my_domname)
|
||||
fstrcpy(my_domname, p);
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Next state function for the Negotiate packet
|
||||
*
|
||||
* @param gensec_security GENSEC state
|
||||
* @param out_mem_ctx Memory context for *out
|
||||
* @param in The request, as a DATA_BLOB. reply.data must be NULL
|
||||
* @param out The reply, as an allocated DATA_BLOB, caller to free.
|
||||
* @return Errors or MORE_PROCESSING_REQUIRED if (normal) a reply is required.
|
||||
*/
|
||||
|
||||
NTSTATUS ntlmssp_server_negotiate(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
DATA_BLOB struct_blob;
|
||||
fstring dnsname, dnsdomname;
|
||||
uint32_t neg_flags = 0;
|
||||
uint32_t ntlmssp_command, chal_flags;
|
||||
const uint8_t *cryptkey;
|
||||
const char *target_name;
|
||||
|
||||
/* parse the NTLMSSP packet */
|
||||
#if 0
|
||||
file_save("ntlmssp_negotiate.dat", request.data, request.length);
|
||||
#endif
|
||||
|
||||
if (in.length) {
|
||||
if ((in.length < 16) || !msrpc_parse(out_mem_ctx, &in, "Cdd",
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&neg_flags)) {
|
||||
DEBUG(1, ("ntlmssp_server_negotiate: failed to parse "
|
||||
"NTLMSSP Negotiate of length %u:\n",
|
||||
(unsigned int)in.length ));
|
||||
dump_data(2, in.data, in.length);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
debug_ntlmssp_flags(neg_flags);
|
||||
}
|
||||
|
||||
ntlmssp_handle_neg_flags(gensec_ntlmssp_state, neg_flags, gensec_ntlmssp_state->allow_lm_key);
|
||||
|
||||
/* Ask our caller what challenge they would like in the packet */
|
||||
cryptkey = gensec_ntlmssp_state->get_challenge(gensec_ntlmssp_state);
|
||||
|
||||
/* Check if we may set the challenge */
|
||||
if (!gensec_ntlmssp_state->may_set_challenge(gensec_ntlmssp_state)) {
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
|
||||
}
|
||||
|
||||
/* The flags we send back are not just the negotiated flags,
|
||||
* they are also 'what is in this packet'. Therfore, we
|
||||
* operate on 'chal_flags' from here on
|
||||
*/
|
||||
|
||||
chal_flags = gensec_ntlmssp_state->neg_flags;
|
||||
|
||||
/* get the right name to fill in as 'target' */
|
||||
target_name = ntlmssp_target_name(gensec_ntlmssp_state,
|
||||
neg_flags, &chal_flags);
|
||||
if (target_name == NULL)
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
|
||||
gensec_ntlmssp_state->chal = data_blob_talloc(gensec_ntlmssp_state, cryptkey, 8);
|
||||
gensec_ntlmssp_state->internal_chal = data_blob_talloc(gensec_ntlmssp_state, cryptkey, 8);
|
||||
|
||||
/* This should be a 'netbios domain -> DNS domain' mapping */
|
||||
dnsdomname[0] = '\0';
|
||||
get_mydomname(dnsdomname);
|
||||
strlower_m(dnsdomname);
|
||||
|
||||
dnsname[0] = '\0';
|
||||
get_myfullname(dnsname);
|
||||
|
||||
/* This creates the 'blob' of names that appears at the end of the packet */
|
||||
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO)
|
||||
{
|
||||
const char *target_name_dns = "";
|
||||
if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) {
|
||||
target_name_dns = dnsdomname;
|
||||
} else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) {
|
||||
target_name_dns = dnsname;
|
||||
}
|
||||
|
||||
msrpc_gen(out_mem_ctx,
|
||||
&struct_blob, "aaaaa",
|
||||
NTLMSSP_NAME_TYPE_DOMAIN, target_name,
|
||||
NTLMSSP_NAME_TYPE_SERVER, gensec_ntlmssp_state->server_name,
|
||||
NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname,
|
||||
NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname,
|
||||
0, "");
|
||||
} else {
|
||||
struct_blob = data_blob(NULL, 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* Marshel the packet in the right format, be it unicode or ASCII */
|
||||
const char *gen_string;
|
||||
if (gensec_ntlmssp_state->unicode) {
|
||||
gen_string = "CdUdbddB";
|
||||
} else {
|
||||
gen_string = "CdAdbddB";
|
||||
}
|
||||
|
||||
msrpc_gen(out_mem_ctx,
|
||||
out, gen_string,
|
||||
"NTLMSSP",
|
||||
NTLMSSP_CHALLENGE,
|
||||
target_name,
|
||||
chal_flags,
|
||||
cryptkey, 8,
|
||||
0, 0,
|
||||
struct_blob.data, struct_blob.length);
|
||||
}
|
||||
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_AUTH;
|
||||
|
||||
return NT_STATUS_MORE_PROCESSING_REQUIRED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Next state function for the Authenticate packet
|
||||
*
|
||||
* @param gensec_ntlmssp_state NTLMSSP State
|
||||
* @param request The request, as a DATA_BLOB
|
||||
* @return Errors or NT_STATUS_OK.
|
||||
*/
|
||||
|
||||
static NTSTATUS ntlmssp_server_preauth(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
const DATA_BLOB request)
|
||||
{
|
||||
uint32_t ntlmssp_command, auth_flags;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
uint8_t session_nonce_hash[16];
|
||||
|
||||
const char *parse_string;
|
||||
char *domain = NULL;
|
||||
char *user = NULL;
|
||||
char *workstation = NULL;
|
||||
|
||||
#if 0
|
||||
file_save("ntlmssp_auth.dat", request.data, request.length);
|
||||
#endif
|
||||
|
||||
if (gensec_ntlmssp_state->unicode) {
|
||||
parse_string = "CdBBUUUBd";
|
||||
} else {
|
||||
parse_string = "CdBBAAABd";
|
||||
}
|
||||
|
||||
/* zero these out */
|
||||
data_blob_free(&gensec_ntlmssp_state->lm_resp);
|
||||
data_blob_free(&gensec_ntlmssp_state->nt_resp);
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
|
||||
gensec_ntlmssp_state->user = NULL;
|
||||
gensec_ntlmssp_state->domain = NULL;
|
||||
gensec_ntlmssp_state->workstation = NULL;
|
||||
|
||||
/* now the NTLMSSP encoded auth hashes */
|
||||
if (!msrpc_parse(gensec_ntlmssp_state,
|
||||
&request, parse_string,
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&gensec_ntlmssp_state->lm_resp,
|
||||
&gensec_ntlmssp_state->nt_resp,
|
||||
&domain,
|
||||
&user,
|
||||
&workstation,
|
||||
&gensec_ntlmssp_state->encrypted_session_key,
|
||||
&auth_flags)) {
|
||||
DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n"));
|
||||
dump_data(10, request.data, request.length);
|
||||
|
||||
/* zero this out */
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
auth_flags = 0;
|
||||
|
||||
/* Try again with a shorter string (Win9X truncates this packet) */
|
||||
if (gensec_ntlmssp_state->unicode) {
|
||||
parse_string = "CdBBUUU";
|
||||
} else {
|
||||
parse_string = "CdBBAAA";
|
||||
}
|
||||
|
||||
/* now the NTLMSSP encoded auth hashes */
|
||||
if (!msrpc_parse(gensec_ntlmssp_state,
|
||||
&request, parse_string,
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&gensec_ntlmssp_state->lm_resp,
|
||||
&gensec_ntlmssp_state->nt_resp,
|
||||
&domain,
|
||||
&user,
|
||||
&workstation)) {
|
||||
DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
|
||||
dump_data(2, request.data, request.length);
|
||||
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
if (auth_flags)
|
||||
ntlmssp_handle_neg_flags(gensec_ntlmssp_state, auth_flags, gensec_ntlmssp_state->allow_lm_key);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(gensec_ntlmssp_state, domain))) {
|
||||
/* zero this out */
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(gensec_ntlmssp_state, user))) {
|
||||
/* zero this out */
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(gensec_ntlmssp_state, workstation))) {
|
||||
/* zero this out */
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n",
|
||||
gensec_ntlmssp_state->user, gensec_ntlmssp_state->domain, gensec_ntlmssp_state->workstation, (unsigned long)gensec_ntlmssp_state->lm_resp.length, (unsigned long)gensec_ntlmssp_state->nt_resp.length));
|
||||
|
||||
#if 0
|
||||
file_save("nthash1.dat", &gensec_ntlmssp_state->nt_resp.data, &gensec_ntlmssp_state->nt_resp.length);
|
||||
file_save("lmhash1.dat", &gensec_ntlmssp_state->lm_resp.data, &gensec_ntlmssp_state->lm_resp.length);
|
||||
#endif
|
||||
|
||||
/* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a
|
||||
client challenge
|
||||
|
||||
However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful.
|
||||
*/
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
if (gensec_ntlmssp_state->nt_resp.length == 24 && gensec_ntlmssp_state->lm_resp.length == 24) {
|
||||
struct MD5Context md5_session_nonce_ctx;
|
||||
SMB_ASSERT(gensec_ntlmssp_state->internal_chal.data
|
||||
&& gensec_ntlmssp_state->internal_chal.length == 8);
|
||||
|
||||
gensec_ntlmssp_state->doing_ntlm2 = True;
|
||||
|
||||
memcpy(gensec_ntlmssp_state->crypt.ntlm2.session_nonce, gensec_ntlmssp_state->internal_chal.data, 8);
|
||||
memcpy(&gensec_ntlmssp_state->crypt.ntlm2.session_nonce[8], gensec_ntlmssp_state->lm_resp.data, 8);
|
||||
|
||||
MD5Init(&md5_session_nonce_ctx);
|
||||
MD5Update(&md5_session_nonce_ctx, gensec_ntlmssp_state->crypt.ntlm2.session_nonce, 16);
|
||||
MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
|
||||
|
||||
gensec_ntlmssp_state->chal = data_blob_talloc(gensec_ntlmssp_state,
|
||||
session_nonce_hash, 8);
|
||||
|
||||
/* LM response is no longer useful, zero it out */
|
||||
data_blob_free(&gensec_ntlmssp_state->lm_resp);
|
||||
|
||||
/* We changed the effective challenge - set it */
|
||||
if (!NT_STATUS_IS_OK(nt_status =
|
||||
gensec_ntlmssp_state->set_challenge(gensec_ntlmssp_state,
|
||||
&gensec_ntlmssp_state->chal))) {
|
||||
/* zero this out */
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* LM Key is incompatible... */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
}
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Next state function for the Authenticate packet
|
||||
* (after authentication - figures out the session keys etc)
|
||||
*
|
||||
* @param gensec_ntlmssp_state NTLMSSP State
|
||||
* @return Errors or NT_STATUS_OK.
|
||||
*/
|
||||
|
||||
static NTSTATUS ntlmssp_server_postauth(struct gensec_security *gensec_security,
|
||||
DATA_BLOB *user_session_key,
|
||||
DATA_BLOB *lm_session_key)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
NTSTATUS nt_status;
|
||||
DATA_BLOB session_key = data_blob(NULL, 0);
|
||||
|
||||
if (user_session_key)
|
||||
dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length);
|
||||
|
||||
if (lm_session_key)
|
||||
dump_data_pw("LM first-8:\n", lm_session_key->data, lm_session_key->length);
|
||||
|
||||
/* Handle the different session key derivation for NTLM2 */
|
||||
if (gensec_ntlmssp_state->doing_ntlm2) {
|
||||
if (user_session_key && user_session_key->data && user_session_key->length == 16) {
|
||||
session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
|
||||
hmac_md5(user_session_key->data, gensec_ntlmssp_state->crypt.ntlm2.session_nonce,
|
||||
sizeof(gensec_ntlmssp_state->crypt.ntlm2.session_nonce), session_key.data);
|
||||
DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n"));
|
||||
dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
|
||||
|
||||
} else {
|
||||
DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n"));
|
||||
session_key = data_blob(NULL, 0);
|
||||
}
|
||||
} else if ((gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
|
||||
/* Ensure we can never get here on NTLMv2 */
|
||||
&& (gensec_ntlmssp_state->nt_resp.length == 0 || gensec_ntlmssp_state->nt_resp.length == 24)) {
|
||||
|
||||
if (lm_session_key && lm_session_key->data && lm_session_key->length >= 8) {
|
||||
if (gensec_ntlmssp_state->lm_resp.data && gensec_ntlmssp_state->lm_resp.length == 24) {
|
||||
session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
|
||||
SMBsesskeygen_lm_sess_key(lm_session_key->data, gensec_ntlmssp_state->lm_resp.data,
|
||||
session_key.data);
|
||||
DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
|
||||
dump_data_pw("LM session key:\n", session_key.data, session_key.length);
|
||||
} else {
|
||||
|
||||
/* When there is no LM response, just use zeros */
|
||||
static const uint8_t zeros[24];
|
||||
session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
|
||||
SMBsesskeygen_lm_sess_key(zeros, zeros,
|
||||
session_key.data);
|
||||
DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
|
||||
dump_data_pw("LM session key:\n", session_key.data, session_key.length);
|
||||
}
|
||||
} else {
|
||||
/* LM Key not selected */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
|
||||
DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n"));
|
||||
session_key = data_blob(NULL, 0);
|
||||
}
|
||||
|
||||
} else if (user_session_key && user_session_key->data) {
|
||||
session_key = *user_session_key;
|
||||
DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n"));
|
||||
dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
|
||||
|
||||
/* LM Key not selected */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
|
||||
} else if (lm_session_key && lm_session_key->data) {
|
||||
/* Very weird to have LM key, but no user session key, but anyway.. */
|
||||
session_key = *lm_session_key;
|
||||
DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n"));
|
||||
dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
|
||||
|
||||
/* LM Key not selected */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
|
||||
} else {
|
||||
DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n"));
|
||||
session_key = data_blob(NULL, 0);
|
||||
|
||||
/* LM Key not selected */
|
||||
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
|
||||
}
|
||||
|
||||
/* With KEY_EXCH, the client supplies the proposed session key,
|
||||
but encrypts it with the long-term key */
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
|
||||
if (!gensec_ntlmssp_state->encrypted_session_key.data
|
||||
|| gensec_ntlmssp_state->encrypted_session_key.length != 16) {
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n",
|
||||
(unsigned)gensec_ntlmssp_state->encrypted_session_key.length));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
} else if (!session_key.data || session_key.length != 16) {
|
||||
DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n",
|
||||
(unsigned)session_key.length));
|
||||
gensec_ntlmssp_state->session_key = session_key;
|
||||
} else {
|
||||
dump_data_pw("KEY_EXCH session key (enc):\n",
|
||||
gensec_ntlmssp_state->encrypted_session_key.data,
|
||||
gensec_ntlmssp_state->encrypted_session_key.length);
|
||||
arcfour_crypt(gensec_ntlmssp_state->encrypted_session_key.data,
|
||||
session_key.data,
|
||||
gensec_ntlmssp_state->encrypted_session_key.length);
|
||||
gensec_ntlmssp_state->session_key = data_blob_talloc(gensec_ntlmssp_state,
|
||||
gensec_ntlmssp_state->encrypted_session_key.data,
|
||||
gensec_ntlmssp_state->encrypted_session_key.length);
|
||||
dump_data_pw("KEY_EXCH session key:\n", gensec_ntlmssp_state->encrypted_session_key.data,
|
||||
gensec_ntlmssp_state->encrypted_session_key.length);
|
||||
}
|
||||
} else {
|
||||
gensec_ntlmssp_state->session_key = session_key;
|
||||
}
|
||||
|
||||
/* keep the session key around on the new context */
|
||||
talloc_steal(gensec_ntlmssp_state, session_key.data);
|
||||
|
||||
if ((gensec_security->want_features & GENSEC_FEATURE_SIGN)
|
||||
|| (gensec_security->want_features & GENSEC_FEATURE_SEAL)) {
|
||||
nt_status = ntlmssp_sign_init(gensec_ntlmssp_state);
|
||||
} else {
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
|
||||
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
|
||||
|
||||
/* allow arbitarily many authentications, but watch that this will cause a
|
||||
memory leak, until the gensec_ntlmssp_state is shutdown
|
||||
*/
|
||||
|
||||
if (gensec_ntlmssp_state->server_multiple_authentications) {
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_AUTH;
|
||||
} else {
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_DONE;
|
||||
}
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Next state function for the Authenticate packet
|
||||
*
|
||||
* @param gensec_security GENSEC state
|
||||
* @param out_mem_ctx Memory context for *out
|
||||
* @param in The request, as a DATA_BLOB. reply.data must be NULL
|
||||
* @param out The reply, as an allocated DATA_BLOB, caller to free.
|
||||
* @return Errors or NT_STATUS_OK if authentication sucessful
|
||||
*/
|
||||
|
||||
NTSTATUS ntlmssp_server_auth(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *out_mem_ctx,
|
||||
const DATA_BLOB in, DATA_BLOB *out)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
DATA_BLOB user_session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB lm_session_key = data_blob(NULL, 0);
|
||||
NTSTATUS nt_status;
|
||||
|
||||
TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* zero the outbound NTLMSSP packet */
|
||||
*out = data_blob_talloc(out_mem_ctx, NULL, 0);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(gensec_ntlmssp_state, in))) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth
|
||||
* is required (by "ntlm auth = no" and "lm auth = no" being set in the
|
||||
* smb.conf file) and no NTLMv2 response was sent then the password check
|
||||
* will fail here. JRA.
|
||||
*/
|
||||
|
||||
/* Finally, actually ask if the password is OK */
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = gensec_ntlmssp_state->check_password(gensec_ntlmssp_state, mem_ctx,
|
||||
&user_session_key, &lm_session_key))) {
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (gensec_security->want_features
|
||||
& (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL|GENSEC_FEATURE_SESSION_KEY)) {
|
||||
nt_status = ntlmssp_server_postauth(gensec_security, &user_session_key, &lm_session_key);
|
||||
talloc_free(mem_ctx);
|
||||
return nt_status;
|
||||
} else {
|
||||
gensec_ntlmssp_state->session_key = data_blob(NULL, 0);
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the challenge as determined by the authentication subsystem
|
||||
* @return an 8 byte random challenge
|
||||
*/
|
||||
|
||||
static const uint8_t *auth_ntlmssp_get_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
|
||||
{
|
||||
NTSTATUS status;
|
||||
const uint8_t *chal;
|
||||
|
||||
status = auth_get_challenge(gensec_ntlmssp_state->auth_context, &chal);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return chal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some authentication methods 'fix' the challenge, so we may not be able to set it
|
||||
*
|
||||
* @return If the effective challenge used by the auth subsystem may be modified
|
||||
*/
|
||||
static BOOL auth_ntlmssp_may_set_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
|
||||
{
|
||||
return auth_challenge_may_be_modified(gensec_ntlmssp_state->auth_context);
|
||||
}
|
||||
|
||||
/**
|
||||
* NTLM2 authentication modifies the effective challenge,
|
||||
* @param challenge The new challenge value
|
||||
*/
|
||||
static NTSTATUS auth_ntlmssp_set_challenge(struct gensec_ntlmssp_state *gensec_ntlmssp_state, DATA_BLOB *challenge)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_context *auth_context = gensec_ntlmssp_state->auth_context;
|
||||
const uint8_t *chal;
|
||||
|
||||
if (challenge->length != 8) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
chal = challenge->data;
|
||||
|
||||
nt_status = auth_context_set_challenge(auth_context, chal, "NTLMSSP callback (NTLM2)");
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the password on an NTLMSSP login.
|
||||
*
|
||||
* Return the session keys used on the connection.
|
||||
*/
|
||||
|
||||
static NTSTATUS auth_ntlmssp_check_password(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct auth_usersupplied_info *user_info = talloc(mem_ctx, struct auth_usersupplied_info);
|
||||
if (!user_info) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
user_info->logon_parameters = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
|
||||
user_info->flags = 0;
|
||||
user_info->mapped_state = False;
|
||||
user_info->client.account_name = gensec_ntlmssp_state->user;
|
||||
user_info->client.domain_name = gensec_ntlmssp_state->domain;
|
||||
user_info->workstation_name = gensec_ntlmssp_state->workstation;
|
||||
user_info->remote_host = gensec_get_peer_addr(gensec_ntlmssp_state->gensec_security);
|
||||
|
||||
user_info->password_state = AUTH_PASSWORD_RESPONSE;
|
||||
user_info->password.response.lanman = gensec_ntlmssp_state->lm_resp;
|
||||
user_info->password.response.lanman.data = talloc_steal(user_info, gensec_ntlmssp_state->lm_resp.data);
|
||||
user_info->password.response.nt = gensec_ntlmssp_state->nt_resp;
|
||||
user_info->password.response.nt.data = talloc_steal(user_info, gensec_ntlmssp_state->nt_resp.data);
|
||||
|
||||
nt_status = auth_check_password(gensec_ntlmssp_state->auth_context, mem_ctx,
|
||||
user_info, &gensec_ntlmssp_state->server_info);
|
||||
talloc_free(user_info);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
talloc_steal(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info);
|
||||
|
||||
if (gensec_ntlmssp_state->server_info->user_session_key.length) {
|
||||
DEBUG(10, ("Got NT session key of length %u\n",
|
||||
(unsigned)gensec_ntlmssp_state->server_info->user_session_key.length));
|
||||
if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->user_session_key.data)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
*user_session_key = gensec_ntlmssp_state->server_info->user_session_key;
|
||||
}
|
||||
if (gensec_ntlmssp_state->server_info->lm_session_key.length) {
|
||||
DEBUG(10, ("Got LM session key of length %u\n",
|
||||
(unsigned)gensec_ntlmssp_state->server_info->lm_session_key.length));
|
||||
if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->lm_session_key.data)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
*lm_session_key = gensec_ntlmssp_state->server_info->lm_session_key;
|
||||
}
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the credentials of a logged on user, including session keys
|
||||
* etc.
|
||||
*
|
||||
* Only valid after a successful authentication
|
||||
*
|
||||
* May only be called once per authentication.
|
||||
*
|
||||
*/
|
||||
|
||||
NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_security,
|
||||
struct auth_session_info **session_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
nt_status = auth_generate_session_info(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info, session_info);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
(*session_info)->session_key = data_blob_talloc(*session_info,
|
||||
gensec_ntlmssp_state->session_key.data,
|
||||
gensec_ntlmssp_state->session_key.length);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start NTLMSSP on the server side
|
||||
*
|
||||
*/
|
||||
NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_security)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state;
|
||||
|
||||
nt_status = gensec_ntlmssp_start(gensec_security);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
gensec_ntlmssp_state->role = NTLMSSP_SERVER;
|
||||
|
||||
gensec_ntlmssp_state->workstation = NULL;
|
||||
gensec_ntlmssp_state->server_name = lp_netbios_name();
|
||||
|
||||
gensec_ntlmssp_state->get_domain = lp_workgroup;
|
||||
|
||||
gensec_ntlmssp_state->expected_state = NTLMSSP_NEGOTIATE;
|
||||
|
||||
gensec_ntlmssp_state->allow_lm_key = (lp_lanman_auth()
|
||||
&& lp_parm_bool(-1, "ntlmssp_server", "allow_lm_key", False));
|
||||
|
||||
gensec_ntlmssp_state->server_multiple_authentications = False;
|
||||
|
||||
gensec_ntlmssp_state->neg_flags =
|
||||
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_UNKNOWN_02000000;
|
||||
|
||||
gensec_ntlmssp_state->lm_resp = data_blob(NULL, 0);
|
||||
gensec_ntlmssp_state->nt_resp = data_blob(NULL, 0);
|
||||
gensec_ntlmssp_state->encrypted_session_key = data_blob(NULL, 0);
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_server", "128bit", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_128;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_server", "56bit", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_server", "keyexchange", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_server", "alwayssign", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
|
||||
}
|
||||
|
||||
if (lp_parm_bool(-1, "ntlmssp_server", "ntlm2", True)) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
|
||||
}
|
||||
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
|
||||
}
|
||||
if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
|
||||
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
|
||||
}
|
||||
|
||||
nt_status = auth_context_create(gensec_ntlmssp_state, lp_auth_methods(),
|
||||
gensec_security->event_ctx,
|
||||
gensec_security->msg_ctx,
|
||||
&gensec_ntlmssp_state->auth_context);
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
gensec_ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge;
|
||||
gensec_ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge;
|
||||
gensec_ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge;
|
||||
gensec_ntlmssp_state->check_password = auth_ntlmssp_check_password;
|
||||
gensec_ntlmssp_state->server_role = lp_server_role();
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* Version 3.0
|
||||
* NTLMSSP Signing routines
|
||||
* Copyright (C) Luke Kenneth Casson Leighton 1996-2001
|
||||
* Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2005
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/ntlmssp/ntlmssp.h"
|
||||
#include "auth/ntlmssp/msrpc_parse.h"
|
||||
#include "lib/crypto/crypto.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
#define CLI_SIGN "session key to client-to-server signing key magic constant"
|
||||
#define CLI_SEAL "session key to client-to-server sealing key magic constant"
|
||||
#define SRV_SIGN "session key to server-to-client signing key magic constant"
|
||||
#define SRV_SEAL "session key to server-to-client sealing key magic constant"
|
||||
|
||||
/**
|
||||
* Some notes on the NTLM2 code:
|
||||
*
|
||||
* NTLM2 is a AEAD system. This means that the data encrypted is not
|
||||
* all the data that is signed. In DCE-RPC case, the headers of the
|
||||
* DCE-RPC packets are also signed. This prevents some of the
|
||||
* fun-and-games one might have by changing them.
|
||||
*
|
||||
*/
|
||||
|
||||
static void calc_ntlmv2_key(TALLOC_CTX *mem_ctx,
|
||||
DATA_BLOB *subkey,
|
||||
DATA_BLOB session_key,
|
||||
const char *constant)
|
||||
{
|
||||
struct MD5Context ctx3;
|
||||
*subkey = data_blob_talloc(mem_ctx, NULL, 16);
|
||||
MD5Init(&ctx3);
|
||||
MD5Update(&ctx3, session_key.data, session_key.length);
|
||||
MD5Update(&ctx3, (const uint8_t *)constant, strlen(constant)+1);
|
||||
MD5Final(subkey->data, &ctx3);
|
||||
}
|
||||
|
||||
enum ntlmssp_direction {
|
||||
NTLMSSP_SEND,
|
||||
NTLMSSP_RECEIVE
|
||||
};
|
||||
|
||||
static NTSTATUS ntlmssp_make_packet_signature(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
enum ntlmssp_direction direction,
|
||||
DATA_BLOB *sig, BOOL encrypt_sig)
|
||||
{
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
|
||||
HMACMD5Context ctx;
|
||||
uint8_t digest[16];
|
||||
uint8_t seq_num[4];
|
||||
|
||||
*sig = data_blob_talloc(sig_mem_ctx, NULL, NTLMSSP_SIG_SIZE);
|
||||
if (!sig->data) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case NTLMSSP_SEND:
|
||||
SIVAL(seq_num, 0, gensec_ntlmssp_state->crypt.ntlm2.send_seq_num);
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_seq_num++;
|
||||
hmac_md5_init_limK_to_64(gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.data,
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.length, &ctx);
|
||||
break;
|
||||
case NTLMSSP_RECEIVE:
|
||||
SIVAL(seq_num, 0, gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num);
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num++;
|
||||
hmac_md5_init_limK_to_64(gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.data,
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.length, &ctx);
|
||||
break;
|
||||
}
|
||||
hmac_md5_update(seq_num, sizeof(seq_num), &ctx);
|
||||
hmac_md5_update(whole_pdu, pdu_length, &ctx);
|
||||
hmac_md5_final(digest, &ctx);
|
||||
|
||||
if (encrypt_sig && gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
|
||||
switch (direction) {
|
||||
case NTLMSSP_SEND:
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, digest, 8);
|
||||
break;
|
||||
case NTLMSSP_RECEIVE:
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state, digest, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SIVAL(sig->data, 0, NTLMSSP_SIGN_VERSION);
|
||||
memcpy(sig->data + 4, digest, 8);
|
||||
memcpy(sig->data + 12, seq_num, 4);
|
||||
|
||||
DEBUG(10, ("NTLM2: created signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
|
||||
dump_data(11, sig->data, sig->length);
|
||||
|
||||
} else {
|
||||
uint32_t crc;
|
||||
crc = crc32_calc_buffer(data, length);
|
||||
if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
gensec_ntlmssp_state->crypt.ntlm.seq_num++;
|
||||
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, sig->data+4, sig->length-4);
|
||||
|
||||
DEBUG(10, ("NTLM1: created signature over %llu bytes of input:\n", (unsigned long long)length));
|
||||
dump_data(11, sig->data, sig->length);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* TODO: make this non-public */
|
||||
_PUBLIC_ NTSTATUS gensec_ntlmssp_sign_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
return ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
|
||||
data, length,
|
||||
whole_pdu, pdu_length,
|
||||
NTLMSSP_SEND, sig, True);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the signature of an incoming packet
|
||||
*
|
||||
*/
|
||||
|
||||
NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
const uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
|
||||
DATA_BLOB local_sig;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
if (!gensec_ntlmssp_state->session_key.length) {
|
||||
DEBUG(3, ("NO session key, cannot check packet signature\n"));
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
|
||||
if (sig->length < 8) {
|
||||
DEBUG(0, ("NTLMSSP packet check failed due to short signature (%lu bytes)!\n",
|
||||
(unsigned long)sig->length));
|
||||
}
|
||||
|
||||
nt_status = ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
|
||||
data, length,
|
||||
whole_pdu, pdu_length,
|
||||
NTLMSSP_RECEIVE, &local_sig, True);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
if (local_sig.length != sig->length ||
|
||||
memcmp(local_sig.data,
|
||||
sig->data, sig->length) != 0) {
|
||||
DEBUG(5, ("BAD SIG NTLM2: wanted signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
|
||||
dump_data(5, local_sig.data, local_sig.length);
|
||||
|
||||
DEBUG(5, ("BAD SIG: got signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
|
||||
dump_data(5, sig->data, sig->length);
|
||||
|
||||
DEBUG(0, ("NTLMSSP NTLM2 packet check failed due to invalid signature on %llu bytes of input!\n", (unsigned long long)pdu_length));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
} else {
|
||||
if (local_sig.length != sig->length ||
|
||||
memcmp(local_sig.data + 8,
|
||||
sig->data + 8, sig->length - 8) != 0) {
|
||||
DEBUG(5, ("BAD SIG NTLM1: wanted signature of %llu bytes of input:\n", (unsigned long long)length));
|
||||
dump_data(5, local_sig.data, local_sig.length);
|
||||
|
||||
DEBUG(5, ("BAD SIG: got signature of %llu bytes of input:\n", (unsigned long long)length));
|
||||
dump_data(5, sig->data, sig->length);
|
||||
|
||||
DEBUG(0, ("NTLMSSP NTLM1 packet check failed due to invalid signature on %llu bytes of input:\n", (unsigned long long)length));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
dump_data_pw("checked ntlmssp signature\n", sig->data, sig->length);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Seal data with the NTLMSSP algorithm
|
||||
*
|
||||
*/
|
||||
|
||||
NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
DATA_BLOB *sig)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
NTSTATUS nt_status;
|
||||
if (!gensec_ntlmssp_state->session_key.length) {
|
||||
DEBUG(3, ("NO session key, cannot seal packet\n"));
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
|
||||
DEBUG(10,("ntlmssp_seal_data: seal\n"));
|
||||
dump_data_pw("ntlmssp clear data\n", data, length);
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
/* The order of these two operations matters - we must first seal the packet,
|
||||
then seal the sequence number - this is becouse the send_seal_hash is not
|
||||
constant, but is is rather updated with each iteration */
|
||||
nt_status = ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
|
||||
data, length,
|
||||
whole_pdu, pdu_length,
|
||||
NTLMSSP_SEND, sig, False);
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, data, length);
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, sig->data+4, 8);
|
||||
}
|
||||
} else {
|
||||
uint32_t crc;
|
||||
crc = crc32_calc_buffer(data, length);
|
||||
if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* The order of these two operations matters - we must
|
||||
first seal the packet, then seal the sequence
|
||||
number - this is becouse the ntlmssp_hash is not
|
||||
constant, but is is rather updated with each
|
||||
iteration */
|
||||
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, data, length);
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, sig->data+4, sig->length-4);
|
||||
/* increment counter on send */
|
||||
gensec_ntlmssp_state->crypt.ntlm.seq_num++;
|
||||
nt_status = NT_STATUS_OK;
|
||||
}
|
||||
dump_data_pw("ntlmssp signature\n", sig->data, sig->length);
|
||||
dump_data_pw("ntlmssp sealed data\n", data, length);
|
||||
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unseal data with the NTLMSSP algorithm
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
wrappers for the ntlmssp_*() functions
|
||||
*/
|
||||
NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
uint8_t *data, size_t length,
|
||||
const uint8_t *whole_pdu, size_t pdu_length,
|
||||
const DATA_BLOB *sig)
|
||||
{
|
||||
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
|
||||
if (!gensec_ntlmssp_state->session_key.length) {
|
||||
DEBUG(3, ("NO session key, cannot unseal packet\n"));
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
|
||||
dump_data_pw("ntlmssp sealed data\n", data, length);
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state, data, length);
|
||||
} else {
|
||||
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, data, length);
|
||||
}
|
||||
dump_data_pw("ntlmssp clear data\n", data, length);
|
||||
return gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx, data, length, whole_pdu, pdu_length, sig);
|
||||
}
|
||||
|
||||
/**
|
||||
Initialise the state for NTLMSSP signing.
|
||||
*/
|
||||
/* TODO: make this non-public */
|
||||
_PUBLIC_ NTSTATUS ntlmssp_sign_init(struct gensec_ntlmssp_state *gensec_ntlmssp_state)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx = talloc_new(gensec_ntlmssp_state);
|
||||
|
||||
if (!mem_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n"));
|
||||
debug_ntlmssp_flags(gensec_ntlmssp_state->neg_flags);
|
||||
|
||||
if (gensec_ntlmssp_state->session_key.length < 8) {
|
||||
talloc_free(mem_ctx);
|
||||
DEBUG(3, ("NO session key, cannot intialise signing\n"));
|
||||
return NT_STATUS_NO_USER_SESSION_KEY;
|
||||
}
|
||||
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
|
||||
{
|
||||
DATA_BLOB weak_session_key = gensec_ntlmssp_state->session_key;
|
||||
const char *send_sign_const;
|
||||
const char *send_seal_const;
|
||||
const char *recv_sign_const;
|
||||
const char *recv_seal_const;
|
||||
|
||||
DATA_BLOB send_seal_key;
|
||||
DATA_BLOB recv_seal_key;
|
||||
|
||||
switch (gensec_ntlmssp_state->role) {
|
||||
case NTLMSSP_CLIENT:
|
||||
send_sign_const = CLI_SIGN;
|
||||
send_seal_const = CLI_SEAL;
|
||||
recv_sign_const = SRV_SIGN;
|
||||
recv_seal_const = SRV_SEAL;
|
||||
break;
|
||||
case NTLMSSP_SERVER:
|
||||
send_sign_const = SRV_SIGN;
|
||||
send_seal_const = SRV_SEAL;
|
||||
recv_sign_const = CLI_SIGN;
|
||||
recv_seal_const = CLI_SEAL;
|
||||
break;
|
||||
default:
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state);
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state);
|
||||
|
||||
/**
|
||||
Weaken NTLMSSP keys to cope with down-level
|
||||
clients, servers and export restrictions.
|
||||
|
||||
We probably should have some parameters to control
|
||||
this, once we get NTLM2 working.
|
||||
*/
|
||||
|
||||
/* Key weakening was not performed on the master key
|
||||
* for NTLM2 (in ntlmssp_weaken_keys()), but must be
|
||||
* done on the encryption subkeys only. That is why
|
||||
* we don't have this code for the ntlmv1 case.
|
||||
*/
|
||||
|
||||
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) {
|
||||
|
||||
} else if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
|
||||
weak_session_key.length = 7;
|
||||
} else { /* forty bits */
|
||||
weak_session_key.length = 5;
|
||||
}
|
||||
dump_data_pw("NTLMSSP weakend master key:\n",
|
||||
weak_session_key.data,
|
||||
weak_session_key.length);
|
||||
|
||||
/* SEND: sign key */
|
||||
calc_ntlmv2_key(gensec_ntlmssp_state,
|
||||
&gensec_ntlmssp_state->crypt.ntlm2.send_sign_key,
|
||||
gensec_ntlmssp_state->session_key, send_sign_const);
|
||||
dump_data_pw("NTLMSSP send sign key:\n",
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.data,
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.length);
|
||||
|
||||
/* SEND: seal ARCFOUR pad */
|
||||
calc_ntlmv2_key(mem_ctx,
|
||||
&send_seal_key,
|
||||
weak_session_key, send_seal_const);
|
||||
dump_data_pw("NTLMSSP send seal key:\n",
|
||||
send_seal_key.data,
|
||||
send_seal_key.length);
|
||||
arcfour_init(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state,
|
||||
&send_seal_key);
|
||||
dump_data_pw("NTLMSSP send sesl hash:\n",
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state->sbox,
|
||||
sizeof(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state->sbox));
|
||||
|
||||
/* RECV: sign key */
|
||||
calc_ntlmv2_key(gensec_ntlmssp_state,
|
||||
&gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key,
|
||||
gensec_ntlmssp_state->session_key, recv_sign_const);
|
||||
dump_data_pw("NTLMSSP recv sign key:\n",
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.data,
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.length);
|
||||
|
||||
/* RECV: seal ARCFOUR pad */
|
||||
calc_ntlmv2_key(mem_ctx,
|
||||
&recv_seal_key,
|
||||
weak_session_key, recv_seal_const);
|
||||
dump_data_pw("NTLMSSP recv seal key:\n",
|
||||
recv_seal_key.data,
|
||||
recv_seal_key.length);
|
||||
arcfour_init(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state,
|
||||
&recv_seal_key);
|
||||
dump_data_pw("NTLMSSP receive seal hash:\n",
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state->sbox,
|
||||
sizeof(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state->sbox));
|
||||
|
||||
gensec_ntlmssp_state->crypt.ntlm2.send_seq_num = 0;
|
||||
gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num = 0;
|
||||
|
||||
} else {
|
||||
DATA_BLOB weak_session_key = ntlmssp_weakend_key(gensec_ntlmssp_state, mem_ctx);
|
||||
DEBUG(5, ("NTLMSSP Sign/Seal - using NTLM1\n"));
|
||||
|
||||
gensec_ntlmssp_state->crypt.ntlm.arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm.arcfour_state);
|
||||
|
||||
arcfour_init(gensec_ntlmssp_state->crypt.ntlm.arcfour_state,
|
||||
&weak_session_key);
|
||||
dump_data_pw("NTLMSSP hash:\n", gensec_ntlmssp_state->crypt.ntlm.arcfour_state->sbox,
|
||||
sizeof(gensec_ntlmssp_state->crypt.ntlm.arcfour_state->sbox));
|
||||
|
||||
gensec_ntlmssp_state->crypt.ntlm.seq_num = 0;
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
size_t gensec_ntlmssp_sig_size(struct gensec_security *gensec_security, size_t data_size)
|
||||
{
|
||||
return NTLMSSP_SIG_SIZE;
|
||||
}
|
||||
|
||||
NTSTATUS gensec_ntlmssp_wrap(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out)
|
||||
{
|
||||
DATA_BLOB sig;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
|
||||
|
||||
*out = data_blob_talloc(sig_mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE);
|
||||
if (!out->data) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length);
|
||||
|
||||
nt_status = gensec_ntlmssp_seal_packet(gensec_security, sig_mem_ctx,
|
||||
out->data + NTLMSSP_SIG_SIZE,
|
||||
out->length - NTLMSSP_SIG_SIZE,
|
||||
out->data + NTLMSSP_SIG_SIZE,
|
||||
out->length - NTLMSSP_SIG_SIZE,
|
||||
&sig);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE);
|
||||
}
|
||||
return nt_status;
|
||||
|
||||
} else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
|
||||
|
||||
*out = data_blob_talloc(sig_mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE);
|
||||
if (!out->data) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length);
|
||||
|
||||
nt_status = gensec_ntlmssp_sign_packet(gensec_security, sig_mem_ctx,
|
||||
out->data + NTLMSSP_SIG_SIZE,
|
||||
out->length - NTLMSSP_SIG_SIZE,
|
||||
out->data + NTLMSSP_SIG_SIZE,
|
||||
out->length - NTLMSSP_SIG_SIZE,
|
||||
&sig);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE);
|
||||
}
|
||||
return nt_status;
|
||||
|
||||
} else {
|
||||
*out = *in;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS gensec_ntlmssp_unwrap(struct gensec_security *gensec_security,
|
||||
TALLOC_CTX *sig_mem_ctx,
|
||||
const DATA_BLOB *in,
|
||||
DATA_BLOB *out)
|
||||
{
|
||||
DATA_BLOB sig;
|
||||
|
||||
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
|
||||
if (in->length < NTLMSSP_SIG_SIZE) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
sig.data = in->data;
|
||||
sig.length = NTLMSSP_SIG_SIZE;
|
||||
|
||||
*out = data_blob_talloc(sig_mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE);
|
||||
|
||||
return gensec_ntlmssp_unseal_packet(gensec_security, sig_mem_ctx,
|
||||
out->data, out->length,
|
||||
out->data, out->length,
|
||||
&sig);
|
||||
|
||||
} else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
|
||||
if (in->length < NTLMSSP_SIG_SIZE) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
sig.data = in->data;
|
||||
sig.length = NTLMSSP_SIG_SIZE;
|
||||
|
||||
*out = data_blob_talloc(sig_mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE);
|
||||
|
||||
return gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx,
|
||||
out->data, out->length,
|
||||
out->data, out->length,
|
||||
&sig);
|
||||
} else {
|
||||
*out = *in;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* PAM error mapping functions
|
||||
* Copyright (C) Andrew Bartlett 2002
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#ifdef WITH_HAVE_SECURITY_PAM_APPL_H
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
#if defined(PAM_AUTHTOK_RECOVERY_ERR) && !defined(PAM_AUTHTOK_RECOVER_ERR)
|
||||
#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
|
||||
#endif
|
||||
|
||||
/* PAM -> NT_STATUS map */
|
||||
static const struct {
|
||||
int pam_code;
|
||||
NTSTATUS ntstatus;
|
||||
} pam_to_nt_status_map[] = {
|
||||
{PAM_OPEN_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_SYMBOL_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_SERVICE_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_SYSTEM_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_BUF_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_PERM_DENIED, NT_STATUS_ACCESS_DENIED},
|
||||
{PAM_AUTH_ERR, NT_STATUS_WRONG_PASSWORD},
|
||||
{PAM_CRED_INSUFFICIENT, NT_STATUS_INSUFFICIENT_LOGON_INFO}, /* FIXME: Is this correct? */
|
||||
{PAM_AUTHINFO_UNAVAIL, NT_STATUS_LOGON_FAILURE},
|
||||
{PAM_USER_UNKNOWN, NT_STATUS_NO_SUCH_USER},
|
||||
{PAM_MAXTRIES, NT_STATUS_REMOTE_SESSION_LIMIT}, /* FIXME: Is this correct? */
|
||||
{PAM_NEW_AUTHTOK_REQD, NT_STATUS_PASSWORD_MUST_CHANGE},
|
||||
{PAM_ACCT_EXPIRED, NT_STATUS_ACCOUNT_EXPIRED},
|
||||
{PAM_SESSION_ERR, NT_STATUS_INSUFFICIENT_RESOURCES},
|
||||
{PAM_CRED_UNAVAIL, NT_STATUS_NO_TOKEN}, /* FIXME: Is this correct? */
|
||||
{PAM_CRED_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, /* FIXME: Is this correct? */
|
||||
{PAM_CRED_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
{PAM_AUTHTOK_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
#ifdef PAM_AUTHTOK_RECOVER_ERR
|
||||
{PAM_AUTHTOK_RECOVER_ERR, NT_STATUS_UNSUCCESSFUL},
|
||||
#endif
|
||||
{PAM_AUTHTOK_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
|
||||
{PAM_SUCCESS, NT_STATUS_OK}
|
||||
};
|
||||
|
||||
/* NT_STATUS -> PAM map */
|
||||
static const struct {
|
||||
NTSTATUS ntstatus;
|
||||
int pam_code;
|
||||
} nt_status_to_pam_map[] = {
|
||||
{NT_STATUS_UNSUCCESSFUL, PAM_SYSTEM_ERR},
|
||||
{NT_STATUS_NO_SUCH_USER, PAM_USER_UNKNOWN},
|
||||
{NT_STATUS_WRONG_PASSWORD, PAM_AUTH_ERR},
|
||||
{NT_STATUS_LOGON_FAILURE, PAM_AUTH_ERR},
|
||||
{NT_STATUS_ACCOUNT_EXPIRED, PAM_ACCT_EXPIRED},
|
||||
{NT_STATUS_PASSWORD_EXPIRED, PAM_AUTHTOK_EXPIRED},
|
||||
{NT_STATUS_PASSWORD_MUST_CHANGE, PAM_NEW_AUTHTOK_REQD},
|
||||
{NT_STATUS_OK, PAM_SUCCESS}
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
convert a PAM error to a NT status32 code
|
||||
*****************************************************************************/
|
||||
NTSTATUS pam_to_nt_status(int pam_error)
|
||||
{
|
||||
int i;
|
||||
if (pam_error == 0) return NT_STATUS_OK;
|
||||
|
||||
for (i=0; NT_STATUS_V(pam_to_nt_status_map[i].ntstatus); i++) {
|
||||
if (pam_error == pam_to_nt_status_map[i].pam_code)
|
||||
return pam_to_nt_status_map[i].ntstatus;
|
||||
}
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
convert an NT status32 code to a PAM error
|
||||
*****************************************************************************/
|
||||
int nt_status_to_pam(NTSTATUS nt_status)
|
||||
{
|
||||
int i;
|
||||
if NT_STATUS_IS_OK(nt_status) return PAM_SUCCESS;
|
||||
|
||||
for (i=0; NT_STATUS_V(nt_status_to_pam_map[i].ntstatus); i++) {
|
||||
if (NT_STATUS_EQUAL(nt_status,nt_status_to_pam_map[i].ntstatus))
|
||||
return nt_status_to_pam_map[i].pam_code;
|
||||
}
|
||||
return PAM_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*****************************************************************************
|
||||
convert a PAM error to a NT status32 code
|
||||
*****************************************************************************/
|
||||
NTSTATUS pam_to_nt_status(int pam_error)
|
||||
{
|
||||
if (pam_error == 0) return NT_STATUS_OK;
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
convert an NT status32 code to a PAM error
|
||||
*****************************************************************************/
|
||||
int nt_status_to_pam(NTSTATUS nt_status)
|
||||
{
|
||||
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OK)) return 0;
|
||||
return 4; /* PAM_SYSTEM_ERR */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Password and authentication handling
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
|
||||
Copyright (C) Gerald Carter 2003
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/time.h"
|
||||
#include "auth/auth.h"
|
||||
#include "db_wrap.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
#include "librpc/gen_ndr/ndr_netlogon.h"
|
||||
|
||||
const char *user_attrs[] = {
|
||||
/* required for the krb5 kdc */
|
||||
"objectClass",
|
||||
"sAMAccountName",
|
||||
"userPrincipalName",
|
||||
"servicePrincipalName",
|
||||
"msDS-KeyVersionNumber",
|
||||
"krb5Key",
|
||||
|
||||
/* passwords */
|
||||
"lmPwdHash",
|
||||
"ntPwdHash",
|
||||
|
||||
"userAccountControl",
|
||||
|
||||
"pwdLastSet",
|
||||
"accountExpires",
|
||||
|
||||
"objectSid",
|
||||
|
||||
/* check 'allowed workstations' */
|
||||
"userWorkstations",
|
||||
|
||||
/* required for server_info, not access control: */
|
||||
"displayName",
|
||||
"scriptPath",
|
||||
"profilePath",
|
||||
"homeDirectory",
|
||||
"homeDrive",
|
||||
"lastLogon",
|
||||
"lastLogoff",
|
||||
"accountExpires",
|
||||
"badPwdCount",
|
||||
"logonCount",
|
||||
"primaryGroupID",
|
||||
NULL,
|
||||
};
|
||||
|
||||
const char *domain_ref_attrs[] = {"nETBIOSName", "nCName",
|
||||
"dnsRoot", "objectClass", NULL};
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Do a specific test for a SAM_ACCOUNT being vaild for this connection
|
||||
(ie not disabled, expired and the like).
|
||||
****************************************************************************/
|
||||
_PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
|
||||
struct ldb_context *sam_ctx,
|
||||
uint32_t logon_parameters,
|
||||
struct ldb_message *msg,
|
||||
struct ldb_message *msg_domain_ref,
|
||||
const char *logon_workstation,
|
||||
const char *name_for_logs)
|
||||
{
|
||||
uint16_t acct_flags;
|
||||
const char *workstation_list;
|
||||
NTTIME acct_expiry;
|
||||
NTTIME must_change_time;
|
||||
NTTIME last_set_time;
|
||||
|
||||
struct ldb_dn *domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", ldb_dn_new(mem_ctx, sam_ctx, NULL));
|
||||
|
||||
NTTIME now;
|
||||
DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
|
||||
|
||||
acct_flags = samdb_result_acct_flags(msg, "userAccountControl");
|
||||
|
||||
acct_expiry = samdb_result_nttime(msg, "accountExpires", 0);
|
||||
must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx,
|
||||
domain_dn, msg);
|
||||
last_set_time = samdb_result_nttime(msg, "pwdLastSet", 0);
|
||||
|
||||
workstation_list = samdb_result_string(msg, "userWorkstations", NULL);
|
||||
|
||||
/* Quit if the account was disabled. */
|
||||
if (acct_flags & ACB_DISABLED) {
|
||||
DEBUG(1,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
|
||||
return NT_STATUS_ACCOUNT_DISABLED;
|
||||
}
|
||||
|
||||
/* Quit if the account was locked out. */
|
||||
if (acct_flags & ACB_AUTOLOCK) {
|
||||
DEBUG(1,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
|
||||
return NT_STATUS_ACCOUNT_LOCKED_OUT;
|
||||
}
|
||||
|
||||
/* Test account expire time */
|
||||
unix_to_nt_time(&now, time(NULL));
|
||||
if (now > acct_expiry) {
|
||||
DEBUG(1,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
|
||||
DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
|
||||
nt_time_string(mem_ctx, acct_expiry)));
|
||||
return NT_STATUS_ACCOUNT_EXPIRED;
|
||||
}
|
||||
|
||||
if (!(acct_flags & ACB_PWNOEXP)) {
|
||||
/* check for immediate expiry "must change at next logon" */
|
||||
if (must_change_time == 0 && last_set_time != 0) {
|
||||
DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n",
|
||||
name_for_logs));
|
||||
return NT_STATUS_PASSWORD_MUST_CHANGE;
|
||||
}
|
||||
|
||||
/* check for expired password */
|
||||
if ((must_change_time != 0) && (must_change_time < now)) {
|
||||
DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n",
|
||||
name_for_logs));
|
||||
DEBUG(1,("sam_account_ok: Password expired at '%s' unix time.\n",
|
||||
nt_time_string(mem_ctx, must_change_time)));
|
||||
return NT_STATUS_PASSWORD_EXPIRED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test workstation. Workstation list is comma separated. */
|
||||
if (logon_workstation && workstation_list && *workstation_list) {
|
||||
BOOL invalid_ws = True;
|
||||
int i;
|
||||
const char **workstations = str_list_make(mem_ctx, workstation_list, ",");
|
||||
|
||||
for (i = 0; workstations && workstations[i]; i++) {
|
||||
DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
|
||||
workstations[i], logon_workstation));
|
||||
|
||||
if (strequal(workstations[i], logon_workstation) == 0) {
|
||||
invalid_ws = False;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(workstations);
|
||||
|
||||
if (invalid_ws) {
|
||||
return NT_STATUS_INVALID_WORKSTATION;
|
||||
}
|
||||
}
|
||||
|
||||
if (acct_flags & ACB_DOMTRUST) {
|
||||
DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
|
||||
return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
|
||||
}
|
||||
|
||||
if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
|
||||
if (acct_flags & ACB_SVRTRUST) {
|
||||
DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
|
||||
return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
|
||||
}
|
||||
}
|
||||
if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
|
||||
if (acct_flags & ACB_WSTRUST) {
|
||||
DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
|
||||
return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
|
||||
struct ldb_message *msg,
|
||||
struct ldb_message *msg_domain_ref,
|
||||
DATA_BLOB user_sess_key, DATA_BLOB lm_sess_key,
|
||||
struct auth_serversupplied_info **_server_info)
|
||||
{
|
||||
struct auth_serversupplied_info *server_info;
|
||||
struct ldb_message **group_msgs;
|
||||
int group_ret;
|
||||
const char *group_attrs[3] = { "sAMAccountType", "objectSid", NULL };
|
||||
/* find list of sids */
|
||||
struct dom_sid **groupSIDs = NULL;
|
||||
struct dom_sid *account_sid;
|
||||
struct dom_sid *primary_group_sid;
|
||||
const char *str;
|
||||
struct ldb_dn *ncname;
|
||||
int i;
|
||||
uint_t rid;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
|
||||
|
||||
group_ret = gendb_search(sam_ctx,
|
||||
tmp_ctx, NULL, &group_msgs, group_attrs,
|
||||
"(&(member=%s)(sAMAccountType=*))",
|
||||
ldb_dn_get_linearized(msg->dn));
|
||||
if (group_ret == -1) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info);
|
||||
|
||||
if (group_ret > 0) {
|
||||
groupSIDs = talloc_array(server_info, struct dom_sid *, group_ret);
|
||||
NT_STATUS_HAVE_NO_MEMORY(groupSIDs);
|
||||
}
|
||||
|
||||
/* Need to unroll some nested groups, but not aliases */
|
||||
for (i = 0; i < group_ret; i++) {
|
||||
groupSIDs[i] = samdb_result_dom_sid(groupSIDs,
|
||||
group_msgs[i], "objectSid");
|
||||
NT_STATUS_HAVE_NO_MEMORY(groupSIDs[i]);
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
account_sid = samdb_result_dom_sid(server_info, msg, "objectSid");
|
||||
NT_STATUS_HAVE_NO_MEMORY(account_sid);
|
||||
|
||||
primary_group_sid = dom_sid_dup(server_info, account_sid);
|
||||
NT_STATUS_HAVE_NO_MEMORY(primary_group_sid);
|
||||
|
||||
rid = samdb_result_uint(msg, "primaryGroupID", ~0);
|
||||
if (rid == ~0) {
|
||||
if (group_ret > 0) {
|
||||
primary_group_sid = groupSIDs[0];
|
||||
} else {
|
||||
primary_group_sid = NULL;
|
||||
}
|
||||
} else {
|
||||
primary_group_sid->sub_auths[primary_group_sid->num_auths-1] = rid;
|
||||
}
|
||||
|
||||
server_info->account_sid = account_sid;
|
||||
server_info->primary_group_sid = primary_group_sid;
|
||||
|
||||
server_info->n_domain_groups = group_ret;
|
||||
server_info->domain_groups = groupSIDs;
|
||||
|
||||
server_info->account_name = talloc_steal(server_info, samdb_result_string(msg, "sAMAccountName", NULL));
|
||||
|
||||
server_info->domain_name = talloc_steal(server_info, samdb_result_string(msg_domain_ref, "nETBIOSName", NULL));
|
||||
|
||||
str = samdb_result_string(msg, "displayName", "");
|
||||
server_info->full_name = talloc_strdup(server_info, str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
|
||||
|
||||
str = samdb_result_string(msg, "scriptPath", "");
|
||||
server_info->logon_script = talloc_strdup(server_info, str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
|
||||
|
||||
str = samdb_result_string(msg, "profilePath", "");
|
||||
server_info->profile_path = talloc_strdup(server_info, str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
|
||||
|
||||
str = samdb_result_string(msg, "homeDirectory", "");
|
||||
server_info->home_directory = talloc_strdup(server_info, str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
|
||||
|
||||
str = samdb_result_string(msg, "homeDrive", "");
|
||||
server_info->home_drive = talloc_strdup(server_info, str);
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
|
||||
|
||||
server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
|
||||
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
|
||||
|
||||
server_info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
|
||||
server_info->last_logoff = samdb_result_nttime(msg, "lastLogoff", 0);
|
||||
server_info->acct_expiry = samdb_result_nttime(msg, "accountExpires", 0);
|
||||
server_info->last_password_change = samdb_result_nttime(msg, "pwdLastSet", 0);
|
||||
|
||||
ncname = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", NULL);
|
||||
if (!ncname) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
server_info->allow_password_change
|
||||
= samdb_result_allow_password_change(sam_ctx, mem_ctx,
|
||||
ncname, msg, "pwdLastSet");
|
||||
server_info->force_password_change
|
||||
= samdb_result_force_password_change(sam_ctx, mem_ctx,
|
||||
ncname, msg);
|
||||
|
||||
server_info->logon_count = samdb_result_uint(msg, "logonCount", 0);
|
||||
server_info->bad_password_count = samdb_result_uint(msg, "badPwdCount", 0);
|
||||
|
||||
server_info->acct_flags = samdb_result_acct_flags(msg, "userAccountControl");
|
||||
|
||||
server_info->user_session_key = user_sess_key;
|
||||
server_info->lm_session_key = lm_sess_key;
|
||||
|
||||
server_info->authenticated = True;
|
||||
|
||||
*_server_info = server_info;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
|
||||
TALLOC_CTX *mem_ctx, const char *principal,
|
||||
struct ldb_message ***msgs,
|
||||
struct ldb_message ***msgs_domain_ref)
|
||||
{
|
||||
struct ldb_dn *user_dn, *domain_dn;
|
||||
NTSTATUS nt_status;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
|
||||
int ret;
|
||||
struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
|
||||
|
||||
if (!tmp_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal, &user_dn, &domain_dn);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
/* grab domain info from the reference */
|
||||
ret = gendb_search(sam_ctx, tmp_ctx, partitions_basedn, msgs_domain_ref, domain_ref_attrs,
|
||||
"(ncName=%s)", ldb_dn_get_linearized(domain_dn));
|
||||
|
||||
if (ret != 1) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
/* pull the user attributes */
|
||||
ret = gendb_search_dn(sam_ctx, tmp_ctx, user_dn, msgs, user_attrs);
|
||||
if (ret != 1) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
talloc_steal(mem_ctx, *msgs);
|
||||
talloc_steal(mem_ctx, *msgs_domain_ref);
|
||||
talloc_free(tmp_ctx);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available */
|
||||
NTSTATUS sam_get_server_info_principal(TALLOC_CTX *mem_ctx, const char *principal,
|
||||
struct auth_serversupplied_info **server_info)
|
||||
{
|
||||
NTSTATUS nt_status;
|
||||
DATA_BLOB user_sess_key = data_blob(NULL, 0);
|
||||
DATA_BLOB lm_sess_key = data_blob(NULL, 0);
|
||||
|
||||
struct ldb_message **msgs;
|
||||
struct ldb_message **msgs_domain_ref;
|
||||
struct ldb_context *sam_ctx;
|
||||
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
|
||||
if (!tmp_ctx) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
sam_ctx = samdb_connect(tmp_ctx, system_session(tmp_ctx));
|
||||
if (sam_ctx == NULL) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_INVALID_SYSTEM_SERVICE;
|
||||
}
|
||||
|
||||
nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
|
||||
&msgs, &msgs_domain_ref);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, msgs[0], msgs_domain_ref[0],
|
||||
user_sess_key, lm_sess_key,
|
||||
server_info);
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
talloc_steal(mem_ctx, *server_info);
|
||||
}
|
||||
talloc_free(tmp_ctx);
|
||||
return nt_status;
|
||||
}
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Run this script to build samba from SVN.
|
||||
|
||||
## insert all possible names (only works with
|
||||
## autoconf 2.x
|
||||
TESTAUTOHEADER="autoheader autoheader-2.53 autoheader2.50 autoheader259 autoheader253"
|
||||
TESTAUTOCONF="autoconf autoconf-2.53 autoconf2.50 autoconf259 autoconf253"
|
||||
|
||||
AUTOHEADERFOUND="0"
|
||||
AUTOCONFFOUND="0"
|
||||
|
||||
|
||||
##
|
||||
## Look for autoheader
|
||||
##
|
||||
for i in $TESTAUTOHEADER; do
|
||||
if which $i > /dev/null 2>&1; then
|
||||
if test `$i --version | head -n 1 | cut -d. -f 2 | tr -d [:alpha:]` -ge 53; then
|
||||
AUTOHEADER=$i
|
||||
AUTOHEADERFOUND="1"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
##
|
||||
## Look for autoconf
|
||||
##
|
||||
|
||||
for i in $TESTAUTOCONF; do
|
||||
if which $i > /dev/null 2>&1; then
|
||||
if test `$i --version | head -n 1 | cut -d. -f 2 | tr -d [:alpha:]` -ge 53; then
|
||||
AUTOCONF=$i
|
||||
AUTOCONFFOUND="1"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
##
|
||||
## do we have it?
|
||||
##
|
||||
if test "$AUTOCONFFOUND" = "0" -o "$AUTOHEADERFOUND" = "0"; then
|
||||
echo "$0: need autoconf 2.53 or later to build samba from SVN" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$0: running script/mkversion.sh"
|
||||
./script/mkversion.sh || exit 1
|
||||
|
||||
rm -rf autom4te*.cache
|
||||
rm -f configure include/config_tmp.h*
|
||||
|
||||
IPATHS="-I. -Ilib/replace"
|
||||
|
||||
echo "$0: running $AUTOHEADER $IPATHS"
|
||||
$AUTOHEADER $IPATHS || exit 1
|
||||
|
||||
echo "$0: running $AUTOCONF $IPATHS"
|
||||
$AUTOCONF $IPATHS || exit 1
|
||||
|
||||
rm -rf autom4te*.cache
|
||||
|
||||
echo "Now run ./configure and then make."
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
dnl @synopsis AX_CFLAGS_GCC_OPTION (optionflag [,[shellvar][,[A][,[NA]]])
|
||||
dnl
|
||||
dnl AX_CFLAGS_GCC_OPTION(-fvomit-frame) would show a message as like
|
||||
dnl "checking CFLAGS for gcc -fvomit-frame ... yes" and adds
|
||||
dnl the optionflag to CFLAGS if it is understood. You can override
|
||||
dnl the shellvar-default of CFLAGS of course. The order of arguments
|
||||
dnl stems from the explicit macros like AX_CFLAGS_WARN_ALL.
|
||||
dnl
|
||||
dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or
|
||||
dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options.
|
||||
dnl However, if you use this macro in a few places, it would be great
|
||||
dnl if you would make up a new function-macro and submit it to the
|
||||
dnl ac-archive.
|
||||
dnl
|
||||
dnl - $1 option-to-check-for : required ("-option" as non-value)
|
||||
dnl - $2 shell-variable-to-add-to : CFLAGS
|
||||
dnl - $3 action-if-found : add value to shellvariable
|
||||
dnl - $4 action-if-not-found : nothing
|
||||
dnl
|
||||
dnl note: in earlier versions, $1-$2 were swapped. We try to detect the
|
||||
dnl situation and accept a $2=~/-/ as being the old option-to-check-for.
|
||||
dnl
|
||||
dnl also: there are other variants that emerged from the original macro
|
||||
dnl variant which did just test an option to be possibly added. However,
|
||||
dnl some compilers accept an option silently, or possibly for just
|
||||
dnl another option that was not intended. Therefore, we have to do a
|
||||
dnl generic test for a compiler family. For gcc we check "-pedantic"
|
||||
dnl being accepted which is also understood by compilers who just want
|
||||
dnl to be compatible with gcc even when not being made from gcc sources.
|
||||
dnl
|
||||
dnl see also:
|
||||
dnl AX_CFLAGS_SUN_OPTION AX_CFLAGS_HPUX_OPTION
|
||||
dnl AX_CFLAGS_AIX_OPTION AX_CFLAGS_IRIX_OPTION
|
||||
dnl
|
||||
dnl @, tested, experimental
|
||||
dnl @version $Id: ax_cflags_gcc_option.m4,v 1.5 2003/11/29 08:13:25 guidod Exp $
|
||||
dnl @author Guido Draheim <guidod@gmx.de>
|
||||
dnl http://ac-archive.sourceforge.net/C_Support/ax_cflags_gcc_option.m4
|
||||
dnl
|
||||
AC_DEFUN([AX_CFLAGS_GCC_OPTION_OLD], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$2])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-pedantic % m4_ifval($2,$2,-option)" dnl GCC
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
|
||||
m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
|
||||
dnl -------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([AX_CFLAGS_GCC_OPTION_NEW], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$1])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-pedantic % m4_ifval($1,$1,-option)" dnl GCC
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
|
||||
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([AX_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
|
||||
[AX_CFLAGS_GCC_OPTION_NEW($@)],[AX_CFLAGS_GCC_OPTION_OLD($@)])])
|
||||
@@ -0,0 +1,174 @@
|
||||
dnl @synopsis AX_CFLAGS_IRIX_OPTION (optionflag [,[shellvar][,[A][,[NA]]])
|
||||
dnl
|
||||
dnl AX_CFLAGS_IRIX_OPTION(-go_for_it) would show a message as like
|
||||
dnl "checking CFLAGS for irix/cc -go_for_it ... yes" and adds the
|
||||
dnl optionflag to CFLAGS if it is understood. You can override the
|
||||
dnl shellvar-default of CFLAGS of course. The order of arguments stems
|
||||
dnl from the explicit macros like AX_CFLAGS_WARN_ALL.
|
||||
dnl
|
||||
dnl The cousin AX_CXXFLAGS_IRIX_OPTION would check for an option to add
|
||||
dnl to CXXFLAGS - and it uses the autoconf setup for C++ instead of C
|
||||
dnl (since it is possible to use different compilers for C and C++).
|
||||
dnl
|
||||
dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or
|
||||
dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options.
|
||||
dnl However, if you use this macro in a few places, it would be great
|
||||
dnl if you would make up a new function-macro and submit it to the
|
||||
dnl ac-archive.
|
||||
dnl
|
||||
dnl - $1 option-to-check-for : required ("-option" as non-value)
|
||||
dnl - $2 shell-variable-to-add-to : CFLAGS (or CXXFLAGS in the other case)
|
||||
dnl - $3 action-if-found : add value to shellvariable
|
||||
dnl - $4 action-if-not-found : nothing
|
||||
dnl
|
||||
dnl note: in earlier versions, $1-$2 were swapped. We try to detect the
|
||||
dnl situation and accept a $2=~/-/ as being the old
|
||||
dnl option-to-check-for.
|
||||
dnl
|
||||
dnl see also: AX_CFLAGS_GCC_OPTION for the widely used original
|
||||
dnl variant.
|
||||
dnl
|
||||
dnl @category C
|
||||
dnl @author Guido Draheim <guidod@gmx.de>
|
||||
dnl @version 2005-01-21
|
||||
dnl @license GPLWithACException
|
||||
|
||||
AC_DEFUN([AX_CFLAGS_IRIX_OPTION_OLD], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_irix_option_$2])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for irix/cc m4_ifval($2,$2,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-fullwarn -use_readonly_const % m4_ifval($2,$2,-option)" dnl IRIX C
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
|
||||
m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
dnl the only difference - the LANG selection... and the default FLAGS
|
||||
|
||||
AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION_OLD], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_irix_option_$2])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for irix/cc m4_ifval($2,$2,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_CXX
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-fullwarn -use_readonly_const % m4_ifval($2,$2,-option)" dnl IRIX C
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
|
||||
m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
dnl --------------------------------------------------------------------------
|
||||
|
||||
AC_DEFUN([AX_CFLAGS_IRIX_OPTION_NEW], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_irix_option_$1])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for irix/cc m4_ifval($1,$1,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-fullwarn -use_readonly_const % m4_ifval($1,$1,-option)" dnl IRIX C
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
|
||||
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
dnl the only difference - the LANG selection... and the default FLAGS
|
||||
|
||||
AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION_NEW], [dnl
|
||||
AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
|
||||
AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_irix_option_$1])dnl
|
||||
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for irix/cc m4_ifval($1,$1,-option)],
|
||||
VAR,[VAR="no, unknown"
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_CXX
|
||||
ac_save_[]FLAGS="$[]FLAGS"
|
||||
for ac_arg dnl
|
||||
in "-fullwarn -use_readonly_const % m4_ifval($1,$1,-option)" dnl IRIX C
|
||||
#
|
||||
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
|
||||
AC_TRY_COMPILE([],[return 0;],
|
||||
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
|
||||
done
|
||||
FLAGS="$ac_save_[]FLAGS"
|
||||
AC_LANG_RESTORE
|
||||
])
|
||||
case ".$VAR" in
|
||||
.ok|.ok,*) m4_ifvaln($3,$3) ;;
|
||||
.|.no|.no,*) m4_ifvaln($4,$4) ;;
|
||||
*) m4_ifvaln($3,$3,[
|
||||
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
|
||||
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
|
||||
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
|
||||
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
|
||||
fi ]) ;;
|
||||
esac
|
||||
AS_VAR_POPDEF([VAR])dnl
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])
|
||||
|
||||
AC_DEFUN([AX_CFLAGS_IRIX_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
|
||||
[AX_CFLAGS_IRIX_OPTION_NEW($@)],[AX_CFLAGS_IRIX_OPTION_OLD($@)])])
|
||||
|
||||
AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
|
||||
[AX_CXXFLAGS_IRIX_OPTION_NEW($@)],[AX_CXXFLAGS_IRIX_OPTION_OLD($@)])])
|
||||
@@ -0,0 +1,154 @@
|
||||
dnl SMB Build Environment CC Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
AC_LIBREPLACE_CC_CHECKS
|
||||
|
||||
#
|
||||
# Set the debug symbol option if we have
|
||||
# --enable-*developer or --enable-debug
|
||||
# and the compiler supports it
|
||||
#
|
||||
if test x$ac_cv_prog_cc_g = xyes -a x$debug = xyes; then
|
||||
CFLAGS="${CFLAGS} -g"
|
||||
fi
|
||||
|
||||
############################################
|
||||
# check if the compiler handles c99 struct initialization
|
||||
LIBREPLACE_C99_STRUCT_INIT(samba_cv_c99_struct_initialization=yes,
|
||||
samba_cv_c99_struct_initialization=no)
|
||||
|
||||
if test x"$samba_cv_c99_struct_initialization" != x"yes"; then
|
||||
AC_MSG_WARN([C compiler does not support c99 struct initialization!])
|
||||
AC_MSG_ERROR([Please Install gcc from http://gcc.gnu.org/])
|
||||
fi
|
||||
|
||||
############################################
|
||||
# check if the compiler can handle negative enum values
|
||||
# and don't truncate the values to INT_MAX
|
||||
# a runtime test is needed here
|
||||
AC_CACHE_CHECK([that the C compiler understands negative enum values],SMB_BUILD_CC_NEGATIVE_ENUM_VALUES, [
|
||||
AC_TRY_RUN(
|
||||
[
|
||||
#include <stdio.h>
|
||||
enum negative_values { NEGATIVE_VALUE = 0xFFFFFFFF };
|
||||
int main(void) {
|
||||
enum negative_values v1 = NEGATIVE_VALUE;
|
||||
unsigned v2 = 0xFFFFFFFF;
|
||||
if (v1 != v2) {
|
||||
printf("v1=0x%08x v2=0x%08x\n", v1, v2);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
],
|
||||
SMB_BUILD_CC_NEGATIVE_ENUM_VALUES=yes,SMB_BUILD_CC_NEGATIVE_ENUM_VALUES=no)])
|
||||
if test x"$SMB_BUILD_CC_NEGATIVE_ENUM_VALUES" != x"yes"; then
|
||||
AC_MSG_WARN([using --unit-enums for pidl])
|
||||
PIDL_ARGS="$PIDL_ARGS --uint-enums"
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([for test routines])
|
||||
AC_TRY_RUN([#include "${srcdir-.}/build/tests/trivial.c"],
|
||||
AC_MSG_RESULT(yes),
|
||||
AC_MSG_ERROR([cant find test code. Aborting config]),
|
||||
AC_MSG_WARN([cannot run when cross-compiling]))
|
||||
|
||||
#
|
||||
# Check if the compiler support ELF visibility for symbols
|
||||
#
|
||||
|
||||
visibility_attribute=no
|
||||
VISIBILITY_CFLAGS=""
|
||||
if test x"$GCC" = x"yes" ; then
|
||||
AX_CFLAGS_GCC_OPTION([-fvisibility=hidden], VISIBILITY_CFLAGS)
|
||||
fi
|
||||
|
||||
if test -n "$VISIBILITY_CFLAGS"; then
|
||||
AC_MSG_CHECKING([whether the C compiler supports the visibility attribute])
|
||||
OLD_CFLAGS="$CFLAGS"
|
||||
|
||||
CFLAGS="$CFLAGS $VISIBILITY_CFLAGS"
|
||||
AC_TRY_RUN([
|
||||
void vis_foo1(void) {}
|
||||
__attribute__((visibility("default"))) void vis_foo2(void) {}
|
||||
#include "${srcdir-.}/build/tests/trivial.c"
|
||||
],[
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(HAVE_VISIBILITY_ATTR,1,[Whether the C compiler supports the visibility attribute])
|
||||
visibility_attribute=yes
|
||||
],[
|
||||
AC_MSG_RESULT(no)
|
||||
])
|
||||
CFLAGS="$OLD_CFLAGS"
|
||||
fi
|
||||
AC_SUBST(visibility_attribute)
|
||||
|
||||
#
|
||||
# Check if the compiler can handle the options we selected by
|
||||
# --enable-*developer
|
||||
#
|
||||
DEVELOPER_CFLAGS=""
|
||||
if test x$developer = xyes; then
|
||||
OLD_CFLAGS="${CFLAGS}"
|
||||
|
||||
CFLAGS="${CFLAGS} -D_SAMBA_DEVELOPER_DONNOT_USE_O2_"
|
||||
DEVELOPER_CFLAGS="-DDEBUG_PASSWORD -DDEVELOPER"
|
||||
if test x"$GCC" = x"yes" ; then
|
||||
#
|
||||
# warnings we want...
|
||||
#
|
||||
AX_CFLAGS_GCC_OPTION(-Wall, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wshadow, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Werror-implicit-function-declaration, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wstrict-prototypes, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wpointer-arith, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wcast-qual, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wcast-align, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wwrite-strings, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wmissing-format-attribute, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wformat=2, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wdeclaration-after-statement, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wunused-macros, DEVELOPER_CFLAGS)
|
||||
# AX_CFLAGS_GCC_OPTION(-Wextra, DEVELOPER_CFLAGS)
|
||||
# AX_CFLAGS_GCC_OPTION(-Wc++-compat, DEVELOPER_CFLAGS)
|
||||
# AX_CFLAGS_GCC_OPTION(-Wmissing-prototypes, DEVELOPER_CFLAGS)
|
||||
# AX_CFLAGS_GCC_OPTION(-Wmissing-declarations, DEVELOPER_CFLAGS)
|
||||
# AX_CFLAGS_GCC_OPTION(-Wmissing-field-initializers, DEVELOPER_CFLAGS)
|
||||
#
|
||||
# warnings we don't want...
|
||||
#
|
||||
AX_CFLAGS_GCC_OPTION(-Wno-format-y2k, DEVELOPER_CFLAGS)
|
||||
AX_CFLAGS_GCC_OPTION(-Wno-unused-parameter, DEVELOPER_CFLAGS)
|
||||
else
|
||||
AX_CFLAGS_IRIX_OPTION(-fullwarn, DEVELOPER_CFLAGS)
|
||||
fi
|
||||
|
||||
CFLAGS="${OLD_CFLAGS}"
|
||||
fi
|
||||
if test -n "$DEVELOPER_CFLAGS"; then
|
||||
OLD_CFLAGS="${CFLAGS}"
|
||||
CFLAGS="${CFLAGS} ${DEVELOPER_CFLAGS}"
|
||||
AC_MSG_CHECKING([that the C compiler can use the DEVELOPER_CFLAGS])
|
||||
AC_TRY_COMPILE([],[],
|
||||
AC_MSG_RESULT(yes),
|
||||
DEVELOPER_CFLAGS=""; AC_MSG_RESULT(no))
|
||||
CFLAGS="${OLD_CFLAGS}"
|
||||
fi
|
||||
|
||||
# allow for --with-hostcc=gcc
|
||||
AC_ARG_WITH(hostcc,[ --with-hostcc=compiler choose host compiler],
|
||||
[HOSTCC=$withval],
|
||||
[
|
||||
if test z"$cross_compiling" = "yes"; then
|
||||
HOSTCC=cc
|
||||
else
|
||||
HOSTCC=$CC
|
||||
fi
|
||||
])
|
||||
AC_SUBST(HOSTCC)
|
||||
|
||||
AC_PATH_PROG(GCOV,gcov)
|
||||
@@ -0,0 +1 @@
|
||||
AC_PATH_PROG(XSLTPROC, xsltproc)
|
||||
@@ -0,0 +1,208 @@
|
||||
dnl SMB Build Environment LD Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Copyright (C) Jelmer Vernooij 2006
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
AC_PATH_PROG(PROG_LD,ld)
|
||||
LD=${PROG_LD}
|
||||
AC_PROG_LD_GNU
|
||||
LD=""
|
||||
AC_PATH_PROG(PROG_AR, ar)
|
||||
|
||||
AC_SUBST(STLD)
|
||||
AC_SUBST(STLD_FLAGS)
|
||||
AC_SUBST(BLDSHARED)
|
||||
AC_SUBST(LD)
|
||||
AC_SUBST(LDFLAGS)
|
||||
AC_SUBST(SHLD)
|
||||
AC_SUBST(SHLD_FLAGS)
|
||||
AC_SUBST(SHLD_UNDEF_FLAGS)
|
||||
AC_SUBST(SHLIBEXT)
|
||||
AC_SUBST(SONAMEFLAG)
|
||||
AC_SUBST(PICFLAG)
|
||||
|
||||
# Assume non-shared by default and override below
|
||||
# these are the defaults, good for lots of systems
|
||||
STLD=${PROG_AR}
|
||||
STLD_FLAGS="-rcs"
|
||||
BLDSHARED="false"
|
||||
LD="${CC}"
|
||||
LDFLAGS=""
|
||||
SHLD="${CC}"
|
||||
SHLD_FLAGS="-shared"
|
||||
SHLIBEXT="so"
|
||||
SONAMEFLAG=""
|
||||
PICFLAG=""
|
||||
|
||||
AC_MSG_CHECKING([ability to build shared libraries])
|
||||
|
||||
# and these are for particular systems
|
||||
case "$host_os" in
|
||||
*linux*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-shared -Wl,-Bsymbolic"
|
||||
SHLD_UNDEF_FLAGS="-Wl,--allow-shlib-undefined"
|
||||
LDFLAGS="-Wl,--export-dynamic"
|
||||
PICFLAG="-fPIC"
|
||||
SONAMEFLAG="-Wl,-soname="
|
||||
;;
|
||||
*solaris*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-G"
|
||||
SONAMEFLAG="-h "
|
||||
if test "${GCC}" = "yes"; then
|
||||
PICFLAG="-fPIC"
|
||||
SONAMEFLAG="-Wl,-soname="
|
||||
if test "${ac_cv_prog_gnu_ld}" = "yes"; then
|
||||
LDFLAGS="-Wl,-E"
|
||||
fi
|
||||
else
|
||||
PICFLAG="-KPIC"
|
||||
## ${CFLAGS} added for building 64-bit shared
|
||||
## libs using Sun's Compiler
|
||||
SHLD_FLAGS="-G \${CFLAGS}"
|
||||
fi
|
||||
;;
|
||||
*sunos*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-G"
|
||||
SONAMEFLAG="-Wl,-h,"
|
||||
PICFLAG="-KPIC" # Is this correct for SunOS
|
||||
;;
|
||||
*netbsd* | *freebsd* | *dragonfly* )
|
||||
BLDSHARED="true"
|
||||
LDFLAGS="-Wl,--export-dynamic"
|
||||
SONAMEFLAG="-Wl,-soname,"
|
||||
PICFLAG="-fPIC -DPIC"
|
||||
;;
|
||||
*openbsd*)
|
||||
BLDSHARED="true"
|
||||
LDFLAGS="-Wl,-Bdynamic"
|
||||
SONAMEFLAG="-Wl,-soname,"
|
||||
PICFLAG="-fPIC"
|
||||
;;
|
||||
*irix*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-set_version sgi1.0 -shared"
|
||||
SONAMEFLAG="-soname "
|
||||
SHLD="${PROG_LD}"
|
||||
if test "${GCC}" = "yes"; then
|
||||
PICFLAG="-fPIC"
|
||||
else
|
||||
PICFLAG="-KPIC"
|
||||
fi
|
||||
;;
|
||||
*aix*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-Wl,-G,-bexpall,-bbigtoc"
|
||||
LDFLAGS="-Wl,-brtl,-bexpall,-bbigtoc"
|
||||
# as AIX code is always position independent...
|
||||
PICFLAG="-O2"
|
||||
;;
|
||||
*hpux*)
|
||||
# Use special PIC flags for the native HP-UX compiler.
|
||||
if test $ac_cv_prog_cc_Ae = yes; then
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-b -Wl,-B,symbolic,-b,-z"
|
||||
SONAMEFLAG="-Wl,+h "
|
||||
PICFLAG="+z"
|
||||
elif test "${GCC}" = "yes"; then
|
||||
BLDSHARED="true" # I hope this is correct
|
||||
PICFLAG="-fPIC"
|
||||
fi
|
||||
if test "$host_cpu" = "ia64"; then
|
||||
SHLIBEXT="so"
|
||||
LDFLAGS="-Wl,-E,+b/usr/local/lib/hpux32:/usr/lib/hpux32"
|
||||
else
|
||||
SHLIBEXT="sl"
|
||||
LDFLAGS="-Wl,-E,+b/usr/local/lib:/usr/lib"
|
||||
fi
|
||||
;;
|
||||
*osf*)
|
||||
BLDSHARED="true"
|
||||
SONAMEFLAG="-Wl,-soname,"
|
||||
PICFLAG="-fPIC"
|
||||
;;
|
||||
*unixware*)
|
||||
BLDSHARED="true"
|
||||
SONAMEFLAG="-Wl,-soname,"
|
||||
PICFLAG="-KPIC"
|
||||
;;
|
||||
*darwin*)
|
||||
BLDSHARED="true"
|
||||
SHLD_FLAGS="-bundle -flat_namespace -undefined suppress"
|
||||
SHLIBEXT="dylib"
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_MSG_RESULT($BLDSHARED)
|
||||
|
||||
AC_MSG_CHECKING([LD])
|
||||
AC_MSG_RESULT([$LD])
|
||||
AC_MSG_CHECKING([LDFLAGS])
|
||||
AC_MSG_RESULT([$LDFLAGS])
|
||||
|
||||
AC_MSG_CHECKING([STLD])
|
||||
AC_MSG_RESULT([$STLD])
|
||||
AC_MSG_CHECKING([STLD_FLAGS])
|
||||
AC_MSG_RESULT([$STLD_FLAGS])
|
||||
|
||||
#######################################################
|
||||
# test whether building a shared library actually works
|
||||
if test $BLDSHARED = true; then
|
||||
|
||||
AC_MSG_CHECKING([SHLD])
|
||||
AC_MSG_RESULT([$SHLD])
|
||||
AC_MSG_CHECKING([SHLD_FLAGS])
|
||||
AC_MSG_RESULT([$SHLD_FLAGS])
|
||||
|
||||
AC_DEFINE_UNQUOTED(SHLIBEXT, "$SHLIBEXT", [Shared library extension])
|
||||
AC_MSG_CHECKING([SHLIBEXT])
|
||||
AC_MSG_RESULT([$SHLIBEXT])
|
||||
AC_MSG_CHECKING([SONAMEFLAG])
|
||||
AC_MSG_RESULT([$SONAMEFLAG])
|
||||
|
||||
AC_MSG_CHECKING([PICFLAG])
|
||||
AC_MSG_RESULT([$PICFLAG])
|
||||
|
||||
AC_CACHE_CHECK([whether building shared libraries actually works],
|
||||
[ac_cv_shlib_works],[
|
||||
ac_cv_shlib_works=no
|
||||
# try building a trivial shared library
|
||||
${CC} ${CFLAGS} ${PICFLAG} -c ${srcdir-.}/build/tests/shlib.c -o shlib.o &&
|
||||
${SHLD} `eval echo ${SHLD_FLAGS} ` -o shlib.${SHLIBEXT} shlib.o &&
|
||||
ac_cv_shlib_works=yes
|
||||
rm -f shlib.${SHLIBEXT} shlib.o
|
||||
])
|
||||
if test $ac_cv_shlib_works = no; then
|
||||
BLDSHARED=false
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if we can link using the selected flags])
|
||||
AC_TRY_RUN([#include "${srcdir-.}/build/tests/trivial.c"],
|
||||
AC_MSG_RESULT(yes),
|
||||
AC_MSG_ERROR([we cannot link with the selected cc and ld flags. Aborting configure]),
|
||||
AC_MSG_WARN([cannot run when cross-compiling]))
|
||||
|
||||
|
||||
USESHARED=false
|
||||
AC_SUBST(USESHARED)
|
||||
|
||||
AC_ARG_ENABLE(dso,
|
||||
[ --enable-dso Enable using shared libraries internally (experimental)],
|
||||
[],[enable_dso=no])
|
||||
|
||||
if test x"$enable_dso" = x"yes" -a x"$BLDSHARED" != x"true"; then
|
||||
AC_MSG_ERROR([--enable-dso: no support for shared libraries])
|
||||
fi
|
||||
|
||||
if test x"$enable_dso" != x"no"; then
|
||||
USESHARED=$BLDSHARED
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if binaries will use shared libraries])
|
||||
AC_MSG_RESULT([$USESHARED])
|
||||
@@ -0,0 +1,48 @@
|
||||
dnl SMB Build Environment make Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Copyright (C) Jelmer Vernooij 2005
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
AC_PATH_PROG(MAKE,make)
|
||||
|
||||
AC_CACHE_CHECK([whether we have GNU make], samba_cv_gnu_make, [
|
||||
if $ac_cv_path_MAKE --version | head -1 | grep GNU 2>/dev/null >/dev/null
|
||||
then
|
||||
samba_cv_gnu_make=yes
|
||||
else
|
||||
samba_cv_gnu_make=no
|
||||
fi
|
||||
])
|
||||
|
||||
GNU_MAKE=$samba_cv_gnu_make
|
||||
AC_SUBST(GNU_MAKE)
|
||||
|
||||
if test "x$GNU_MAKE" = x"yes"; then
|
||||
AC_CACHE_CHECK([GNU make version], samba_cv_gnu_make_version,[
|
||||
samba_cv_gnu_make_version=`$ac_cv_path_MAKE --version | head -1 | cut -d " " -f 3 2>/dev/null`
|
||||
])
|
||||
GNU_MAKE_VERSION=$samba_cv_gnu_make_version
|
||||
AC_SUBST(GNU_MAKE_VERSION)
|
||||
fi
|
||||
|
||||
|
||||
new_make=no
|
||||
AC_MSG_CHECKING([for GNU make >= 3.81])
|
||||
if test x$GNU_MAKE = x"yes"; then
|
||||
if $PERL -e " \$_ = '$GNU_MAKE_VERSION'; s/@<:@^\d\.@:>@.*//g; exit (\$_ < 3.81);"; then
|
||||
new_make=yes
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT($new_make)
|
||||
automatic_dependencies=no
|
||||
AX_CFLAGS_GCC_OPTION([-M -MT conftest.d -MF conftest.o], [], [ automatic_dependencies=$new_make ], [])
|
||||
AC_MSG_CHECKING([Whether to use automatic dependencies])
|
||||
AC_ARG_ENABLE(automatic-dependencies,
|
||||
[ --enable-automatic-dependencies Enable automatic dependencies],
|
||||
[ automatic_dependencies=$enableval ],
|
||||
[ automatic_dependencies=no ])
|
||||
AC_MSG_RESULT($automatic_dependencies)
|
||||
AC_SUBST(automatic_dependencies)
|
||||
@@ -0,0 +1,169 @@
|
||||
dnl SMB Build Environment Path Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
AC_LIBREPLACE_LOCATION_CHECKS
|
||||
|
||||
#################################################
|
||||
# Directory handling stuff to support both the
|
||||
# legacy SAMBA directories and FHS compliant
|
||||
# ones...
|
||||
AC_PREFIX_DEFAULT(/usr/local/samba)
|
||||
|
||||
# Defaults and --without-fhs
|
||||
logfilebase="${localstatedir}"
|
||||
lockdir="${localstatedir}/locks"
|
||||
piddir="${localstatedir}/run"
|
||||
privatedir="\${prefix}/private"
|
||||
modulesdir="\${prefix}/modules"
|
||||
winbindd_socket_dir="${localstatedir}/run/winbind_pipe"
|
||||
|
||||
AC_ARG_WITH(fhs,
|
||||
[ --with-fhs Use FHS-compliant paths (default=no)],
|
||||
lockdir="${localstatedir}/lib/samba"
|
||||
piddir="${localstatedir}/run/samba"
|
||||
logfilebase="${localstatedir}/log/samba"
|
||||
privatedir="${localstatedir}/lib/samba/private"
|
||||
sysconfdir="${sysconfdir}/samba"
|
||||
modulesdir="${libdir}/samba"
|
||||
datadir="${datadir}/samba"
|
||||
includedir="${includedir}/samba-4.0"
|
||||
winbindd_socket_dir="${localstatedir}/run/samba/winbind_pipe"
|
||||
)
|
||||
|
||||
#################################################
|
||||
# set private directory location
|
||||
AC_ARG_WITH(privatedir,
|
||||
[ --with-privatedir=DIR Where to put sam.ldb and other private files containing key material ($ac_default_prefix/private)],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
#
|
||||
# Just in case anybody calls it without argument
|
||||
#
|
||||
AC_MSG_WARN([--with-privatedir called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
privatedir="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
#################################################
|
||||
# set where the winbindd socket should be put
|
||||
AC_ARG_WITH(winbindd-socket-dir,
|
||||
[ --with-winbindd-socket-dir=DIR Where to put the winbindd socket ($ac_default_prefix/run/winbind_pipe)],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
#
|
||||
# Just in case anybody calls it without argument
|
||||
#
|
||||
AC_MSG_WARN([--with-winbind-socketdir called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
winbindd_socket_dir="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
#################################################
|
||||
# set lock directory location
|
||||
AC_ARG_WITH(lockdir,
|
||||
[ --with-lockdir=DIR Where to put lock files ($ac_default_prefix/var/locks)],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
#
|
||||
# Just in case anybody calls it without argument
|
||||
#
|
||||
AC_MSG_WARN([--with-lockdir called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
lockdir="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
#################################################
|
||||
# set pid directory location
|
||||
AC_ARG_WITH(piddir,
|
||||
[ --with-piddir=DIR Where to put pid files ($ac_default_prefix/var/locks)],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
#
|
||||
# Just in case anybody calls it without argument
|
||||
#
|
||||
AC_MSG_WARN([--with-piddir called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
piddir="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
#################################################
|
||||
# set log directory location
|
||||
AC_ARG_WITH(logfilebase,
|
||||
[ --with-logfilebase=DIR Where to put log files (\$(VARDIR))],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
#
|
||||
# Just in case anybody does it
|
||||
#
|
||||
AC_MSG_WARN([--with-logfilebase called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
logfilebase="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
|
||||
AC_SUBST(lockdir)
|
||||
AC_SUBST(piddir)
|
||||
AC_SUBST(logfilebase)
|
||||
AC_SUBST(privatedir)
|
||||
AC_SUBST(bindir)
|
||||
AC_SUBST(sbindir)
|
||||
AC_SUBST(winbindd_socket_dir)
|
||||
AC_SUBST(modulesdir)
|
||||
|
||||
#################################################
|
||||
# set prefix for 'make test'
|
||||
# this is needed to workarround the 108 char
|
||||
# unix socket path limitation!
|
||||
#
|
||||
selftest_prefix="./st"
|
||||
AC_SUBST(selftest_prefix)
|
||||
AC_ARG_WITH(selftest-prefix,
|
||||
[ --with-selftest-prefix=DIR The prefix where make test will be runned ($selftest_prefix)],
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
AC_MSG_WARN([--with-selftest-prefix called without argument - will use default])
|
||||
;;
|
||||
* )
|
||||
selftest_prefix="$withval"
|
||||
;;
|
||||
esac])
|
||||
|
||||
debug=no
|
||||
AC_ARG_ENABLE(debug,
|
||||
[ --enable-debug Turn on compiler debugging information (default=no)],
|
||||
[if test x$enable_debug = xyes; then
|
||||
debug=yes
|
||||
fi])
|
||||
|
||||
developer=no
|
||||
AC_SUBST(developer)
|
||||
AC_ARG_ENABLE(developer,
|
||||
[ --enable-developer Turn on developer warnings and debugging (default=no)],
|
||||
[if test x$enable_developer = xyes; then
|
||||
debug=yes
|
||||
developer=yes
|
||||
fi])
|
||||
|
||||
dnl disable these external libs
|
||||
AC_ARG_WITH(disable-ext-lib,
|
||||
[ --with-disable-ext-lib=LIB Comma-seperated list of external libraries],
|
||||
[ if test $withval; then
|
||||
for i in `echo $withval | sed -e's/,/ /g'`
|
||||
do
|
||||
eval SMB_$i=NO
|
||||
done
|
||||
fi ])
|
||||
@@ -0,0 +1,31 @@
|
||||
dnl SMB Build Environment Perl Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
case "$host_os" in
|
||||
*irix*)
|
||||
# On IRIX, we prefer Freeware or Nekoware Perl, because the
|
||||
# system perl is so ancient.
|
||||
AC_PATH_PROG(PERL, perl, "", "/usr/freeware/bin:/usr/nekoware/bin:$PATH")
|
||||
;;
|
||||
*)
|
||||
AC_PATH_PROG(PERL, perl)
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x"$PERL" = x""; then
|
||||
AC_MSG_WARN([No version of perl was found!])
|
||||
AC_MSG_ERROR([Please install perl from http://www.perl.com/])
|
||||
fi
|
||||
if test x"$debug" = x"yes";then
|
||||
PERL="$PERL -W"
|
||||
fi
|
||||
export PERL
|
||||
|
||||
AC_PATH_PROG(YAPP, yapp, false)
|
||||
|
||||
PIDL_ARGS=""
|
||||
AC_SUBST(PIDL_ARGS)
|
||||
@@ -0,0 +1,32 @@
|
||||
dnl SMB Build Environment Checks
|
||||
dnl -------------------------------------------------------
|
||||
dnl Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
dnl Released under the GNU GPL
|
||||
dnl -------------------------------------------------------
|
||||
dnl
|
||||
|
||||
AC_SUBST(srcdir)
|
||||
export srcdir;
|
||||
|
||||
# we always set builddir to "." as that's nicer than
|
||||
# having the absolute path of the current work directory
|
||||
builddir=.
|
||||
AC_SUBST(builddir)
|
||||
export builddir;
|
||||
|
||||
AC_SUBST(datarootdir)
|
||||
|
||||
SMB_VERSION_STRING=`cat ${srcdir}/version.h | grep 'SAMBA_VERSION_OFFICIAL_STRING' | cut -d '"' -f2`
|
||||
echo "SAMBA VERSION: ${SMB_VERSION_STRING}"
|
||||
|
||||
SAMBA_VERSION_SVN_REVISION=`cat ${srcdir}/version.h | grep 'SAMBA_VERSION_SVN_REVISION' | cut -d ' ' -f3-`
|
||||
if test -n "${SAMBA_VERSION_SVN_REVISION}";then
|
||||
echo "BUILD REVISION: ${SAMBA_VERSION_SVN_REVISION}"
|
||||
fi
|
||||
|
||||
m4_include(build/m4/check_path.m4)
|
||||
m4_include(build/m4/check_perl.m4)
|
||||
m4_include(build/m4/check_cc.m4)
|
||||
m4_include(build/m4/check_ld.m4)
|
||||
m4_include(build/m4/check_make.m4)
|
||||
m4_include(build/m4/check_doc.m4)
|
||||
@@ -0,0 +1,119 @@
|
||||
dnl SMB Build System
|
||||
dnl ----------------
|
||||
dnl Copyright (C) 2004 Stefan Metzmacher
|
||||
dnl Copyright (C) 2004-2005 Jelmer Vernooij
|
||||
dnl Published under the GPL
|
||||
dnl
|
||||
dnl SMB_SUBSYSTEM(name,obj_files,required_subsystems)
|
||||
dnl
|
||||
dnl SMB_EXT_LIB_FROM_PKGCONFIG(name,pkg-config name)
|
||||
dnl
|
||||
dnl SMB_EXT_LIB(name,libs,cflags,cppflags,ldflags)
|
||||
dnl
|
||||
dnl SMB_ENABLE(name,default_build)
|
||||
dnl
|
||||
dnl #######################################################
|
||||
dnl ### And now the implementation ###
|
||||
dnl #######################################################
|
||||
|
||||
dnl SMB_SUBSYSTEM(name,obj_files,required_subsystems,cflags)
|
||||
AC_DEFUN([SMB_SUBSYSTEM],
|
||||
[
|
||||
SMB_INFO_SUBSYSTEMS="$SMB_INFO_SUBSYSTEMS
|
||||
###################################
|
||||
# Start Subsystem $1
|
||||
@<:@SUBSYSTEM::$1@:>@
|
||||
OBJ_FILES = $2
|
||||
PRIVATE_DEPENDENCIES = $3
|
||||
CFLAGS = $4
|
||||
ENABLE = YES
|
||||
# End Subsystem $1
|
||||
###################################
|
||||
"
|
||||
])
|
||||
|
||||
dnl SMB_EXT_LIB_FROM_PKGCONFIG(name,pkg-config name)
|
||||
AC_DEFUN([SMB_EXT_LIB_FROM_PKGCONFIG],
|
||||
[
|
||||
dnl Figure out the correct variables and call SMB_EXT_LIB()
|
||||
|
||||
if test -z "$PKG_CONFIG"; then
|
||||
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
|
||||
fi
|
||||
|
||||
if test "$PKG_CONFIG" = "no" ; then
|
||||
echo "*** The pkg-config script could not be found. Make sure it is"
|
||||
echo "*** in your path, or set the PKG_CONFIG environment variable"
|
||||
echo "*** to the full path to pkg-config."
|
||||
echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config."
|
||||
SMB_EXT_LIB($1)
|
||||
SMB_ENABLE($1, NO)
|
||||
else
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.9.0; then
|
||||
AC_MSG_CHECKING(for $2)
|
||||
|
||||
if test "$SMB_$1"x = "NO"x ; then
|
||||
SMB_ENABLE($1, NO)
|
||||
AC_MSG_RESULT(disabled)
|
||||
elif $PKG_CONFIG --exists '$2' ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
|
||||
|
||||
$1_CFLAGS="`$PKG_CONFIG --cflags '$2'`"
|
||||
OLD_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $$1_CFLAGS"
|
||||
AC_MSG_CHECKING([that the C compiler can use the $1_CFLAGS])
|
||||
AC_TRY_RUN([#include "${srcdir-.}/build/tests/trivial.c"],
|
||||
SMB_ENABLE($1, YES)
|
||||
AC_MSG_RESULT(yes),
|
||||
AC_MSG_RESULT(no),
|
||||
AC_MSG_WARN([cannot run when cross-compiling]))
|
||||
CFLAGS="$OLD_CFLAGS"
|
||||
|
||||
|
||||
SMB_EXT_LIB($1,
|
||||
[`$PKG_CONFIG --libs-only-l '$2'`],
|
||||
[`$PKG_CONFIG --cflags-only-other '$2'`],
|
||||
[`$PKG_CONFIG --cflags-only-I '$2'`],
|
||||
[`$PKG_CONFIG --libs-only-other '$2'` `$PKG_CONFIG --libs-only-L '$2'`])
|
||||
|
||||
else
|
||||
SMB_EXT_LIB($1)
|
||||
SMB_ENABLE($1, NO)
|
||||
AC_MSG_RESULT(no)
|
||||
$PKG_CONFIG --errors-to-stdout --print-errors '$2'
|
||||
fi
|
||||
else
|
||||
echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer."
|
||||
echo "*** See http://www.freedesktop.org/software/pkgconfig"
|
||||
SMB_EXT_LIB($1)
|
||||
SMB_ENABLE($1, NO)
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
dnl SMB_EXT_LIB(name,libs,cflags,cppflags,ldflags)
|
||||
AC_DEFUN([SMB_EXT_LIB],
|
||||
[
|
||||
|
||||
SMB_INFO_EXT_LIBS="$SMB_INFO_EXT_LIBS
|
||||
###################################
|
||||
# Start Ext Lib $1
|
||||
@<:@EXT_LIB::$1@:>@
|
||||
LIBS = $2
|
||||
CFLAGS = $3
|
||||
CPPFLAGS = $4
|
||||
LDFLAGS = $5
|
||||
# End Ext Lib $1
|
||||
###################################
|
||||
"
|
||||
])
|
||||
|
||||
dnl SMB_ENABLE(name,default_build)
|
||||
AC_DEFUN([SMB_ENABLE],
|
||||
[
|
||||
[SMB_ENABLE_][$1]="$2";
|
||||
|
||||
SMB_INFO_ENABLES="$SMB_INFO_ENABLES
|
||||
\$enabled{$1} = \"$2\";"
|
||||
])
|
||||
@@ -0,0 +1,5 @@
|
||||
asn1.pm: asn1.yp
|
||||
yapp -s asn1.yp
|
||||
|
||||
clean:
|
||||
rm -f asn1.pm
|
||||
@@ -0,0 +1,306 @@
|
||||
########################
|
||||
# ASN.1 Parse::Yapp parser
|
||||
# Copyright (C) Stefan (metze) Metzmacher <metze@samba.org>
|
||||
# released under the GNU GPL version 2 or later
|
||||
|
||||
|
||||
|
||||
# the precedence actually doesn't matter at all for this grammer, but
|
||||
# by providing a precedence we reduce the number of conflicts
|
||||
# enormously
|
||||
%left '-' '+' '&' '|' '*' '>' '.' '/' '(' ')' '[' ']' ':' ',' ';'
|
||||
|
||||
|
||||
################
|
||||
# grammer
|
||||
%%
|
||||
|
||||
asn1:
|
||||
identifier asn1_definitions asn1_delimitter asn1_begin asn1_decls asn1_end
|
||||
{{
|
||||
"OBJECT" => "ASN1_DEFINITION",
|
||||
"IDENTIFIER" => $_[1],
|
||||
"DATA" => $_[5]
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_delimitter:
|
||||
delimitter
|
||||
;
|
||||
|
||||
asn1_definitions:
|
||||
'DEFINITIONS'
|
||||
;
|
||||
|
||||
asn1_begin:
|
||||
'BEGIN'
|
||||
;
|
||||
|
||||
asn1_end:
|
||||
'END'
|
||||
;
|
||||
|
||||
asn1_decls:
|
||||
asn1_def
|
||||
{ [ $_[1] ] }
|
||||
| asn1_decls asn1_def
|
||||
{ push(@{$_[1]}, $_[2]); $_[1] }
|
||||
;
|
||||
|
||||
|
||||
|
||||
asn1_def:
|
||||
asn1_target asn1_delimitter asn1_application asn1_type
|
||||
{{
|
||||
"OBJECT" => "ASN1_DEF",
|
||||
"IDENTIFIER" => $_[1],
|
||||
"APPLICATION" => $_[3],
|
||||
"STRUCTURE" => $_[4]
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_target:
|
||||
identifier
|
||||
;
|
||||
|
||||
asn1_application:
|
||||
#empty
|
||||
| '[' 'APPLICATION' constant ']'
|
||||
{ $_[3] }
|
||||
;
|
||||
|
||||
asn1_type:
|
||||
asn1_boolean
|
||||
| asn1_integer
|
||||
| asn1_bit_string
|
||||
| asn1_octet_string
|
||||
| asn1_null
|
||||
| asn1_object_identifier
|
||||
| asn1_real
|
||||
| asn1_enumerated
|
||||
| asn1_sequence
|
||||
| identifier
|
||||
;
|
||||
|
||||
asn1_boolean:
|
||||
'BOOLEAN'
|
||||
{{
|
||||
"TYPE" => "BOOLEAN",
|
||||
"TAG" => 1
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_integer:
|
||||
'INTEGER'
|
||||
{{
|
||||
"TYPE" => "INTEGER",
|
||||
"TAG" => 2
|
||||
}}
|
||||
| 'INTEGER' '(' constant '.' '.' constant ')'
|
||||
{{
|
||||
"TYPE" => "INTEGER",
|
||||
"TAG" => 2,
|
||||
"RANGE_LOW" => $_[3],
|
||||
"RENAGE_HIGH" => $_[6]
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_bit_string:
|
||||
'BIT' 'STRING'
|
||||
{{
|
||||
"TYPE" => "BIT STRING",
|
||||
"TAG" => 3
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_octet_string:
|
||||
'OCTET' 'STRING'
|
||||
{{
|
||||
"TYPE" => "OCTET STRING",
|
||||
"TAG" => 4
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_null:
|
||||
'NULL'
|
||||
{{
|
||||
"TYPE" => "NULL",
|
||||
"TAG" => 5
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_object_identifier:
|
||||
'OBJECT' 'IDENTIFIER'
|
||||
{{
|
||||
"TYPE" => "OBJECT IDENTIFIER",
|
||||
"TAG" => 6
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_real:
|
||||
'REAL'
|
||||
{{
|
||||
"TYPE" => "REAL",
|
||||
"TAG" => 9
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_enumerated:
|
||||
'ENUMERATED'
|
||||
{{
|
||||
"TYPE" => "ENUMERATED",
|
||||
"TAG" => 10
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_sequence:
|
||||
'SEQUENCE' '{' asn1_var_dec_list '}'
|
||||
{{
|
||||
"TYPE" => "SEQUENCE",
|
||||
"TAG" => 16,
|
||||
"STRUCTURE" => $_[3]
|
||||
}}
|
||||
;
|
||||
|
||||
asn1_var_dec_list:
|
||||
asn1_var_dec
|
||||
{ [ $_[1] ] }
|
||||
| asn1_var_dec_list ',' asn1_var_dec
|
||||
{ push(@{$_[1]}, $_[3]); $_[1] }
|
||||
;
|
||||
|
||||
asn1_var_dec:
|
||||
identifier asn1_type
|
||||
{{
|
||||
"NAME" => $_[1],
|
||||
"TYPE" => $_[2]
|
||||
}}
|
||||
;
|
||||
|
||||
anytext: #empty { "" }
|
||||
| identifier | constant | text
|
||||
| anytext '-' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '.' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '*' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '>' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '|' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '&' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '/' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '+' anytext { "$_[1]$_[2]$_[3]" }
|
||||
| anytext '(' anytext ')' anytext { "$_[1]$_[2]$_[3]$_[4]$_[5]" }
|
||||
;
|
||||
|
||||
delimitter: DELIMITTER
|
||||
;
|
||||
|
||||
identifier: IDENTIFIER
|
||||
;
|
||||
|
||||
constant: CONSTANT
|
||||
;
|
||||
|
||||
text: TEXT { "\"$_[1]\"" }
|
||||
;
|
||||
|
||||
#####################################
|
||||
# start code
|
||||
%%
|
||||
|
||||
use util;
|
||||
|
||||
sub _ASN1_Error {
|
||||
if (exists $_[0]->YYData->{ERRMSG}) {
|
||||
print $_[0]->YYData->{ERRMSG};
|
||||
delete $_[0]->YYData->{ERRMSG};
|
||||
return;
|
||||
};
|
||||
my $line = $_[0]->YYData->{LINE};
|
||||
my $last_token = $_[0]->YYData->{LAST_TOKEN};
|
||||
my $file = $_[0]->YYData->{INPUT_FILENAME};
|
||||
|
||||
print "$file:$line: Syntax error near '$last_token'\n";
|
||||
}
|
||||
|
||||
sub _ASN1_Lexer($)
|
||||
{
|
||||
my($parser)=shift;
|
||||
|
||||
$parser->YYData->{INPUT}
|
||||
or return('',undef);
|
||||
|
||||
again:
|
||||
$parser->YYData->{INPUT} =~ s/^[ \t]*//;
|
||||
|
||||
for ($parser->YYData->{INPUT}) {
|
||||
if (/^\#/) {
|
||||
if (s/^\# (\d+) \"(.*?)\"( \d+|)//) {
|
||||
$parser->YYData->{LINE} = $1-1;
|
||||
$parser->YYData->{INPUT_FILENAME} = $2;
|
||||
goto again;
|
||||
}
|
||||
if (s/^\#line (\d+) \"(.*?)\"( \d+|)//) {
|
||||
$parser->YYData->{LINE} = $1-1;
|
||||
$parser->YYData->{INPUT_FILENAME} = $2;
|
||||
goto again;
|
||||
}
|
||||
if (s/^(\#.*)$//m) {
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
if (s/^(\n)//) {
|
||||
$parser->YYData->{LINE}++;
|
||||
goto again;
|
||||
}
|
||||
if (s/^(--.*\n)//) {
|
||||
$parser->YYData->{LINE}++;
|
||||
goto again;
|
||||
}
|
||||
if (s/^(::=)//) {
|
||||
$parser->YYData->{LAST_TOKEN} = $1;
|
||||
return('DELIMITTER',$1);
|
||||
}
|
||||
if (s/^\"(.*?)\"//) {
|
||||
$parser->YYData->{LAST_TOKEN} = $1;
|
||||
return('TEXT',$1);
|
||||
}
|
||||
if (s/^(\d+)(\W|$)/$2/) {
|
||||
$parser->YYData->{LAST_TOKEN} = $1;
|
||||
return('CONSTANT',$1);
|
||||
}
|
||||
if (s/^([\w_-]+)//) {
|
||||
$parser->YYData->{LAST_TOKEN} = $1;
|
||||
if ($1 =~
|
||||
/^(SEQUENCE|INTEGER|OCTET|STRING|
|
||||
APPLICATION|OPTIONAL|NULL|COMPONENTS|OF|
|
||||
BOOLEAN|ENUMERATED|CHOISE|REAL|BIT|OBJECT|IDENTIFIER|
|
||||
DEFAULT|FALSE|TRUE|SET|DEFINITIONS|BEGIN|END)$/x) {
|
||||
return $1;
|
||||
}
|
||||
return('IDENTIFIER',$1);
|
||||
}
|
||||
if (s/^(.)//s) {
|
||||
$parser->YYData->{LAST_TOKEN} = $1;
|
||||
return($1,$1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub parse_asn1($$)
|
||||
{
|
||||
my $self = shift;
|
||||
my $filename = shift;
|
||||
|
||||
my $saved_delim = $/;
|
||||
undef $/;
|
||||
my $cpp = $ENV{CPP};
|
||||
if (! defined $cpp) {
|
||||
$cpp = "cpp"
|
||||
}
|
||||
my $data = `$cpp -xc $filename`;
|
||||
$/ = $saved_delim;
|
||||
|
||||
$self->YYData->{INPUT} = $data;
|
||||
$self->YYData->{LINE} = 0;
|
||||
$self->YYData->{LAST_TOKEN} = "NONE";
|
||||
return $self->YYParse( yylex => \&_ASN1_Lexer, yyerror => \&_ASN1_Error );
|
||||
}
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
###################################################
|
||||
# package to parse ASN.1 files and generate code for
|
||||
# LDAP functions in Samba
|
||||
# Copyright tridge@samba.org 2002-2003
|
||||
# Copyright metze@samba.org 2004
|
||||
|
||||
# released under the GNU GPL
|
||||
|
||||
use strict;
|
||||
|
||||
use FindBin qw($RealBin);
|
||||
use lib "$RealBin";
|
||||
use lib "$RealBin/lib";
|
||||
use Getopt::Long;
|
||||
use File::Basename;
|
||||
use asn1;
|
||||
use util;
|
||||
|
||||
my($opt_help) = 0;
|
||||
my($opt_output);
|
||||
|
||||
my $asn1_parser = new asn1;
|
||||
|
||||
#####################################################################
|
||||
# parse an ASN.1 file returning a structure containing all the data
|
||||
sub ASN1Parse($)
|
||||
{
|
||||
my $filename = shift;
|
||||
my $asn1 = $asn1_parser->parse_asn1($filename);
|
||||
util::CleanData($asn1);
|
||||
return $asn1;
|
||||
}
|
||||
|
||||
|
||||
#########################################
|
||||
# display help text
|
||||
sub ShowHelp()
|
||||
{
|
||||
print "
|
||||
perl ASN.1 parser and code generator
|
||||
Copyright (C) tridge\@samba.org
|
||||
Copyright (C) metze\@samba.org
|
||||
|
||||
Usage: pasn1.pl [options] <asn1file>
|
||||
|
||||
Options:
|
||||
--help this help page
|
||||
--output OUTNAME put output in OUTNAME
|
||||
\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
# main program
|
||||
GetOptions (
|
||||
'help|h|?' => \$opt_help,
|
||||
'output|o=s' => \$opt_output,
|
||||
);
|
||||
|
||||
if ($opt_help) {
|
||||
ShowHelp();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
sub process_file($)
|
||||
{
|
||||
my $input_file = shift;
|
||||
my $output_file;
|
||||
my $pasn1;
|
||||
|
||||
my $basename = basename($input_file, ".asn1");
|
||||
|
||||
if (!defined($opt_output)) {
|
||||
$output_file = util::ChangeExtension($input_file, ".pasn1");
|
||||
} else {
|
||||
$output_file = $opt_output;
|
||||
}
|
||||
|
||||
# if (file is .pasn1) {
|
||||
# $pasn1 = util::LoadStructure($pasn1_file);
|
||||
# defined $pasn1 || die "Failed to load $pasn1_file - maybe you need --parse\n";
|
||||
# } else {
|
||||
$pasn1 = ASN1Parse($input_file);
|
||||
defined $pasn1 || die "Failed to parse $input_file";
|
||||
util::SaveStructure($output_file, $pasn1) ||
|
||||
die "Failed to save $output_file\n";
|
||||
#}
|
||||
}
|
||||
|
||||
foreach my $filename (@ARGV) {
|
||||
process_file($filename);
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
###################################################
|
||||
# utility functions to support pidl
|
||||
# Copyright tridge@samba.org 2000
|
||||
# released under the GNU GPL
|
||||
package util;
|
||||
|
||||
#####################################################################
|
||||
# load a data structure from a file (as saved with SaveStructure)
|
||||
sub LoadStructure($)
|
||||
{
|
||||
my $f = shift;
|
||||
my $contents = FileLoad($f);
|
||||
defined $contents || return undef;
|
||||
return eval "$contents";
|
||||
}
|
||||
|
||||
use strict;
|
||||
|
||||
#####################################################################
|
||||
# flatten an array of arrays into a single array
|
||||
sub FlattenArray2($)
|
||||
{
|
||||
my $a = shift;
|
||||
my @b;
|
||||
for my $d (@{$a}) {
|
||||
for my $d1 (@{$d}) {
|
||||
push(@b, $d1);
|
||||
}
|
||||
}
|
||||
return \@b;
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# flatten an array of arrays into a single array
|
||||
sub FlattenArray($)
|
||||
{
|
||||
my $a = shift;
|
||||
my @b;
|
||||
for my $d (@{$a}) {
|
||||
for my $d1 (@{$d}) {
|
||||
push(@b, $d1);
|
||||
}
|
||||
}
|
||||
return \@b;
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# flatten an array of hashes into a single hash
|
||||
sub FlattenHash($)
|
||||
{
|
||||
my $a = shift;
|
||||
my %b;
|
||||
for my $d (@{$a}) {
|
||||
for my $k (keys %{$d}) {
|
||||
$b{$k} = $d->{$k};
|
||||
}
|
||||
}
|
||||
return \%b;
|
||||
}
|
||||
|
||||
|
||||
#####################################################################
|
||||
# traverse a perl data structure removing any empty arrays or
|
||||
# hashes and any hash elements that map to undef
|
||||
sub CleanData($)
|
||||
{
|
||||
sub CleanData($);
|
||||
my($v) = shift;
|
||||
if (ref($v) eq "ARRAY") {
|
||||
foreach my $i (0 .. $#{$v}) {
|
||||
CleanData($v->[$i]);
|
||||
if (ref($v->[$i]) eq "ARRAY" && $#{$v->[$i]}==-1) {
|
||||
$v->[$i] = undef;
|
||||
next;
|
||||
}
|
||||
}
|
||||
# this removes any undefined elements from the array
|
||||
@{$v} = grep { defined $_ } @{$v};
|
||||
} elsif (ref($v) eq "HASH") {
|
||||
foreach my $x (keys %{$v}) {
|
||||
CleanData($v->{$x});
|
||||
if (!defined $v->{$x}) { delete($v->{$x}); next; }
|
||||
if (ref($v->{$x}) eq "ARRAY" && $#{$v->{$x}}==-1) { delete($v->{$x}); next; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#####################################################################
|
||||
# return the modification time of a file
|
||||
sub FileModtime($)
|
||||
{
|
||||
my($filename) = shift;
|
||||
return (stat($filename))[9];
|
||||
}
|
||||
|
||||
|
||||
#####################################################################
|
||||
# read a file into a string
|
||||
sub FileLoad($)
|
||||
{
|
||||
my($filename) = shift;
|
||||
local(*INPUTFILE);
|
||||
open(INPUTFILE, $filename) || return undef;
|
||||
my($saved_delim) = $/;
|
||||
undef $/;
|
||||
my($data) = <INPUTFILE>;
|
||||
close(INPUTFILE);
|
||||
$/ = $saved_delim;
|
||||
return $data;
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# write a string into a file
|
||||
sub FileSave($$)
|
||||
{
|
||||
my($filename) = shift;
|
||||
my($v) = shift;
|
||||
local(*FILE);
|
||||
open(FILE, ">$filename") || die "can't open $filename";
|
||||
print FILE $v;
|
||||
close(FILE);
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# return a filename with a changed extension
|
||||
sub ChangeExtension($$)
|
||||
{
|
||||
my($fname) = shift;
|
||||
my($ext) = shift;
|
||||
if ($fname =~ /^(.*)\.(.*?)$/) {
|
||||
return "$1$ext";
|
||||
}
|
||||
return "$fname$ext";
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# a dumper wrapper to prevent dependence on the Data::Dumper module
|
||||
# unless we actually need it
|
||||
sub MyDumper($)
|
||||
{
|
||||
require Data::Dumper;
|
||||
my $s = shift;
|
||||
return Data::Dumper::Dumper($s);
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# save a data structure into a file
|
||||
sub SaveStructure($$)
|
||||
{
|
||||
my($filename) = shift;
|
||||
my($v) = shift;
|
||||
FileSave($filename, MyDumper($v));
|
||||
}
|
||||
|
||||
#####################################################################
|
||||
# see if a pidl property list contains a give property
|
||||
sub has_property($$)
|
||||
{
|
||||
my($e) = shift;
|
||||
my($p) = shift;
|
||||
|
||||
if (!defined $e->{PROPERTIES}) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
return $e->{PROPERTIES}->{$p};
|
||||
}
|
||||
|
||||
|
||||
sub is_scalar_type($)
|
||||
{
|
||||
my($type) = shift;
|
||||
|
||||
if ($type =~ /^u?int\d+/) {
|
||||
return 1;
|
||||
}
|
||||
if ($type =~ /char|short|long|NTTIME|
|
||||
time_t|error_status_t|boolean32|unsigned32|
|
||||
HYPER_T|wchar_t|DATA_BLOB/x) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
# return the NDR alignment for a type
|
||||
sub type_align($)
|
||||
{
|
||||
my($e) = shift;
|
||||
my $type = $e->{TYPE};
|
||||
|
||||
if (need_wire_pointer($e)) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 4, if ($type eq "uint32");
|
||||
return 4, if ($type eq "long");
|
||||
return 2, if ($type eq "short");
|
||||
return 1, if ($type eq "char");
|
||||
return 1, if ($type eq "uint8");
|
||||
return 2, if ($type eq "uint16");
|
||||
return 4, if ($type eq "NTTIME");
|
||||
return 4, if ($type eq "time_t");
|
||||
return 8, if ($type eq "HYPER_T");
|
||||
return 2, if ($type eq "wchar_t");
|
||||
return 4, if ($type eq "DATA_BLOB");
|
||||
|
||||
# it must be an external type - all we can do is guess
|
||||
return 4;
|
||||
}
|
||||
|
||||
# this is used to determine if the ndr push/pull functions will need
|
||||
# a ndr_flags field to split by buffers/scalars
|
||||
sub is_builtin_type($)
|
||||
{
|
||||
my($type) = shift;
|
||||
|
||||
return 1, if (is_scalar_type($type));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
# determine if an element needs a reference pointer on the wire
|
||||
# in its NDR representation
|
||||
sub need_wire_pointer($)
|
||||
{
|
||||
my $e = shift;
|
||||
if ($e->{POINTERS} &&
|
||||
!has_property($e, "ref")) {
|
||||
return $e->{POINTERS};
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
# determine if an element is a pass-by-reference structure
|
||||
sub is_ref_struct($)
|
||||
{
|
||||
my $e = shift;
|
||||
if (!is_scalar_type($e->{TYPE}) &&
|
||||
has_property($e, "ref")) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# determine if an element is a pure scalar. pure scalars do not
|
||||
# have a "buffers" section in NDR
|
||||
sub is_pure_scalar($)
|
||||
{
|
||||
my $e = shift;
|
||||
if (has_property($e, "ref")) {
|
||||
return 1;
|
||||
}
|
||||
if (is_scalar_type($e->{TYPE}) &&
|
||||
!$e->{POINTERS} &&
|
||||
!array_size($e)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# determine the array size (size_is() or ARRAY_LEN)
|
||||
sub array_size($)
|
||||
{
|
||||
my $e = shift;
|
||||
my $size = has_property($e, "size_is");
|
||||
if ($size) {
|
||||
return $size;
|
||||
}
|
||||
$size = $e->{ARRAY_LEN};
|
||||
if ($size) {
|
||||
return $size;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
# see if a variable needs to be allocated by the NDR subsystem on pull
|
||||
sub need_alloc($)
|
||||
{
|
||||
my $e = shift;
|
||||
|
||||
if (has_property($e, "ref")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($e->{POINTERS} || array_size($e)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
# determine the C prefix used to refer to a variable when passing to a push
|
||||
# function. This will be '*' for pointers to scalar types, '' for scalar
|
||||
# types and normal pointers and '&' for pass-by-reference structures
|
||||
sub c_push_prefix($)
|
||||
{
|
||||
my $e = shift;
|
||||
|
||||
if ($e->{TYPE} =~ "string") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (is_scalar_type($e->{TYPE}) &&
|
||||
$e->{POINTERS}) {
|
||||
return "*";
|
||||
}
|
||||
if (!is_scalar_type($e->{TYPE}) &&
|
||||
!$e->{POINTERS} &&
|
||||
!array_size($e)) {
|
||||
return "&";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
# determine the C prefix used to refer to a variable when passing to a pull
|
||||
# return '&' or ''
|
||||
sub c_pull_prefix($)
|
||||
{
|
||||
my $e = shift;
|
||||
|
||||
if (!$e->{POINTERS} && !array_size($e)) {
|
||||
return "&";
|
||||
}
|
||||
|
||||
if ($e->{TYPE} =~ "string") {
|
||||
return "&";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
# determine if an element has a direct buffers component
|
||||
sub has_direct_buffers($)
|
||||
{
|
||||
my $e = shift;
|
||||
if ($e->{POINTERS} || array_size($e)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# return 1 if the string is a C constant
|
||||
sub is_constant($)
|
||||
{
|
||||
my $s = shift;
|
||||
if ($s =~ /^\d/) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# return 1 if this is a fixed array
|
||||
sub is_fixed_array($)
|
||||
{
|
||||
my $e = shift;
|
||||
my $len = $e->{"ARRAY_LEN"};
|
||||
if (defined $len && is_constant($len)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# return 1 if this is a inline array
|
||||
sub is_inline_array($)
|
||||
{
|
||||
my $e = shift;
|
||||
my $len = $e->{"ARRAY_LEN"};
|
||||
if (is_fixed_array($e) ||
|
||||
defined $len && $len ne "*") {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
The Samba Build System
|
||||
----------------------
|
||||
----------------------
|
||||
|
||||
The build system basically has two main parts: the autoconf-generated
|
||||
shell scripts which check for availability of functions and libraries
|
||||
which is stored in the .m4 files and the information about the various
|
||||
subsystems which is stored in the .mk files.
|
||||
|
||||
Object Types
|
||||
------------
|
||||
the build system knows about the following object types
|
||||
|
||||
SUBSYSTEM:
|
||||
a SUBSYSTEM is basicly a collection of functions, which provide an
|
||||
an generic API for a specific problem (e.g. libldb provides an api
|
||||
for gneric ldb databases. libldb_plugin provides a generic api
|
||||
for calling ldb plugins, so 'libldb' and 'libldb_plugin' are subsystems)
|
||||
|
||||
MODULE:
|
||||
a MODULE is a specify implementation of a API provided by a SUBSYSTEM.
|
||||
(e.g. 'libldb_tdb' and 'libldb_ldap' are implementations of the subsystem 'libldb' API,
|
||||
and 'libldb_plugin_timestamp' is a module of the 'libldb_plugin' subsystem)
|
||||
|
||||
EXT_LIB:
|
||||
an EXT_LIB is an external library which is needed by a SUBSYSTEM, MODULE, BINARY or LIBRARY.
|
||||
(e.g. 'gtk' or 'KRB5')
|
||||
|
||||
BINARY:
|
||||
a BINARY means a executable binary file.
|
||||
(e.g. 'smbtorture' or 'ldbedit')
|
||||
a BINARY typicly has only commandline handling and basic
|
||||
functionality code in it and depends on the functions of
|
||||
SUBSYSTEM's (REQUIRED_SUBSYSTEMS).
|
||||
|
||||
LIBRARY:
|
||||
a LIBRARY means a static and/or shared library,
|
||||
which depends on the used OS.
|
||||
(e.g. for libldb 'libldb.so', 'libldb.so.0' 'libldb.so.0.0.1'
|
||||
and libldb.a are created on linux)
|
||||
a LIBRARY typicly has only glue code in it and depends on
|
||||
SUBSYSTEM's (REQUIRED_SUBSYSTEMS).
|
||||
|
||||
File summary:
|
||||
-------------
|
||||
public.m4 - public M4 macros of the build system
|
||||
config_mk.pm - Support for reading .mk files
|
||||
dot.pm - Support for generating .dot files for analysis of dependencies
|
||||
input.pm - Input validation
|
||||
main.pm - Main
|
||||
makefile.pm - Makefile generation
|
||||
output.pm - Dependency calculation
|
||||
header.pm - build.h generation
|
||||
cflags.pm - Generates cflags.txt for file-specific cflags
|
||||
|
||||
Layout
|
||||
-------
|
||||
|
||||
Toplevel file: configure.in
|
||||
- included by autogen.sh: aclocal.m4
|
||||
which includes the SMB_YXZ*() macros
|
||||
|
||||
- default tests of the build system
|
||||
are in build/smb_build/check_*.m4
|
||||
(mostly compiler and basic C type and function
|
||||
checks)
|
||||
|
||||
- subsystem specific stuff should be included by 'SMB_INCLUDE_M4()'
|
||||
|
||||
|
||||
Generating the configure file
|
||||
-------------------------
|
||||
you need to rerun ./autogen.sh when 'configure.in' or any
|
||||
'.m4' file was modified, then you need to rerun configure.
|
||||
|
||||
|
||||
Generating config.status
|
||||
-----------------------------
|
||||
you need to run ./config.status (or 'configure') after a '.mk'
|
||||
file was changed.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
for now please take a look at the .m4 and .mk files
|
||||
you find in the source tree, they should be a good reference to start.
|
||||
@@ -0,0 +1,25 @@
|
||||
- use pkg-config files in the buildsystem?
|
||||
- let the build system implement some make functions($(patsubst),$(wildcard),...) and use our own implementations where `make' does not support them
|
||||
- include extra_flags.txt using Makefile construction if
|
||||
supported by current make
|
||||
- fix shared module loading for selftest during builds without install
|
||||
- remove recursive dependency between LIBSOCKET, LIBCLI_NBT and LIBCLI_RESOLVE
|
||||
- clearer distinction between dcerpc and ndr. seperate interface tables? Maybe get rid of
|
||||
NDR's table altogether and use dlopen/dlsym ?
|
||||
- saner names for:
|
||||
libcli.so.0.0.1 (rename to libsmb?)
|
||||
libcli_cldap.so.0.0.1 (rename to libcldap?)
|
||||
libcli_nbt.so.0.0.1 (rename to libnbt?)
|
||||
libcli_wrepl.so.0.0.1 (rename to libwrepl?)
|
||||
- generate headermap.txt
|
||||
|
||||
set of test scripts that check the code:
|
||||
- configure_check_unused.pl
|
||||
- find_unused_macros.pl
|
||||
- find_unused_makefilevars.pl
|
||||
- find_unused_options.sh
|
||||
- findstatic.pl
|
||||
- minimal_includes.pl
|
||||
- check dependencies based on #include lines ?
|
||||
- check whether private headers are not used outside their own subsystem
|
||||
- undocumented (no manpage) installed binaries
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
# SMB Build System
|
||||
#
|
||||
# Copyright (C) Jelmer Vernooij 2006
|
||||
# Released under the GNU GPL
|
||||
|
||||
package cflags;
|
||||
use strict;
|
||||
|
||||
sub create_cflags($$)
|
||||
{
|
||||
my ($CTX, $file) = @_;
|
||||
|
||||
open(CFLAGS_TXT,">$file") || die ("Can't open `$file'\n");
|
||||
|
||||
foreach my $key (values %{$CTX}) {
|
||||
next unless defined ($key->{OBJ_LIST});
|
||||
|
||||
next unless defined ($key->{FINAL_CFLAGS});
|
||||
next unless ($#{$key->{FINAL_CFLAGS}} >= 0);
|
||||
|
||||
my $cflags = join(' ', @{$key->{FINAL_CFLAGS}});
|
||||
|
||||
foreach (@{$key->{OBJ_LIST}}) {
|
||||
my $ofile = $_;
|
||||
my $dfile = $_;
|
||||
$dfile =~ s/\.o$/.d/;
|
||||
$dfile =~ s/\.ho$/.d/;
|
||||
print CFLAGS_TXT "$ofile $dfile: CFLAGS+= $cflags\n";
|
||||
}
|
||||
}
|
||||
close(CFLAGS_TXT);
|
||||
|
||||
print __FILE__.": creating $file\n";
|
||||
}
|
||||
1;
|
||||
@@ -0,0 +1,263 @@
|
||||
# Samba Build System
|
||||
# - config.mk parsing functions
|
||||
#
|
||||
# Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
# Copyright (C) Jelmer Vernooij 2005
|
||||
# Released under the GNU GPL
|
||||
#
|
||||
|
||||
package smb_build::config_mk;
|
||||
use smb_build::input;
|
||||
use File::Basename;
|
||||
|
||||
use strict;
|
||||
|
||||
my $section_types = {
|
||||
"EXT_LIB" => {
|
||||
"LIBS" => "list",
|
||||
"CFLAGS" => "list",
|
||||
"CPPFLAGS" => "list",
|
||||
"LDFLAGS" => "list",
|
||||
},
|
||||
"SUBSYSTEM" => {
|
||||
"OBJ_FILES" => "list",
|
||||
|
||||
"PRIVATE_DEPENDENCIES" => "list",
|
||||
"PUBLIC_DEPENDENCIES" => "list",
|
||||
|
||||
"ENABLE" => "bool",
|
||||
|
||||
"MANPAGE" => "string",
|
||||
|
||||
"PUBLIC_PROTO_HEADER" => "string",
|
||||
"PRIVATE_PROTO_HEADER" => "string",
|
||||
|
||||
"PUBLIC_HEADERS" => "list",
|
||||
|
||||
"CFLAGS" => "list",
|
||||
"LDFLAGS" => "list",
|
||||
"STANDARD_VISIBILITY" => "string"
|
||||
},
|
||||
"MODULE" => {
|
||||
"SUBSYSTEM" => "string",
|
||||
|
||||
"INIT_FUNCTION" => "string",
|
||||
"OBJ_FILES" => "list",
|
||||
|
||||
"PUBLIC_DEPENDENCIES" => "list",
|
||||
"PRIVATE_DEPENDENCIES" => "list",
|
||||
|
||||
"ALIASES" => "list",
|
||||
|
||||
"ENABLE" => "bool",
|
||||
|
||||
"OUTPUT_TYPE" => "list",
|
||||
|
||||
"MANPAGE" => "string",
|
||||
"PRIVATE_PROTO_HEADER" => "string",
|
||||
"PUBLIC_PROTO_HEADER" => "string",
|
||||
|
||||
|
||||
"PUBLIC_HEADERS" => "list",
|
||||
|
||||
"CFLAGS" => "list"
|
||||
},
|
||||
"BINARY" => {
|
||||
"OBJ_FILES" => "list",
|
||||
|
||||
"PRIVATE_DEPENDENCIES" => "list",
|
||||
|
||||
"ENABLE" => "bool",
|
||||
|
||||
"MANPAGE" => "string",
|
||||
"INSTALLDIR" => "string",
|
||||
"PRIVATE_PROTO_HEADER" => "string",
|
||||
"PUBLIC_PROTO_HEADER" => "string",
|
||||
"PUBLIC_HEADERS" => "list",
|
||||
|
||||
"CFLAGS" => "list",
|
||||
"LDFLAGS" => "list",
|
||||
"STANDARD_VISIBILITY" => "string",
|
||||
|
||||
"USE_HOSTCC" => "bool"
|
||||
},
|
||||
"LIBRARY" => {
|
||||
"VERSION" => "string",
|
||||
"SO_VERSION" => "string",
|
||||
"LIBRARY_REALNAME" => "string",
|
||||
|
||||
"INIT_FUNCTION_TYPE" => "string",
|
||||
|
||||
"OBJ_FILES" => "list",
|
||||
|
||||
"DESCRIPTION" => "string",
|
||||
|
||||
"PRIVATE_DEPENDENCIES" => "list",
|
||||
"PUBLIC_DEPENDENCIES" => "list",
|
||||
|
||||
"ENABLE" => "bool",
|
||||
|
||||
"MANPAGE" => "string",
|
||||
|
||||
"PUBLIC_HEADERS" => "list",
|
||||
|
||||
"PUBLIC_PROTO_HEADER" => "string",
|
||||
"PRIVATE_PROTO_HEADER" => "string",
|
||||
|
||||
"CFLAGS" => "list",
|
||||
"LDFLAGS" => "list",
|
||||
"STANDARD_VISIBILITY" => "string"
|
||||
}
|
||||
};
|
||||
|
||||
use vars qw(@parsed_files);
|
||||
|
||||
@parsed_files = ();
|
||||
|
||||
###########################################################
|
||||
# The parsing function which parses the file
|
||||
#
|
||||
# $result = _parse_config_mk($filename)
|
||||
#
|
||||
# $filename - the path of the config.mk file
|
||||
# which should be parsed
|
||||
sub run_config_mk($$$$)
|
||||
{
|
||||
sub run_config_mk($$$$);
|
||||
my ($input, $srcdir, $builddir, $filename) = @_;
|
||||
my $result;
|
||||
my $linenum = -1;
|
||||
my $infragment = 0;
|
||||
my $section = "GLOBAL";
|
||||
my $makefile = "";
|
||||
|
||||
my $parsing_file = $filename;
|
||||
my $retry_parsing_file = undef;
|
||||
|
||||
$ENV{samba_builddir} = $builddir;
|
||||
$ENV{samba_srcdir} = $srcdir;
|
||||
|
||||
if (($srcdir ne ".") or ($builddir ne ".")) {
|
||||
$parsing_file = $builddir."/".$filename;
|
||||
$retry_parsing_file = $srcdir."/".$filename;
|
||||
}
|
||||
|
||||
if (open(CONFIG_MK, $parsing_file)) {
|
||||
$retry_parsing_file = undef;
|
||||
} else {
|
||||
die("Can't open $parsing_file") unless defined($retry_parsing_file);
|
||||
}
|
||||
|
||||
if (defined($retry_parsing_file)) {
|
||||
if (open(CONFIG_MK, $parsing_file)) {
|
||||
$parsing_file = $retry_parsing_file;
|
||||
$retry_parsing_file = undef;
|
||||
} else {
|
||||
die("Can't open neither '$parsing_file' nor '$retry_parsing_file'\n");
|
||||
}
|
||||
}
|
||||
|
||||
push (@parsed_files, $parsing_file);
|
||||
|
||||
|
||||
my @lines = <CONFIG_MK>;
|
||||
close(CONFIG_MK);
|
||||
|
||||
my $line = "";
|
||||
my $prev = "";
|
||||
|
||||
foreach (@lines) {
|
||||
$linenum++;
|
||||
|
||||
# lines beginning with '#' are ignored
|
||||
next if (/^\#.*$/);
|
||||
|
||||
if (/^(.*)\\$/) {
|
||||
$prev .= $1;
|
||||
next;
|
||||
} else {
|
||||
$line = "$prev$_";
|
||||
$prev = "";
|
||||
}
|
||||
|
||||
if ($line =~ /^\[([-a-zA-Z0-9_:]+)\][\t ]*$/)
|
||||
{
|
||||
$section = $1;
|
||||
$infragment = 0;
|
||||
next;
|
||||
}
|
||||
|
||||
# include
|
||||
if ($line =~ /^include (.*)$/) {
|
||||
my $subfile= $1;
|
||||
my $subdir = dirname($filename);
|
||||
$subdir =~ s/^\.$//g;
|
||||
$subdir =~ s/^\.\///g;
|
||||
$subdir .= "/" if ($subdir ne "");
|
||||
$makefile .= run_config_mk($input, $srcdir, $builddir, $subdir.$subfile);
|
||||
next;
|
||||
}
|
||||
|
||||
# empty line
|
||||
if ($line =~ /^[ \t]*$/) {
|
||||
$section = "GLOBAL";
|
||||
if ($infragment) { $makefile.="\n"; }
|
||||
next;
|
||||
}
|
||||
|
||||
# global stuff is considered part of the makefile
|
||||
if ($section eq "GLOBAL") {
|
||||
if (!$infragment) { $makefile.="\n"; }
|
||||
$makefile .= $line;
|
||||
$infragment = 1;
|
||||
next;
|
||||
}
|
||||
|
||||
|
||||
# Assignment
|
||||
if ($line =~ /^([a-zA-Z0-9_]+)[\t ]*=(.*)$/) {
|
||||
$result->{$section}{$1}{VAL} = $2;
|
||||
$result->{$section}{$1}{KEY} = $1;
|
||||
|
||||
next;
|
||||
}
|
||||
|
||||
die("$parsing_file:$linenum: Bad line while parsing $parsing_file");
|
||||
}
|
||||
|
||||
foreach my $section (keys %{$result}) {
|
||||
my ($type, $name) = split(/::/, $section, 2);
|
||||
|
||||
my $sectype = $section_types->{$type};
|
||||
if (not defined($sectype)) {
|
||||
die($parsing_file.":[".$section."] unknown section type \"".$type."\"!");
|
||||
}
|
||||
|
||||
$input->{$name}{NAME} = $name;
|
||||
$input->{$name}{TYPE} = $type;
|
||||
$input->{$name}{MK_FILE} = $parsing_file;
|
||||
$input->{$name}{BASEDIR} = dirname($filename);
|
||||
|
||||
foreach my $key (values %{$result->{$section}}) {
|
||||
$key->{VAL} = smb_build::input::strtrim($key->{VAL});
|
||||
my $vartype = $sectype->{$key->{KEY}};
|
||||
if (not defined($vartype)) {
|
||||
die($parsing_file.":[".$section."]: unknown attribute type \"$key->{KEY}\"!");
|
||||
}
|
||||
if ($vartype eq "string") {
|
||||
$input->{$name}{$key->{KEY}} = $key->{VAL};
|
||||
} elsif ($vartype eq "list") {
|
||||
$input->{$name}{$key->{KEY}} = [smb_build::input::str2array($key->{VAL})];
|
||||
} elsif ($vartype eq "bool") {
|
||||
if (($key->{VAL} ne "YES") and ($key->{VAL} ne "NO")) {
|
||||
die("Invalid value for bool attribute $key->{KEY}: $key->{VAL} in section $section");
|
||||
}
|
||||
$input->{$name}{$key->{KEY}} = $key->{VAL};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $makefile;
|
||||
}
|
||||
|
||||
1;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user