wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+134
View File
@@ -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
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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).
+2
View File
@@ -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
View File
@@ -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
+171
View File
@@ -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
View File
@@ -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.
+30
View File
@@ -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
+788
View File
@@ -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
+269
View File
@@ -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:
*/
%>
+13
View File
@@ -0,0 +1,13 @@
<%
/* Return true to allow access; false otherwise */
function json_authenticate(serviceComponents, method)
{
return true;
}
/*
* Local Variables:
* mode: c
* End:
*/
%>
+200
View File
@@ -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:
*/
%>
+236
View File
@@ -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:
*/
%>
+492
View File
@@ -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:
*/
%>
+170
View File
@@ -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:
*/
%>
+632
View File
@@ -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:
*/
%>
+34
View File
@@ -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:
*/
%>
+85
View File
@@ -0,0 +1,85 @@
# add valgrind suppressions for the build farm here. Get the format
# from the build farm log
{
samba_dlopen1
Memcheck:Cond
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/tls/libc-2.3.6.so
obj:/lib/ld-2.3.6.so
fun:_dl_open
}
{
samba_dlopen2
Memcheck:Cond
obj:/lib/ld-2.3.6.so
fun:_dl_open
}
{
samba_dlopen3
Memcheck:Addr4
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/tls/libc-2.3.6.so
obj:/lib/ld-2.3.6.so
fun:_dl_open
}
{
samba_dlopen4
Memcheck:Cond
obj:/lib/ld-2.3.6.so
obj:/lib/tls/libc-2.3.6.so
obj:/lib/ld-2.3.6.so
fun:_dl_open
}
{
samba_dlopen5
Memcheck:Addr4
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/tls/libc-2.3.6.so
obj:/lib/ld-2.3.6.so
fun:_dl_open
}
{
samba_dlopen6
Memcheck:Cond
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/tls/libc-2.3.6.so
obj:/lib/ld-2.3.6.so
fun:_dl_open
}
{
samba_dlopen7
Memcheck:Addr4
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/tls/libc-2.3.6.so
obj:/lib/ld-2.3.6.so
fun:_dl_open
}
{
samba_libc_dlsym1
Memcheck:Addr4
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/ld-2.3.6.so
obj:/lib/tls/libc-2.3.6.so
obj:/lib/ld-2.3.6.so
fun:__libc_dlsym
}
+107
View File
@@ -0,0 +1,107 @@
########################################################
# SAMBA Version #
# #
# Samba versions are as follows #
# 3.0.x New production series #
# 3.0.x{tp,pre,rc}y Preview/Testing & RC #
# 3.0.x[a-z] Patch releases #
# 3.0.x[a-z]-VENDOR-z Vendor patch releases #
# #
# script/mkversion.sh #
# will use this file to create #
# include/version.h #
# #
########################################################
########################################################
# This are the main SAMBA version numbers #
# #
# <MAJOR>.<MINOR>.<RELEASE> #
# #
# e.g. SAMBA_VERSION_MAJOR=3 #
# SAMBA_VERSION_MINOR=0 #
# SAMBA_VERSION_RELEASE=0 #
# -> "3.0.0" #
########################################################
SAMBA_VERSION_MAJOR=4
SAMBA_VERSION_MINOR=0
SAMBA_VERSION_RELEASE=0
########################################################
# If a official release has a serious bug #
# a security release will have 'a' sufffix #
# #
# so SAMBA's version will be #
# <MAJOR>.<MINOR>.<RELEASE><REVISION> #
# #
# e.g. SAMBA_VERSION_REVISION=a #
# -> "2.2.8a" #
########################################################
SAMBA_VERSION_REVISION=
########################################################
# For 'tp' releases the version will be #
# #
# <MAJOR>.<MINOR>.<RELEASE>tp<TP_RELEASE> #
# #
# e.g. SAMBA_VERSION_TP_RELEASE=1 #
# -> "4.0.0tp1" #
########################################################
SAMBA_VERSION_TP_RELEASE=4
########################################################
# For 'pre' releases the version will be #
# #
# <MAJOR>.<MINOR>.<RELEASE>pre<PRE_RELEASE> #
# #
# e.g. SAMBA_VERSION_PRE_RELEASE=1 #
# -> "2.2.9pre1" #
########################################################
SAMBA_VERSION_PRE_RELEASE=
########################################################
# For 'rc' releases the version will be #
# #
# <MAJOR>.<MINOR>.<RELEASE>rc<RC_RELEASE> #
# #
# e.g. SAMBA_VERSION_RC_RELEASE=1 #
# -> "3.0.0rc1" #
########################################################
SAMBA_VERSION_RC_RELEASE=
########################################################
# To mark SVN snapshots this should be set to 'yes' #
# in the development BRANCH, and set to 'no' only in #
# the SAMBA_X_X_RELEASE BRANCH #
# #
# <MAJOR>.<MINOR>.<RELEASE>[...]-SVN-build-xxx #
# #
# e.g. SAMBA_VERSION_IS_SVN_SNAPSHOT=yes #
# -> "3.0.0-SVN-build-199" #
########################################################
SAMBA_VERSION_IS_SVN_SNAPSHOT=yes
########################################################
# This is for specifying a release nickname #
# #
# e.g. SAMBA_VERSION_RELEASE_NICKNAME=Nicky Nickname #
# smbd --version will then give: #
# -> "4.0.0-tp1-VendorVersion (Nicky Nickname)" #
########################################################
SAMBA_VERSION_RELEASE_NICKNAME=
########################################################
# This can be set by vendors if they want... #
# This can be a string constant or a function which #
# returns a string (const char *) #
# #
# <MAJOR>.<MINOR>.<RELEASE>[...]-<VENDOR_SUFFIX> #
# #
# Note the '-' is automaticaly added #
# #
# e.g. SAMBA_VERSION_VENDOR_SUFFIX=VendorVersion #
# -> "3.0.0rc2-VendorVersion" #
# #
########################################################
SAMBA_VERSION_VENDOR_SUFFIX=
SAMBA_VERSION_VENDOR_PATCH=
+59
View File
@@ -0,0 +1,59 @@
dnl Copied from libtool.m4
AC_DEFUN(AC_PROG_LD_GNU,
[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], ac_cv_prog_gnu_ld,
[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
ac_cv_prog_gnu_ld=yes
else
ac_cv_prog_gnu_ld=no
fi])
])
dnl Removes -I/usr/include/? from given variable
AC_DEFUN(CFLAGS_REMOVE_USR_INCLUDE,[
ac_new_flags=""
for i in [$]$1; do
case [$]i in
-I/usr/include|-I/usr/include/) ;;
*) ac_new_flags="[$]ac_new_flags [$]i" ;;
esac
done
$1=[$]ac_new_flags
])
dnl Removes '-L/usr/lib[/]', '-Wl,-rpath,/usr/lib[/]'
dnl and '-Wl,-rpath -Wl,/usr/lib[/]' from given variable
AC_DEFUN(LIB_REMOVE_USR_LIB,[
ac_new_flags=""
l=""
for i in [$]$1; do
case [$]l[$]i in
-L/usr/lib) ;;
-L/usr/lib/) ;;
-Wl,-rpath,/usr/lib) ;;
-Wl,-rpath,/usr/lib/) ;;
-Wl,-rpath) l=[$]i;;
-Wl,-rpath-Wl,/usr/lib) l="";;
-Wl,-rpath-Wl,/usr/lib/) l="";;
*)
s=" "
if test x"[$]ac_new_flags" = x""; then
s="";
fi
if test x"[$]l" = x""; then
ac_new_flags="[$]ac_new_flags[$]s[$]i";
else
ac_new_flags="[$]ac_new_flags[$]s[$]l [$]i";
fi
l=""
;;
esac
done
$1=[$]ac_new_flags
])
m4_include(lib/replace/libreplace.m4)
m4_include(build/m4/ax_cflags_gcc_option.m4)
m4_include(build/m4/ax_cflags_irix_option.m4)
m4_include(build/m4/public.m4)
+510
View File
@@ -0,0 +1,510 @@
/*
Unix SMB/CIFS implementation.
Password and authentication handling
Copyright (C) Andrew Bartlett 2001-2002
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "lib/util/dlinklist.h"
#include "auth/auth.h"
#include "lib/events/events.h"
#include "build.h"
/***************************************************************************
Set a fixed challenge
***************************************************************************/
NTSTATUS auth_context_set_challenge(struct auth_context *auth_ctx, const uint8_t chal[8], const char *set_by)
{
auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
return NT_STATUS_OK;
}
/***************************************************************************
Set a fixed challenge
***************************************************************************/
BOOL auth_challenge_may_be_modified(struct auth_context *auth_ctx)
{
return auth_ctx->challenge.may_be_modified;
}
/****************************************************************************
Try to get a challenge out of the various authentication modules.
Returns a const char of length 8 bytes.
****************************************************************************/
_PUBLIC_ NTSTATUS auth_get_challenge(struct auth_context *auth_ctx, const uint8_t **_chal)
{
NTSTATUS nt_status;
struct auth_method_context *method;
if (auth_ctx->challenge.data.length) {
DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
auth_ctx->challenge.set_by));
*_chal = auth_ctx->challenge.data.data;
return NT_STATUS_OK;
}
for (method = auth_ctx->methods; method; method = method->next) {
DATA_BLOB challenge = data_blob(NULL,0);
nt_status = method->ops->get_challenge(method, auth_ctx, &challenge);
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOT_IMPLEMENTED)) {
continue;
}
NT_STATUS_NOT_OK_RETURN(nt_status);
if (challenge.length != 8) {
DEBUG(0, ("auth_get_challenge: invalid challenge (length %u) by mothod [%s]\n",
(unsigned)challenge.length, method->ops->name));
return NT_STATUS_INTERNAL_ERROR;
}
auth_ctx->challenge.data = challenge;
auth_ctx->challenge.set_by = method->ops->name;
break;
}
if (!auth_ctx->challenge.set_by) {
uint8_t chal[8];
generate_random_buffer(chal, 8);
auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
auth_ctx->challenge.set_by = "random";
auth_ctx->challenge.may_be_modified = True;
}
DEBUG(10,("auth_get_challenge: challenge set by %s\n",
auth_ctx->challenge.set_by));
*_chal = auth_ctx->challenge.data.data;
return NT_STATUS_OK;
}
struct auth_check_password_sync_state {
BOOL finished;
NTSTATUS status;
struct auth_serversupplied_info *server_info;
};
static void auth_check_password_sync_callback(struct auth_check_password_request *req,
void *private_data)
{
struct auth_check_password_sync_state *s = talloc_get_type(private_data,
struct auth_check_password_sync_state);
s->finished = True;
s->status = auth_check_password_recv(req, s, &s->server_info);
}
/**
* Check a user's Plaintext, LM or NTLM password.
* (sync version)
*
* Check a user's password, as given in the user_info struct and return various
* interesting details in the server_info struct.
*
* The return value takes precedence over the contents of the server_info
* struct. When the return is other than NT_STATUS_OK the contents
* of that structure is undefined.
*
* @param auth_ctx Supplies the challenges and some other data.
* Must be created with auth_context_create(), and the challenges should be
* filled in, either at creation or by calling the challenge geneation
* function auth_get_challenge().
*
* @param user_info Contains the user supplied components, including the passwords.
*
* @param mem_ctx The parent memory context for the server_info structure
*
* @param server_info If successful, contains information about the authentication,
* including a SAM_ACCOUNT struct describing the user.
*
* @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
*
**/
NTSTATUS auth_check_password(struct auth_context *auth_ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **server_info)
{
struct auth_check_password_sync_state *sync_state;
NTSTATUS status;
sync_state = talloc_zero(auth_ctx, struct auth_check_password_sync_state);
NT_STATUS_HAVE_NO_MEMORY(sync_state);
auth_check_password_send(auth_ctx, user_info, auth_check_password_sync_callback, sync_state);
while (!sync_state->finished) {
event_loop_once(auth_ctx->event_ctx);
}
status = sync_state->status;
if (NT_STATUS_IS_OK(status)) {
*server_info = talloc_steal(mem_ctx, sync_state->server_info);
}
talloc_free(sync_state);
return status;
}
struct auth_check_password_request {
struct auth_context *auth_ctx;
const struct auth_usersupplied_info *user_info;
struct auth_serversupplied_info *server_info;
struct auth_method_context *method;
NTSTATUS status;
struct {
void (*fn)(struct auth_check_password_request *req, void *private_data);
void *private_data;
} callback;
};
static void auth_check_password_async_timed_handler(struct event_context *ev, struct timed_event *te,
struct timeval t, void *ptr)
{
struct auth_check_password_request *req = talloc_get_type(ptr, struct auth_check_password_request);
req->status = req->method->ops->check_password(req->method, req, req->user_info, &req->server_info);
req->callback.fn(req, req->callback.private_data);
}
/**
* Check a user's Plaintext, LM or NTLM password.
* async send hook
*
* Check a user's password, as given in the user_info struct and return various
* interesting details in the server_info struct.
*
* The return value takes precedence over the contents of the server_info
* struct. When the return is other than NT_STATUS_OK the contents
* of that structure is undefined.
*
* @param auth_ctx Supplies the challenges and some other data.
* Must be created with make_auth_context(), and the challenges should be
* filled in, either at creation or by calling the challenge geneation
* function auth_get_challenge().
*
* @param user_info Contains the user supplied components, including the passwords.
*
* @param callback A callback function which will be called when the operation is finished.
* The callback function needs to call auth_check_password_recv() to get the return values
*
* @param private_data A private pointer which will ba passed to the callback function
*
**/
void auth_check_password_send(struct auth_context *auth_ctx,
const struct auth_usersupplied_info *user_info,
void (*callback)(struct auth_check_password_request *req, void *private_data),
void *private_data)
{
/* if all the modules say 'not for me' this is reasonable */
NTSTATUS nt_status;
struct auth_method_context *method;
const uint8_t *challenge;
struct auth_usersupplied_info *user_info_tmp;
struct auth_check_password_request *req = NULL;
DEBUG(3, ("auth_check_password_send: Checking password for unmapped user [%s]\\[%s]@[%s]\n",
user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
req = talloc_zero(auth_ctx, struct auth_check_password_request);
if (!req) {
callback(NULL, private_data);
return;
}
req->auth_ctx = auth_ctx;
req->user_info = user_info;
req->callback.fn = callback;
req->callback.private_data = private_data;
if (!user_info->mapped_state) {
nt_status = map_user_info(req, user_info, &user_info_tmp);
if (!NT_STATUS_IS_OK(nt_status)) goto failed;
user_info = user_info_tmp;
req->user_info = user_info_tmp;
}
DEBUGADD(3,("auth_check_password_send: mapped user is: [%s]\\[%s]@[%s]\n",
user_info->mapped.domain_name, user_info->mapped.account_name, user_info->workstation_name));
nt_status = auth_get_challenge(auth_ctx, &challenge);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(0, ("auth_check_password_send: Invalid challenge (length %u) stored for this auth context set_by %s - cannot continue: %s\n",
(unsigned)auth_ctx->challenge.data.length, auth_ctx->challenge.set_by, nt_errstr(nt_status)));
goto failed;
}
if (auth_ctx->challenge.set_by) {
DEBUG(10, ("auth_check_password_send: auth_context challenge created by %s\n",
auth_ctx->challenge.set_by));
}
DEBUG(10, ("auth_check_password_send: challenge is: \n"));
dump_data(5, auth_ctx->challenge.data.data, auth_ctx->challenge.data.length);
nt_status = NT_STATUS_NO_SUCH_USER; /* If all the modules say 'not for me', then this is reasonable */
for (method = auth_ctx->methods; method; method = method->next) {
NTSTATUS result;
struct timed_event *te = NULL;
/* check if the module wants to chek the password */
result = method->ops->want_check(method, req, user_info);
if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
DEBUG(11,("auth_check_password_send: %s had nothing to say\n", method->ops->name));
continue;
}
nt_status = result;
req->method = method;
if (!NT_STATUS_IS_OK(nt_status)) break;
te = event_add_timed(auth_ctx->event_ctx, req,
timeval_zero(),
auth_check_password_async_timed_handler, req);
if (!te) {
nt_status = NT_STATUS_NO_MEMORY;
goto failed;
}
return;
}
failed:
req->status = nt_status;
req->callback.fn(req, req->callback.private_data);
}
/**
* Check a user's Plaintext, LM or NTLM password.
* async receive function
*
* The return value takes precedence over the contents of the server_info
* struct. When the return is other than NT_STATUS_OK the contents
* of that structure is undefined.
*
*
* @param req The async auth_check_password state, passes to the callers callback function
*
* @param mem_ctx The parent memory context for the server_info structure
*
* @param server_info If successful, contains information about the authentication,
* including a SAM_ACCOUNT struct describing the user.
*
* @return An NTSTATUS with NT_STATUS_OK or an appropriate error.
*
**/
NTSTATUS auth_check_password_recv(struct auth_check_password_request *req,
TALLOC_CTX *mem_ctx,
struct auth_serversupplied_info **server_info)
{
NTSTATUS status;
NT_STATUS_HAVE_NO_MEMORY(req);
if (NT_STATUS_IS_OK(req->status)) {
DEBUG(5,("auth_check_password_recv: %s authentication for user [%s\\%s] succeeded\n",
req->method->ops->name, req->server_info->domain_name, req->server_info->account_name));
*server_info = talloc_steal(mem_ctx, req->server_info);
} else {
DEBUG(2,("auth_check_password_recv: %s authentication for user [%s\\%s] FAILED with error %s\n",
(req->method ? req->method->ops->name : "NO_METHOD"),
req->user_info->mapped.domain_name,
req->user_info->mapped.account_name,
nt_errstr(req->status)));
}
status = req->status;
talloc_free(req);
return status;
}
/***************************************************************************
Make a auth_info struct for the auth subsystem
***************************************************************************/
NTSTATUS auth_context_create(TALLOC_CTX *mem_ctx, const char **methods,
struct event_context *ev,
struct messaging_context *msg,
struct auth_context **auth_ctx)
{
int i;
struct auth_context *ctx;
if (!methods) {
DEBUG(0,("auth_context_create: No auth method list!?\n"));
return NT_STATUS_INTERNAL_ERROR;
}
if (!ev) {
DEBUG(0,("auth_context_create: called with out event context\n"));
return NT_STATUS_INTERNAL_ERROR;
}
if (!msg) {
DEBUG(0,("auth_context_create: called with out messaging context\n"));
return NT_STATUS_INTERNAL_ERROR;
}
ctx = talloc(mem_ctx, struct auth_context);
NT_STATUS_HAVE_NO_MEMORY(ctx);
ctx->challenge.set_by = NULL;
ctx->challenge.may_be_modified = False;
ctx->challenge.data = data_blob(NULL, 0);
ctx->methods = NULL;
ctx->event_ctx = ev;
ctx->msg_ctx = msg;
for (i=0; methods[i] ; i++) {
struct auth_method_context *method;
method = talloc(ctx, struct auth_method_context);
NT_STATUS_HAVE_NO_MEMORY(method);
method->ops = auth_backend_byname(methods[i]);
if (!method->ops) {
DEBUG(1,("auth_context_create: failed to find method=%s\n",
methods[i]));
return NT_STATUS_INTERNAL_ERROR;
}
method->auth_ctx = ctx;
method->depth = i;
DLIST_ADD_END(ctx->methods, method, struct auth_method_context *);
}
if (!ctx->methods) {
return NT_STATUS_INTERNAL_ERROR;
}
*auth_ctx = ctx;
return NT_STATUS_OK;
}
/* the list of currently registered AUTH backends */
static struct auth_backend {
const struct auth_operations *ops;
} *backends = NULL;
static int num_backends;
/*
register a AUTH backend.
The 'name' can be later used by other backends to find the operations
structure for this backend.
*/
NTSTATUS auth_register(const void *_ops)
{
const struct auth_operations *ops = _ops;
struct auth_operations *new_ops;
if (auth_backend_byname(ops->name) != NULL) {
/* its already registered! */
DEBUG(0,("AUTH backend '%s' already registered\n",
ops->name));
return NT_STATUS_OBJECT_NAME_COLLISION;
}
backends = realloc_p(backends, struct auth_backend, num_backends+1);
if (!backends) {
return NT_STATUS_NO_MEMORY;
}
new_ops = smb_xmemdup(ops, sizeof(*ops));
new_ops->name = smb_xstrdup(ops->name);
backends[num_backends].ops = new_ops;
num_backends++;
DEBUG(3,("AUTH backend '%s' registered\n",
ops->name));
return NT_STATUS_OK;
}
/*
return the operations structure for a named backend of the specified type
*/
const struct auth_operations *auth_backend_byname(const char *name)
{
int i;
for (i=0;i<num_backends;i++) {
if (strcmp(backends[i].ops->name, name) == 0) {
return backends[i].ops;
}
}
return NULL;
}
/*
return the AUTH interface version, and the size of some critical types
This can be used by backends to either detect compilation errors, or provide
multiple implementations for different smbd compilation options in one module
*/
const struct auth_critical_sizes *auth_interface_version(void)
{
static const struct auth_critical_sizes critical_sizes = {
AUTH_INTERFACE_VERSION,
sizeof(struct auth_operations),
sizeof(struct auth_method_context),
sizeof(struct auth_context),
sizeof(struct auth_usersupplied_info),
sizeof(struct auth_serversupplied_info)
};
return &critical_sizes;
}
NTSTATUS auth_init(void)
{
static BOOL initialized = False;
init_module_fn static_init[] = STATIC_auth_MODULES;
init_module_fn *shared_init;
if (initialized) return NT_STATUS_OK;
initialized = True;
shared_init = load_samba_modules(NULL, "auth");
run_init_functions(static_init);
run_init_functions(shared_init);
talloc_free(shared_init);
return NT_STATUS_OK;
}
NTSTATUS server_service_auth_init(void)
{
return auth_init();
}
+195
View File
@@ -0,0 +1,195 @@
/*
Unix SMB/CIFS implementation.
Standardised Authentication types
Copyright (C) Andrew Bartlett 2001
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _SAMBA_AUTH_H
#define _SAMBA_AUTH_H
union netr_Validation;
struct netr_SamBaseInfo;
struct netr_SamInfo3;
/* modules can use the following to determine if the interface has changed
* please increment the version number after each interface change
* with a comment and maybe update struct auth_critical_sizes.
*/
/* version 1 - version from samba 3.0 - metze */
/* version 2 - initial samba4 version - metze */
/* version 3 - subsequent samba4 version - abartlet */
/* version 4 - subsequent samba4 version - metze */
/* version 0 - till samba4 is stable - metze */
#define AUTH_INTERFACE_VERSION 0
#define USER_INFO_CASE_INSENSITIVE_USERNAME 0x01 /* username may be in any case */
#define USER_INFO_CASE_INSENSITIVE_PASSWORD 0x02 /* password may be in any case */
#define USER_INFO_DONT_CHECK_UNIX_ACCOUNT 0x04 /* dont check unix account status */
#define USER_INFO_INTERACTIVE_LOGON 0x08 /* dont check unix account status */
enum auth_password_state {
AUTH_PASSWORD_RESPONSE,
AUTH_PASSWORD_HASH,
AUTH_PASSWORD_PLAIN
};
struct auth_usersupplied_info
{
const char *workstation_name;
struct socket_address *remote_host;
uint32_t logon_parameters;
BOOL mapped_state;
/* the values the client gives us */
struct {
const char *account_name;
const char *domain_name;
} client, mapped;
enum auth_password_state password_state;
union {
struct {
DATA_BLOB lanman;
DATA_BLOB nt;
} response;
struct {
struct samr_Password *lanman;
struct samr_Password *nt;
} hash;
char *plaintext;
} password;
uint32_t flags;
};
struct auth_serversupplied_info
{
struct dom_sid *account_sid;
struct dom_sid *primary_group_sid;
size_t n_domain_groups;
struct dom_sid **domain_groups;
DATA_BLOB user_session_key;
DATA_BLOB lm_session_key;
const char *account_name;
const char *domain_name;
const char *full_name;
const char *logon_script;
const char *profile_path;
const char *home_directory;
const char *home_drive;
const char *logon_server;
NTTIME last_logon;
NTTIME last_logoff;
NTTIME acct_expiry;
NTTIME last_password_change;
NTTIME allow_password_change;
NTTIME force_password_change;
uint16_t logon_count;
uint16_t bad_password_count;
uint32_t acct_flags;
BOOL authenticated;
};
struct auth_session_info {
struct security_token *security_token;
struct auth_serversupplied_info *server_info;
DATA_BLOB session_key;
struct cli_credentials *credentials;
};
struct auth_method_context;
struct auth_check_password_request;
struct auth_operations {
const char *name;
/* If you are using this interface, then you are probably
* getting something wrong. This interface is only for
* security=server, and makes a number of compromises to allow
* that. It is not compatible with being a PDC. */
NTSTATUS (*get_challenge)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge);
/* Given the user supplied info, check if this backend want to handle the password checking */
NTSTATUS (*want_check)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info);
/* Given the user supplied info, check a password */
NTSTATUS (*check_password)(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **server_info);
};
struct auth_method_context {
struct auth_method_context *prev, *next;
struct auth_context *auth_ctx;
const struct auth_operations *ops;
int depth;
void *private_data;
};
struct auth_context {
struct {
/* Who set this up in the first place? */
const char *set_by;
BOOL may_be_modified;
DATA_BLOB data;
} challenge;
/* methods, in the order they should be called */
struct auth_method_context *methods;
/* the event context to use for calls that can block */
struct event_context *event_ctx;
/* the messaging context which can be used by backends */
struct messaging_context *msg_ctx;
};
/* this structure is used by backends to determine the size of some critical types */
struct auth_critical_sizes {
int interface_version;
int sizeof_auth_operations;
int sizeof_auth_methods;
int sizeof_auth_context;
int sizeof_auth_usersupplied_info;
int sizeof_auth_serversupplied_info;
};
NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
enum auth_password_state to_state,
const struct auth_usersupplied_info *user_info_in,
const struct auth_usersupplied_info **user_info_encrypted);
#include "auth/auth_proto.h"
#endif /* _SMBAUTH_H_ */
+77
View File
@@ -0,0 +1,77 @@
/*
Unix SMB/CIFS implementation.
Anonymous Authentification
Copyright (C) Stefan Metzmacher 2004-2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/auth.h"
/**
* Return a anonymous logon for anonymous users (username = "")
*
* Typically used as the first module in the auth chain, this allows
* anonymou logons to be dealt with in one place. Non-anonymou logons 'fail'
* and pass onto the next module.
**/
static NTSTATUS anonymous_want_check(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info)
{
if (user_info->client.account_name && *user_info->client.account_name) {
return NT_STATUS_NOT_IMPLEMENTED;
}
return NT_STATUS_OK;
}
/**
* Return a anonymous logon for anonymous users (username = "")
*
* Typically used as the first module in the auth chain, this allows
* anonymou logons to be dealt with in one place. Non-anonymou logons 'fail'
* and pass onto the next module.
**/
static NTSTATUS anonymous_check_password(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **_server_info)
{
return auth_anonymous_server_info(mem_ctx, _server_info);
}
static struct auth_operations anonymous_auth_ops = {
.name = "anonymous",
.get_challenge = auth_get_challenge_not_implemented,
.want_check = anonymous_want_check,
.check_password = anonymous_check_password
};
NTSTATUS auth_anonymous_init(void)
{
NTSTATUS ret;
ret = auth_register(&anonymous_auth_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register 'anonymous' auth backend!\n"));
return ret;
}
return ret;
}
+207
View File
@@ -0,0 +1,207 @@
/*
Unix SMB/CIFS implementation.
Generic authentication types
Copyright (C) Andrew Bartlett 2001-2002
Copyright (C) Jelmer Vernooij 2002
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/auth.h"
#include "libcli/security/security.h"
#include "librpc/gen_ndr/ndr_samr.h"
static NTSTATUS name_to_ntstatus_want_check(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info)
{
return NT_STATUS_OK;
}
/**
* Return an error based on username
*
* This function allows the testing of obsure errors, as well as the generation
* of NT_STATUS -> DOS error mapping tables.
*
* This module is of no value to end-users.
*
* The password is ignored.
*
* @return An NTSTATUS value based on the username
**/
static NTSTATUS name_to_ntstatus_check_password(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **_server_info)
{
NTSTATUS nt_status;
struct auth_serversupplied_info *server_info;
uint32_t error_num;
const char *user;
user = user_info->client.account_name;
if (strncasecmp("NT_STATUS", user, strlen("NT_STATUS")) == 0) {
nt_status = nt_status_string_to_code(user);
} else {
error_num = strtoul(user, NULL, 16);
DEBUG(5,("name_to_ntstatus_check_password: Error for user %s was 0x%08X\n", user, error_num));
nt_status = NT_STATUS(error_num);
}
NT_STATUS_NOT_OK_RETURN(nt_status);
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
NT_STATUS_HAVE_NO_MEMORY(server_info);
server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
/* is this correct? */
server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
server_info->n_domain_groups = 0;
server_info->domain_groups = NULL;
/* annoying, but the Anonymous really does have a session key,
and it is all zeros! */
server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
data_blob_clear(&server_info->user_session_key);
data_blob_clear(&server_info->lm_session_key);
server_info->account_name = talloc_asprintf(server_info, "NAME TO NTSTATUS %s ANONYMOUS LOGON", user);
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
server_info->full_name = talloc_asprintf(server_info, "NAME TO NTSTATUS %s Anonymous Logon", user);
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
server_info->logon_script = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
server_info->profile_path = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
server_info->home_directory = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
server_info->home_drive = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
server_info->last_logon = 0;
server_info->last_logoff = 0;
server_info->acct_expiry = 0;
server_info->last_password_change = 0;
server_info->allow_password_change = 0;
server_info->force_password_change = 0;
server_info->logon_count = 0;
server_info->bad_password_count = 0;
server_info->acct_flags = ACB_NORMAL;
server_info->authenticated = False;
*_server_info = server_info;
return nt_status;
}
static struct auth_operations name_to_ntstatus_auth_ops = {
.name = "name_to_ntstatus",
.get_challenge = auth_get_challenge_not_implemented,
.want_check = name_to_ntstatus_want_check,
.check_password = name_to_ntstatus_check_password
};
/**
* Return a 'fixed' challenge instead of a variable one.
*
* The idea of this function is to make packet snifs consistant
* with a fixed challenge, so as to aid debugging.
*
* This module is of no value to end-users.
*
* This module does not actually authenticate the user, but
* just pretenteds to need a specified challenge.
* This module removes *all* security from the challenge-response system
*
* @return NT_STATUS_UNSUCCESSFUL
**/
static NTSTATUS fixed_challenge_get_challenge(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob)
{
DATA_BLOB blob;
const char *challenge = "I am a teapot";
blob = data_blob_talloc(mem_ctx, challenge, 8);
NT_STATUS_HAVE_NO_MEMORY(blob.data);
*_blob = blob;
return NT_STATUS_OK;
}
static NTSTATUS fixed_challenge_want_check(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info)
{
/* don't handle any users */
return NT_STATUS_NOT_IMPLEMENTED;
}
static NTSTATUS fixed_challenge_check_password(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **_server_info)
{
/* don't handle any users */
return NT_STATUS_NO_SUCH_USER;
}
static struct auth_operations fixed_challenge_auth_ops = {
.name = "fixed_challenge",
.get_challenge = fixed_challenge_get_challenge,
.want_check = fixed_challenge_want_check,
.check_password = fixed_challenge_check_password
};
NTSTATUS auth_developer_init(void)
{
NTSTATUS ret;
ret = auth_register(&name_to_ntstatus_auth_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register 'name_to_ntstatus' auth backend!\n"));
return ret;
}
ret = auth_register(&fixed_challenge_auth_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register 'fixed_challenge' auth backend!\n"));
return ret;
}
return ret;
}
+459
View File
@@ -0,0 +1,459 @@
/*
Unix SMB/CIFS implementation.
Password and authentication handling
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
Copyright (C) Gerald Carter 2003
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "librpc/gen_ndr/ndr_netlogon.h"
#include "system/time.h"
#include "db_wrap.h"
#include "lib/ldb/include/ldb.h"
#include "auth/auth.h"
#include "auth/auth_sam.h"
#include "dsdb/samdb/samdb.h"
#include "libcli/security/security.h"
#include "libcli/ldap/ldap.h"
extern const char *user_attrs[];
extern const char *domain_ref_attrs[];
/****************************************************************************
Look for the specified user in the sam, return ldb result structures
****************************************************************************/
static NTSTATUS authsam_search_account(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
const char *account_name,
const char *domain_name,
struct ldb_message ***ret_msgs,
struct ldb_message ***ret_msgs_domain_ref)
{
struct ldb_message **msgs_tmp;
struct ldb_message **msgs;
struct ldb_message **msgs_domain_ref;
struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
int ret;
int ret_domain;
struct ldb_dn *domain_dn = NULL;
if (domain_name) {
char *escaped_domain = ldb_binary_encode_string(mem_ctx, domain_name);
/* find the domain's DN */
ret_domain = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &msgs_domain_ref, domain_ref_attrs,
"(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))",
escaped_domain, escaped_domain);
if (ret_domain == -1) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
if (ret_domain == 0) {
DEBUG(3,("sam_search_user: Couldn't find domain [%s] in samdb.\n",
domain_name));
return NT_STATUS_NO_SUCH_USER;
}
if (ret_domain > 1) {
DEBUG(0,("Found %d records matching domain [%s]\n",
ret_domain, domain_name));
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msgs_domain_ref[0], "nCName", NULL);
}
/* pull the user attributes */
ret = gendb_search(sam_ctx, mem_ctx, domain_dn, &msgs, user_attrs,
"(&(sAMAccountName=%s)(objectclass=user))",
ldb_binary_encode_string(mem_ctx, account_name));
if (ret == -1) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
if (ret == 0) {
DEBUG(3,("sam_search_user: Couldn't find user [%s\\%s] in samdb, under %s\n",
domain_name, account_name, ldb_dn_get_linearized(domain_dn)));
return NT_STATUS_NO_SUCH_USER;
}
if (ret > 1) {
DEBUG(0,("Found %d records matching user [%s]\n", ret, account_name));
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
if (!domain_name) {
struct dom_sid *domain_sid;
domain_sid = samdb_result_sid_prefix(mem_ctx, msgs[0], "objectSid");
if (!domain_sid) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
/* find the domain's DN */
ret = gendb_search(sam_ctx, mem_ctx, NULL, &msgs_tmp, NULL,
"(&(objectSid=%s)(objectClass=domain))",
ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
if (ret == -1) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
if (ret == 0) {
DEBUG(3,("check_sam_security: Couldn't find domain_sid [%s] in passdb file.\n",
dom_sid_string(mem_ctx, domain_sid)));
return NT_STATUS_NO_SUCH_USER;
}
if (ret > 1) {
DEBUG(0,("Found %d records matching domain_sid [%s]\n",
ret, dom_sid_string(mem_ctx, domain_sid)));
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
ret_domain = gendb_search(sam_ctx, mem_ctx, partitions_basedn, &msgs_domain_ref, domain_ref_attrs,
"(nCName=%s)", ldb_dn_alloc_linearized(msgs_tmp, msgs_tmp[0]->dn));
if (ret_domain == -1) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
if (ret_domain == 0) {
DEBUG(3,("check_sam_security: Couldn't find domain [%s] in passdb file.\n",
ldb_dn_get_linearized(msgs_tmp[0]->dn)));
return NT_STATUS_NO_SUCH_USER;
}
if (ret_domain > 1) {
DEBUG(0,("Found %d records matching domain [%s]\n",
ret_domain, ldb_dn_get_linearized(msgs_tmp[0]->dn)));
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
}
*ret_msgs = msgs;
*ret_msgs_domain_ref = msgs_domain_ref;
return NT_STATUS_OK;
}
/****************************************************************************
Do a specific test for an smb password being correct, given a smb_password and
the lanman and NT responses.
****************************************************************************/
static NTSTATUS authsam_password_ok(struct auth_context *auth_context,
TALLOC_CTX *mem_ctx,
uint16_t acct_flags,
const struct samr_Password *lm_pwd,
const struct samr_Password *nt_pwd,
const struct auth_usersupplied_info *user_info,
DATA_BLOB *user_sess_key,
DATA_BLOB *lm_sess_key)
{
NTSTATUS status;
if (acct_flags & ACB_PWNOTREQ) {
if (lp_null_passwords()) {
DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n",
user_info->mapped.account_name));
return NT_STATUS_OK;
} else {
DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n",
user_info->mapped.account_name));
return NT_STATUS_LOGON_FAILURE;
}
}
switch (user_info->password_state) {
case AUTH_PASSWORD_PLAIN:
{
const struct auth_usersupplied_info *user_info_temp;
status = encrypt_user_info(mem_ctx, auth_context,
AUTH_PASSWORD_HASH,
user_info, &user_info_temp);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("Failed to convert plaintext password to password HASH: %s\n", nt_errstr(status)));
return status;
}
user_info = user_info_temp;
/*fall through*/
}
case AUTH_PASSWORD_HASH:
*lm_sess_key = data_blob(NULL, 0);
*user_sess_key = data_blob(NULL, 0);
status = hash_password_check(mem_ctx,
user_info->password.hash.lanman,
user_info->password.hash.nt,
user_info->mapped.account_name,
lm_pwd, nt_pwd);
NT_STATUS_NOT_OK_RETURN(status);
break;
case AUTH_PASSWORD_RESPONSE:
status = ntlm_password_check(mem_ctx, user_info->logon_parameters,
&auth_context->challenge.data,
&user_info->password.response.lanman,
&user_info->password.response.nt,
user_info->mapped.account_name,
user_info->client.account_name,
user_info->client.domain_name,
lm_pwd, nt_pwd,
user_sess_key, lm_sess_key);
NT_STATUS_NOT_OK_RETURN(status);
break;
}
if (user_sess_key && user_sess_key->data) {
talloc_steal(auth_context, user_sess_key->data);
}
if (lm_sess_key && lm_sess_key->data) {
talloc_steal(auth_context, lm_sess_key->data);
}
return NT_STATUS_OK;
}
static NTSTATUS authsam_authenticate(struct auth_context *auth_context,
TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
struct ldb_message **msgs,
struct ldb_message **msgs_domain_ref,
const struct auth_usersupplied_info *user_info,
DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key)
{
struct samr_Password *lm_pwd, *nt_pwd;
NTSTATUS nt_status;
uint16_t acct_flags = samdb_result_acct_flags(msgs[0], "userAccountControl");
/* Quit if the account was locked out. */
if (acct_flags & ACB_AUTOLOCK) {
DEBUG(3,("check_sam_security: Account for user %s was locked out.\n",
user_info->mapped.account_name));
return NT_STATUS_ACCOUNT_LOCKED_OUT;
}
/* You can only do an interactive login to normal accounts */
if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
if (!(acct_flags & ACB_NORMAL)) {
return NT_STATUS_NO_SUCH_USER;
}
}
nt_status = samdb_result_passwords(mem_ctx, msgs[0], &lm_pwd, &nt_pwd);
NT_STATUS_NOT_OK_RETURN(nt_status);
nt_status = authsam_password_ok(auth_context, mem_ctx,
acct_flags, lm_pwd, nt_pwd,
user_info, user_sess_key, lm_sess_key);
NT_STATUS_NOT_OK_RETURN(nt_status);
nt_status = authsam_account_ok(mem_ctx, sam_ctx,
user_info->logon_parameters,
msgs[0],
msgs_domain_ref[0],
user_info->workstation_name,
user_info->mapped.account_name);
return nt_status;
}
static NTSTATUS authsam_check_password_internals(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const char *domain,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **server_info)
{
NTSTATUS nt_status;
const char *account_name = user_info->mapped.account_name;
struct ldb_message **msgs;
struct ldb_message **domain_ref_msgs;
struct ldb_context *sam_ctx;
DATA_BLOB user_sess_key, lm_sess_key;
TALLOC_CTX *tmp_ctx;
if (!account_name || !*account_name) {
/* 'not for me' */
return NT_STATUS_NOT_IMPLEMENTED;
}
tmp_ctx = talloc_new(mem_ctx);
if (!tmp_ctx) {
return NT_STATUS_NO_MEMORY;
}
sam_ctx = samdb_connect(tmp_ctx, system_session(mem_ctx));
if (sam_ctx == NULL) {
talloc_free(tmp_ctx);
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
nt_status = authsam_search_account(tmp_ctx, sam_ctx, account_name, domain, &msgs, &domain_ref_msgs);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(tmp_ctx);
return nt_status;
}
nt_status = authsam_authenticate(ctx->auth_ctx, tmp_ctx, sam_ctx, msgs, domain_ref_msgs, user_info,
&user_sess_key, &lm_sess_key);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(tmp_ctx);
return nt_status;
}
nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, msgs[0], domain_ref_msgs[0],
user_sess_key, lm_sess_key,
server_info);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(tmp_ctx);
return nt_status;
}
talloc_steal(mem_ctx, *server_info);
talloc_free(tmp_ctx);
return NT_STATUS_OK;
}
static NTSTATUS authsam_ignoredomain_want_check(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info)
{
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
return NT_STATUS_NOT_IMPLEMENTED;
}
return NT_STATUS_OK;
}
static NTSTATUS authsam_ignoredomain_check_password(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **server_info)
{
return authsam_check_password_internals(ctx, mem_ctx, NULL, user_info, server_info);
}
/****************************************************************************
Check SAM security (above) but with a few extra checks.
****************************************************************************/
static NTSTATUS authsam_want_check(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info)
{
BOOL is_local_name, is_my_domain;
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
return NT_STATUS_NOT_IMPLEMENTED;
}
is_local_name = is_myname(user_info->mapped.domain_name);
is_my_domain = strequal(user_info->mapped.domain_name, lp_workgroup());
/* check whether or not we service this domain/workgroup name */
switch (lp_server_role()) {
case ROLE_STANDALONE:
return NT_STATUS_OK;
case ROLE_DOMAIN_MEMBER:
if (!is_local_name) {
DEBUG(6,("authsam_check_password: %s is not one of my local names (DOMAIN_MEMBER)\n",
user_info->mapped.domain_name));
return NT_STATUS_NOT_IMPLEMENTED;
}
return NT_STATUS_OK;
case ROLE_DOMAIN_PDC:
case ROLE_DOMAIN_BDC:
if (!is_local_name && !is_my_domain) {
DEBUG(6,("authsam_check_password: %s is not one of my local names or domain name (DC)\n",
user_info->mapped.domain_name));
return NT_STATUS_NOT_IMPLEMENTED;
}
return NT_STATUS_OK;
}
DEBUG(6,("authsam_check_password: lp_server_role() has an undefined value\n"));
return NT_STATUS_NOT_IMPLEMENTED;
}
/****************************************************************************
Check SAM security (above) but with a few extra checks.
****************************************************************************/
static NTSTATUS authsam_check_password(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **server_info)
{
const char *domain;
/* check whether or not we service this domain/workgroup name */
switch (lp_server_role()) {
case ROLE_STANDALONE:
case ROLE_DOMAIN_MEMBER:
domain = lp_netbios_name();
break;
case ROLE_DOMAIN_PDC:
case ROLE_DOMAIN_BDC:
domain = lp_workgroup();
break;
default:
return NT_STATUS_NO_SUCH_USER;
}
return authsam_check_password_internals(ctx, mem_ctx, domain, user_info, server_info);
}
static const struct auth_operations sam_ignoredomain_ops = {
.name = "sam_ignoredomain",
.get_challenge = auth_get_challenge_not_implemented,
.want_check = authsam_ignoredomain_want_check,
.check_password = authsam_ignoredomain_check_password
};
static const struct auth_operations sam_ops = {
.name = "sam",
.get_challenge = auth_get_challenge_not_implemented,
.want_check = authsam_want_check,
.check_password = authsam_check_password
};
NTSTATUS auth_sam_init(void)
{
NTSTATUS ret;
ret = auth_register(&sam_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register 'sam' auth backend!\n"));
return ret;
}
ret = auth_register(&sam_ignoredomain_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register 'sam_ignoredomain' auth backend!\n"));
return ret;
}
return ret;
}
+151
View File
@@ -0,0 +1,151 @@
/*
Unix SMB/CIFS implementation.
Convert a server info struct into the form for PAC and NETLOGON replies
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
Copyright (C) Stefan Metzmacher <metze@samba.org> 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/auth.h"
#include "libcli/security/security.h"
#include "librpc/gen_ndr/ndr_netlogon.h"
NTSTATUS auth_convert_server_info_sambaseinfo(TALLOC_CTX *mem_ctx,
struct auth_serversupplied_info *server_info,
struct netr_SamBaseInfo **_sam)
{
struct netr_SamBaseInfo *sam = talloc_zero(mem_ctx, struct netr_SamBaseInfo);
NT_STATUS_HAVE_NO_MEMORY(sam);
sam->domain_sid = dom_sid_dup(mem_ctx, server_info->account_sid);
NT_STATUS_HAVE_NO_MEMORY(sam->domain_sid);
sam->domain_sid->num_auths--;
sam->last_logon = server_info->last_logon;
sam->last_logoff = server_info->last_logoff;
sam->acct_expiry = server_info->acct_expiry;
sam->last_password_change = server_info->last_password_change;
sam->allow_password_change = server_info->allow_password_change;
sam->force_password_change = server_info->force_password_change;
sam->account_name.string = server_info->account_name;
sam->full_name.string = server_info->full_name;
sam->logon_script.string = server_info->logon_script;
sam->profile_path.string = server_info->profile_path;
sam->home_directory.string = server_info->home_directory;
sam->home_drive.string = server_info->home_drive;
sam->logon_count = server_info->logon_count;
sam->bad_password_count = sam->bad_password_count;
sam->rid = server_info->account_sid->sub_auths[server_info->account_sid->num_auths-1];
sam->primary_gid = server_info->primary_group_sid->sub_auths[server_info->primary_group_sid->num_auths-1];
sam->groups.count = 0;
sam->groups.rids = NULL;
if (server_info->n_domain_groups > 0) {
int i;
sam->groups.rids = talloc_array(sam, struct samr_RidWithAttribute,
server_info->n_domain_groups);
if (sam->groups.rids == NULL)
return NT_STATUS_NO_MEMORY;
for (i=0; i<server_info->n_domain_groups; i++) {
struct dom_sid *group_sid = server_info->domain_groups[i];
if (!dom_sid_in_domain(sam->domain_sid, group_sid)) {
/* We handle this elsewhere */
continue;
}
sam->groups.rids[sam->groups.count].rid =
group_sid->sub_auths[group_sid->num_auths-1];
sam->groups.rids[sam->groups.count].attributes =
SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
sam->groups.count += 1;
}
}
sam->user_flags = 0; /* TODO: w2k3 uses 0x120. We know 0x20
* as extra sids (PAC doc) but what is
* 0x100? */
sam->acct_flags = server_info->acct_flags;
sam->logon_server.string = server_info->logon_server;
sam->domain.string = server_info->domain_name;
ZERO_STRUCT(sam->unknown);
ZERO_STRUCT(sam->key);
if (server_info->user_session_key.length == sizeof(sam->key.key)) {
memcpy(sam->key.key, server_info->user_session_key.data, sizeof(sam->key.key));
}
ZERO_STRUCT(sam->LMSessKey);
if (server_info->lm_session_key.length == sizeof(sam->LMSessKey.key)) {
memcpy(sam->LMSessKey.key, server_info->lm_session_key.data,
sizeof(sam->LMSessKey.key));
}
*_sam = sam;
return NT_STATUS_OK;
}
NTSTATUS auth_convert_server_info_saminfo3(TALLOC_CTX *mem_ctx,
struct auth_serversupplied_info *server_info,
struct netr_SamInfo3 **_sam3)
{
struct netr_SamBaseInfo *sam;
struct netr_SamInfo3 *sam3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
NTSTATUS status;
int i;
NT_STATUS_HAVE_NO_MEMORY(sam3);
status = auth_convert_server_info_sambaseinfo(mem_ctx, server_info, &sam);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
sam3->base = *sam;
sam3->sidcount = 0;
sam3->sids = NULL;
sam3->sids = talloc_array(sam, struct netr_SidAttr,
server_info->n_domain_groups);
NT_STATUS_HAVE_NO_MEMORY(sam3->sids);
for (i=0; i<server_info->n_domain_groups; i++) {
if (dom_sid_in_domain(sam->domain_sid, server_info->domain_groups[i])) {
continue;
}
sam3->sids[sam3->sidcount].sid = talloc_reference(sam3->sids,server_info->domain_groups[i]);
sam3->sids[sam3->sidcount].attribute =
SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED;
sam3->sidcount += 1;
}
if (sam3->sidcount) {
sam3->base.user_flags |= NETLOGON_EXTRA_SIDS;
} else {
sam3->sids = NULL;
}
*_sam3 = sam3;
return NT_STATUS_OK;
}
+378
View File
@@ -0,0 +1,378 @@
/*
Unix SMB/CIFS implementation.
Authenticate to a remote server
Copyright (C) Andrew Tridgell 1992-1998
Copyright (C) Andrew Bartlett 2001
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
/****************************************************************************
Support for server level security.
****************************************************************************/
static struct smbcli_state *server_cryptkey(TALLOC_CTX *mem_ctx)
{
struct smbcli_state *cli = NULL;
fstring desthost;
struct ipv4_addr dest_ip;
const char *p;
char *pserver;
BOOL connected_ok = False;
if (!(cli = smbcli_initialise(cli)))
return NULL;
/* security = server just can't function with spnego */
cli->use_spnego = False;
pserver = talloc_strdup(mem_ctx, lp_passwordserver());
p = pserver;
while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) {
strupper(desthost);
if(!resolve_name( desthost, &dest_ip, 0x20)) {
DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost));
continue;
}
if (ismyip(dest_ip)) {
DEBUG(1,("Password server loop - disabling password server %s\n",desthost));
continue;
}
/* we use a mutex to prevent two connections at once - when a
Win2k PDC get two connections where one hasn't completed a
session setup yet it will send a TCP reset to the first
connection (tridge) */
if (!grab_server_mutex(desthost)) {
return NULL;
}
if (smbcli_connect(cli, desthost, &dest_ip)) {
DEBUG(3,("connected to password server %s\n",desthost));
connected_ok = True;
break;
}
}
if (!connected_ok) {
release_server_mutex();
DEBUG(0,("password server not available\n"));
talloc_free(cli);
return NULL;
}
if (!attempt_netbios_session_request(cli, lp_netbios_name(),
desthost, &dest_ip)) {
release_server_mutex();
DEBUG(1,("password server fails session request\n"));
talloc_free(cli);
return NULL;
}
if (strequal(desthost,myhostname(mem_ctx))) {
exit_server("Password server loop!");
}
DEBUG(3,("got session\n"));
if (!smbcli_negprot(cli)) {
DEBUG(1,("%s rejected the negprot\n",desthost));
release_server_mutex();
talloc_free(cli);
return NULL;
}
if (cli->protocol < PROTOCOL_LANMAN2 ||
!(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) {
DEBUG(1,("%s isn't in user level security mode\n",desthost));
release_server_mutex();
talloc_free(cli);
return NULL;
}
/* Get the first session setup done quickly, to avoid silly
Win2k bugs. (The next connection to the server will kill
this one...
*/
if (!smbcli_session_setup(cli, "", "", 0, "", 0,
"")) {
DEBUG(0,("%s rejected the initial session setup (%s)\n",
desthost, smbcli_errstr(cli)));
release_server_mutex();
talloc_free(cli);
return NULL;
}
release_server_mutex();
DEBUG(3,("password server OK\n"));
return cli;
}
/****************************************************************************
Clean up our allocated cli.
****************************************************************************/
static void free_server_private_data(void **private_data_pointer)
{
struct smbcli_state **cli = (struct smbcli_state **)private_data_pointer;
if (*cli && (*cli)->initialised) {
talloc_free(*cli);
}
}
/****************************************************************************
Get the challenge out of a password server.
****************************************************************************/
static DATA_BLOB auth_get_challenge_server(const struct auth_context *auth_context,
void **my_private_data,
TALLOC_CTX *mem_ctx)
{
struct smbcli_state *cli = server_cryptkey(mem_ctx);
if (cli) {
DEBUG(3,("using password server validation\n"));
if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
/* We can't work with unencrypted password servers
unless 'encrypt passwords = no' */
DEBUG(5,("make_auth_info_server: Server is unencrypted, no challenge available..\n"));
/* However, it is still a perfectly fine connection
to pass that unencrypted password over */
*my_private_data = (void *)cli;
return data_blob(NULL, 0);
} else if (cli->secblob.length < 8) {
/* We can't do much if we don't get a full challenge */
DEBUG(2,("make_auth_info_server: Didn't receive a full challenge from server\n"));
talloc_free(cli);
return data_blob(NULL, 0);
}
*my_private_data = (void *)cli;
/* The return must be allocated on the caller's mem_ctx, as our own will be
destoyed just after the call. */
return data_blob_talloc(auth_context->mem_ctx, cli->secblob.data,8);
} else {
return data_blob(NULL, 0);
}
}
/****************************************************************************
Check for a valid username and password in security=server mode.
- Validate a password with the password server.
****************************************************************************/
static NTSTATUS check_smbserver_security(const struct auth_context *auth_context,
void *my_private_data,
TALLOC_CTX *mem_ctx,
const auth_usersupplied_info *user_info,
auth_serversupplied_info **server_info)
{
struct smbcli_state *cli;
static uint8_t badpass[24];
static fstring baduser;
static BOOL tested_password_server = False;
static BOOL bad_password_server = False;
NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE;
BOOL locally_made_cli = False;
/*
* Check that the requested domain is not our own machine name.
* If it is, we should never check the PDC here, we use our own local
* password file.
*/
if(is_myname(user_info->domain.str)) {
DEBUG(3,("check_smbserver_security: Requested domain was for this machine.\n"));
return NT_STATUS_LOGON_FAILURE;
}
cli = my_private_data;
if (cli) {
} else {
cli = server_cryptkey(mem_ctx);
locally_made_cli = True;
}
if (!cli || !cli->initialised) {
DEBUG(1,("password server is not connected (cli not initilised)\n"));
return NT_STATUS_LOGON_FAILURE;
}
if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
if (user_info->encrypted) {
DEBUG(1,("password server %s is plaintext, but we are encrypted. This just can't work :-(\n", cli->desthost));
return NT_STATUS_LOGON_FAILURE;
}
} else {
if (memcmp(cli->secblob.data, auth_context->challenge.data, 8) != 0) {
DEBUG(1,("the challenge that the password server (%s) supplied us is not the one we gave our client. This just can't work :-(\n", cli->desthost));
return NT_STATUS_LOGON_FAILURE;
}
}
if(badpass[0] == 0)
memset(badpass, 0x1f, sizeof(badpass));
if((user_info->nt_resp.length == sizeof(badpass)) &&
!memcmp(badpass, user_info->nt_resp.data, sizeof(badpass))) {
/*
* Very unlikely, our random bad password is the same as the users
* password.
*/
memset(badpass, badpass[0]+1, sizeof(badpass));
}
if(baduser[0] == 0) {
fstrcpy(baduser, INVALID_USER_PREFIX);
fstrcat(baduser, lp_netbios_name());
}
/*
* Attempt a session setup with a totally incorrect password.
* If this succeeds with the guest bit *NOT* set then the password
* server is broken and is not correctly setting the guest bit. We
* need to detect this as some versions of NT4.x are broken. JRA.
*/
/* I sure as hell hope that there aren't servers out there that take
* NTLMv2 and have this bug, as we don't test for that...
* - abartlet@samba.org
*/
if ((!tested_password_server) && (lp_paranoid_server_security())) {
if (smbcli_session_setup(cli, baduser, (char *)badpass, sizeof(badpass),
(char *)badpass, sizeof(badpass), user_info->domain.str)) {
/*
* We connected to the password server so we
* can say we've tested it.
*/
tested_password_server = True;
if ((SVAL(cli->inbuf,smb_vwv2) & 1) == 0) {
DEBUG(0,("server_validate: password server %s allows users as non-guest \
with a bad password.\n", cli->desthost));
DEBUG(0,("server_validate: This is broken (and insecure) behaviour. Please do not \
use this machine as the password server.\n"));
smbcli_ulogoff(cli);
/*
* Password server has the bug.
*/
bad_password_server = True;
return NT_STATUS_LOGON_FAILURE;
}
smbcli_ulogoff(cli);
}
} else {
/*
* We have already tested the password server.
* Fail immediately if it has the bug.
*/
if(bad_password_server) {
DEBUG(0,("server_validate: [1] password server %s allows users as non-guest \
with a bad password.\n", cli->desthost));
DEBUG(0,("server_validate: [1] This is broken (and insecure) behaviour. Please do not \
use this machine as the password server.\n"));
return NT_STATUS_LOGON_FAILURE;
}
}
/*
* Now we know the password server will correctly set the guest bit, or is
* not guest enabled, we can try with the real password.
*/
if (!user_info->encrypted) {
/* Plaintext available */
if (!smbcli_session_setup(cli, user_info->smb_name.str,
(char *)user_info->plaintext_password.data,
user_info->plaintext_password.length,
NULL, 0,
user_info->domain.str)) {
DEBUG(1,("password server %s rejected the password\n", cli->desthost));
/* Make this smbcli_nt_error() when the conversion is in */
nt_status = smbcli_nt_error(cli);
} else {
nt_status = NT_STATUS_OK;
}
} else {
if (!smbcli_session_setup(cli, user_info->smb_name.str,
(char *)user_info->lm_resp.data,
user_info->lm_resp.length,
(char *)user_info->nt_resp.data,
user_info->nt_resp.length,
user_info->domain.str)) {
DEBUG(1,("password server %s rejected the password\n", cli->desthost));
/* Make this smbcli_nt_error() when the conversion is in */
nt_status = smbcli_nt_error(cli);
} else {
nt_status = NT_STATUS_OK;
}
}
/* if logged in as guest then reject */
if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) {
DEBUG(1,("password server %s gave us guest only\n", cli->desthost));
nt_status = NT_STATUS_LOGON_FAILURE;
}
smbcli_ulogoff(cli);
if NT_STATUS_IS_OK(nt_status) {
struct passwd *pass = Get_Pwnam(user_info->internal_username.str);
if (pass) {
nt_status = make_server_info_pw(auth_context, server_info, pass);
} else {
nt_status = NT_STATUS_NO_SUCH_USER;
}
}
if (locally_made_cli) {
talloc_free(cli);
}
return(nt_status);
}
NTSTATUS auth_init_smbserver(struct auth_context *auth_context, const char* param, auth_methods **auth_method)
{
if (!make_auth_methods(auth_context, auth_method)) {
return NT_STATUS_NO_MEMORY;
}
(*auth_method)->name = "smbserver";
(*auth_method)->auth = check_smbserver_security;
(*auth_method)->get_chal = auth_get_challenge_server;
(*auth_method)->send_keepalive = send_server_keepalive;
(*auth_method)->free_private_data = free_server_private_data;
return NT_STATUS_OK;
}
+93
View File
@@ -0,0 +1,93 @@
/*
Unix SMB/CIFS implementation.
auth functions
Copyright (C) Simo Sorce 2005
Copyright (C) Andrew Tridgell 2005
Copyright (C) Andrew Bartlett 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/auth.h"
#include "lib/events/events.h"
_PUBLIC_ NTSTATUS authenticate_username_pw(TALLOC_CTX *mem_ctx,
struct event_context *ev,
struct messaging_context *msg,
const char *nt4_domain,
const char *nt4_username,
const char *password,
struct auth_session_info **session_info)
{
struct auth_context *auth_context;
struct auth_usersupplied_info *user_info;
struct auth_serversupplied_info *server_info;
NTSTATUS nt_status;
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
if (!tmp_ctx) {
return NT_STATUS_NO_MEMORY;
}
nt_status = auth_context_create(tmp_ctx, lp_auth_methods(),
ev, msg,
&auth_context);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(tmp_ctx);
return nt_status;
}
user_info = talloc(tmp_ctx, struct auth_usersupplied_info);
if (!user_info) {
talloc_free(tmp_ctx);
return NT_STATUS_NO_MEMORY;
}
user_info->mapped_state = True;
user_info->client.account_name = nt4_username;
user_info->mapped.account_name = nt4_username;
user_info->client.domain_name = nt4_domain;
user_info->mapped.domain_name = nt4_domain;
user_info->workstation_name = NULL;
user_info->remote_host = NULL;
user_info->password_state = AUTH_PASSWORD_PLAIN;
user_info->password.plaintext = talloc_strdup(user_info, password);
user_info->flags = USER_INFO_CASE_INSENSITIVE_USERNAME |
USER_INFO_DONT_CHECK_UNIX_ACCOUNT;
user_info->logon_parameters = 0;
nt_status = auth_check_password(auth_context, tmp_ctx, user_info, &server_info);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(tmp_ctx);
return nt_status;
}
nt_status = auth_generate_session_info(tmp_ctx, server_info, session_info);
if (NT_STATUS_IS_OK(nt_status)) {
talloc_steal(mem_ctx, *session_info);
}
return nt_status;
}
+839
View File
@@ -0,0 +1,839 @@
/*
Unix SMB/CIFS implementation.
Password and authentication handling
Copyright (C) Andrew Bartlett 2001
Copyright (C) Jeremy Allison 2001
Copyright (C) Simo Sorce 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/auth.h"
#include "system/passwd.h" /* needed by some systems for struct passwd */
#include "lib/socket/socket.h"
#include "auth/pam_errors.h"
/* TODO: look at how to best fill in parms retrieveing a struct passwd info
* except in case USER_INFO_DONT_CHECK_UNIX_ACCOUNT is set
*/
static NTSTATUS authunix_make_server_info(TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct passwd *pwd,
struct auth_serversupplied_info **_server_info)
{
struct auth_serversupplied_info *server_info;
NTSTATUS status;
/* This is a real, real hack */
if (pwd->pw_uid == 0) {
status = auth_system_server_info(mem_ctx, &server_info);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
server_info->account_name = talloc_steal(server_info, pwd->pw_name);
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
server_info->domain_name = talloc_strdup(server_info, "unix");
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
} else {
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
NT_STATUS_HAVE_NO_MEMORY(server_info);
server_info->authenticated = True;
server_info->account_name = talloc_steal(server_info, pwd->pw_name);
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
server_info->domain_name = talloc_strdup(server_info, "unix");
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
/* This isn't in any way correct.. */
server_info->account_sid = NULL;
server_info->primary_group_sid = NULL;
server_info->n_domain_groups = 0;
server_info->domain_groups = NULL;
}
server_info->user_session_key = data_blob(NULL,0);
server_info->lm_session_key = data_blob(NULL,0);
server_info->full_name = talloc_steal(server_info, pwd->pw_gecos);
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
server_info->logon_script = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
server_info->profile_path = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
server_info->home_directory = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
server_info->home_drive = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
server_info->last_logon = 0;
server_info->last_logoff = 0;
server_info->acct_expiry = 0;
server_info->last_password_change = 0;
server_info->allow_password_change = 0;
server_info->force_password_change = 0;
server_info->logon_count = 0;
server_info->bad_password_count = 0;
server_info->acct_flags = 0;
*_server_info = server_info;
return NT_STATUS_OK;
}
static NTSTATUS talloc_getpwnam(TALLOC_CTX *ctx, const char *username, struct passwd **pws)
{
struct passwd *ret;
struct passwd *from;
*pws = NULL;
ret = talloc(ctx, struct passwd);
NT_STATUS_HAVE_NO_MEMORY(ret);
from = getpwnam(username);
if (!from) {
return NT_STATUS_NO_SUCH_USER;
}
ret->pw_name = talloc_strdup(ctx, from->pw_name);
NT_STATUS_HAVE_NO_MEMORY(ret->pw_name);
ret->pw_passwd = talloc_strdup(ctx, from->pw_passwd);
NT_STATUS_HAVE_NO_MEMORY(ret->pw_passwd);
ret->pw_uid = from->pw_uid;
ret->pw_gid = from->pw_gid;
ret->pw_gecos = talloc_strdup(ctx, from->pw_gecos);
NT_STATUS_HAVE_NO_MEMORY(ret->pw_gecos);
ret->pw_dir = talloc_strdup(ctx, from->pw_dir);
NT_STATUS_HAVE_NO_MEMORY(ret->pw_dir);
ret->pw_shell = talloc_strdup(ctx, from->pw_shell);
NT_STATUS_HAVE_NO_MEMORY(ret->pw_shell);
*pws = ret;
return NT_STATUS_OK;
}
#ifdef HAVE_SECURITY_PAM_APPL_H
#include <security/pam_appl.h>
struct smb_pam_user_info {
const char *account_name;
const char *plaintext_password;
};
#define COPY_STRING(s) (s) ? strdup(s) : NULL
/*
* Check user password
* Currently it uses PAM only and fails on systems without PAM
* Samba3 code located in pass_check.c is to ugly to be used directly it will
* need major rework that's why pass_check.c is still there.
*/
static int smb_pam_conv(int num_msg, const struct pam_message **msg,
struct pam_response **reply, void *appdata_ptr)
{
struct smb_pam_user_info *info = (struct smb_pam_user_info *)appdata_ptr;
int num;
if (num_msg <= 0) {
*reply = NULL;
return PAM_CONV_ERR;
}
/*
* Apparantly HPUX has a buggy PAM that doesn't support the
* data pointer. Fail if this is the case. JRA.
*/
if (info == NULL) {
*reply = NULL;
return PAM_CONV_ERR;
}
/*
* PAM frees memory in reply messages by itself
* so use malloc instead of talloc here.
*/
*reply = malloc_array_p(struct pam_response, num_msg);
if (*reply == NULL) {
return PAM_CONV_ERR;
}
for (num = 0; num < num_msg; num++) {
switch (msg[num]->msg_style) {
case PAM_PROMPT_ECHO_ON:
(*reply)[num].resp_retcode = PAM_SUCCESS;
(*reply)[num].resp = COPY_STRING(info->account_name);
break;
case PAM_PROMPT_ECHO_OFF:
(*reply)[num].resp_retcode = PAM_SUCCESS;
(*reply)[num].resp = COPY_STRING(info->plaintext_password);
break;
case PAM_TEXT_INFO:
(*reply)[num].resp_retcode = PAM_SUCCESS;
(*reply)[num].resp = NULL;
DEBUG(4,("PAM Info message in conversation function: %s\n", (msg[num]->msg)));
break;
case PAM_ERROR_MSG:
(*reply)[num].resp_retcode = PAM_SUCCESS;
(*reply)[num].resp = NULL;
DEBUG(4,("PAM Error message in conversation function: %s\n", (msg[num]->msg)));
break;
default:
while (num > 0) {
SAFE_FREE((*reply)[num-1].resp);
num--;
}
SAFE_FREE(*reply);
*reply = NULL;
DEBUG(1,("Error: PAM subsystme sent an UNKNOWN message type to the conversation function!\n"));
return PAM_CONV_ERR;
}
}
return PAM_SUCCESS;
}
/*
* Start PAM authentication for specified account
*/
static NTSTATUS smb_pam_start(pam_handle_t **pamh, const char *account_name, const char *remote_host, struct pam_conv *pconv)
{
int pam_error;
if (account_name == NULL || remote_host == NULL) {
return NT_STATUS_INVALID_PARAMETER;
}
DEBUG(4,("smb_pam_start: PAM: Init user: %s\n", account_name));
pam_error = pam_start("samba", account_name, pconv, pamh);
if (pam_error != PAM_SUCCESS) {
/* no vaild pamh here, can we reliably call pam_strerror ? */
DEBUG(4,("smb_pam_start: pam_start failed!\n"));
return NT_STATUS_UNSUCCESSFUL;
}
#ifdef PAM_RHOST
DEBUG(4,("smb_pam_start: PAM: setting rhost to: %s\n", remote_host));
pam_error = pam_set_item(*pamh, PAM_RHOST, remote_host);
if (pam_error != PAM_SUCCESS) {
NTSTATUS nt_status;
DEBUG(4,("smb_pam_start: setting rhost failed with error: %s\n",
pam_strerror(*pamh, pam_error)));
nt_status = pam_to_nt_status(pam_error);
pam_error = pam_end(*pamh, 0);
if (pam_error != PAM_SUCCESS) {
/* no vaild pamh here, can we reliably call pam_strerror ? */
DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
pam_error));
return pam_to_nt_status(pam_error);
}
return nt_status;
}
#endif
#ifdef PAM_TTY
DEBUG(4,("smb_pam_start: PAM: setting tty\n"));
pam_error = pam_set_item(*pamh, PAM_TTY, "samba");
if (pam_error != PAM_SUCCESS) {
NTSTATUS nt_status;
DEBUG(4,("smb_pam_start: setting tty failed with error: %s\n",
pam_strerror(*pamh, pam_error)));
nt_status = pam_to_nt_status(pam_error);
pam_error = pam_end(*pamh, 0);
if (pam_error != PAM_SUCCESS) {
/* no vaild pamh here, can we reliably call pam_strerror ? */
DEBUG(4,("smb_pam_start: clean up failed, pam_end gave error %d.\n",
pam_error));
return pam_to_nt_status(pam_error);
}
return nt_status;
}
#endif
DEBUG(4,("smb_pam_start: PAM: Init passed for user: %s\n", account_name));
return NT_STATUS_OK;
}
static NTSTATUS smb_pam_end(pam_handle_t *pamh)
{
int pam_error;
if (pamh != NULL) {
pam_error = pam_end(pamh, 0);
if (pam_error != PAM_SUCCESS) {
/* no vaild pamh here, can we reliably call pam_strerror ? */
DEBUG(4,("smb_pam_end: clean up failed, pam_end gave error %d.\n",
pam_error));
return pam_to_nt_status(pam_error);
}
return NT_STATUS_OK;
}
DEBUG(2,("smb_pam_end: pamh is NULL, PAM not initialized ?\n"));
return NT_STATUS_UNSUCCESSFUL;
}
/*
* PAM Authentication Handler
*/
static NTSTATUS smb_pam_auth(pam_handle_t *pamh, const char *user)
{
int pam_error;
/*
* To enable debugging set in /etc/pam.d/samba:
* auth required /lib/security/pam_pwdb.so nullok shadow audit
*/
DEBUG(4,("smb_pam_auth: PAM: Authenticate User: %s\n", user));
pam_error = pam_authenticate(pamh, PAM_SILENT | lp_null_passwords() ? 0 : PAM_DISALLOW_NULL_AUTHTOK);
switch( pam_error ){
case PAM_AUTH_ERR:
DEBUG(2, ("smb_pam_auth: PAM: Authentication Error for user %s\n", user));
break;
case PAM_CRED_INSUFFICIENT:
DEBUG(2, ("smb_pam_auth: PAM: Insufficient Credentials for user %s\n", user));
break;
case PAM_AUTHINFO_UNAVAIL:
DEBUG(2, ("smb_pam_auth: PAM: Authentication Information Unavailable for user %s\n", user));
break;
case PAM_USER_UNKNOWN:
DEBUG(2, ("smb_pam_auth: PAM: Username %s NOT known to Authentication system\n", user));
break;
case PAM_MAXTRIES:
DEBUG(2, ("smb_pam_auth: PAM: One or more authentication modules reports user limit for user %s exceeeded\n", user));
break;
case PAM_ABORT:
DEBUG(0, ("smb_pam_auth: PAM: One or more PAM modules failed to load for user %s\n", user));
break;
case PAM_SUCCESS:
DEBUG(4, ("smb_pam_auth: PAM: User %s Authenticated OK\n", user));
break;
default:
DEBUG(0, ("smb_pam_auth: PAM: UNKNOWN ERROR while authenticating user %s\n", user));
break;
}
return pam_to_nt_status(pam_error);
}
/*
* PAM Account Handler
*/
static NTSTATUS smb_pam_account(pam_handle_t *pamh, const char * user)
{
int pam_error;
DEBUG(4,("smb_pam_account: PAM: Account Management for User: %s\n", user));
pam_error = pam_acct_mgmt(pamh, PAM_SILENT); /* Is user account enabled? */
switch( pam_error ) {
case PAM_AUTHTOK_EXPIRED:
DEBUG(2, ("smb_pam_account: PAM: User %s is valid but password is expired\n", user));
break;
case PAM_ACCT_EXPIRED:
DEBUG(2, ("smb_pam_account: PAM: User %s no longer permitted to access system\n", user));
break;
case PAM_AUTH_ERR:
DEBUG(2, ("smb_pam_account: PAM: There was an authentication error for user %s\n", user));
break;
case PAM_PERM_DENIED:
DEBUG(0, ("smb_pam_account: PAM: User %s is NOT permitted to access system at this time\n", user));
break;
case PAM_USER_UNKNOWN:
DEBUG(0, ("smb_pam_account: PAM: User \"%s\" is NOT known to account management\n", user));
break;
case PAM_SUCCESS:
DEBUG(4, ("smb_pam_account: PAM: Account OK for User: %s\n", user));
break;
default:
DEBUG(0, ("smb_pam_account: PAM: UNKNOWN PAM ERROR (%d) during Account Management for User: %s\n", pam_error, user));
break;
}
return pam_to_nt_status(pam_error);
}
/*
* PAM Credential Setting
*/
static NTSTATUS smb_pam_setcred(pam_handle_t *pamh, const char * user)
{
int pam_error;
/*
* This will allow samba to aquire a kerberos token. And, when
* exporting an AFS cell, be able to /write/ to this cell.
*/
DEBUG(4,("PAM: Account Management SetCredentials for User: %s\n", user));
pam_error = pam_setcred(pamh, (PAM_ESTABLISH_CRED|PAM_SILENT));
switch( pam_error ) {
case PAM_CRED_UNAVAIL:
DEBUG(0, ("smb_pam_setcred: PAM: Credentials not found for user:%s\n", user ));
break;
case PAM_CRED_EXPIRED:
DEBUG(0, ("smb_pam_setcred: PAM: Credentials for user: \"%s\" EXPIRED!\n", user ));
break;
case PAM_USER_UNKNOWN:
DEBUG(0, ("smb_pam_setcred: PAM: User: \"%s\" is NOT known so can not set credentials!\n", user ));
break;
case PAM_CRED_ERR:
DEBUG(0, ("smb_pam_setcred: PAM: Unknown setcredentials error - unable to set credentials for %s\n", user ));
break;
case PAM_SUCCESS:
DEBUG(4, ("smb_pam_setcred: PAM: SetCredentials OK for User: %s\n", user));
break;
default:
DEBUG(0, ("smb_pam_setcred: PAM: UNKNOWN PAM ERROR (%d) during SetCredentials for User: %s\n", pam_error, user));
break;
}
return pam_to_nt_status(pam_error);
}
static NTSTATUS check_unix_password(TALLOC_CTX *ctx, const struct auth_usersupplied_info *user_info, struct passwd **pws)
{
struct smb_pam_user_info *info;
struct pam_conv *pamconv;
pam_handle_t *pamh;
NTSTATUS nt_status;
info = talloc(ctx, struct smb_pam_user_info);
if (info == NULL) {
return NT_STATUS_NO_MEMORY;
}
info->account_name = user_info->mapped.account_name;
info->plaintext_password = user_info->password.plaintext;
pamconv = talloc(ctx, struct pam_conv);
if (pamconv == NULL) {
return NT_STATUS_NO_MEMORY;
}
pamconv->conv = smb_pam_conv;
pamconv->appdata_ptr = (void *)info;
/* TODO:
* check for user_info->flags & USER_INFO_CASE_INSENSITIVE_USERNAME
* if true set up a crack name routine.
*/
nt_status = smb_pam_start(&pamh, user_info->mapped.account_name, user_info->remote_host ? user_info->remote_host->addr : NULL, pamconv);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
nt_status = smb_pam_auth(pamh, user_info->mapped.account_name);
if (!NT_STATUS_IS_OK(nt_status)) {
smb_pam_end(pamh);
return nt_status;
}
if ( ! (user_info->flags & USER_INFO_DONT_CHECK_UNIX_ACCOUNT)) {
nt_status = smb_pam_account(pamh, user_info->mapped.account_name);
if (!NT_STATUS_IS_OK(nt_status)) {
smb_pam_end(pamh);
return nt_status;
}
nt_status = smb_pam_setcred(pamh, user_info->mapped.account_name);
if (!NT_STATUS_IS_OK(nt_status)) {
smb_pam_end(pamh);
return nt_status;
}
}
smb_pam_end(pamh);
nt_status = talloc_getpwnam(ctx, user_info->mapped.account_name, pws);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
return NT_STATUS_OK;
}
#else
/****************************************************************************
core of password checking routine
****************************************************************************/
static NTSTATUS password_check(const char *username, const char *password,
const char *crypted, const char *salt)
{
BOOL ret;
#ifdef WITH_AFS
if (afs_auth(username, password))
return NT_STATUS_OK;
#endif /* WITH_AFS */
#ifdef WITH_DFS
if (dfs_auth(username, password))
return NT_STATUS_OK;
#endif /* WITH_DFS */
#ifdef OSF1_ENH_SEC
ret = (strcmp(osf1_bigcrypt(password, salt), crypted) == 0);
if (!ret) {
DEBUG(2,
("OSF1_ENH_SEC failed. Trying normal crypt.\n"));
ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
}
if (ret) {
return NT_STATUS_OK;
} else {
return NT_STATUS_WRONG_PASSWORD;
}
#endif /* OSF1_ENH_SEC */
#ifdef ULTRIX_AUTH
ret = (strcmp((char *)crypt16(password, salt), crypted) == 0);
if (ret) {
return NT_STATUS_OK;
} else {
return NT_STATUS_WRONG_PASSWORD;
}
#endif /* ULTRIX_AUTH */
#ifdef LINUX_BIGCRYPT
ret = (linux_bigcrypt(password, salt, crypted));
if (ret) {
return NT_STATUS_OK;
} else {
return NT_STATUS_WRONG_PASSWORD;
}
#endif /* LINUX_BIGCRYPT */
#if defined(HAVE_BIGCRYPT) && defined(HAVE_CRYPT) && defined(USE_BOTH_CRYPT_CALLS)
/*
* Some systems have bigcrypt in the C library but might not
* actually use it for the password hashes (HPUX 10.20) is
* a noteable example. So we try bigcrypt first, followed
* by crypt.
*/
if (strcmp(bigcrypt(password, salt), crypted) == 0)
return NT_STATUS_OK;
else
ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
if (ret) {
return NT_STATUS_OK;
} else {
return NT_STATUS_WRONG_PASSWORD;
}
#else /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
#ifdef HAVE_BIGCRYPT
ret = (strcmp(bigcrypt(password, salt), crypted) == 0);
if (ret) {
return NT_STATUS_OK;
} else {
return NT_STATUS_WRONG_PASSWORD;
}
#endif /* HAVE_BIGCRYPT */
#ifndef HAVE_CRYPT
DEBUG(1, ("Warning - no crypt available\n"));
return NT_STATUS_LOGON_FAILURE;
#else /* HAVE_CRYPT */
ret = (strcmp((char *)crypt(password, salt), crypted) == 0);
if (ret) {
return NT_STATUS_OK;
} else {
return NT_STATUS_WRONG_PASSWORD;
}
#endif /* HAVE_CRYPT */
#endif /* HAVE_BIGCRYPT && HAVE_CRYPT && USE_BOTH_CRYPT_CALLS */
}
static NTSTATUS check_unix_password(TALLOC_CTX *ctx, const struct auth_usersupplied_info *user_info, struct passwd **ret_passwd)
{
char *username;
char *password;
char *pwcopy;
char *salt;
char *crypted;
struct passwd *pws;
NTSTATUS nt_status;
int level = lp_passwordlevel();
*ret_passwd = NULL;
username = talloc_strdup(ctx, user_info->mapped.account_name);
password = talloc_strdup(ctx, user_info->password.plaintext);
nt_status = talloc_getpwnam(ctx, username, &pws);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
crypted = pws->pw_passwd;
salt = pws->pw_passwd;
#ifdef HAVE_GETSPNAM
{
struct spwd *spass;
/* many shadow systems require you to be root to get
the password, in most cases this should already be
the case when this function is called, except
perhaps for IPC password changing requests */
spass = getspnam(pws->pw_name);
if (spass && spass->sp_pwdp) {
crypted = talloc_strdup(ctx, spass->sp_pwdp);
NT_STATUS_HAVE_NO_MEMORY(crypted);
salt = talloc_strdup(ctx, spass->sp_pwdp);
NT_STATUS_HAVE_NO_MEMORY(salt);
}
}
#elif defined(IA_UINFO)
{
char *ia_password;
/* Need to get password with SVR4.2's ia_ functions
instead of get{sp,pw}ent functions. Required by
UnixWare 2.x, tested on version
2.1. (tangent@cyberport.com) */
uinfo_t uinfo;
if (ia_openinfo(pws->pw_name, &uinfo) != -1) {
ia_get_logpwd(uinfo, &ia_password);
crypted = talloc_strdup(ctx, ia_password);
NT_STATUS_HAVE_NO_MEMORY(crypted);
}
}
#endif
#ifdef HAVE_GETPRPWNAM
{
struct pr_passwd *pr_pw = getprpwnam(pws->pw_name);
if (pr_pw && pr_pw->ufld.fd_encrypt) {
crypted = talloc_strdup(ctx, pr_pw->ufld.fd_encrypt);
NT_STATUS_HAVE_NO_MEMORY(crypted);
}
}
#endif
#ifdef HAVE_GETPWANAM
{
struct passwd_adjunct *pwret;
pwret = getpwanam(s);
if (pwret && pwret->pwa_passwd) {
crypted = talloc_strdup(ctx, pwret->pwa_passwd);
NT_STATUS_HAVE_NO_MEMORY(crypted);
}
}
#endif
#ifdef OSF1_ENH_SEC
{
struct pr_passwd *mypasswd;
DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n", username));
mypasswd = getprpwnam(username);
if (mypasswd) {
username = talloc_strdup(ctx, mypasswd->ufld.fd_name);
NT_STATUS_HAVE_NO_MEMORY(username);
crypted = talloc_strdup(ctx, mypasswd->ufld.fd_encrypt);
NT_STATUS_HAVE_NO_MEMORY(crypted);
} else {
DEBUG(5,("OSF1_ENH_SEC: No entry for user %s in protected database !\n", username));
}
}
#endif
#ifdef ULTRIX_AUTH
{
AUTHORIZATION *ap = getauthuid(pws->pw_uid);
if (ap) {
crypted = talloc_strdup(ctx, ap->a_password);
endauthent();
NT_STATUS_HAVE_NO_MEMORY(crypted);
}
}
#endif
#if defined(HAVE_TRUNCATED_SALT)
/* crypt on some platforms (HPUX in particular)
won't work with more than 2 salt characters. */
salt[2] = 0;
#endif
if (crypted[0] == '\0') {
if (!lp_null_passwords()) {
DEBUG(2, ("Disallowing %s with null password\n", username));
return NT_STATUS_LOGON_FAILURE;
}
if (password == NULL) {
DEBUG(3, ("Allowing access to %s with null password\n", username));
*ret_passwd = pws;
return NT_STATUS_OK;
}
}
/* try it as it came to us */
nt_status = password_check(username, password, crypted, salt);
if (NT_STATUS_IS_OK(nt_status)) {
*ret_passwd = pws;
return nt_status;
}
else if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
/* No point continuing if its not the password thats to blame (ie PAM disabled). */
return nt_status;
}
if ( user_info->flags | USER_INFO_CASE_INSENSITIVE_PASSWORD) {
return nt_status;
}
/* if the password was given to us with mixed case then we don't
* need to proceed as we know it hasn't been case modified by the
* client */
if (strhasupper(password) && strhaslower(password)) {
return nt_status;
}
/* make a copy of it */
pwcopy = talloc_strdup(ctx, password);
if (!pwcopy)
return NT_STATUS_NO_MEMORY;
/* try all lowercase if it's currently all uppercase */
if (strhasupper(pwcopy)) {
strlower(pwcopy);
nt_status = password_check(username, pwcopy, crypted, salt);
if NT_STATUS_IS_OK(nt_status) {
*ret_passwd = pws;
return nt_status;
}
}
/* give up? */
if (level < 1) {
return NT_STATUS_WRONG_PASSWORD;
}
/* last chance - all combinations of up to level chars upper! */
strlower(pwcopy);
#if 0
if (NT_STATUS_IS_OK(nt_status = string_combinations(pwcopy, password_check, level))) {
*ret_passwd = pws;
return nt_status;
}
#endif
return NT_STATUS_WRONG_PASSWORD;
}
#endif
/** Check a plaintext username/password
*
**/
static NTSTATUS authunix_want_check(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info)
{
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
return NT_STATUS_NOT_IMPLEMENTED;
}
return NT_STATUS_OK;
}
static NTSTATUS authunix_check_password(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **server_info)
{
TALLOC_CTX *check_ctx;
NTSTATUS nt_status;
struct passwd *pwd;
if (user_info->password_state != AUTH_PASSWORD_PLAIN) {
return NT_STATUS_INVALID_PARAMETER;
}
check_ctx = talloc_named_const(mem_ctx, 0, "check_unix_password");
if (check_ctx == NULL) {
return NT_STATUS_NO_MEMORY;
}
nt_status = check_unix_password(check_ctx, user_info, &pwd);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(check_ctx);
return nt_status;
}
nt_status = authunix_make_server_info(mem_ctx, user_info, pwd, server_info);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(check_ctx);
return nt_status;
}
talloc_free(check_ctx);
return NT_STATUS_OK;
}
static const struct auth_operations unix_ops = {
.name = "unix",
.get_challenge = auth_get_challenge_not_implemented,
.want_check = authunix_want_check,
.check_password = authunix_check_password
};
NTSTATUS auth_unix_init(void)
{
NTSTATUS ret;
ret = auth_register(&unix_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register unix auth backend!\n"));
return ret;
}
return ret;
}
+669
View File
@@ -0,0 +1,669 @@
/*
Unix SMB/CIFS implementation.
Authentication utility functions
Copyright (C) Andrew Tridgell 1992-1998
Copyright (C) Andrew Bartlett 2001
Copyright (C) Jeremy Allison 2000-2001
Copyright (C) Rafal Szczesniak 2002
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/auth.h"
#include "libcli/security/security.h"
#include "libcli/auth/libcli_auth.h"
#include "dsdb/samdb/samdb.h"
#include "auth/credentials/credentials.h"
#include "auth/credentials/credentials_krb5.h"
/* this default function can be used by mostly all backends
* which don't want to set a challenge
*/
NTSTATUS auth_get_challenge_not_implemented(struct auth_method_context *ctx, TALLOC_CTX *mem_ctx, DATA_BLOB *challenge)
{
/* we don't want to set a challenge */
return NT_STATUS_NOT_IMPLEMENTED;
}
/****************************************************************************
Create an auth_usersupplied_data structure after appropriate mapping.
****************************************************************************/
NTSTATUS map_user_info(TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_usersupplied_info **user_info_mapped)
{
const char *domain;
char *account_name;
char *d;
DEBUG(5,("map_user_info: Mapping user [%s]\\[%s] from workstation [%s]\n",
user_info->client.domain_name, user_info->client.account_name, user_info->workstation_name));
account_name = talloc_strdup(mem_ctx, user_info->client.account_name);
if (!account_name) {
return NT_STATUS_NO_MEMORY;
}
/* don't allow "" as a domain, fixes a Win9X bug
where it doens't supply a domain for logon script
'net use' commands. */
/* Split user@realm names into user and realm components. This is TODO to fix with proper userprincipalname support */
if (user_info->client.domain_name && *user_info->client.domain_name) {
domain = user_info->client.domain_name;
} else if (strchr_m(user_info->client.account_name, '@')) {
d = strchr_m(account_name, '@');
if (!d) {
return NT_STATUS_INTERNAL_ERROR;
}
d[0] = '\0';
d++;
domain = d;
} else {
domain = lp_workgroup();
}
*user_info_mapped = talloc(mem_ctx, struct auth_usersupplied_info);
if (!*user_info_mapped) {
return NT_STATUS_NO_MEMORY;
}
talloc_reference(*user_info_mapped, user_info);
**user_info_mapped = *user_info;
(*user_info_mapped)->mapped_state = True;
(*user_info_mapped)->mapped.domain_name = talloc_strdup(*user_info_mapped, domain);
(*user_info_mapped)->mapped.account_name = talloc_strdup(*user_info_mapped, account_name);
talloc_free(account_name);
if (!(*user_info_mapped)->mapped.domain_name
|| !(*user_info_mapped)->mapped.account_name) {
return NT_STATUS_NO_MEMORY;
}
return NT_STATUS_OK;
}
/****************************************************************************
Create an auth_usersupplied_data structure after appropriate mapping.
****************************************************************************/
NTSTATUS encrypt_user_info(TALLOC_CTX *mem_ctx, struct auth_context *auth_context,
enum auth_password_state to_state,
const struct auth_usersupplied_info *user_info_in,
const struct auth_usersupplied_info **user_info_encrypted)
{
NTSTATUS nt_status;
struct auth_usersupplied_info *user_info_temp;
switch (to_state) {
case AUTH_PASSWORD_RESPONSE:
switch (user_info_in->password_state) {
case AUTH_PASSWORD_PLAIN:
{
const struct auth_usersupplied_info *user_info_temp2;
nt_status = encrypt_user_info(mem_ctx, auth_context,
AUTH_PASSWORD_HASH,
user_info_in, &user_info_temp2);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
user_info_in = user_info_temp2;
/* fall through */
}
case AUTH_PASSWORD_HASH:
{
const uint8_t *challenge;
DATA_BLOB chall_blob;
user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
if (!user_info_temp) {
return NT_STATUS_NO_MEMORY;
}
talloc_reference(user_info_temp, user_info_in);
*user_info_temp = *user_info_in;
user_info_temp->mapped_state = to_state;
nt_status = auth_get_challenge(auth_context, &challenge);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
chall_blob = data_blob_talloc(mem_ctx, challenge, 8);
if (lp_client_ntlmv2_auth()) {
DATA_BLOB names_blob = NTLMv2_generate_names_blob(mem_ctx, lp_netbios_name(), lp_workgroup());
DATA_BLOB lmv2_response, ntlmv2_response, lmv2_session_key, ntlmv2_session_key;
if (!SMBNTLMv2encrypt_hash(user_info_temp,
user_info_in->client.account_name,
user_info_in->client.domain_name,
user_info_in->password.hash.nt->hash, &chall_blob,
&names_blob,
&lmv2_response, &ntlmv2_response,
&lmv2_session_key, &ntlmv2_session_key)) {
data_blob_free(&names_blob);
return NT_STATUS_NO_MEMORY;
}
data_blob_free(&names_blob);
user_info_temp->password.response.lanman = lmv2_response;
user_info_temp->password.response.nt = ntlmv2_response;
data_blob_free(&lmv2_session_key);
data_blob_free(&ntlmv2_session_key);
} else {
DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24);
SMBOWFencrypt(user_info_in->password.hash.nt->hash, challenge, blob.data);
user_info_temp->password.response.nt = blob;
if (lp_client_lanman_auth() && user_info_in->password.hash.lanman) {
DATA_BLOB lm_blob = data_blob_talloc(mem_ctx, NULL, 24);
SMBOWFencrypt(user_info_in->password.hash.lanman->hash, challenge, blob.data);
user_info_temp->password.response.lanman = lm_blob;
} else {
/* if not sending the LM password, send the NT password twice */
user_info_temp->password.response.lanman = user_info_temp->password.response.nt;
}
}
user_info_in = user_info_temp;
/* fall through */
}
case AUTH_PASSWORD_RESPONSE:
*user_info_encrypted = user_info_in;
}
break;
case AUTH_PASSWORD_HASH:
{
switch (user_info_in->password_state) {
case AUTH_PASSWORD_PLAIN:
{
struct samr_Password lanman;
struct samr_Password nt;
user_info_temp = talloc(mem_ctx, struct auth_usersupplied_info);
if (!user_info_temp) {
return NT_STATUS_NO_MEMORY;
}
talloc_reference(user_info_temp, user_info_in);
*user_info_temp = *user_info_in;
user_info_temp->mapped_state = to_state;
if (E_deshash(user_info_in->password.plaintext, lanman.hash)) {
user_info_temp->password.hash.lanman = talloc(user_info_temp,
struct samr_Password);
*user_info_temp->password.hash.lanman = lanman;
} else {
user_info_temp->password.hash.lanman = NULL;
}
E_md4hash(user_info_in->password.plaintext, nt.hash);
user_info_temp->password.hash.nt = talloc(user_info_temp,
struct samr_Password);
*user_info_temp->password.hash.nt = nt;
user_info_in = user_info_temp;
/* fall through */
}
case AUTH_PASSWORD_HASH:
*user_info_encrypted = user_info_in;
break;
default:
return NT_STATUS_INVALID_PARAMETER;
break;
}
break;
}
default:
return NT_STATUS_INVALID_PARAMETER;
}
return NT_STATUS_OK;
}
/***************************************************************************
Make a server_info struct from the info3 returned by a domain logon
***************************************************************************/
NTSTATUS make_server_info_netlogon_validation(TALLOC_CTX *mem_ctx,
const char *account_name,
uint16_t validation_level,
union netr_Validation *validation,
struct auth_serversupplied_info **_server_info)
{
struct auth_serversupplied_info *server_info;
struct netr_SamBaseInfo *base = NULL;
int i;
switch (validation_level) {
case 2:
if (!validation || !validation->sam2) {
return NT_STATUS_INVALID_PARAMETER;
}
base = &validation->sam2->base;
break;
case 3:
if (!validation || !validation->sam3) {
return NT_STATUS_INVALID_PARAMETER;
}
base = &validation->sam3->base;
break;
case 6:
if (!validation || !validation->sam6) {
return NT_STATUS_INVALID_PARAMETER;
}
base = &validation->sam6->base;
break;
default:
return NT_STATUS_INVALID_LEVEL;
}
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
NT_STATUS_HAVE_NO_MEMORY(server_info);
/*
Here is where we should check the list of
trusted domains, and verify that the SID
matches.
*/
server_info->account_sid = dom_sid_add_rid(server_info, base->domain_sid, base->rid);
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
server_info->primary_group_sid = dom_sid_add_rid(server_info, base->domain_sid, base->primary_gid);
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
server_info->n_domain_groups = base->groups.count;
if (base->groups.count) {
server_info->domain_groups = talloc_array(server_info, struct dom_sid*, base->groups.count);
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_groups);
} else {
server_info->domain_groups = NULL;
}
for (i = 0; i < base->groups.count; i++) {
server_info->domain_groups[i] = dom_sid_add_rid(server_info, base->domain_sid, base->groups.rids[i].rid);
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_groups[i]);
}
/* Copy 'other' sids. We need to do sid filtering here to
prevent possible elevation of privileges. See:
http://www.microsoft.com/windows2000/techinfo/administration/security/sidfilter.asp
*/
if (validation_level == 3) {
struct dom_sid **dgrps = server_info->domain_groups;
size_t sidcount = server_info->n_domain_groups + validation->sam3->sidcount;
size_t n_dgrps = server_info->n_domain_groups;
if (validation->sam3->sidcount > 0) {
dgrps = talloc_realloc(server_info, dgrps, struct dom_sid*, sidcount);
NT_STATUS_HAVE_NO_MEMORY(dgrps);
for (i = 0; i < validation->sam3->sidcount; i++) {
dgrps[n_dgrps + i] = talloc_reference(dgrps, validation->sam3->sids[i].sid);
}
}
server_info->n_domain_groups = sidcount;
server_info->domain_groups = dgrps;
/* Where are the 'global' sids?... */
}
if (base->account_name.string) {
server_info->account_name = talloc_reference(server_info, base->account_name.string);
} else {
server_info->account_name = talloc_strdup(server_info, account_name);
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
}
server_info->domain_name = talloc_reference(server_info, base->domain.string);
server_info->full_name = talloc_reference(server_info, base->full_name.string);
server_info->logon_script = talloc_reference(server_info, base->logon_script.string);
server_info->profile_path = talloc_reference(server_info, base->profile_path.string);
server_info->home_directory = talloc_reference(server_info, base->home_directory.string);
server_info->home_drive = talloc_reference(server_info, base->home_drive.string);
server_info->logon_server = talloc_reference(server_info, base->logon_server.string);
server_info->last_logon = base->last_logon;
server_info->last_logoff = base->last_logoff;
server_info->acct_expiry = base->acct_expiry;
server_info->last_password_change = base->last_password_change;
server_info->allow_password_change = base->allow_password_change;
server_info->force_password_change = base->force_password_change;
server_info->logon_count = base->logon_count;
server_info->bad_password_count = base->bad_password_count;
server_info->acct_flags = base->acct_flags;
server_info->authenticated = True;
/* ensure we are never given NULL session keys */
if (all_zero(base->key.key, sizeof(base->key.key))) {
server_info->user_session_key = data_blob(NULL, 0);
} else {
server_info->user_session_key = data_blob_talloc(server_info, base->key.key, sizeof(base->key.key));
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
}
if (all_zero(base->LMSessKey.key, sizeof(base->LMSessKey.key))) {
server_info->lm_session_key = data_blob(NULL, 0);
} else {
server_info->lm_session_key = data_blob_talloc(server_info, base->LMSessKey.key, sizeof(base->LMSessKey.key));
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
}
*_server_info = server_info;
return NT_STATUS_OK;
}
NTSTATUS auth_anonymous_server_info(TALLOC_CTX *mem_ctx, struct auth_serversupplied_info **_server_info)
{
struct auth_serversupplied_info *server_info;
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
NT_STATUS_HAVE_NO_MEMORY(server_info);
server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_ANONYMOUS);
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
/* is this correct? */
server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_GUESTS);
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
server_info->n_domain_groups = 0;
server_info->domain_groups = NULL;
/* annoying, but the Anonymous really does have a session key,
and it is all zeros! */
server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
data_blob_clear(&server_info->user_session_key);
data_blob_clear(&server_info->lm_session_key);
server_info->account_name = talloc_strdup(server_info, "ANONYMOUS LOGON");
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
server_info->full_name = talloc_strdup(server_info, "Anonymous Logon");
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
server_info->logon_script = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
server_info->profile_path = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
server_info->home_directory = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
server_info->home_drive = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
server_info->last_logon = 0;
server_info->last_logoff = 0;
server_info->acct_expiry = 0;
server_info->last_password_change = 0;
server_info->allow_password_change = 0;
server_info->force_password_change = 0;
server_info->logon_count = 0;
server_info->bad_password_count = 0;
server_info->acct_flags = ACB_NORMAL;
server_info->authenticated = False;
*_server_info = server_info;
return NT_STATUS_OK;
}
NTSTATUS auth_system_server_info(TALLOC_CTX *mem_ctx, struct auth_serversupplied_info **_server_info)
{
struct auth_serversupplied_info *server_info;
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
NT_STATUS_HAVE_NO_MEMORY(server_info);
server_info->account_sid = dom_sid_parse_talloc(server_info, SID_NT_SYSTEM);
NT_STATUS_HAVE_NO_MEMORY(server_info->account_sid);
/* is this correct? */
server_info->primary_group_sid = dom_sid_parse_talloc(server_info, SID_BUILTIN_ADMINISTRATORS);
NT_STATUS_HAVE_NO_MEMORY(server_info->primary_group_sid);
server_info->n_domain_groups = 0;
server_info->domain_groups = NULL;
/* annoying, but the Anonymous really does have a session key,
and it is all zeros! */
server_info->user_session_key = data_blob_talloc(server_info, NULL, 16);
NT_STATUS_HAVE_NO_MEMORY(server_info->user_session_key.data);
server_info->lm_session_key = data_blob_talloc(server_info, NULL, 16);
NT_STATUS_HAVE_NO_MEMORY(server_info->lm_session_key.data);
data_blob_clear(&server_info->user_session_key);
data_blob_clear(&server_info->lm_session_key);
server_info->account_name = talloc_strdup(server_info, "SYSTEM");
NT_STATUS_HAVE_NO_MEMORY(server_info->account_name);
server_info->domain_name = talloc_strdup(server_info, "NT AUTHORITY");
NT_STATUS_HAVE_NO_MEMORY(server_info->domain_name);
server_info->full_name = talloc_strdup(server_info, "System");
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
server_info->logon_script = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
server_info->profile_path = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
server_info->home_directory = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
server_info->home_drive = talloc_strdup(server_info, "");
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
server_info->last_logon = 0;
server_info->last_logoff = 0;
server_info->acct_expiry = 0;
server_info->last_password_change = 0;
server_info->allow_password_change = 0;
server_info->force_password_change = 0;
server_info->logon_count = 0;
server_info->bad_password_count = 0;
server_info->acct_flags = ACB_NORMAL;
server_info->authenticated = True;
*_server_info = server_info;
return NT_STATUS_OK;
}
NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx,
struct auth_serversupplied_info *server_info,
struct auth_session_info **_session_info)
{
struct auth_session_info *session_info;
NTSTATUS nt_status;
session_info = talloc(mem_ctx, struct auth_session_info);
NT_STATUS_HAVE_NO_MEMORY(session_info);
session_info->server_info = talloc_reference(session_info, server_info);
/* unless set otherwise, the session key is the user session
* key from the auth subsystem */
session_info->session_key = server_info->user_session_key;
nt_status = security_token_create(session_info,
server_info->account_sid,
server_info->primary_group_sid,
server_info->n_domain_groups,
server_info->domain_groups,
server_info->authenticated,
&session_info->security_token);
NT_STATUS_NOT_OK_RETURN(nt_status);
session_info->credentials = NULL;
*_session_info = session_info;
return NT_STATUS_OK;
}
NTSTATUS auth_anonymous_session_info(TALLOC_CTX *parent_ctx,
struct auth_session_info **_session_info)
{
NTSTATUS nt_status;
struct auth_serversupplied_info *server_info = NULL;
struct auth_session_info *session_info = NULL;
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
nt_status = auth_anonymous_server_info(mem_ctx,
&server_info);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
/* references the server_info into the session_info */
nt_status = auth_generate_session_info(parent_ctx, server_info, &session_info);
talloc_free(mem_ctx);
NT_STATUS_NOT_OK_RETURN(nt_status);
session_info->credentials = cli_credentials_init(session_info);
if (!session_info->credentials) {
return NT_STATUS_NO_MEMORY;
}
cli_credentials_set_conf(session_info->credentials);
cli_credentials_set_anonymous(session_info->credentials);
*_session_info = session_info;
return NT_STATUS_OK;
}
struct auth_session_info *anonymous_session(TALLOC_CTX *mem_ctx)
{
NTSTATUS nt_status;
struct auth_session_info *session_info = NULL;
nt_status = auth_anonymous_session_info(mem_ctx, &session_info);
if (!NT_STATUS_IS_OK(nt_status)) {
return NULL;
}
return session_info;
}
NTSTATUS auth_system_session_info(TALLOC_CTX *parent_ctx,
struct auth_session_info **_session_info)
{
NTSTATUS nt_status;
struct auth_serversupplied_info *server_info = NULL;
struct auth_session_info *session_info = NULL;
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
nt_status = auth_system_server_info(mem_ctx,
&server_info);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
/* references the server_info into the session_info */
nt_status = auth_generate_session_info(parent_ctx, server_info, &session_info);
talloc_free(mem_ctx);
NT_STATUS_NOT_OK_RETURN(nt_status);
session_info->credentials = cli_credentials_init(session_info);
if (!session_info->credentials) {
return NT_STATUS_NO_MEMORY;
}
cli_credentials_set_conf(session_info->credentials);
if (lp_parm_bool(-1,"system","anonymous", False)) {
cli_credentials_set_anonymous(session_info->credentials);
} else {
cli_credentials_set_machine_account_pending(session_info->credentials);
}
*_session_info = session_info;
return NT_STATUS_OK;
}
_PUBLIC_ struct auth_session_info *system_session(TALLOC_CTX *mem_ctx)
{
NTSTATUS nt_status;
struct auth_session_info *session_info = NULL;
nt_status = auth_system_session_info(mem_ctx, &session_info);
if (!NT_STATUS_IS_OK(nt_status)) {
return NULL;
}
return session_info;
}
/****************************************************************************
prints a struct auth_session_info security token to debug output.
****************************************************************************/
void auth_session_info_debug(int dbg_lev,
const struct auth_session_info *session_info)
{
if (!session_info) {
DEBUG(dbg_lev, ("Session Info: (NULL)\n"));
return;
}
security_token_debug(dbg_lev, session_info->security_token);
}
/**
* Squash an NT_STATUS in line with security requirements.
* In an attempt to avoid giving the whole game away when users
* are authenticating, NT replaces both NT_STATUS_NO_SUCH_USER and
* NT_STATUS_WRONG_PASSWORD with NT_STATUS_LOGON_FAILURE in certain situations
* (session setups in particular).
*
* @param nt_status NTSTATUS input for squashing.
* @return the 'squashed' nt_status
**/
NTSTATUS auth_nt_status_squash(NTSTATUS nt_status)
{
if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) {
/* Match WinXP and don't give the game away */
return NT_STATUS_LOGON_FAILURE;
} else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) {
/* Match WinXP and don't give the game away */
return NT_STATUS_LOGON_FAILURE;
}
return nt_status;
}
+275
View File
@@ -0,0 +1,275 @@
/*
Unix SMB/CIFS implementation.
Winbind authentication mechnism
Copyright (C) Tim Potter 2000
Copyright (C) Andrew Bartlett 2001 - 2002
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/auth.h"
#include "nsswitch/winbind_client.h"
#include "librpc/gen_ndr/ndr_netlogon.h"
#include "librpc/gen_ndr/ndr_winbind.h"
#include "lib/messaging/irpc.h"
static NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct winbindd_response *response, struct netr_SamInfo3 *info3)
{
size_t len = response->length - sizeof(struct winbindd_response);
if (len > 4) {
NTSTATUS status;
DATA_BLOB blob;
blob.length = len - 4;
blob.data = (uint8_t *)(((char *)response->extra_data) + 4);
status = ndr_pull_struct_blob(&blob, mem_ctx, info3,
(ndr_pull_flags_fn_t)ndr_pull_netr_SamInfo3);
return status;
} else {
DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
return NT_STATUS_UNSUCCESSFUL;
}
}
static NTSTATUS winbind_want_check(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info)
{
if (!user_info->mapped.account_name || !*user_info->mapped.account_name) {
return NT_STATUS_NOT_IMPLEMENTED;
}
/* TODO: maybe limit the user scope to remote users only */
return NT_STATUS_OK;
}
/*
Authenticate a user with a challenge/response
using the samba3 winbind protocol
*/
static NTSTATUS winbind_check_password_samba3(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **server_info)
{
struct winbindd_request request;
struct winbindd_response response;
NSS_STATUS result;
NTSTATUS nt_status;
struct netr_SamInfo3 info3;
/* Send off request */
const struct auth_usersupplied_info *user_info_temp;
nt_status = encrypt_user_info(mem_ctx, ctx->auth_ctx,
AUTH_PASSWORD_RESPONSE,
user_info, &user_info_temp);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
user_info = user_info_temp;
ZERO_STRUCT(request);
ZERO_STRUCT(response);
request.flags = WBFLAG_PAM_INFO3_NDR;
request.data.auth_crap.logon_parameters = user_info->logon_parameters;
winbind_strcpy(request.data.auth_crap.user,
user_info->client.account_name);
winbind_strcpy(request.data.auth_crap.domain,
user_info->client.domain_name);
winbind_strcpy(request.data.auth_crap.workstation,
user_info->workstation_name);
memcpy(request.data.auth_crap.chal, ctx->auth_ctx->challenge.data.data, sizeof(request.data.auth_crap.chal));
request.data.auth_crap.lm_resp_len = MIN(user_info->password.response.lanman.length,
sizeof(request.data.auth_crap.lm_resp));
request.data.auth_crap.nt_resp_len = MIN(user_info->password.response.nt.length,
sizeof(request.data.auth_crap.nt_resp));
memcpy(request.data.auth_crap.lm_resp, user_info->password.response.lanman.data,
request.data.auth_crap.lm_resp_len);
memcpy(request.data.auth_crap.nt_resp, user_info->password.response.nt.data,
request.data.auth_crap.nt_resp_len);
result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
nt_status = NT_STATUS(response.data.auth.nt_status);
NT_STATUS_NOT_OK_RETURN(nt_status);
if (result == NSS_STATUS_SUCCESS && response.extra_data) {
union netr_Validation validation;
nt_status = get_info3_from_ndr(mem_ctx, &response, &info3);
SAFE_FREE(response.extra_data);
NT_STATUS_NOT_OK_RETURN(nt_status);
validation.sam3 = &info3;
nt_status = make_server_info_netlogon_validation(mem_ctx,
user_info->client.account_name,
3, &validation,
server_info);
return nt_status;
} else if (result == NSS_STATUS_SUCCESS && !response.extra_data) {
DEBUG(0, ("Winbindd authenticated the user [%s]\\[%s], "
"but did not include the required info3 reply!\n",
user_info->client.domain_name, user_info->client.account_name));
return NT_STATUS_INSUFFICIENT_LOGON_INFO;
} else if (NT_STATUS_IS_OK(nt_status)) {
DEBUG(1, ("Winbindd authentication for [%s]\\[%s] failed, "
"but no error code is available!\n",
user_info->client.domain_name, user_info->client.account_name));
return NT_STATUS_NO_LOGON_SERVERS;
}
return nt_status;
}
struct winbind_check_password_state {
struct winbind_SamLogon req;
};
/*
Authenticate a user with a challenge/response
using IRPC to the winbind task
*/
static NTSTATUS winbind_check_password(struct auth_method_context *ctx,
TALLOC_CTX *mem_ctx,
const struct auth_usersupplied_info *user_info,
struct auth_serversupplied_info **server_info)
{
NTSTATUS status;
uint32_t *winbind_servers;
struct winbind_check_password_state *s;
const struct auth_usersupplied_info *user_info_new;
struct netr_IdentityInfo *identity_info;
winbind_servers = irpc_servers_byname(ctx->auth_ctx->msg_ctx, "winbind_server");
if ((winbind_servers == NULL) || (winbind_servers[0] == 0)) {
DEBUG(0, ("Winbind authentication for [%s]\\[%s] failed, "
"no winbind_server running!\n",
user_info->client.domain_name, user_info->client.account_name));
return NT_STATUS_NO_LOGON_SERVERS;
}
s = talloc(mem_ctx, struct winbind_check_password_state);
NT_STATUS_HAVE_NO_MEMORY(s);
if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) {
struct netr_PasswordInfo *password_info;
status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_HASH,
user_info, &user_info_new);
NT_STATUS_NOT_OK_RETURN(status);
user_info = user_info_new;
password_info = talloc(s, struct netr_PasswordInfo);
NT_STATUS_HAVE_NO_MEMORY(password_info);
password_info->lmpassword = *user_info->password.hash.lanman;
password_info->ntpassword = *user_info->password.hash.nt;
identity_info = &password_info->identity_info;
s->req.in.logon_level = 1;
s->req.in.logon.password= password_info;
} else {
struct netr_NetworkInfo *network_info;
const uint8_t *challenge;
status = encrypt_user_info(s, ctx->auth_ctx, AUTH_PASSWORD_RESPONSE,
user_info, &user_info_new);
NT_STATUS_NOT_OK_RETURN(status);
user_info = user_info_new;
network_info = talloc(s, struct netr_NetworkInfo);
NT_STATUS_HAVE_NO_MEMORY(network_info);
status = auth_get_challenge(ctx->auth_ctx, &challenge);
NT_STATUS_NOT_OK_RETURN(status);
memcpy(network_info->challenge, challenge, sizeof(network_info->challenge));
network_info->nt.length = user_info->password.response.nt.length;
network_info->nt.data = user_info->password.response.nt.data;
network_info->nt.length = user_info->password.response.lanman.length;
network_info->nt.data = user_info->password.response.lanman.data;
identity_info = &network_info->identity_info;
s->req.in.logon_level = 2;
s->req.in.logon.network = network_info;
}
identity_info->domain_name.string = user_info->client.domain_name;
identity_info->parameter_control = user_info->logon_parameters; /* see MSV1_0_* */
identity_info->logon_id_low = 0;
identity_info->logon_id_high = 0;
identity_info->account_name.string = user_info->client.account_name;
identity_info->workstation.string = user_info->workstation_name;
s->req.in.validation_level = 3;
status = IRPC_CALL(ctx->auth_ctx->msg_ctx, winbind_servers[0],
winbind, WINBIND_SAMLOGON,
&s->req, s);
NT_STATUS_NOT_OK_RETURN(status);
status = make_server_info_netlogon_validation(mem_ctx,
user_info->client.account_name,
s->req.in.validation_level,
&s->req.out.validation,
server_info);
NT_STATUS_NOT_OK_RETURN(status);
return NT_STATUS_OK;
}
static const struct auth_operations winbind_samba3_ops = {
.name = "winbind_samba3",
.get_challenge = auth_get_challenge_not_implemented,
.want_check = winbind_want_check,
.check_password = winbind_check_password_samba3
};
static const struct auth_operations winbind_ops = {
.name = "winbind",
.get_challenge = auth_get_challenge_not_implemented,
.want_check = winbind_want_check,
.check_password = winbind_check_password
};
NTSTATUS auth_winbind_init(void)
{
NTSTATUS ret;
ret = auth_register(&winbind_samba3_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register 'winbind_samba3' auth backend!\n"));
return ret;
}
ret = auth_register(&winbind_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register 'winbind' auth backend!\n"));
return ret;
}
return NT_STATUS_OK;
}
+30
View File
@@ -0,0 +1,30 @@
###############################
# start SMB_EXT_LIB_PAM
# check for security/pam_appl.h and -lpam
AC_CHECK_HEADERS(security/pam_appl.h)
AC_CHECK_LIB_EXT(pam, PAM_LIBS, pam_start)
if test x"$ac_cv_header_security_pam_appl_h" = x"yes" -a x"$ac_cv_lib_ext_pam_pam_start" = x"yes";then
SMB_ENABLE(PAM,YES)
fi
SMB_EXT_LIB(PAM, $PAM_LIBS)
# end SMB_EXT_LIB_PAM
###############################
################################################
# test for where we get crypt() from
AC_CHECK_LIB_EXT(crypt, CRYPT_LIBS, crypt)
SMB_ENABLE(CRYPT,YES)
SMB_EXT_LIB(CRYPT, $CRYPT_LIBS)
AC_CHECK_FUNCS(crypt16 getauthuid getpwanam)
AC_CHECK_HEADERS(sasl/sasl.h)
AC_CHECK_LIB_EXT(sasl2, SASL_LIBS, sasl_client_init)
SMB_EXT_LIB(SASL, $SASL_LIBS)
if test x"$ac_cv_header_sasl_sasl_h" = x"yes" -a x"$ac_cv_lib_ext_sasl2_sasl_client_init" = x"yes";then
SMB_ENABLE(SASL,YES)
SMB_ENABLE(cyrus_sasl,YES)
else
SMB_ENABLE(cyrus_sasl,NO)
fi
+79
View File
@@ -0,0 +1,79 @@
# auth server subsystem
include gensec/config.mk
include kerberos/config.mk
include ntlmssp/config.mk
include credentials/config.mk
[SUBSYSTEM::auth_sam]
PRIVATE_PROTO_HEADER = auth_sam.h
OBJ_FILES = sam.o auth_sam_reply.o ntlm_check.o
PUBLIC_DEPENDENCIES = SAMDB
#######################
# Start MODULE auth_sam
[MODULE::auth_sam_module]
# gensec_krb5 and gensec_gssapi depend on it
INIT_FUNCTION = auth_sam_init
SUBSYSTEM = auth
OBJ_FILES = auth_sam.o
PUBLIC_DEPENDENCIES = \
SAMDB auth_sam
# End MODULE auth_sam
#######################
#######################
# Start MODULE auth_anonymous
[MODULE::auth_anonymous]
INIT_FUNCTION = auth_anonymous_init
SUBSYSTEM = auth
OBJ_FILES = auth_anonymous.o
# End MODULE auth_anonymous
#######################
#######################
# Start MODULE auth_winbind
[MODULE::auth_winbind]
INIT_FUNCTION = auth_winbind_init
SUBSYSTEM = auth
OBJ_FILES = auth_winbind.o
PUBLIC_DEPENDENCIES = NDR_WINBIND MESSAGING LIBWINBIND-CLIENT
# End MODULE auth_winbind
#######################
#######################
# Start MODULE auth_developer
[MODULE::auth_developer]
INIT_FUNCTION = auth_developer_init
SUBSYSTEM = auth
OBJ_FILES = auth_developer.o
# End MODULE auth_developer
#######################
#######################
# Start MODULE auth_unix
[MODULE::auth_unix]
INIT_FUNCTION = auth_unix_init
SUBSYSTEM = auth
OBJ_FILES = auth_unix.o
PUBLIC_DEPENDENCIES = CRYPT PAM PAM_ERRORS
# End MODULE auth_unix
#######################
[SUBSYSTEM::PAM_ERRORS]
PRIVATE_PROTO_HEADER = pam_errors.h
OBJ_FILES = pam_errors.o
#######################
# Start SUBSYSTEM auth
[SUBSYSTEM::auth]
#VERSION = 0.0.1
#SO_VERSION = 0
PUBLIC_HEADERS = auth.h
PUBLIC_PROTO_HEADER = auth_proto.h
OBJ_FILES = \
auth.o \
auth_util.o \
auth_simple.o
PUBLIC_DEPENDENCIES = LIBSECURITY SAMDB CREDENTIALS
# End SUBSYSTEM auth
#######################
+24
View File
@@ -0,0 +1,24 @@
#################################
# Start SUBSYSTEM CREDENTIALS
[SUBSYSTEM::CREDENTIALS]
PUBLIC_PROTO_HEADER = credentials_proto.h
PUBLIC_HEADERS = credentials.h
OBJ_FILES = credentials.o \
credentials_files.o \
credentials_ntlm.o
PUBLIC_DEPENDENCIES = \
LIBCLI_AUTH SECRETS LIBCRYPTO KERBEROS
PRIVATE_DEPENDENCIES = CREDENTIALS_KRB5
# End SUBSYSTEM CREDENTIALS
#################################
#################################
# Start SUBSYSTEM CREDENTIALS
[SUBSYSTEM::CREDENTIALS_KRB5]
PUBLIC_PROTO_HEADER = credentials_krb5_proto.h
PUBLIC_HEADERS = credentials_krb5.h
OBJ_FILES = credentials_krb5.o
PUBLIC_DEPENDENCIES = \
HEIMDAL_GSSAPI
# End SUBSYSTEM CREDENTIALS
#################################
+717
View File
@@ -0,0 +1,717 @@
/*
Unix SMB/CIFS implementation.
User credentials handling
Copyright (C) Jelmer Vernooij 2005
Copyright (C) Tim Potter 2001
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
#include "auth/credentials/credentials.h"
#include "auth/credentials/credentials_krb5.h"
#include "libcli/auth/libcli_auth.h"
/**
* Create a new credentials structure
* @param mem_ctx TALLOC_CTX parent for credentials structure
*/
struct cli_credentials *cli_credentials_init(TALLOC_CTX *mem_ctx)
{
struct cli_credentials *cred = talloc(mem_ctx, struct cli_credentials);
if (!cred) {
return cred;
}
cred->netlogon_creds = NULL;
cred->machine_account_pending = False;
cred->workstation_obtained = CRED_UNINITIALISED;
cred->username_obtained = CRED_UNINITIALISED;
cred->password_obtained = CRED_UNINITIALISED;
cred->domain_obtained = CRED_UNINITIALISED;
cred->realm_obtained = CRED_UNINITIALISED;
cred->ccache_obtained = CRED_UNINITIALISED;
cred->client_gss_creds_obtained = CRED_UNINITIALISED;
cred->server_gss_creds_obtained = CRED_UNINITIALISED;
cred->keytab_obtained = CRED_UNINITIALISED;
cred->principal_obtained = CRED_UNINITIALISED;
cred->old_password = NULL;
cred->smb_krb5_context = NULL;
cred->salt_principal = NULL;
cred->machine_account = False;
cred->bind_dn = NULL;
cred->tries = 3;
cred->callback_running = False;
cli_credentials_set_kerberos_state(cred, CRED_AUTO_USE_KERBEROS);
return cred;
}
void cli_credentials_set_kerberos_state(struct cli_credentials *creds,
enum credentials_use_kerberos use_kerberos)
{
creds->use_kerberos = use_kerberos;
}
enum credentials_use_kerberos cli_credentials_get_kerberos_state(struct cli_credentials *creds)
{
return creds->use_kerberos;
}
/**
* Obtain the username for this credentials context.
* @param cred credentials context
* @retval The username set on this context.
* @note Return value will never be NULL except by programmer error.
*/
const char *cli_credentials_get_username(struct cli_credentials *cred)
{
if (cred->machine_account_pending) {
cli_credentials_set_machine_account(cred);
}
if (cred->username_obtained == CRED_CALLBACK &&
!cred->callback_running) {
cred->callback_running = True;
cred->username = cred->username_cb(cred);
cred->callback_running = False;
cred->username_obtained = CRED_SPECIFIED;
}
return cred->username;
}
BOOL cli_credentials_set_username(struct cli_credentials *cred,
const char *val, enum credentials_obtained obtained)
{
if (obtained >= cred->username_obtained) {
cred->username = talloc_strdup(cred, val);
cred->username_obtained = obtained;
return True;
}
return False;
}
BOOL cli_credentials_set_username_callback(struct cli_credentials *cred,
const char *(*username_cb) (struct cli_credentials *))
{
if (cred->username_obtained < CRED_CALLBACK) {
cred->username_cb = username_cb;
cred->username_obtained = CRED_CALLBACK;
return True;
}
return False;
}
BOOL cli_credentials_set_bind_dn(struct cli_credentials *cred,
const char *bind_dn)
{
cred->bind_dn = talloc_strdup(cred, bind_dn);
return True;
}
/**
* Obtain the BIND DN for this credentials context.
* @param cred credentials context
* @retval The username set on this context.
* @note Return value will be NULL if not specified explictly
*/
const char *cli_credentials_get_bind_dn(struct cli_credentials *cred)
{
return cred->bind_dn;
}
/**
* Obtain the client principal for this credentials context.
* @param cred credentials context
* @retval The username set on this context.
* @note Return value will never be NULL except by programmer error.
*/
const char *cli_credentials_get_principal(struct cli_credentials *cred, TALLOC_CTX *mem_ctx)
{
if (cred->machine_account_pending) {
cli_credentials_set_machine_account(cred);
}
if (cred->principal_obtained == CRED_CALLBACK &&
!cred->callback_running) {
cred->callback_running = True;
cred->principal = cred->principal_cb(cred);
cred->callback_running = False;
cred->principal_obtained = CRED_SPECIFIED;
}
if (cred->principal_obtained < cred->username_obtained) {
if (cred->domain_obtained > cred->realm_obtained) {
return talloc_asprintf(mem_ctx, "%s@%s",
cli_credentials_get_username(cred),
cli_credentials_get_domain(cred));
} else {
return talloc_asprintf(mem_ctx, "%s@%s",
cli_credentials_get_username(cred),
cli_credentials_get_realm(cred));
}
}
return talloc_reference(mem_ctx, cred->principal);
}
BOOL cli_credentials_set_principal(struct cli_credentials *cred,
const char *val,
enum credentials_obtained obtained)
{
if (obtained >= cred->principal_obtained) {
cred->principal = talloc_strdup(cred, val);
cred->principal_obtained = obtained;
return True;
}
return False;
}
/* Set a callback to get the principal. This could be a popup dialog,
* a terminal prompt or similar. */
BOOL cli_credentials_set_principal_callback(struct cli_credentials *cred,
const char *(*principal_cb) (struct cli_credentials *))
{
if (cred->principal_obtained < CRED_CALLBACK) {
cred->principal_cb = principal_cb;
cred->principal_obtained = CRED_CALLBACK;
return True;
}
return False;
}
/* Some of our tools are 'anonymous by default'. This is a single
* function to determine if authentication has been explicitly
* requested */
BOOL cli_credentials_authentication_requested(struct cli_credentials *cred)
{
if (cred->bind_dn) {
return True;
}
if (cli_credentials_is_anonymous(cred)){
return False;
}
if (cred->principal_obtained >= CRED_SPECIFIED) {
return True;
}
if (cred->username_obtained >= CRED_SPECIFIED) {
return True;
}
return False;
}
/**
* Obtain the password for this credentials context.
* @param cred credentials context
* @retval If set, the cleartext password, otherwise NULL
*/
const char *cli_credentials_get_password(struct cli_credentials *cred)
{
if (cred->machine_account_pending) {
cli_credentials_set_machine_account(cred);
}
if (cred->password_obtained == CRED_CALLBACK &&
!cred->callback_running) {
cred->callback_running = True;
cred->password = cred->password_cb(cred);
cred->callback_running = False;
cred->password_obtained = CRED_CALLBACK_RESULT;
}
return cred->password;
}
/* Set a password on the credentials context, including an indication
* of 'how' the password was obtained */
BOOL cli_credentials_set_password(struct cli_credentials *cred,
const char *val,
enum credentials_obtained obtained)
{
if (obtained >= cred->password_obtained) {
cred->password = talloc_strdup(cred, val);
cred->password_obtained = obtained;
cred->nt_hash = NULL;
return True;
}
return False;
}
BOOL cli_credentials_set_password_callback(struct cli_credentials *cred,
const char *(*password_cb) (struct cli_credentials *))
{
if (cred->password_obtained < CRED_CALLBACK) {
cred->password_cb = password_cb;
cred->password_obtained = CRED_CALLBACK;
return True;
}
return False;
}
/**
* Obtain the 'old' password for this credentials context (used for join accounts).
* @param cred credentials context
* @retval If set, the cleartext password, otherwise NULL
*/
const char *cli_credentials_get_old_password(struct cli_credentials *cred)
{
if (cred->machine_account_pending) {
cli_credentials_set_machine_account(cred);
}
return cred->old_password;
}
BOOL cli_credentials_set_old_password(struct cli_credentials *cred,
const char *val,
enum credentials_obtained obtained)
{
cred->old_password = talloc_strdup(cred, val);
return True;
}
/**
* Obtain the password, in the form MD4(unicode(password)) for this credentials context.
*
* Sometimes we only have this much of the password, while the rest of
* the time this call avoids calling E_md4hash themselves.
*
* @param cred credentials context
* @retval If set, the cleartext password, otherwise NULL
*/
const struct samr_Password *cli_credentials_get_nt_hash(struct cli_credentials *cred,
TALLOC_CTX *mem_ctx)
{
const char *password = cli_credentials_get_password(cred);
if (password) {
struct samr_Password *nt_hash = talloc(mem_ctx, struct samr_Password);
if (!nt_hash) {
return NULL;
}
E_md4hash(password, nt_hash->hash);
return nt_hash;
} else {
return cred->nt_hash;
}
}
BOOL cli_credentials_set_nt_hash(struct cli_credentials *cred,
const struct samr_Password *nt_hash,
enum credentials_obtained obtained)
{
if (obtained >= cred->password_obtained) {
cli_credentials_set_password(cred, NULL, obtained);
cred->nt_hash = talloc(cred, struct samr_Password);
*cred->nt_hash = *nt_hash;
return True;
}
return False;
}
/**
* Obtain the 'short' or 'NetBIOS' domain for this credentials context.
* @param cred credentials context
* @retval The domain set on this context.
* @note Return value will never be NULL except by programmer error.
*/
const char *cli_credentials_get_domain(struct cli_credentials *cred)
{
if (cred->machine_account_pending) {
cli_credentials_set_machine_account(cred);
}
if (cred->domain_obtained == CRED_CALLBACK &&
!cred->callback_running) {
cred->callback_running = True;
cred->domain = cred->domain_cb(cred);
cred->callback_running = False;
cred->domain_obtained = CRED_SPECIFIED;
}
return cred->domain;
}
BOOL cli_credentials_set_domain(struct cli_credentials *cred,
const char *val,
enum credentials_obtained obtained)
{
if (obtained >= cred->domain_obtained) {
/* it is important that the domain be in upper case,
* particularly for the sensitive NTLMv2
* calculations */
cred->domain = strupper_talloc(cred, val);
cred->domain_obtained = obtained;
return True;
}
return False;
}
BOOL cli_credentials_set_domain_callback(struct cli_credentials *cred,
const char *(*domain_cb) (struct cli_credentials *))
{
if (cred->domain_obtained < CRED_CALLBACK) {
cred->domain_cb = domain_cb;
cred->domain_obtained = CRED_CALLBACK;
return True;
}
return False;
}
/**
* Obtain the Kerberos realm for this credentials context.
* @param cred credentials context
* @retval The realm set on this context.
* @note Return value will never be NULL except by programmer error.
*/
const char *cli_credentials_get_realm(struct cli_credentials *cred)
{
if (cred->machine_account_pending) {
cli_credentials_set_machine_account(cred);
}
if (cred->realm_obtained == CRED_CALLBACK &&
!cred->callback_running) {
cred->callback_running = True;
cred->realm = cred->realm_cb(cred);
cred->callback_running = False;
cred->realm_obtained = CRED_SPECIFIED;
}
return cred->realm;
}
/**
* Set the realm for this credentials context, and force it to
* uppercase for the sainity of our local kerberos libraries
*/
BOOL cli_credentials_set_realm(struct cli_credentials *cred,
const char *val,
enum credentials_obtained obtained)
{
if (obtained >= cred->realm_obtained) {
cred->realm = strupper_talloc(cred, val);
cred->realm_obtained = obtained;
return True;
}
return False;
}
BOOL cli_credentials_set_realm_callback(struct cli_credentials *cred,
const char *(*realm_cb) (struct cli_credentials *))
{
if (cred->realm_obtained < CRED_CALLBACK) {
cred->realm_cb = realm_cb;
cred->realm_obtained = CRED_CALLBACK;
return True;
}
return False;
}
/**
* Obtain the 'short' or 'NetBIOS' workstation name for this credentials context.
*
* @param cred credentials context
* @retval The workstation name set on this context.
* @note Return value will never be NULL except by programmer error.
*/
const char *cli_credentials_get_workstation(struct cli_credentials *cred)
{
if (cred->workstation_obtained == CRED_CALLBACK &&
!cred->callback_running) {
cred->callback_running = True;
cred->workstation = cred->workstation_cb(cred);
cred->callback_running = False;
cred->workstation_obtained = CRED_SPECIFIED;
}
return cred->workstation;
}
BOOL cli_credentials_set_workstation(struct cli_credentials *cred,
const char *val,
enum credentials_obtained obtained)
{
if (obtained >= cred->workstation_obtained) {
cred->workstation = talloc_strdup(cred, val);
cred->workstation_obtained = obtained;
return True;
}
return False;
}
BOOL cli_credentials_set_workstation_callback(struct cli_credentials *cred,
const char *(*workstation_cb) (struct cli_credentials *))
{
if (cred->workstation_obtained < CRED_CALLBACK) {
cred->workstation_cb = workstation_cb;
cred->workstation_obtained = CRED_CALLBACK;
return True;
}
return False;
}
/**
* Given a string, typically obtained from a -U argument, parse it into domain, username, realm and password fields
*
* The format accepted is [domain\\]user[%password] or user[@realm][%password]
*
* @param credentials Credentials structure on which to set the password
* @param data the string containing the username, password etc
* @param obtained This enum describes how 'specified' this password is
*/
void cli_credentials_parse_string(struct cli_credentials *credentials, const char *data, enum credentials_obtained obtained)
{
char *uname, *p;
if (strcmp("%",data) == 0) {
cli_credentials_set_anonymous(credentials);
return;
}
uname = talloc_strdup(credentials, data);
if ((p = strchr_m(uname,'%'))) {
*p = 0;
cli_credentials_set_password(credentials, p+1, obtained);
}
if ((p = strchr_m(uname,'@'))) {
cli_credentials_set_principal(credentials, uname, obtained);
*p = 0;
cli_credentials_set_realm(credentials, p+1, obtained);
return;
} else if ((p = strchr_m(uname,'\\')) || (p = strchr_m(uname, '/'))) {
*p = 0;
cli_credentials_set_domain(credentials, uname, obtained);
uname = p+1;
}
cli_credentials_set_username(credentials, uname, obtained);
}
/**
* Given a a credentials structure, print it as a string
*
* The format output is [domain\\]user[%password] or user[@realm][%password]
*
* @param credentials Credentials structure on which to set the password
* @param mem_ctx The memory context to place the result on
*/
const char *cli_credentials_get_unparsed_name(struct cli_credentials *credentials, TALLOC_CTX *mem_ctx)
{
const char *bind_dn = cli_credentials_get_bind_dn(credentials);
const char *domain;
const char *username;
const char *name;
if (bind_dn) {
name = talloc_reference(mem_ctx, bind_dn);
} else {
cli_credentials_get_ntlm_username_domain(credentials, mem_ctx, &username, &domain);
if (domain && domain[0]) {
name = talloc_asprintf(mem_ctx, "%s\\%s",
domain, username);
} else {
name = talloc_asprintf(mem_ctx, "%s",
username);
}
}
return name;
}
/**
* Specifies default values for domain, workstation and realm
* from the smb.conf configuration file
*
* @param cred Credentials structure to fill in
*/
void cli_credentials_set_conf(struct cli_credentials *cred)
{
cli_credentials_set_username(cred, "", CRED_UNINITIALISED);
cli_credentials_set_domain(cred, lp_workgroup(), CRED_UNINITIALISED);
cli_credentials_set_workstation(cred, lp_netbios_name(), CRED_UNINITIALISED);
cli_credentials_set_realm(cred, lp_realm(), CRED_UNINITIALISED);
}
/**
* Guess defaults for credentials from environment variables,
* and from the configuration file
*
* @param cred Credentials structure to fill in
*/
void cli_credentials_guess(struct cli_credentials *cred)
{
char *p;
cli_credentials_set_conf(cred);
if (getenv("LOGNAME")) {
cli_credentials_set_username(cred, getenv("LOGNAME"), CRED_GUESS_ENV);
}
if (getenv("USER")) {
cli_credentials_parse_string(cred, getenv("USER"), CRED_GUESS_ENV);
if ((p = strchr_m(getenv("USER"),'%'))) {
memset(p,0,strlen(cred->password));
}
}
if (getenv("DOMAIN")) {
cli_credentials_set_domain(cred, getenv("DOMAIN"), CRED_GUESS_ENV);
}
if (getenv("PASSWD")) {
cli_credentials_set_password(cred, getenv("PASSWD"), CRED_GUESS_ENV);
}
if (getenv("PASSWD_FD")) {
cli_credentials_parse_password_fd(cred, atoi(getenv("PASSWD_FD")), CRED_GUESS_FILE);
}
if (getenv("PASSWD_FILE")) {
cli_credentials_parse_password_file(cred, getenv("PASSWD_FILE"), CRED_GUESS_FILE);
}
if (cli_credentials_get_kerberos_state(cred) != CRED_DONT_USE_KERBEROS) {
cli_credentials_set_ccache(cred, NULL, CRED_GUESS_FILE);
}
}
/**
* Attach NETLOGON credentials for use with SCHANNEL
*/
void cli_credentials_set_netlogon_creds(struct cli_credentials *cred,
struct creds_CredentialState *netlogon_creds)
{
cred->netlogon_creds = talloc_reference(cred, netlogon_creds);
}
/**
* Return attached NETLOGON credentials
*/
struct creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred)
{
return cred->netlogon_creds;
}
/**
* Set NETLOGON secure channel type
*/
void cli_credentials_set_secure_channel_type(struct cli_credentials *cred,
enum netr_SchannelType secure_channel_type)
{
cred->secure_channel_type = secure_channel_type;
}
/**
* Return NETLOGON secure chanel type
*/
enum netr_SchannelType cli_credentials_get_secure_channel_type(struct cli_credentials *cred)
{
return cred->secure_channel_type;
}
/**
* Fill in a credentials structure as the anonymous user
*/
void cli_credentials_set_anonymous(struct cli_credentials *cred)
{
cli_credentials_set_username(cred, "", CRED_SPECIFIED);
cli_credentials_set_domain(cred, "", CRED_SPECIFIED);
cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
}
/**
* Describe a credentials context as anonymous or authenticated
* @retval True if anonymous, False if a username is specified
*/
BOOL cli_credentials_is_anonymous(struct cli_credentials *cred)
{
const char *username;
if (cred->machine_account_pending) {
cli_credentials_set_machine_account(cred);
}
username = cli_credentials_get_username(cred);
/* Yes, it is deliberate that we die if we have a NULL pointer
* here - anonymous is "", not NULL, which is 'never specified,
* never guessed', ie programmer bug */
if (!username[0]) {
return True;
}
return False;
}
/**
* Mark the current password for a credentials struct as wrong. This will
* cause the password to be prompted again (if a callback is set).
*
* This will decrement the number of times the password can be tried.
*
* @retval whether the credentials struct is finished
*/
BOOL cli_credentials_wrong_password(struct cli_credentials *cred)
{
if (cred->password_obtained != CRED_CALLBACK_RESULT) {
return False;
}
cred->password_obtained = CRED_CALLBACK;
cred->tries--;
return (cred->tries > 0);
}
+118
View File
@@ -0,0 +1,118 @@
/*
samba -- Unix SMB/CIFS implementation.
Client credentials structure
Copyright (C) Jelmer Vernooij 2004-2006
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __CREDENTIALS_H__
#define __CREDENTIALS_H__
#include "librpc/gen_ndr/misc.h"
struct ccache_container;
/* In order of priority */
enum credentials_obtained {
CRED_UNINITIALISED = 0, /* We don't even have a guess yet */
CRED_GUESS_ENV, /* Current value should be used, which was guessed */
CRED_CALLBACK, /* Callback should be used to obtain value */
CRED_GUESS_FILE, /* A guess from a file (or file pointed at in env variable) */
CRED_CALLBACK_RESULT, /* Value was obtained from a callback */
CRED_SPECIFIED /* Was explicitly specified on the command-line */
};
enum credentials_use_kerberos {
CRED_AUTO_USE_KERBEROS = 0, /* Default, we try kerberos if available */
CRED_DONT_USE_KERBEROS, /* Sometimes trying kerberos just does 'bad things', so don't */
CRED_MUST_USE_KERBEROS /* Sometimes administrators are parinoid, so always do kerberos */
};
#define CLI_CRED_NTLM2 0x01
#define CLI_CRED_NTLMv2_AUTH 0x02
#define CLI_CRED_LANMAN_AUTH 0x04
#define CLI_CRED_NTLM_AUTH 0x08
#define CLI_CRED_CLEAR_AUTH 0x10 /* TODO: Push cleartext auth with this flag */
struct cli_credentials {
enum credentials_obtained workstation_obtained;
enum credentials_obtained username_obtained;
enum credentials_obtained password_obtained;
enum credentials_obtained domain_obtained;
enum credentials_obtained realm_obtained;
enum credentials_obtained ccache_obtained;
enum credentials_obtained client_gss_creds_obtained;
enum credentials_obtained principal_obtained;
enum credentials_obtained keytab_obtained;
enum credentials_obtained server_gss_creds_obtained;
const char *workstation;
const char *username;
const char *password;
const char *old_password;
const char *domain;
const char *realm;
const char *principal;
const char *salt_principal;
const char *bind_dn;
struct samr_Password *nt_hash;
struct ccache_container *ccache;
struct gssapi_creds_container *client_gss_creds;
struct keytab_container *keytab;
struct gssapi_creds_container *server_gss_creds;
const char *(*workstation_cb) (struct cli_credentials *);
const char *(*password_cb) (struct cli_credentials *);
const char *(*username_cb) (struct cli_credentials *);
const char *(*domain_cb) (struct cli_credentials *);
const char *(*realm_cb) (struct cli_credentials *);
const char *(*principal_cb) (struct cli_credentials *);
/* Private handle for the callback routines to use */
void *priv_data;
struct creds_CredentialState *netlogon_creds;
enum netr_SchannelType secure_channel_type;
int kvno;
struct smb_krb5_context *smb_krb5_context;
/* We are flagged to get machine account details from the
* secrets.ldb when we are asked for a username or password */
BOOL machine_account_pending;
/* Is this a machine account? */
BOOL machine_account;
/* Should we be trying to use kerberos? */
enum credentials_use_kerberos use_kerberos;
/* Number of retries left before bailing out */
int tries;
/* Whether any callback is currently running */
BOOL callback_running;
};
#include "auth/credentials/credentials_proto.h"
#endif /* __CREDENTIALS_H__ */
@@ -0,0 +1,453 @@
/*
Unix SMB/CIFS implementation.
User credentials handling (as regards on-disk files)
Copyright (C) Jelmer Vernooij 2005
Copyright (C) Tim Potter 2001
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "lib/ldb/include/ldb.h"
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
#include "param/secrets.h"
#include "system/filesys.h"
#include "db_wrap.h"
#include "auth/credentials/credentials.h"
#include "auth/credentials/credentials_krb5.h"
/**
* Read a file descriptor, and parse it for a password (eg from a file or stdin)
*
* @param credentials Credentials structure on which to set the password
* @param fd open file descriptor to read the password from
* @param obtained This enum describes how 'specified' this password is
*/
BOOL cli_credentials_parse_password_fd(struct cli_credentials *credentials,
int fd, enum credentials_obtained obtained)
{
char *p;
char pass[128];
for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
p && p - pass < sizeof(pass);) {
switch (read(fd, p, 1)) {
case 1:
if (*p != '\n' && *p != '\0') {
*++p = '\0'; /* advance p, and null-terminate pass */
break;
}
/* fall through */
case 0:
if (p - pass) {
*p = '\0'; /* null-terminate it, just in case... */
p = NULL; /* then force the loop condition to become false */
break;
} else {
fprintf(stderr, "Error reading password from file descriptor %d: %s\n", fd, "empty password\n");
return False;
}
default:
fprintf(stderr, "Error reading password from file descriptor %d: %s\n",
fd, strerror(errno));
return False;
}
}
cli_credentials_set_password(credentials, pass, obtained);
return True;
}
/**
* Read a named file, and parse it for a password
*
* @param credentials Credentials structure on which to set the password
* @param file a named file to read the password from
* @param obtained This enum describes how 'specified' this password is
*/
BOOL cli_credentials_parse_password_file(struct cli_credentials *credentials, const char *file, enum credentials_obtained obtained)
{
int fd = open(file, O_RDONLY, 0);
BOOL ret;
if (fd < 0) {
fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
file, strerror(errno));
return False;
}
ret = cli_credentials_parse_password_fd(credentials, fd, obtained);
close(fd);
return ret;
}
/**
* Read a named file, and parse it for username, domain, realm and password
*
* @param credentials Credentials structure on which to set the password
* @param file a named file to read the details from
* @param obtained This enum describes how 'specified' this password is
*/
BOOL cli_credentials_parse_file(struct cli_credentials *cred, const char *file, enum credentials_obtained obtained)
{
uint16_t len = 0;
char *ptr, *val, *param;
char **lines;
int i, numlines;
lines = file_lines_load(file, &numlines, NULL);
if (lines == NULL)
{
/* fail if we can't open the credentials file */
d_printf("ERROR: Unable to open credentials file!\n");
return False;
}
for (i = 0; i < numlines; i++) {
len = strlen(lines[i]);
if (len == 0)
continue;
/* break up the line into parameter & value.
* will need to eat a little whitespace possibly */
param = lines[i];
if (!(ptr = strchr_m (lines[i], '=')))
continue;
val = ptr+1;
*ptr = '\0';
/* eat leading white space */
while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
val++;
if (strwicmp("password", param) == 0) {
cli_credentials_set_password(cred, val, obtained);
} else if (strwicmp("username", param) == 0) {
cli_credentials_set_username(cred, val, obtained);
} else if (strwicmp("domain", param) == 0) {
cli_credentials_set_domain(cred, val, obtained);
} else if (strwicmp("realm", param) == 0) {
cli_credentials_set_realm(cred, val, obtained);
}
memset(lines[i], 0, len);
}
talloc_free(lines);
return True;
}
/**
* Fill in credentials for the machine trust account, from the secrets database.
*
* @param cred Credentials structure to fill in
* @retval NTSTATUS error detailing any failure
*/
NTSTATUS cli_credentials_set_secrets(struct cli_credentials *cred,
const char *base,
const char *filter)
{
TALLOC_CTX *mem_ctx;
struct ldb_context *ldb;
int ldb_ret;
struct ldb_message **msgs;
const char *attrs[] = {
"secret",
"priorSecret",
"samAccountName",
"flatname",
"realm",
"secureChannelType",
"ntPwdHash",
"msDS-KeyVersionNumber",
"saltPrincipal",
"privateKeytab",
"krb5Keytab",
NULL
};
const char *machine_account;
const char *password;
const char *old_password;
const char *domain;
const char *realm;
enum netr_SchannelType sct;
const char *salt_principal;
const char *keytab;
/* ok, we are going to get it now, don't recurse back here */
cred->machine_account_pending = False;
/* some other parts of the system will key off this */
cred->machine_account = True;
mem_ctx = talloc_named(cred, 0, "cli_credentials fetch machine password");
/* Local secrets are stored in secrets.ldb */
ldb = secrets_db_connect(mem_ctx);
if (!ldb) {
/* set anonymous as the fallback, if the machine account won't work */
cli_credentials_set_anonymous(cred);
DEBUG(1, ("Could not open secrets.ldb\n"));
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
/* search for the secret record */
ldb_ret = gendb_search(ldb,
mem_ctx, ldb_dn_new(mem_ctx, ldb, base),
&msgs, attrs,
"%s", filter);
if (ldb_ret == 0) {
DEBUG(1, ("Could not find entry to match filter: %s\n",
filter));
/* set anonymous as the fallback, if the machine account won't work */
cli_credentials_set_anonymous(cred);
talloc_free(mem_ctx);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
} else if (ldb_ret != 1) {
DEBUG(1, ("Found more than one (%d) entry to match filter: %s\n",
ldb_ret, filter));
/* set anonymous as the fallback, if the machine account won't work */
cli_credentials_set_anonymous(cred);
talloc_free(mem_ctx);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
password = ldb_msg_find_attr_as_string(msgs[0], "secret", NULL);
old_password = ldb_msg_find_attr_as_string(msgs[0], "priorSecret", NULL);
machine_account = ldb_msg_find_attr_as_string(msgs[0], "samAccountName", NULL);
if (!machine_account) {
DEBUG(1, ("Could not find 'samAccountName' in join record to domain: %s\n",
cli_credentials_get_domain(cred)));
/* set anonymous as the fallback, if the machine account won't work */
cli_credentials_set_anonymous(cred);
talloc_free(mem_ctx);
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
salt_principal = ldb_msg_find_attr_as_string(msgs[0], "saltPrincipal", NULL);
cli_credentials_set_salt_principal(cred, salt_principal);
sct = ldb_msg_find_attr_as_int(msgs[0], "secureChannelType", 0);
if (sct) {
cli_credentials_set_secure_channel_type(cred, sct);
}
if (!password) {
const struct ldb_val *nt_password_hash = ldb_msg_find_ldb_val(msgs[0], "ntPwdHash");
struct samr_Password hash;
ZERO_STRUCT(hash);
if (nt_password_hash) {
memcpy(hash.hash, nt_password_hash->data,
MIN(nt_password_hash->length, sizeof(hash.hash)));
cli_credentials_set_nt_hash(cred, &hash, CRED_SPECIFIED);
} else {
cli_credentials_set_password(cred, NULL, CRED_SPECIFIED);
}
} else {
cli_credentials_set_password(cred, password, CRED_SPECIFIED);
}
domain = ldb_msg_find_attr_as_string(msgs[0], "flatname", NULL);
if (domain) {
cli_credentials_set_domain(cred, domain, CRED_SPECIFIED);
}
realm = ldb_msg_find_attr_as_string(msgs[0], "realm", NULL);
if (realm) {
cli_credentials_set_realm(cred, realm, CRED_SPECIFIED);
}
cli_credentials_set_username(cred, machine_account, CRED_SPECIFIED);
cli_credentials_set_kvno(cred, ldb_msg_find_attr_as_int(msgs[0], "msDS-KeyVersionNumber", 0));
/* If there was an external keytab specified by reference in
* the LDB, then use this. Otherwise we will make one up
* (chewing CPU time) from the password */
keytab = ldb_msg_find_attr_as_string(msgs[0], "krb5Keytab", NULL);
if (keytab) {
cli_credentials_set_keytab_name(cred, keytab, CRED_SPECIFIED);
} else {
keytab = ldb_msg_find_attr_as_string(msgs[0], "privateKeytab", NULL);
if (keytab) {
keytab = talloc_asprintf(mem_ctx, "FILE:%s", private_path(mem_ctx, keytab));
if (keytab) {
cli_credentials_set_keytab_name(cred, keytab, CRED_SPECIFIED);
}
}
}
talloc_free(mem_ctx);
return NT_STATUS_OK;
}
/**
* Fill in credentials for the machine trust account, from the secrets database.
*
* @param cred Credentials structure to fill in
* @retval NTSTATUS error detailing any failure
*/
NTSTATUS cli_credentials_set_machine_account(struct cli_credentials *cred)
{
char *filter;
/* Bleh, nasty recursion issues: We are setting a machine
* account here, so we don't want the 'pending' flag around
* any more */
cred->machine_account_pending = False;
filter = talloc_asprintf(cred, SECRETS_PRIMARY_DOMAIN_FILTER,
cli_credentials_get_domain(cred));
return cli_credentials_set_secrets(cred, SECRETS_PRIMARY_DOMAIN_DN,
filter);
}
/**
* Fill in credentials for the machine trust account, from the secrets database.
*
* @param cred Credentials structure to fill in
* @retval NTSTATUS error detailing any failure
*/
NTSTATUS cli_credentials_set_krbtgt(struct cli_credentials *cred)
{
char *filter;
/* Bleh, nasty recursion issues: We are setting a machine
* account here, so we don't want the 'pending' flag around
* any more */
cred->machine_account_pending = False;
filter = talloc_asprintf(cred, SECRETS_KRBTGT_SEARCH,
cli_credentials_get_realm(cred),
cli_credentials_get_domain(cred));
return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN,
filter);
}
/**
* Fill in credentials for the machine trust account, from the secrets database.
*
* @param cred Credentials structure to fill in
* @retval NTSTATUS error detailing any failure
*/
NTSTATUS cli_credentials_set_stored_principal(struct cli_credentials *cred,
const char *serviceprincipal)
{
char *filter;
/* Bleh, nasty recursion issues: We are setting a machine
* account here, so we don't want the 'pending' flag around
* any more */
cred->machine_account_pending = False;
filter = talloc_asprintf(cred, SECRETS_PRINCIPAL_SEARCH,
cli_credentials_get_realm(cred),
cli_credentials_get_domain(cred),
serviceprincipal);
return cli_credentials_set_secrets(cred, SECRETS_PRINCIPALS_DN,
filter);
}
/**
* Ask that when required, the credentials system will be filled with
* machine trust account, from the secrets database.
*
* @param cred Credentials structure to fill in
* @note This function is used to call the above function after, rather
* than during, popt processing.
*
*/
void cli_credentials_set_machine_account_pending(struct cli_credentials *cred)
{
cred->machine_account_pending = True;
}
NTSTATUS cli_credentials_update_all_keytabs(TALLOC_CTX *parent_ctx)
{
TALLOC_CTX *mem_ctx;
int ldb_ret;
struct ldb_context *ldb;
struct ldb_message **msgs;
const char *attrs[] = { NULL };
struct cli_credentials *creds;
const char *filter;
NTSTATUS status;
int i, ret;
mem_ctx = talloc_new(parent_ctx);
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
/* Local secrets are stored in secrets.ldb */
ldb = secrets_db_connect(mem_ctx);
if (!ldb) {
DEBUG(1, ("Could not open secrets.ldb\n"));
talloc_free(mem_ctx);
return NT_STATUS_ACCESS_DENIED;
}
/* search for the secret record, but only of things we can
* actually update */
ldb_ret = gendb_search(ldb,
mem_ctx, NULL,
&msgs, attrs,
"(&(objectClass=kerberosSecret)(|(secret=*)(ntPwdHash=*)))");
if (ldb_ret == -1) {
DEBUG(1, ("Error looking for kerberos type secrets to push into a keytab:: %s", ldb_errstring(ldb)));
talloc_free(mem_ctx);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
for (i=0; i < ldb_ret; i++) {
/* Make a credentials structure from it */
creds = cli_credentials_init(mem_ctx);
if (!creds) {
DEBUG(1, ("cli_credentials_init failed!"));
talloc_free(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
cli_credentials_set_conf(creds);
filter = talloc_asprintf(mem_ctx, "dn=%s", ldb_dn_get_linearized(msgs[i]->dn));
status = cli_credentials_set_secrets(creds, NULL, filter);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("Failed to read secrets for keytab update for %s\n",
filter));
continue;
}
ret = cli_credentials_update_keytab(creds);
if (ret != 0) {
DEBUG(1, ("Failed to update keytab for %s\n",
filter));
continue;
}
}
return NT_STATUS_OK;
}
@@ -0,0 +1,603 @@
/*
Unix SMB/CIFS implementation.
Handle user credentials (as regards krb5)
Copyright (C) Jelmer Vernooij 2005
Copyright (C) Tim Potter 2001
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "system/kerberos.h"
#include "auth/kerberos/kerberos.h"
#include "auth/credentials/credentials.h"
#include "auth/credentials/credentials_krb5.h"
int cli_credentials_get_krb5_context(struct cli_credentials *cred,
struct smb_krb5_context **smb_krb5_context)
{
int ret;
if (cred->smb_krb5_context) {
*smb_krb5_context = cred->smb_krb5_context;
return 0;
}
ret = smb_krb5_init_context(cred, &cred->smb_krb5_context);
if (ret) {
return ret;
}
*smb_krb5_context = cred->smb_krb5_context;
return 0;
}
/* This needs to be called directly after the cli_credentials_init(),
* otherwise we might have problems with the krb5 context already
* being here.
*/
NTSTATUS cli_credentials_set_krb5_context(struct cli_credentials *cred,
struct smb_krb5_context *smb_krb5_context)
{
if (!talloc_reference(cred, smb_krb5_context)) {
return NT_STATUS_NO_MEMORY;
}
cred->smb_krb5_context = smb_krb5_context;
return NT_STATUS_OK;
}
int cli_credentials_set_from_ccache(struct cli_credentials *cred,
enum credentials_obtained obtained)
{
krb5_principal princ;
krb5_error_code ret;
char *name;
char **realm;
if (cred->ccache_obtained > obtained) {
return 0;
}
ret = krb5_cc_get_principal(cred->ccache->smb_krb5_context->krb5_context,
cred->ccache->ccache, &princ);
if (ret) {
char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
DEBUG(1,("failed to get principal from ccache: %s\n",
err_mess));
talloc_free(err_mess);
return ret;
}
ret = krb5_unparse_name(cred->ccache->smb_krb5_context->krb5_context, princ, &name);
if (ret) {
char *err_mess = smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context, ret, cred);
DEBUG(1,("failed to unparse principal from ccache: %s\n",
err_mess));
talloc_free(err_mess);
return ret;
}
realm = krb5_princ_realm(cred->ccache->smb_krb5_context->krb5_context, princ);
cli_credentials_set_principal(cred, name, obtained);
free(name);
krb5_free_principal(cred->ccache->smb_krb5_context->krb5_context, princ);
cred->ccache_obtained = obtained;
return 0;
}
/* Free a memory ccache */
static int free_mccache(struct ccache_container *ccc)
{
krb5_cc_destroy(ccc->smb_krb5_context->krb5_context, ccc->ccache);
return 0;
}
/* Free a disk-based ccache */
static int free_dccache(struct ccache_container *ccc) {
krb5_cc_close(ccc->smb_krb5_context->krb5_context, ccc->ccache);
return 0;
}
int cli_credentials_set_ccache(struct cli_credentials *cred,
const char *name,
enum credentials_obtained obtained)
{
krb5_error_code ret;
krb5_principal princ;
struct ccache_container *ccc;
if (cred->ccache_obtained > obtained) {
return 0;
}
ccc = talloc(cred, struct ccache_container);
if (!ccc) {
return ENOMEM;
}
ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
if (ret) {
talloc_free(ccc);
return ret;
}
talloc_reference(ccc, ccc->smb_krb5_context);
if (name) {
ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
if (ret) {
DEBUG(1,("failed to read krb5 ccache: %s: %s\n",
name,
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
talloc_free(ccc);
return ret;
}
} else {
ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
if (ret) {
DEBUG(3,("failed to read default krb5 ccache: %s\n",
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
talloc_free(ccc);
return ret;
}
}
talloc_set_destructor(ccc, free_dccache);
ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);
if (ret) {
DEBUG(3,("failed to get principal from default ccache: %s\n",
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
talloc_free(ccc);
return ret;
}
krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
cred->ccache = ccc;
talloc_steal(cred, ccc);
ret = cli_credentials_set_from_ccache(cred, obtained);
if (ret) {
return ret;
}
return 0;
}
int cli_credentials_new_ccache(struct cli_credentials *cred, struct ccache_container **_ccc)
{
krb5_error_code ret;
char *rand_string;
struct ccache_container *ccc = talloc(cred, struct ccache_container);
char *ccache_name;
if (!ccc) {
return ENOMEM;
}
rand_string = generate_random_str(NULL, 16);
if (!rand_string) {
talloc_free(ccc);
return ENOMEM;
}
ccache_name = talloc_asprintf(ccc, "MEMORY:%s",
rand_string);
talloc_free(rand_string);
if (!ccache_name) {
talloc_free(ccc);
return ENOMEM;
}
ret = cli_credentials_get_krb5_context(cred, &ccc->smb_krb5_context);
if (ret) {
talloc_free(ccc);
return ret;
}
talloc_reference(ccc, ccc->smb_krb5_context);
ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, &ccc->ccache);
if (ret) {
DEBUG(1,("failed to generate a new krb5 ccache (%s): %s\n",
ccache_name,
smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context, ret, ccc)));
talloc_free(ccache_name);
talloc_free(ccc);
return ret;
}
talloc_set_destructor(ccc, free_mccache);
cred->ccache = ccc;
talloc_steal(cred, ccc);
talloc_free(ccache_name);
if (_ccc) {
*_ccc = ccc;
}
return ret;
}
int cli_credentials_get_ccache(struct cli_credentials *cred,
struct ccache_container **ccc)
{
krb5_error_code ret;
if (cred->ccache_obtained >= (MAX(cred->principal_obtained,
cred->username_obtained))) {
*ccc = cred->ccache;
return 0;
}
if (cli_credentials_is_anonymous(cred)) {
return EINVAL;
}
ret = cli_credentials_new_ccache(cred, NULL);
if (ret) {
return ret;
}
ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
if (ret) {
return ret;
}
ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);
if (ret) {
return ret;
}
*ccc = cred->ccache;
return ret;
}
static int free_gssapi_creds(struct gssapi_creds_container *gcc)
{
OM_uint32 min_stat, maj_stat;
maj_stat = gss_release_cred(&min_stat, &gcc->creds);
return 0;
}
int cli_credentials_get_client_gss_creds(struct cli_credentials *cred,
struct gssapi_creds_container **_gcc)
{
int ret = 0;
OM_uint32 maj_stat, min_stat;
struct gssapi_creds_container *gcc;
struct ccache_container *ccache;
if (cred->client_gss_creds_obtained >= (MAX(cred->ccache_obtained,
MAX(cred->principal_obtained,
cred->username_obtained)))) {
*_gcc = cred->client_gss_creds;
return 0;
}
ret = cli_credentials_get_ccache(cred,
&ccache);
if (ret) {
DEBUG(1, ("Failed to get CCACHE for GSSAPI client: %s\n", error_message(ret)));
return ret;
}
gcc = talloc(cred, struct gssapi_creds_container);
if (!gcc) {
return ENOMEM;
}
maj_stat = gss_krb5_import_cred(&min_stat, ccache->ccache, NULL, NULL,
&gcc->creds);
if (maj_stat) {
if (min_stat) {
ret = min_stat;
} else {
ret = EINVAL;
}
}
if (ret == 0) {
cred->client_gss_creds_obtained = cred->ccache_obtained;
talloc_set_destructor(gcc, free_gssapi_creds);
cred->client_gss_creds = gcc;
*_gcc = gcc;
}
return ret;
}
/**
Set a gssapi cred_id_t into the credentails system. (Client case)
This grabs the credentials both 'intact' and getting the krb5
ccache out of it. This routine can be generalised in future for
the case where we deal with GSSAPI mechs other than krb5.
On sucess, the caller must not free gssapi_cred, as it now belongs
to the credentials system.
*/
int cli_credentials_set_client_gss_creds(struct cli_credentials *cred,
gss_cred_id_t gssapi_cred,
enum credentials_obtained obtained)
{
int ret;
OM_uint32 maj_stat, min_stat;
struct ccache_container *ccc;
struct gssapi_creds_container *gcc;
if (cred->client_gss_creds_obtained > obtained) {
return 0;
}
gcc = talloc(cred, struct gssapi_creds_container);
if (!gcc) {
return ENOMEM;
}
ret = cli_credentials_new_ccache(cred, &ccc);
if (ret != 0) {
return ret;
}
maj_stat = gss_krb5_copy_ccache(&min_stat,
gssapi_cred, ccc->ccache);
if (maj_stat) {
if (min_stat) {
ret = min_stat;
} else {
ret = EINVAL;
}
}
if (ret == 0) {
ret = cli_credentials_set_from_ccache(cred, obtained);
}
if (ret == 0) {
gcc->creds = gssapi_cred;
talloc_set_destructor(gcc, free_gssapi_creds);
cred->client_gss_creds_obtained = obtained;
cred->client_gss_creds = gcc;
}
return ret;
}
/* Get the keytab (actually, a container containing the krb5_keytab)
* attached to this context. If this hasn't been done or set before,
* it will be generated from the password.
*/
int cli_credentials_get_keytab(struct cli_credentials *cred,
struct keytab_container **_ktc)
{
krb5_error_code ret;
struct keytab_container *ktc;
struct smb_krb5_context *smb_krb5_context;
TALLOC_CTX *mem_ctx;
if (cred->keytab_obtained >= (MAX(cred->principal_obtained,
cred->username_obtained))) {
*_ktc = cred->keytab;
return 0;
}
if (cli_credentials_is_anonymous(cred)) {
return EINVAL;
}
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
if (ret) {
return ret;
}
mem_ctx = talloc_new(cred);
if (!mem_ctx) {
return ENOMEM;
}
ret = smb_krb5_create_memory_keytab(mem_ctx, cred, smb_krb5_context, &ktc);
if (ret) {
talloc_free(mem_ctx);
return ret;
}
cred->keytab_obtained = (MAX(cred->principal_obtained,
cred->username_obtained));
talloc_steal(cred, ktc);
cred->keytab = ktc;
*_ktc = cred->keytab;
talloc_free(mem_ctx);
return ret;
}
/* Given the name of a keytab (presumably in the format
* FILE:/etc/krb5.keytab), open it and attach it */
int cli_credentials_set_keytab_name(struct cli_credentials *cred,
const char *keytab_name,
enum credentials_obtained obtained)
{
krb5_error_code ret;
struct keytab_container *ktc;
struct smb_krb5_context *smb_krb5_context;
TALLOC_CTX *mem_ctx;
if (cred->keytab_obtained >= obtained) {
return 0;
}
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
if (ret) {
return ret;
}
mem_ctx = talloc_new(cred);
if (!mem_ctx) {
return ENOMEM;
}
ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context,
keytab_name, &ktc);
if (ret) {
return ret;
}
cred->keytab_obtained = obtained;
talloc_steal(cred, ktc);
cred->keytab = ktc;
talloc_free(mem_ctx);
return ret;
}
int cli_credentials_update_keytab(struct cli_credentials *cred)
{
krb5_error_code ret;
struct keytab_container *ktc;
struct smb_krb5_context *smb_krb5_context;
TALLOC_CTX *mem_ctx;
mem_ctx = talloc_new(cred);
if (!mem_ctx) {
return ENOMEM;
}
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
if (ret) {
talloc_free(mem_ctx);
return ret;
}
ret = cli_credentials_get_keytab(cred, &ktc);
if (ret != 0) {
talloc_free(mem_ctx);
return ret;
}
ret = smb_krb5_update_keytab(mem_ctx, cred, smb_krb5_context, ktc);
talloc_free(mem_ctx);
return ret;
}
/* Get server gss credentials (in gsskrb5, this means the keytab) */
int cli_credentials_get_server_gss_creds(struct cli_credentials *cred,
struct gssapi_creds_container **_gcc)
{
int ret = 0;
OM_uint32 maj_stat, min_stat;
struct gssapi_creds_container *gcc;
struct keytab_container *ktc;
struct smb_krb5_context *smb_krb5_context;
TALLOC_CTX *mem_ctx;
krb5_principal princ;
if (cred->server_gss_creds_obtained >= (MAX(cred->keytab_obtained,
MAX(cred->principal_obtained,
cred->username_obtained)))) {
*_gcc = cred->server_gss_creds;
return 0;
}
ret = cli_credentials_get_krb5_context(cred, &smb_krb5_context);
if (ret) {
return ret;
}
ret = cli_credentials_get_keytab(cred,
&ktc);
if (ret) {
DEBUG(1, ("Failed to get keytab for GSSAPI server: %s\n", error_message(ret)));
return ret;
}
mem_ctx = talloc_new(cred);
if (!mem_ctx) {
return ENOMEM;
}
ret = principal_from_credentials(mem_ctx, cred, smb_krb5_context, &princ);
if (ret) {
DEBUG(1,("cli_credentials_get_server_gss_creds: makeing krb5 principal failed (%s)\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
gcc = talloc(cred, struct gssapi_creds_container);
if (!gcc) {
talloc_free(mem_ctx);
return ENOMEM;
}
/* This creates a GSSAPI cred_id_t with the principal and keytab set */
maj_stat = gss_krb5_import_cred(&min_stat, NULL, princ, ktc->keytab,
&gcc->creds);
if (maj_stat) {
if (min_stat) {
ret = min_stat;
} else {
ret = EINVAL;
}
}
if (ret == 0) {
cred->server_gss_creds_obtained = cred->keytab_obtained;
talloc_set_destructor(gcc, free_gssapi_creds);
cred->server_gss_creds = gcc;
*_gcc = gcc;
}
talloc_free(mem_ctx);
return ret;
}
/**
* Set Kerberos KVNO
*/
void cli_credentials_set_kvno(struct cli_credentials *cred,
int kvno)
{
cred->kvno = kvno;
}
/**
* Return Kerberos KVNO
*/
int cli_credentials_get_kvno(struct cli_credentials *cred)
{
return cred->kvno;
}
const char *cli_credentials_get_salt_principal(struct cli_credentials *cred)
{
return cred->salt_principal;
}
void cli_credentials_set_salt_principal(struct cli_credentials *cred, const char *principal)
{
cred->salt_principal = talloc_strdup(cred, principal);
}
@@ -0,0 +1,32 @@
/*
samba -- Unix SMB/CIFS implementation.
Client credentials structure
Copyright (C) Jelmer Vernooij 2004-2006
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "heimdal/lib/gssapi/gssapi/gssapi.h"
struct ccache_container;
struct gssapi_creds_container {
gss_cred_id_t creds;
};
#include "auth/credentials/credentials_krb5_proto.h"
@@ -0,0 +1,218 @@
/*
Unix SMB/CIFS implementation.
User credentials handling
Copyright (C) Andrew Tridgell 2001
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "librpc/gen_ndr/samr.h" /* for struct samrPassword */
#include "lib/crypto/crypto.h"
#include "libcli/auth/libcli_auth.h"
#include "auth/credentials/credentials.h"
void cli_credentials_get_ntlm_username_domain(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
const char **username,
const char **domain)
{
if (cred->principal_obtained > cred->username_obtained) {
*domain = talloc_strdup(mem_ctx, "");
*username = cli_credentials_get_principal(cred, mem_ctx);
} else {
*domain = cli_credentials_get_domain(cred);
*username = cli_credentials_get_username(cred);
}
}
NTSTATUS cli_credentials_get_ntlm_response(struct cli_credentials *cred, TALLOC_CTX *mem_ctx,
int *flags,
DATA_BLOB challenge, DATA_BLOB target_info,
DATA_BLOB *_lm_response, DATA_BLOB *_nt_response,
DATA_BLOB *_lm_session_key, DATA_BLOB *_session_key)
{
const char *user, *domain;
DATA_BLOB lm_response, nt_response;
DATA_BLOB lm_session_key, session_key;
const struct samr_Password *nt_hash;
lm_session_key = data_blob(NULL, 0);
nt_hash = cli_credentials_get_nt_hash(cred, mem_ctx);
cli_credentials_get_ntlm_username_domain(cred, mem_ctx, &user, &domain);
/* If we are sending a username@realm login (see function
* above), then we will not send LM, it will not be
* accepted */
if (cred->principal_obtained > cred->username_obtained) {
*flags = *flags & ~CLI_CRED_LANMAN_AUTH;
}
/* Likewise if we are a machine account (avoid protocol downgrade attacks) */
if (cred->machine_account) {
*flags = *flags & ~CLI_CRED_LANMAN_AUTH;
}
if (cred->use_kerberos == CRED_MUST_USE_KERBEROS) {
return NT_STATUS_ACCESS_DENIED;
}
if (!nt_hash) {
static const uint8_t zeros[16];
/* do nothing - blobs are zero length */
/* session key is all zeros */
session_key = data_blob_talloc(mem_ctx, zeros, 16);
lm_session_key = data_blob_talloc(mem_ctx, zeros, 16);
lm_response = data_blob(NULL, 0);
nt_response = data_blob(NULL, 0);
/* not doing NTLM2 without a password */
*flags &= ~CLI_CRED_NTLM2;
} else if (*flags & CLI_CRED_NTLMv2_AUTH) {
if (!target_info.length) {
/* be lazy, match win2k - we can't do NTLMv2 without it */
DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
return NT_STATUS_INVALID_PARAMETER;
}
/* TODO: if the remote server is standalone, then we should replace 'domain'
with the server name as supplied above */
if (!SMBNTLMv2encrypt_hash(mem_ctx,
user,
domain,
nt_hash->hash, &challenge,
&target_info,
&lm_response, &nt_response,
NULL, &session_key)) {
return NT_STATUS_NO_MEMORY;
}
/* LM Key is incompatible... */
*flags &= ~CLI_CRED_LANMAN_AUTH;
} else if (*flags & CLI_CRED_NTLM2) {
struct MD5Context md5_session_nonce_ctx;
uint8_t session_nonce[16];
uint8_t session_nonce_hash[16];
uint8_t user_session_key[16];
lm_response = data_blob_talloc(mem_ctx, NULL, 24);
generate_random_buffer(lm_response.data, 8);
memset(lm_response.data+8, 0, 16);
memcpy(session_nonce, challenge.data, 8);
memcpy(&session_nonce[8], lm_response.data, 8);
MD5Init(&md5_session_nonce_ctx);
MD5Update(&md5_session_nonce_ctx, challenge.data, 8);
MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
DEBUG(5, ("challenge is: \n"));
dump_data(5, session_nonce_hash, 8);
nt_response = data_blob_talloc(mem_ctx, NULL, 24);
SMBOWFencrypt(nt_hash->hash,
session_nonce_hash,
nt_response.data);
session_key = data_blob_talloc(mem_ctx, NULL, 16);
SMBsesskeygen_ntv1(nt_hash->hash, user_session_key);
hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data);
dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
/* LM Key is incompatible... */
*flags &= ~CLI_CRED_LANMAN_AUTH;
} else {
uint8_t lm_hash[16];
nt_response = data_blob_talloc(mem_ctx, NULL, 24);
SMBOWFencrypt(nt_hash->hash, challenge.data,
nt_response.data);
session_key = data_blob_talloc(mem_ctx, NULL, 16);
SMBsesskeygen_ntv1(nt_hash->hash, session_key.data);
dump_data_pw("NT session key:\n", session_key.data, session_key.length);
/* lanman auth is insecure, it may be disabled.
We may also not have a password */
if (*flags & CLI_CRED_LANMAN_AUTH) {
const char *password;
password = cli_credentials_get_password(cred);
if (!password) {
lm_response = nt_response;
} else {
lm_response = data_blob_talloc(mem_ctx, NULL, 24);
if (!SMBencrypt(password,challenge.data,
lm_response.data)) {
/* If the LM password was too long (and therefore the LM hash being
of the first 14 chars only), don't send it.
We don't have any better options but to send the NT response
*/
data_blob_free(&lm_response);
lm_response = nt_response;
/* LM Key is incompatible with 'long' passwords */
*flags &= ~CLI_CRED_LANMAN_AUTH;
} else {
E_deshash(password, lm_hash);
lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
memcpy(lm_session_key.data, lm_hash, 8);
memset(&lm_session_key.data[8], '\0', 8);
if (!(*flags & CLI_CRED_NTLM_AUTH)) {
session_key = lm_session_key;
}
}
}
} else {
const char *password;
/* LM Key is incompatible... */
lm_response = nt_response;
*flags &= ~CLI_CRED_LANMAN_AUTH;
password = cli_credentials_get_password(cred);
if (password) {
E_deshash(password, lm_hash);
lm_session_key = data_blob_talloc(mem_ctx, NULL, 16);
memcpy(lm_session_key.data, lm_hash, 8);
memset(&lm_session_key.data[8], '\0', 8);
}
}
}
if (_lm_response) {
*_lm_response = lm_response;
}
if (_nt_response) {
*_nt_response = nt_response;
}
if (_lm_session_key) {
*_lm_session_key = lm_session_key;
}
if (_session_key) {
*_session_key = session_key;
}
return NT_STATUS_OK;
}
+2
View File
@@ -0,0 +1,2 @@
SMB_ENABLE(gensec_krb5, $HAVE_KRB5)
SMB_ENABLE(gensec_gssapi, $HAVE_KRB5)
+91
View File
@@ -0,0 +1,91 @@
#################################
# Start SUBSYSTEM gensec
[LIBRARY::gensec]
VERSION = 0.0.1
SO_VERSION = 0
DESCRIPTION = Generic Security Library
PUBLIC_HEADERS = gensec.h spnego.h
PUBLIC_PROTO_HEADER = gensec_proto.h
OBJ_FILES = gensec.o
PUBLIC_DEPENDENCIES = \
CREDENTIALS LIBSAMBA-UTIL LIBCRYPTO ASN1_UTIL
# End SUBSYSTEM gensec
#################################
################################################
# Start MODULE gensec_krb5
[MODULE::gensec_krb5]
SUBSYSTEM = gensec
INIT_FUNCTION = gensec_krb5_init
OBJ_FILES = gensec_krb5.o
PUBLIC_DEPENDENCIES = CREDENTIALS_KRB5 KERBEROS auth auth_sam
# End MODULE gensec_krb5
################################################
################################################
# Start MODULE gensec_gssapi
[MODULE::gensec_gssapi]
SUBSYSTEM = gensec
INIT_FUNCTION = gensec_gssapi_init
OBJ_FILES = gensec_gssapi.o
PUBLIC_DEPENDENCIES = CREDENTIALS_KRB5 KERBEROS auth HEIMDAL_GSSAPI
# End MODULE gensec_gssapi
################################################
################################################
# Start MODULE cyrus_sasl
[MODULE::cyrus_sasl]
SUBSYSTEM = gensec
INIT_FUNCTION = gensec_sasl_init
OBJ_FILES = cyrus_sasl.o
PUBLIC_DEPENDENCIES = CREDENTIALS SASL auth
# End MODULE cyrus_sasl
################################################
################################################
# Start MODULE gensec_spnego
[MODULE::gensec_spnego]
SUBSYSTEM = gensec
INIT_FUNCTION = gensec_spnego_init
PRIVATE_PROTO_HEADER = spnego_proto.h
PRIVATE_DEPENDENCIES = ASN1_UTIL GENSEC_SOCKET
PUBLIC_DEPENDENCIES = CREDENTIALS
OBJ_FILES = spnego.o \
spnego_parse.o
# End MODULE gensec_spnego
################################################
################################################
# Start MODULE gensec_schannel
[MODULE::gensec_schannel]
SUBSYSTEM = gensec
PRIVATE_PROTO_HEADER = schannel_proto.h
INIT_FUNCTION = gensec_schannel_init
OBJ_FILES = schannel.o \
schannel_sign.o
PUBLIC_DEPENDENCIES = auth SCHANNELDB NDR_SCHANNEL CREDENTIALS
OUTPUT_TYPE = INTEGRATED
# End MODULE gensec_schannel
################################################
################################################
# Start SUBSYSTEM SCHANNELDB
[SUBSYSTEM::SCHANNELDB]
PRIVATE_PROTO_HEADER = schannel_state.h
OBJ_FILES = \
schannel_state.o
#
# End SUBSYSTEM SCHANNELDB
################################################
################################################
# Start SUBSYSTEM GENSEC_SOCKET
[SUBSYSTEM::GENSEC_SOCKET]
OBJ_FILES = \
socket.o
PUBLIC_DEPENDENCIES = samba-socket LIBPACKET
#PUBLIC_DEPENDENCIES = gensec
#
# End SUBSYSTEM GENSEC_SOCKET
################################################
+424
View File
@@ -0,0 +1,424 @@
/*
Unix SMB/CIFS implementation.
Connect GENSEC to an external SASL lib
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/auth.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
#include "lib/socket/socket.h"
#include <sasl/sasl.h>
struct gensec_sasl_state {
sasl_conn_t *conn;
int step;
};
static NTSTATUS sasl_nt_status(int sasl_ret)
{
switch (sasl_ret) {
case SASL_CONTINUE:
return NT_STATUS_MORE_PROCESSING_REQUIRED;
case SASL_NOMEM:
return NT_STATUS_NO_MEMORY;
case SASL_BADPARAM:
case SASL_NOMECH:
return NT_STATUS_INVALID_PARAMETER;
case SASL_BADMAC:
return NT_STATUS_ACCESS_DENIED;
case SASL_OK:
return NT_STATUS_OK;
default:
return NT_STATUS_UNSUCCESSFUL;
}
}
static int gensec_sasl_get_user(void *context, int id,
const char **result, unsigned *len)
{
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
const char *username = cli_credentials_get_username(gensec_get_credentials(gensec_security));
if (id != SASL_CB_USER && id != SASL_CB_AUTHNAME) {
return SASL_FAIL;
}
*result = username;
return SASL_OK;
}
static int gensec_sasl_get_realm(void *context, int id,
const char **availrealms,
const char **result)
{
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
const char *realm = cli_credentials_get_realm(gensec_get_credentials(gensec_security));
int i;
if (id != SASL_CB_GETREALM) {
return SASL_FAIL;
}
for (i=0; availrealms && availrealms[i]; i++) {
if (strcasecmp_m(realm, availrealms[i]) == 0) {
result[i] = availrealms[i];
return SASL_OK;
}
}
/* None of the realms match, so lets not specify one */
*result = "";
return SASL_OK;
}
static int gensec_sasl_get_password(sasl_conn_t *conn, void *context, int id,
sasl_secret_t **psecret)
{
struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
const char *password = cli_credentials_get_password(gensec_get_credentials(gensec_security));
sasl_secret_t *secret;
if (!password) {
*psecret = NULL;
return SASL_OK;
}
secret = talloc_size(gensec_security, sizeof(sasl_secret_t)+strlen(password));
if (!secret) {
return SASL_NOMEM;
}
secret->len = strlen(password);
safe_strcpy(secret->data, password, secret->len+1);
*psecret = secret;
return SASL_OK;
}
static int gensec_sasl_dispose(struct gensec_sasl_state *gensec_sasl_state)
{
sasl_dispose(&gensec_sasl_state->conn);
return 0;
}
static NTSTATUS gensec_sasl_client_start(struct gensec_security *gensec_security)
{
struct gensec_sasl_state *gensec_sasl_state;
const char *service = gensec_get_target_service(gensec_security);
const char *target_name = gensec_get_target_hostname(gensec_security);
struct socket_address *local_socket_addr = gensec_get_my_addr(gensec_security);
struct socket_address *remote_socket_addr = gensec_get_peer_addr(gensec_security);
char *local_addr = NULL;
char *remote_addr = NULL;
int sasl_ret;
sasl_callback_t *callbacks;
gensec_sasl_state = talloc(gensec_security, struct gensec_sasl_state);
if (!gensec_sasl_state) {
return NT_STATUS_NO_MEMORY;
}
callbacks = talloc_array(gensec_sasl_state, sasl_callback_t, 5);
callbacks[0].id = SASL_CB_USER;
callbacks[0].proc = gensec_sasl_get_user;
callbacks[0].context = gensec_security;
callbacks[1].id = SASL_CB_AUTHNAME;
callbacks[1].proc = gensec_sasl_get_user;
callbacks[1].context = gensec_security;
callbacks[2].id = SASL_CB_GETREALM;
callbacks[2].proc = gensec_sasl_get_realm;
callbacks[2].context = gensec_security;
callbacks[3].id = SASL_CB_PASS;
callbacks[3].proc = gensec_sasl_get_password;
callbacks[3].context = gensec_security;
callbacks[4].id = SASL_CB_LIST_END;
callbacks[4].proc = NULL;
callbacks[4].context = NULL;
gensec_security->private_data = gensec_sasl_state;
if (local_socket_addr) {
local_addr = talloc_asprintf(gensec_sasl_state,
"%s;%d",
local_socket_addr->addr,
local_socket_addr->port);
}
if (remote_socket_addr) {
remote_addr = talloc_asprintf(gensec_sasl_state,
"%s;%d",
remote_socket_addr->addr,
remote_socket_addr->port);
}
gensec_sasl_state->step = 0;
sasl_ret = sasl_client_new(service,
target_name,
local_addr, remote_addr, callbacks, 0,
&gensec_sasl_state->conn);
if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
sasl_security_properties_t props;
talloc_set_destructor(gensec_sasl_state, gensec_sasl_dispose);
ZERO_STRUCT(props);
if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
props.min_ssf = 1;
}
if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
props.min_ssf = 40;
}
props.max_ssf = UINT_MAX;
props.maxbufsize = 65536;
sasl_ret = sasl_setprop(gensec_sasl_state->conn, SASL_SEC_PROPS, &props);
if (sasl_ret != SASL_OK) {
return sasl_nt_status(sasl_ret);
}
} else {
DEBUG(1, ("GENSEC SASL: client_new failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
}
return sasl_nt_status(sasl_ret);
}
static NTSTATUS gensec_sasl_update(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out)
{
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
struct gensec_sasl_state);
int sasl_ret;
const char *out_data;
unsigned int out_len;
if (gensec_sasl_state->step == 0) {
const char *mech;
sasl_ret = sasl_client_start(gensec_sasl_state->conn, gensec_security->ops->sasl_name,
NULL, &out_data, &out_len, &mech);
} else {
sasl_ret = sasl_client_step(gensec_sasl_state->conn,
in.data, in.length, NULL, &out_data, &out_len);
}
if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
} else {
DEBUG(1, ("GENSEC SASL: step %d update failed: %s\n", gensec_sasl_state->step,
sasl_errdetail(gensec_sasl_state->conn)));
}
gensec_sasl_state->step++;
return sasl_nt_status(sasl_ret);
}
static NTSTATUS gensec_sasl_unwrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed)
{
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
struct gensec_sasl_state);
const char *out_data;
unsigned int out_len;
int sasl_ret = sasl_decode(gensec_sasl_state->conn,
in->data, in->length, &out_data, &out_len);
if (sasl_ret == SASL_OK) {
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
*len_processed = in->length;
} else {
DEBUG(1, ("GENSEC SASL: unwrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
}
return sasl_nt_status(sasl_ret);
}
static NTSTATUS gensec_sasl_wrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed)
{
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
struct gensec_sasl_state);
const char *out_data;
unsigned int out_len;
int sasl_ret = sasl_encode(gensec_sasl_state->conn,
in->data, in->length, &out_data, &out_len);
if (sasl_ret == SASL_OK) {
*out = data_blob_talloc(out_mem_ctx, out_data, out_len);
*len_processed = in->length;
} else {
DEBUG(1, ("GENSEC SASL: wrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
}
return sasl_nt_status(sasl_ret);
}
/* Try to figure out what features we actually got on the connection */
static BOOL gensec_sasl_have_feature(struct gensec_security *gensec_security,
uint32_t feature)
{
struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
struct gensec_sasl_state);
sasl_ssf_t ssf;
int sasl_ret = sasl_getprop(gensec_sasl_state->conn, SASL_SSF, &ssf);
if (sasl_ret != SASL_OK) {
return False;
}
if (feature & GENSEC_FEATURE_SIGN) {
if (ssf == 0) {
return False;
}
if (ssf >= 1) {
return True;
}
}
if (feature & GENSEC_FEATURE_SEAL) {
if (ssf <= 1) {
return False;
}
if (ssf > 1) {
return True;
}
}
return False;
}
/* This could in theory work with any SASL mech */
static const struct gensec_security_ops gensec_sasl_security_ops = {
.name = "sasl-DIGEST-MD5",
.sasl_name = "DIGEST-MD5",
.client_start = gensec_sasl_client_start,
.update = gensec_sasl_update,
.wrap_packets = gensec_sasl_wrap_packets,
.unwrap_packets = gensec_sasl_unwrap_packets,
.have_feature = gensec_sasl_have_feature,
.enabled = True,
.priority = GENSEC_SASL
};
int gensec_sasl_log(void *context,
int sasl_log_level,
const char *message)
{
int debug_level;
switch (sasl_log_level) {
case SASL_LOG_NONE:
debug_level = 0;
break;
case SASL_LOG_ERR:
debug_level = 1;
break;
case SASL_LOG_FAIL:
debug_level = 2;
break;
case SASL_LOG_WARN:
debug_level = 3;
break;
case SASL_LOG_NOTE:
debug_level = 5;
break;
case SASL_LOG_DEBUG:
debug_level = 10;
break;
case SASL_LOG_TRACE:
debug_level = 11;
break;
#if DEBUG_PASSWORD
case SASL_LOG_PASS:
debug_level = 100;
break;
#endif
default:
debug_level = 0;
break;
}
DEBUG(debug_level, ("gensec_sasl: %s\n", message));
return SASL_OK;
}
NTSTATUS gensec_sasl_init(void)
{
NTSTATUS ret;
int sasl_ret, i;
const char **sasl_mechs;
static const sasl_callback_t callbacks[] = {
{
.id = SASL_CB_LOG,
.proc = gensec_sasl_log,
.context = NULL,
},
{
.id = SASL_CB_LIST_END,
.proc = gensec_sasl_log,
.context = NULL,
}
};
sasl_ret = sasl_client_init(callbacks);
if (sasl_ret == SASL_NOMECH) {
/* Nothing to do here */
return NT_STATUS_OK;
}
if (sasl_ret != SASL_OK) {
return sasl_nt_status(sasl_ret);
}
/* For now, we just register DIGEST-MD5 */
#if 1
ret = gensec_register(&gensec_sasl_security_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_sasl_security_ops.name));
return ret;
}
#else
sasl_mechs = sasl_global_listmech();
for (i = 0; sasl_mechs && sasl_mechs[i]; i++) {
const struct gensec_security_ops *oldmech;
struct gensec_security_ops *newmech;
oldmech = gensec_security_by_sasl_name(NULL, sasl_mechs[i]);
if (oldmech) {
continue;
}
newmech = talloc(talloc_autofree_context(), struct gensec_security_ops);
if (!newmech) {
return NT_STATUS_NO_MEMORY;
}
*newmech = gensec_sasl_security_ops;
newmech->sasl_name = talloc_strdup(newmech, sasl_mechs[i]);
newmech->name = talloc_asprintf(newmech, "sasl-%s", sasl_mechs[i]);
if (!newmech->sasl_name || !newmech->name) {
return NT_STATUS_NO_MEMORY;
}
ret = gensec_register(newmech);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_sasl_security_ops.name));
return ret;
}
}
#endif
return NT_STATUS_OK;
}
File diff suppressed because it is too large Load Diff
+175
View File
@@ -0,0 +1,175 @@
/*
Unix SMB/CIFS implementation.
Generic Authentication Interface
Copyright (C) Andrew Tridgell 2003
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __GENSEC_H__
#define __GENSEC_H__
#include "core.h"
#include "auth/credentials/credentials.h"
#define GENSEC_OID_NTLMSSP "1 3 6 1 4 1 311 2 2 10"
#define GENSEC_OID_SPNEGO "1 3 6 1 5 5 2"
#define GENSEC_OID_KERBEROS5 "1 2 840 113554 1 2 2"
#define GENSEC_OID_KERBEROS5_OLD "1 2 840 48018 1 2 2"
#define GENSEC_OID_KERBEROS5_USER2USER "1 2 840 113554 1 2 2 3"
enum gensec_priority {
GENSEC_SPNEGO = 90,
GENSEC_GSSAPI = 80,
GENSEC_KRB5 = 70,
GENSEC_SCHANNEL = 60,
GENSEC_NTLMSSP = 50,
GENSEC_SASL = 20,
GENSEC_OTHER = 0
};
enum credentials_use_kerberos;
struct gensec_security;
struct gensec_target {
const char *principal;
const char *hostname;
const char *service;
};
#define GENSEC_FEATURE_SESSION_KEY 0x00000001
#define GENSEC_FEATURE_SIGN 0x00000002
#define GENSEC_FEATURE_SEAL 0x00000004
#define GENSEC_FEATURE_DCE_STYLE 0x00000008
#define GENSEC_FEATURE_ASYNC_REPLIES 0x00000010
#define GENSEC_FEATURE_DATAGRAM_MODE 0x00000020
/* GENSEC mode */
enum gensec_role
{
GENSEC_SERVER,
GENSEC_CLIENT
};
struct auth_session_info;
struct gensec_update_request {
struct gensec_security *gensec_security;
void *private_data;
DATA_BLOB in;
DATA_BLOB out;
NTSTATUS status;
struct {
void (*fn)(struct gensec_update_request *req, void *private_data);
void *private_data;
} callback;
};
struct gensec_security_ops {
const char *name;
const char *sasl_name;
uint8_t auth_type; /* 0 if not offered on DCE-RPC */
const char **oid; /* NULL if not offered by SPNEGO */
NTSTATUS (*client_start)(struct gensec_security *gensec_security);
NTSTATUS (*server_start)(struct gensec_security *gensec_security);
/**
Determine if a packet has the right 'magic' for this mechanism
*/
NTSTATUS (*magic)(struct gensec_security *gensec_security,
const DATA_BLOB *first_packet);
NTSTATUS (*update)(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out);
NTSTATUS (*seal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig);
NTSTATUS (*sign_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig);
size_t (*sig_size)(struct gensec_security *gensec_security, size_t data_size);
size_t (*max_input_size)(struct gensec_security *gensec_security);
size_t (*max_wrapped_size)(struct gensec_security *gensec_security);
NTSTATUS (*check_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig);
NTSTATUS (*unseal_packet)(struct gensec_security *gensec_security, TALLOC_CTX *sig_mem_ctx,
uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig);
NTSTATUS (*wrap)(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out);
NTSTATUS (*unwrap)(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out);
NTSTATUS (*wrap_packets)(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed);
NTSTATUS (*unwrap_packets)(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed);
NTSTATUS (*packet_full_request)(struct gensec_security *gensec_security,
DATA_BLOB blob, size_t *size);
NTSTATUS (*session_key)(struct gensec_security *gensec_security, DATA_BLOB *session_key);
NTSTATUS (*session_info)(struct gensec_security *gensec_security,
struct auth_session_info **session_info);
BOOL (*have_feature)(struct gensec_security *gensec_security,
uint32_t feature);
BOOL enabled;
BOOL kerberos;
enum gensec_priority priority;
};
struct gensec_security_ops_wrapper {
const struct gensec_security_ops *op;
const char *oid;
};
#define GENSEC_INTERFACE_VERSION 0
struct gensec_security {
const struct gensec_security_ops *ops;
void *private_data;
struct cli_credentials *credentials;
struct gensec_target target;
enum gensec_role gensec_role;
BOOL subcontext;
uint32_t want_features;
struct event_context *event_ctx;
struct messaging_context *msg_ctx; /* only valid as server */
struct socket_address *my_addr, *peer_addr;
};
/* this structure is used by backends to determine the size of some critical types */
struct gensec_critical_sizes {
int interface_version;
int sizeof_gensec_security_ops;
int sizeof_gensec_security;
};
#include "auth/gensec/gensec_proto.h"
#endif /* __GENSEC_H__ */
File diff suppressed because it is too large Load Diff
+792
View File
@@ -0,0 +1,792 @@
/*
Unix SMB/CIFS implementation.
Kerberos backend for GENSEC
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
Copyright (C) Andrew Tridgell 2001
Copyright (C) Luke Howard 2002-2003
Copyright (C) Stefan Metzmacher 2004-2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "system/kerberos.h"
#include "auth/kerberos/kerberos.h"
#include "librpc/gen_ndr/krb5pac.h"
#include "auth/auth.h"
#include "lib/ldb/include/ldb.h"
#include "auth/auth_sam.h"
#include "system/network.h"
#include "lib/socket/socket.h"
#include "librpc/rpc/dcerpc.h"
#include "auth/credentials/credentials.h"
#include "auth/credentials/credentials_krb5.h"
#include "auth/gensec/gensec.h"
enum GENSEC_KRB5_STATE {
GENSEC_KRB5_SERVER_START,
GENSEC_KRB5_CLIENT_START,
GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
GENSEC_KRB5_DONE
};
struct gensec_krb5_state {
DATA_BLOB session_key;
DATA_BLOB pac;
enum GENSEC_KRB5_STATE state_position;
struct smb_krb5_context *smb_krb5_context;
krb5_auth_context auth_context;
krb5_data enc_ticket;
krb5_keyblock *keyblock;
krb5_ticket *ticket;
BOOL gssapi;
};
static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
{
if (!gensec_krb5_state->smb_krb5_context) {
/* We can't clean anything else up unless we started up this far */
return 0;
}
if (gensec_krb5_state->enc_ticket.length) {
kerberos_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context,
&gensec_krb5_state->enc_ticket);
}
if (gensec_krb5_state->ticket) {
krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->ticket);
}
/* ccache freed in a child destructor */
krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->keyblock);
if (gensec_krb5_state->auth_context) {
krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->auth_context);
}
return 0;
}
static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
{
krb5_error_code ret;
struct gensec_krb5_state *gensec_krb5_state;
struct cli_credentials *creds;
const struct socket_address *my_addr, *peer_addr;
krb5_address my_krb5_addr, peer_krb5_addr;
creds = gensec_get_credentials(gensec_security);
if (!creds) {
return NT_STATUS_INVALID_PARAMETER;
}
gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state);
if (!gensec_krb5_state) {
return NT_STATUS_NO_MEMORY;
}
gensec_security->private_data = gensec_krb5_state;
gensec_krb5_state->smb_krb5_context = NULL;
gensec_krb5_state->auth_context = NULL;
gensec_krb5_state->ticket = NULL;
ZERO_STRUCT(gensec_krb5_state->enc_ticket);
gensec_krb5_state->keyblock = NULL;
gensec_krb5_state->session_key = data_blob(NULL, 0);
gensec_krb5_state->pac = data_blob(NULL, 0);
gensec_krb5_state->gssapi = False;
talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy);
if (cli_credentials_get_krb5_context(creds, &gensec_krb5_state->smb_krb5_context)) {
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, gensec_krb5_state)));
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->auth_context,
KRB5_AUTH_CONTEXT_DO_SEQUENCE);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, gensec_krb5_state)));
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
my_addr = gensec_get_my_addr(gensec_security);
if (my_addr && my_addr->sockaddr) {
ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
my_addr->sockaddr, &my_krb5_addr);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, gensec_krb5_state)));
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
}
peer_addr = gensec_get_peer_addr(gensec_security);
if (peer_addr && peer_addr->sockaddr) {
ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
peer_addr->sockaddr, &peer_krb5_addr);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, gensec_krb5_state)));
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
}
ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->auth_context,
my_addr ? &my_krb5_addr : NULL,
peer_addr ? &peer_krb5_addr : NULL);
if (ret) {
DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, gensec_krb5_state)));
talloc_free(gensec_krb5_state);
return NT_STATUS_INTERNAL_ERROR;
}
return NT_STATUS_OK;
}
static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security)
{
NTSTATUS nt_status;
struct gensec_krb5_state *gensec_krb5_state;
nt_status = gensec_krb5_start(gensec_security);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
gensec_krb5_state = gensec_security->private_data;
gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START;
return NT_STATUS_OK;
}
static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security)
{
NTSTATUS nt_status = gensec_krb5_server_start(gensec_security);
if (NT_STATUS_IS_OK(nt_status)) {
struct gensec_krb5_state *gensec_krb5_state;
gensec_krb5_state = gensec_security->private_data;
gensec_krb5_state->gssapi = True;
}
return nt_status;
}
static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security)
{
struct gensec_krb5_state *gensec_krb5_state;
krb5_error_code ret;
NTSTATUS nt_status;
struct ccache_container *ccache_container;
const char *hostname;
krb5_flags ap_req_options = AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED;
const char *principal;
krb5_data in_data;
hostname = gensec_get_target_hostname(gensec_security);
if (!hostname) {
DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
return NT_STATUS_INVALID_PARAMETER;
}
if (is_ipaddress(hostname)) {
DEBUG(2, ("Cannot do krb5 to an IP address"));
return NT_STATUS_INVALID_PARAMETER;
}
if (strcmp(hostname, "localhost") == 0) {
DEBUG(2, ("krb5 to 'localhost' does not make sense"));
return NT_STATUS_INVALID_PARAMETER;
}
nt_status = gensec_krb5_start(gensec_security);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
gensec_krb5_state = gensec_security->private_data;
gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), &ccache_container);
if (ret) {
DEBUG(1,("gensec_krb5_start: cli_credentials_get_ccache failed: %s\n",
error_message(ret)));
return NT_STATUS_UNSUCCESSFUL;
}
in_data.length = 0;
principal = gensec_get_target_principal(gensec_security);
if (principal && lp_client_use_spnego_principal()) {
krb5_principal target_principal;
ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
&target_principal);
if (ret == 0) {
ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context,
&gensec_krb5_state->auth_context,
ap_req_options,
target_principal,
&in_data, ccache_container->ccache,
&gensec_krb5_state->enc_ticket);
krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context,
target_principal);
}
} else {
ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context,
&gensec_krb5_state->auth_context,
ap_req_options,
gensec_get_target_service(gensec_security),
hostname,
&in_data, ccache_container->ccache,
&gensec_krb5_state->enc_ticket);
}
switch (ret) {
case 0:
return NT_STATUS_OK;
case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n",
hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
case KRB5_KDC_UNREACH:
DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
case KRB5KDC_ERR_PREAUTH_FAILED:
case KRB5KRB_AP_ERR_TKT_EXPIRED:
case KRB5_CC_END:
/* Too much clock skew - we will need to kinit to re-skew the clock */
case KRB5KRB_AP_ERR_SKEW:
case KRB5_KDCREP_SKEW:
{
DEBUG(3, ("kerberos (mk_req) failed: %s\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
/*fall through*/
}
/* just don't print a message for these really ordinary messages */
case KRB5_FCC_NOFILE:
case KRB5_CC_NOTFOUND:
case ENOENT:
return NT_STATUS_UNSUCCESSFUL;
break;
default:
DEBUG(0, ("kerberos: %s\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
return NT_STATUS_UNSUCCESSFUL;
}
}
static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security)
{
NTSTATUS nt_status = gensec_krb5_client_start(gensec_security);
if (NT_STATUS_IS_OK(nt_status)) {
struct gensec_krb5_state *gensec_krb5_state;
gensec_krb5_state = gensec_security->private_data;
gensec_krb5_state->gssapi = True;
}
return nt_status;
}
/**
* Check if the packet is one for this mechansim
*
* @param gensec_security GENSEC state
* @param in The request, as a DATA_BLOB
* @return Error, INVALID_PARAMETER if it's not a packet for us
* or NT_STATUS_OK if the packet is ok.
*/
static NTSTATUS gensec_fake_gssapi_krb5_magic(struct gensec_security *gensec_security,
const DATA_BLOB *in)
{
if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) {
return NT_STATUS_OK;
} else {
return NT_STATUS_INVALID_PARAMETER;
}
}
/**
* Next state function for the Krb5 GENSEC mechanism
*
* @param gensec_krb5_state KRB5 State
* @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
* @param in The request, as a DATA_BLOB
* @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
* @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
* or NT_STATUS_OK if the user is authenticated.
*/
static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_error_code ret = 0;
NTSTATUS nt_status;
switch (gensec_krb5_state->state_position) {
case GENSEC_KRB5_CLIENT_START:
{
DATA_BLOB unwrapped_out;
if (gensec_krb5_state->gssapi) {
unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
/* wrap that up in a nice GSS-API wrapping */
*out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
} else {
*out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
}
gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
return nt_status;
}
case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
{
DATA_BLOB unwrapped_in;
krb5_data inbuf;
krb5_ap_rep_enc_part *repl = NULL;
uint8_t tok_id[2];
if (gensec_krb5_state->gssapi) {
if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
dump_data_pw("Mutual authentication message:\n", in.data, in.length);
return NT_STATUS_INVALID_PARAMETER;
}
} else {
unwrapped_in = in;
}
/* TODO: check the tok_id */
inbuf.data = unwrapped_in.data;
inbuf.length = unwrapped_in.length;
ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context,
gensec_krb5_state->auth_context,
&inbuf, &repl);
if (ret) {
DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
dump_data_pw("Mutual authentication message:\n", inbuf.data, inbuf.length);
nt_status = NT_STATUS_ACCESS_DENIED;
} else {
*out = data_blob(NULL, 0);
nt_status = NT_STATUS_OK;
gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
}
if (repl) {
krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
}
return nt_status;
}
case GENSEC_KRB5_SERVER_START:
{
DATA_BLOB unwrapped_in;
DATA_BLOB unwrapped_out = data_blob(NULL, 0);
krb5_data inbuf, outbuf;
uint8_t tok_id[2];
struct keytab_container *keytab;
krb5_principal server_in_keytab;
if (!in.data) {
return NT_STATUS_INVALID_PARAMETER;
}
/* Grab the keytab, however generated */
ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security), &keytab);
if (ret) {
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
/* This ensures we lookup the correct entry in that keytab */
ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security),
gensec_krb5_state->smb_krb5_context,
&server_in_keytab);
if (ret) {
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
}
/* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
if (gensec_krb5_state->gssapi
&& gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
inbuf.data = unwrapped_in.data;
inbuf.length = unwrapped_in.length;
} else {
inbuf.data = in.data;
inbuf.length = in.length;
}
ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context,
&gensec_krb5_state->auth_context,
&inbuf, keytab->keytab, server_in_keytab,
&outbuf,
&gensec_krb5_state->ticket,
&gensec_krb5_state->keyblock);
if (ret) {
return NT_STATUS_LOGON_FAILURE;
}
unwrapped_out.data = outbuf.data;
unwrapped_out.length = outbuf.length;
gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
/* wrap that up in a nice GSS-API wrapping */
if (gensec_krb5_state->gssapi) {
*out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
} else {
*out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
}
krb5_data_free(&outbuf);
return NT_STATUS_OK;
}
case GENSEC_KRB5_DONE:
default:
/* Asking too many times... */
return NT_STATUS_INVALID_PARAMETER;
}
}
static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security,
DATA_BLOB *session_key)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
krb5_keyblock *skey;
krb5_error_code err = -1;
if (gensec_krb5_state->session_key.data) {
*session_key = gensec_krb5_state->session_key;
return NT_STATUS_OK;
}
switch (gensec_security->gensec_role) {
case GENSEC_CLIENT:
err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
break;
case GENSEC_SERVER:
err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
break;
}
if (err == 0 && skey != NULL) {
DEBUG(10, ("Got KRB5 session key of length %d\n",
(int)KRB5_KEY_LENGTH(skey)));
gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state,
KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
*session_key = gensec_krb5_state->session_key;
dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
krb5_free_keyblock(context, skey);
return NT_STATUS_OK;
} else {
DEBUG(10, ("KRB5 error getting session key %d\n", err));
return NT_STATUS_NO_USER_SESSION_KEY;
}
}
static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
struct auth_session_info **_session_info)
{
NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
struct auth_serversupplied_info *server_info = NULL;
struct auth_session_info *session_info = NULL;
struct PAC_LOGON_INFO *logon_info;
krb5_principal client_principal;
char *principal_string;
DATA_BLOB pac;
krb5_data pac_data;
krb5_error_code ret;
TALLOC_CTX *mem_ctx = talloc_new(gensec_security);
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
if (ret) {
DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n",
smb_get_krb5_error_message(context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context,
client_principal, &principal_string);
if (ret) {
DEBUG(1, ("Unable to parse client principal: %s\n",
smb_get_krb5_error_message(context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket,
KRB5_AUTHDATA_WIN2K_PAC,
&pac_data);
if (ret && lp_parm_bool(-1, "gensec", "require_pac", False)) {
DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access: %s \n",
principal_string,
smb_get_krb5_error_message(context,
ret, mem_ctx)));
krb5_free_principal(context, client_principal);
free(principal_string);
return NT_STATUS_ACCESS_DENIED;
} else if (ret) {
/* NO pac */
DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n",
smb_get_krb5_error_message(context,
ret, mem_ctx)));
nt_status = sam_get_server_info_principal(mem_ctx, principal_string,
&server_info);
krb5_free_principal(context, client_principal);
free(principal_string);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
} else {
/* Found pac */
union netr_Validation validation;
free(principal_string);
pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length);
if (!pac.data) {
krb5_free_principal(context, client_principal);
talloc_free(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
/* decode and verify the pac */
nt_status = kerberos_pac_logon_info(gensec_krb5_state, &logon_info, pac,
gensec_krb5_state->smb_krb5_context->krb5_context,
NULL, gensec_krb5_state->keyblock,
client_principal,
gensec_krb5_state->ticket->ticket.authtime, NULL);
krb5_free_principal(context, client_principal);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
validation.sam3 = &logon_info->info3;
nt_status = make_server_info_netlogon_validation(mem_ctx,
NULL,
3, &validation,
&server_info);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
}
/* references the server_info into the session_info */
nt_status = auth_generate_session_info(mem_ctx, server_info, &session_info);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
nt_status = gensec_krb5_session_key(gensec_security, &session_info->session_key);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
*_session_info = session_info;
talloc_steal(gensec_krb5_state, session_info);
talloc_free(mem_ctx);
return NT_STATUS_OK;
}
static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
krb5_error_code ret;
krb5_data input, output;
input.length = in->length;
input.data = in->data;
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
ret = krb5_mk_priv(context, auth_context, &input, &output, NULL);
if (ret) {
DEBUG(1, ("krb5_mk_priv failed: %s\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, mem_ctx)));
return NT_STATUS_ACCESS_DENIED;
}
*out = data_blob_talloc(mem_ctx, output.data, output.length);
krb5_data_free(&output);
} else {
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_OK;
}
static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
krb5_auth_context auth_context = gensec_krb5_state->auth_context;
krb5_error_code ret;
krb5_data input, output;
krb5_replay_data replay;
input.length = in->length;
input.data = in->data;
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
if (ret) {
DEBUG(1, ("krb5_rd_priv failed: %s\n",
smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context,
ret, mem_ctx)));
return NT_STATUS_ACCESS_DENIED;
}
*out = data_blob_talloc(mem_ctx, output.data, output.length);
krb5_data_free(&output);
} else {
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_OK;
}
static BOOL gensec_krb5_have_feature(struct gensec_security *gensec_security,
uint32_t feature)
{
struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
if (feature & GENSEC_FEATURE_SESSION_KEY) {
return True;
}
if (!gensec_krb5_state->gssapi &&
(feature & GENSEC_FEATURE_SEAL)) {
return True;
}
return False;
}
static const char *gensec_krb5_oids[] = {
GENSEC_OID_KERBEROS5,
GENSEC_OID_KERBEROS5_OLD,
NULL
};
static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
.name = "fake_gssapi_krb5",
.auth_type = DCERPC_AUTH_TYPE_KRB5,
.oid = gensec_krb5_oids,
.client_start = gensec_fake_gssapi_krb5_client_start,
.server_start = gensec_fake_gssapi_krb5_server_start,
.update = gensec_krb5_update,
.magic = gensec_fake_gssapi_krb5_magic,
.session_key = gensec_krb5_session_key,
.session_info = gensec_krb5_session_info,
.have_feature = gensec_krb5_have_feature,
.enabled = False,
.kerberos = True,
.priority = GENSEC_KRB5
};
static const struct gensec_security_ops gensec_krb5_security_ops = {
.name = "krb5",
.client_start = gensec_krb5_client_start,
.server_start = gensec_krb5_server_start,
.update = gensec_krb5_update,
.session_key = gensec_krb5_session_key,
.session_info = gensec_krb5_session_info,
.have_feature = gensec_krb5_have_feature,
.wrap = gensec_krb5_wrap,
.unwrap = gensec_krb5_unwrap,
.enabled = True,
.kerberos = True,
.priority = GENSEC_KRB5
};
NTSTATUS gensec_krb5_init(void)
{
NTSTATUS ret;
auth_init();
ret = gensec_register(&gensec_krb5_security_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_krb5_security_ops.name));
return ret;
}
ret = gensec_register(&gensec_fake_gssapi_krb5_security_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_krb5_security_ops.name));
return ret;
}
return ret;
}
+275
View File
@@ -0,0 +1,275 @@
/*
Unix SMB/CIFS implementation.
dcerpc schannel operations
Copyright (C) Andrew Tridgell 2004
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "librpc/gen_ndr/ndr_schannel.h"
#include "auth/auth.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
#include "auth/gensec/schannel.h"
#include "auth/gensec/schannel_state.h"
#include "auth/gensec/schannel_proto.h"
#include "librpc/rpc/dcerpc.h"
static size_t schannel_sig_size(struct gensec_security *gensec_security, size_t data_size)
{
return 32;
}
static NTSTATUS schannel_session_key(struct gensec_security *gensec_security,
DATA_BLOB *session_key)
{
return NT_STATUS_NOT_IMPLEMENTED;
}
static NTSTATUS schannel_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out)
{
struct schannel_state *state = gensec_security->private_data;
NTSTATUS status;
struct schannel_bind bind_schannel;
struct schannel_bind_ack bind_schannel_ack;
struct creds_CredentialState *creds;
const char *workstation;
const char *domain;
*out = data_blob(NULL, 0);
switch (gensec_security->gensec_role) {
case GENSEC_CLIENT:
if (state->state != SCHANNEL_STATE_START) {
/* we could parse the bind ack, but we don't know what it is yet */
return NT_STATUS_OK;
}
state->creds = talloc_reference(state, cli_credentials_get_netlogon_creds(gensec_security->credentials));
bind_schannel.unknown1 = 0;
#if 0
/* to support this we'd need to have access to the full domain name */
bind_schannel.bind_type = 23;
bind_schannel.u.info23.domain = cli_credentials_get_domain(gensec_security->credentials);
bind_schannel.u.info23.workstation = cli_credentials_get_workstation(gensec_security->credentials);
bind_schannel.u.info23.dnsdomain = cli_credentials_get_realm(gensec_security->credentials);
/* w2k3 refuses us if we use the full DNS workstation?
why? perhaps because we don't fill in the dNSHostName
attribute in the machine account? */
bind_schannel.u.info23.dnsworkstation = cli_credentials_get_workstation(gensec_security->credentials);
#else
bind_schannel.bind_type = 3;
bind_schannel.u.info3.domain = cli_credentials_get_domain(gensec_security->credentials);
bind_schannel.u.info3.workstation = cli_credentials_get_workstation(gensec_security->credentials);
#endif
status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel,
(ndr_push_flags_fn_t)ndr_push_schannel_bind);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("Could not create schannel bind: %s\n",
nt_errstr(status)));
return status;
}
state->state = SCHANNEL_STATE_UPDATE_1;
return NT_STATUS_MORE_PROCESSING_REQUIRED;
case GENSEC_SERVER:
if (state->state != SCHANNEL_STATE_START) {
/* no third leg on this protocol */
return NT_STATUS_INVALID_PARAMETER;
}
/* parse the schannel startup blob */
status = ndr_pull_struct_blob(&in, out_mem_ctx, &bind_schannel,
(ndr_pull_flags_fn_t)ndr_pull_schannel_bind);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (bind_schannel.bind_type == 23) {
workstation = bind_schannel.u.info23.workstation;
domain = bind_schannel.u.info23.domain;
} else {
workstation = bind_schannel.u.info3.workstation;
domain = bind_schannel.u.info3.domain;
}
/* pull the session key for this client */
status = schannel_fetch_session_key(out_mem_ctx, workstation,
domain, &creds);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("Could not find session key for attempted schannel connection from %s: %s\n",
workstation, nt_errstr(status)));
return status;
}
state->creds = talloc_reference(state, creds);
bind_schannel_ack.unknown1 = 1;
bind_schannel_ack.unknown2 = 0;
bind_schannel_ack.unknown3 = 0x6c0000;
status = ndr_push_struct_blob(out, out_mem_ctx, &bind_schannel_ack,
(ndr_push_flags_fn_t)ndr_push_schannel_bind_ack);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("Could not return schannel bind ack for client %s: %s\n",
workstation, nt_errstr(status)));
return status;
}
state->state = SCHANNEL_STATE_UPDATE_1;
return NT_STATUS_OK;
}
return NT_STATUS_INVALID_PARAMETER;
}
/**
* Return the struct creds_CredentialState.
*
* Make sure not to call this unless gensec is using schannel...
*/
/* TODO: make this non-public */
_PUBLIC_ NTSTATUS dcerpc_schannel_creds(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
struct creds_CredentialState **creds)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
*creds = talloc_reference(mem_ctx, state->creds);
if (!*creds) {
return NT_STATUS_NO_MEMORY;
}
return NT_STATUS_OK;
}
/**
* Returns anonymous credentials for schannel, matching Win2k3.
*
*/
static NTSTATUS schannel_session_info(struct gensec_security *gensec_security,
struct auth_session_info **_session_info)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
return auth_anonymous_session_info(state, _session_info);
}
static NTSTATUS schannel_start(struct gensec_security *gensec_security)
{
struct schannel_state *state;
state = talloc(gensec_security, struct schannel_state);
if (!state) {
return NT_STATUS_NO_MEMORY;
}
state->state = SCHANNEL_STATE_START;
state->seq_num = 0;
gensec_security->private_data = state;
return NT_STATUS_OK;
}
static NTSTATUS schannel_server_start(struct gensec_security *gensec_security)
{
NTSTATUS status;
struct schannel_state *state;
status = schannel_start(gensec_security);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
state = gensec_security->private_data;
state->initiator = False;
return NT_STATUS_OK;
}
static NTSTATUS schannel_client_start(struct gensec_security *gensec_security)
{
NTSTATUS status;
struct schannel_state *state;
status = schannel_start(gensec_security);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
state = gensec_security->private_data;
state->initiator = True;
return NT_STATUS_OK;
}
static BOOL schannel_have_feature(struct gensec_security *gensec_security,
uint32_t feature)
{
if (feature & (GENSEC_FEATURE_SIGN |
GENSEC_FEATURE_SEAL)) {
return True;
}
if (feature & GENSEC_FEATURE_DCE_STYLE) {
return True;
}
if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
return True;
}
return False;
}
static const struct gensec_security_ops gensec_schannel_security_ops = {
.name = "schannel",
.auth_type = DCERPC_AUTH_TYPE_SCHANNEL,
.client_start = schannel_client_start,
.server_start = schannel_server_start,
.update = schannel_update,
.seal_packet = schannel_seal_packet,
.sign_packet = schannel_sign_packet,
.check_packet = schannel_check_packet,
.unseal_packet = schannel_unseal_packet,
.session_key = schannel_session_key,
.session_info = schannel_session_info,
.sig_size = schannel_sig_size,
.have_feature = schannel_have_feature,
.enabled = True,
.priority = GENSEC_SCHANNEL
};
NTSTATUS gensec_schannel_init(void)
{
NTSTATUS ret;
ret = gensec_register(&gensec_schannel_security_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_schannel_security_ops.name));
return ret;
}
return ret;
}
+37
View File
@@ -0,0 +1,37 @@
/*
Unix SMB/CIFS implementation.
dcerpc schannel operations
Copyright (C) Andrew Tridgell 2004
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "libcli/auth/credentials.h"
enum schannel_position {
SCHANNEL_STATE_START = 0,
SCHANNEL_STATE_UPDATE_1
};
struct schannel_state {
enum schannel_position state;
uint32_t seq_num;
BOOL initiator;
struct creds_CredentialState *creds;
};
+285
View File
@@ -0,0 +1,285 @@
/*
Unix SMB/CIFS implementation.
schannel library code
Copyright (C) Andrew Tridgell 2004
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "lib/crypto/crypto.h"
#include "auth/auth.h"
#include "auth/gensec/schannel.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
#define NETSEC_SIGN_SIGNATURE { 0x77, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 }
#define NETSEC_SEAL_SIGNATURE { 0x77, 0x00, 0x7a, 0x00, 0xff, 0xff, 0x00, 0x00 }
/*******************************************************************
Encode or Decode the sequence number (which is symmetric)
********************************************************************/
static void netsec_deal_with_seq_num(struct schannel_state *state,
const uint8_t packet_digest[8],
uint8_t seq_num[8])
{
static const uint8_t zeros[4];
uint8_t sequence_key[16];
uint8_t digest1[16];
hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1);
hmac_md5(digest1, packet_digest, 8, sequence_key);
arcfour_crypt(seq_num, sequence_key, 8);
state->seq_num++;
}
/*******************************************************************
Calculate the key with which to encode the data payload
********************************************************************/
static void netsec_get_sealing_key(const uint8_t session_key[16],
const uint8_t seq_num[8],
uint8_t sealing_key[16])
{
static const uint8_t zeros[4];
uint8_t digest2[16];
uint8_t sess_kf0[16];
int i;
for (i = 0; i < 16; i++) {
sess_kf0[i] = session_key[i] ^ 0xf0;
}
hmac_md5(sess_kf0, zeros, 4, digest2);
hmac_md5(digest2, seq_num, 8, sealing_key);
}
/*******************************************************************
Create a digest over the entire packet (including the data), and
MD5 it with the session key.
********************************************************************/
static void schannel_digest(const uint8_t sess_key[16],
const uint8_t netsec_sig[8],
const uint8_t *confounder,
const uint8_t *data, size_t data_len,
uint8_t digest_final[16])
{
uint8_t packet_digest[16];
static const uint8_t zeros[4];
struct MD5Context ctx;
MD5Init(&ctx);
MD5Update(&ctx, zeros, 4);
MD5Update(&ctx, netsec_sig, 8);
if (confounder) {
MD5Update(&ctx, confounder, 8);
}
MD5Update(&ctx, data, data_len);
MD5Final(packet_digest, &ctx);
hmac_md5(sess_key, packet_digest, sizeof(packet_digest), digest_final);
}
/*
unseal a packet
*/
NTSTATUS schannel_unseal_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
uint8_t digest_final[16];
uint8_t confounder[8];
uint8_t seq_num[8];
uint8_t sealing_key[16];
static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
if (sig->length != 32) {
return NT_STATUS_ACCESS_DENIED;
}
memcpy(confounder, sig->data+24, 8);
RSIVAL(seq_num, 0, state->seq_num);
SIVAL(seq_num, 4, state->initiator?0:0x80);
netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
arcfour_crypt(confounder, sealing_key, 8);
arcfour_crypt(data, sealing_key, length);
schannel_digest(state->creds->session_key,
netsec_sig, confounder,
data, length, digest_final);
if (memcmp(digest_final, sig->data+16, 8) != 0) {
dump_data_pw("calc digest:", digest_final, 8);
dump_data_pw("wire digest:", sig->data+16, 8);
return NT_STATUS_ACCESS_DENIED;
}
netsec_deal_with_seq_num(state, digest_final, seq_num);
if (memcmp(seq_num, sig->data+8, 8) != 0) {
dump_data_pw("calc seq num:", seq_num, 8);
dump_data_pw("wire seq num:", sig->data+8, 8);
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_OK;
}
/*
check the signature on a packet
*/
NTSTATUS schannel_check_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
uint8_t digest_final[16];
uint8_t seq_num[8];
static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
/* w2k sends just 24 bytes and skip the confounder */
if (sig->length != 32 && sig->length != 24) {
return NT_STATUS_ACCESS_DENIED;
}
RSIVAL(seq_num, 0, state->seq_num);
SIVAL(seq_num, 4, state->initiator?0:0x80);
dump_data_pw("seq_num:\n", seq_num, 8);
dump_data_pw("sess_key:\n", state->creds->session_key, 16);
schannel_digest(state->creds->session_key,
netsec_sig, NULL,
data, length, digest_final);
netsec_deal_with_seq_num(state, digest_final, seq_num);
if (memcmp(seq_num, sig->data+8, 8) != 0) {
dump_data_pw("calc seq num:", seq_num, 8);
dump_data_pw("wire seq num:", sig->data+8, 8);
return NT_STATUS_ACCESS_DENIED;
}
if (memcmp(digest_final, sig->data+16, 8) != 0) {
dump_data_pw("calc digest:", digest_final, 8);
dump_data_pw("wire digest:", sig->data+16, 8);
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_OK;
}
/*
seal a packet
*/
NTSTATUS schannel_seal_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
uint8_t digest_final[16];
uint8_t confounder[8];
uint8_t seq_num[8];
uint8_t sealing_key[16];
static const uint8_t netsec_sig[8] = NETSEC_SEAL_SIGNATURE;
generate_random_buffer(confounder, 8);
RSIVAL(seq_num, 0, state->seq_num);
SIVAL(seq_num, 4, state->initiator?0x80:0);
schannel_digest(state->creds->session_key,
netsec_sig, confounder,
data, length, digest_final);
netsec_get_sealing_key(state->creds->session_key, seq_num, sealing_key);
arcfour_crypt(confounder, sealing_key, 8);
arcfour_crypt(data, sealing_key, length);
netsec_deal_with_seq_num(state, digest_final, seq_num);
(*sig) = data_blob_talloc(mem_ctx, NULL, 32);
memcpy(sig->data, netsec_sig, 8);
memcpy(sig->data+8, seq_num, 8);
memcpy(sig->data+16, digest_final, 8);
memcpy(sig->data+24, confounder, 8);
dump_data_pw("signature:", sig->data+ 0, 8);
dump_data_pw("seq_num :", sig->data+ 8, 8);
dump_data_pw("digest :", sig->data+16, 8);
dump_data_pw("confound :", sig->data+24, 8);
return NT_STATUS_OK;
}
/*
sign a packet
*/
NTSTATUS schannel_sign_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig)
{
struct schannel_state *state = talloc_get_type(gensec_security->private_data, struct schannel_state);
uint8_t digest_final[16];
uint8_t seq_num[8];
static const uint8_t netsec_sig[8] = NETSEC_SIGN_SIGNATURE;
RSIVAL(seq_num, 0, state->seq_num);
SIVAL(seq_num, 4, state->initiator?0x80:0);
schannel_digest(state->creds->session_key,
netsec_sig, NULL,
data, length, digest_final);
netsec_deal_with_seq_num(state, digest_final, seq_num);
(*sig) = data_blob_talloc(mem_ctx, NULL, 32);
memcpy(sig->data, netsec_sig, 8);
memcpy(sig->data+8, seq_num, 8);
memcpy(sig->data+16, digest_final, 8);
memset(sig->data+24, 0, 8);
dump_data_pw("signature:", sig->data+ 0, 8);
dump_data_pw("seq_num :", sig->data+ 8, 8);
dump_data_pw("digest :", sig->data+16, 8);
dump_data_pw("confound :", sig->data+24, 8);
return NT_STATUS_OK;
}
+285
View File
@@ -0,0 +1,285 @@
/*
Unix SMB/CIFS implementation.
module to store/fetch session keys for the schannel server
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "lib/ldb/include/ldb.h"
#include "lib/ldb/include/ldb_errors.h"
#include "dsdb/samdb/samdb.h"
#include "db_wrap.h"
#include "libcli/auth/libcli_auth.h"
#include "auth/auth.h"
/**
connect to the schannel ldb
*/
struct ldb_context *schannel_db_connect(TALLOC_CTX *mem_ctx)
{
char *path;
struct ldb_context *ldb;
BOOL existed;
const char *init_ldif =
"dn: @ATTRIBUTES\n" \
"computerName: CASE_INSENSITIVE\n" \
"flatname: CASE_INSENSITIVE\n";
path = smbd_tmp_path(mem_ctx, "schannel.ldb");
if (!path) {
return NULL;
}
existed = file_exist(path);
ldb = ldb_wrap_connect(mem_ctx, path, system_session(mem_ctx),
NULL, LDB_FLG_NOSYNC, NULL);
talloc_free(path);
if (!ldb) {
return NULL;
}
if (!existed) {
gendb_add_ldif(ldb, init_ldif);
}
return ldb;
}
/*
remember an established session key for a netr server authentication
use a simple ldb structure
*/
NTSTATUS schannel_store_session_key_ldb(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb,
struct creds_CredentialState *creds)
{
struct ldb_message *msg;
struct ldb_val val, seed, client_state, server_state;
char *f;
char *sct;
int ret;
f = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->negotiate_flags);
if (f == NULL) {
return NT_STATUS_NO_MEMORY;
}
sct = talloc_asprintf(mem_ctx, "%u", (unsigned int)creds->secure_channel_type);
if (sct == NULL) {
return NT_STATUS_NO_MEMORY;
}
msg = ldb_msg_new(ldb);
if (msg == NULL) {
return NT_STATUS_NO_MEMORY;
}
msg->dn = ldb_dn_new_fmt(msg, ldb, "computerName=%s", creds->computer_name);
if ( ! msg->dn) {
return NT_STATUS_NO_MEMORY;
}
val.data = creds->session_key;
val.length = sizeof(creds->session_key);
seed.data = creds->seed.data;
seed.length = sizeof(creds->seed.data);
client_state.data = creds->client.data;
client_state.length = sizeof(creds->client.data);
server_state.data = creds->server.data;
server_state.length = sizeof(creds->server.data);
ldb_msg_add_string(msg, "objectClass", "schannelState");
ldb_msg_add_value(msg, "sessionKey", &val, NULL);
ldb_msg_add_value(msg, "seed", &seed, NULL);
ldb_msg_add_value(msg, "clientState", &client_state, NULL);
ldb_msg_add_value(msg, "serverState", &server_state, NULL);
ldb_msg_add_string(msg, "negotiateFlags", f);
ldb_msg_add_string(msg, "secureChannelType", sct);
ldb_msg_add_string(msg, "accountName", creds->account_name);
ldb_msg_add_string(msg, "computerName", creds->computer_name);
ldb_msg_add_string(msg, "flatname", creds->domain);
samdb_msg_add_dom_sid(ldb, mem_ctx, msg, "objectSid", creds->sid);
ldb_delete(ldb, msg->dn);
ret = ldb_add(ldb, msg);
if (ret != 0) {
DEBUG(0,("Unable to add %s to session key db - %s\n",
ldb_dn_get_linearized(msg->dn), ldb_errstring(ldb)));
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
return NT_STATUS_OK;
}
NTSTATUS schannel_store_session_key(TALLOC_CTX *mem_ctx,
struct creds_CredentialState *creds)
{
struct ldb_context *ldb;
NTSTATUS nt_status;
int ret;
ldb = schannel_db_connect(mem_ctx);
if (!ldb) {
return NT_STATUS_ACCESS_DENIED;
}
ret = ldb_transaction_start(ldb);
if (ret != 0) {
talloc_free(ldb);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
nt_status = schannel_store_session_key_ldb(mem_ctx, ldb, creds);
if (NT_STATUS_IS_OK(nt_status)) {
ret = ldb_transaction_commit(ldb);
} else {
ret = ldb_transaction_cancel(ldb);
}
if (ret != 0) {
DEBUG(0,("Unable to commit adding credentials for %s to schannel key db - %s\n",
creds->computer_name, ldb_errstring(ldb)));
talloc_free(ldb);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
talloc_free(ldb);
return nt_status;
}
/*
read back a credentials back for a computer
*/
NTSTATUS schannel_fetch_session_key_ldb(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb,
const char *computer_name,
const char *domain,
struct creds_CredentialState **creds)
{
struct ldb_result *res;
int ret;
const struct ldb_val *val;
*creds = talloc_zero(mem_ctx, struct creds_CredentialState);
if (!*creds) {
return NT_STATUS_NO_MEMORY;
}
ret = ldb_search_exp_fmt(ldb, mem_ctx, &res,
NULL, LDB_SCOPE_SUBTREE, NULL,
"(&(computerName=%s)(flatname=%s))", computer_name, domain);
if (ret != LDB_SUCCESS) {
DEBUG(3,("schannel: Failed to find a record for client %s: %s\n", computer_name, ldb_errstring(ldb)));
return NT_STATUS_INVALID_HANDLE;
}
if (res->count != 1) {
DEBUG(3,("schannel: Failed to find a record for client: %s (found %d records)\n", computer_name, res->count));
talloc_free(res);
return NT_STATUS_INVALID_HANDLE;
}
val = ldb_msg_find_ldb_val(res->msgs[0], "sessionKey");
if (val == NULL || val->length != 16) {
DEBUG(1,("schannel: record in schannel DB must contain a sessionKey of length 16, when searching for client: %s\n", computer_name));
talloc_free(res);
return NT_STATUS_INTERNAL_ERROR;
}
memcpy((*creds)->session_key, val->data, 16);
val = ldb_msg_find_ldb_val(res->msgs[0], "seed");
if (val == NULL || val->length != 8) {
DEBUG(1,("schannel: record in schannel DB must contain a vaid seed of length 8, when searching for client: %s\n", computer_name));
talloc_free(res);
return NT_STATUS_INTERNAL_ERROR;
}
memcpy((*creds)->seed.data, val->data, 8);
val = ldb_msg_find_ldb_val(res->msgs[0], "clientState");
if (val == NULL || val->length != 8) {
DEBUG(1,("schannel: record in schannel DB must contain a vaid clientState of length 8, when searching for client: %s\n", computer_name));
talloc_free(res);
return NT_STATUS_INTERNAL_ERROR;
}
memcpy((*creds)->client.data, val->data, 8);
val = ldb_msg_find_ldb_val(res->msgs[0], "serverState");
if (val == NULL || val->length != 8) {
DEBUG(1,("schannel: record in schannel DB must contain a vaid serverState of length 8, when searching for client: %s\n", computer_name));
talloc_free(res);
return NT_STATUS_INTERNAL_ERROR;
}
memcpy((*creds)->server.data, val->data, 8);
(*creds)->negotiate_flags = ldb_msg_find_attr_as_int(res->msgs[0], "negotiateFlags", 0);
(*creds)->secure_channel_type = ldb_msg_find_attr_as_int(res->msgs[0], "secureChannelType", 0);
(*creds)->account_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "accountName", NULL));
if ((*creds)->account_name == NULL) {
talloc_free(res);
return NT_STATUS_NO_MEMORY;
}
(*creds)->computer_name = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "computerName", NULL));
if ((*creds)->computer_name == NULL) {
talloc_free(res);
return NT_STATUS_NO_MEMORY;
}
(*creds)->domain = talloc_strdup(*creds, ldb_msg_find_attr_as_string(res->msgs[0], "flatname", NULL));
if ((*creds)->domain == NULL) {
talloc_free(res);
return NT_STATUS_NO_MEMORY;
}
(*creds)->sid = samdb_result_dom_sid(*creds, res->msgs[0], "objectSid");
talloc_free(res);
return NT_STATUS_OK;
}
NTSTATUS schannel_fetch_session_key(TALLOC_CTX *mem_ctx,
const char *computer_name,
const char *domain,
struct creds_CredentialState **creds)
{
NTSTATUS nt_status;
struct ldb_context *ldb;
ldb = schannel_db_connect(mem_ctx);
if (!ldb) {
return NT_STATUS_ACCESS_DENIED;
}
nt_status = schannel_fetch_session_key_ldb(mem_ctx, ldb,
computer_name, domain,
creds);
talloc_free(ldb);
return nt_status;
}
+530
View File
@@ -0,0 +1,530 @@
/*
Unix SMB/CIFS implementation.
GENSEC socket interface
Copyright (C) Andrew Bartlett 2006
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "lib/events/events.h"
#include "lib/socket/socket.h"
#include "lib/stream/packet.h"
#include "auth/gensec/gensec.h"
static const struct socket_ops gensec_socket_ops;
struct gensec_socket {
struct gensec_security *gensec_security;
struct socket_context *socket;
struct event_context *ev;
struct packet_context *packet;
DATA_BLOB read_buffer; /* SASL packets are turned into liniarlised data here, for reading */
size_t orig_send_len;
BOOL eof;
NTSTATUS error;
BOOL interrupted;
void (*recv_handler)(void *, uint16_t);
void *recv_private;
int in_extra_read;
BOOL wrap; /* Should we be wrapping on this socket at all? */
};
static NTSTATUS gensec_socket_init_fn(struct socket_context *sock)
{
switch (sock->type) {
case SOCKET_TYPE_STREAM:
break;
default:
return NT_STATUS_INVALID_PARAMETER;
}
sock->backend_name = "gensec";
return NT_STATUS_OK;
}
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
_PUBLIC_ NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed)
{
if (!gensec_security->ops->wrap_packets) {
NTSTATUS nt_status;
size_t max_input_size;
DATA_BLOB unwrapped, wrapped;
max_input_size = gensec_max_input_size(gensec_security);
unwrapped = data_blob_const(in->data, MIN(max_input_size, (size_t)in->length));
nt_status = gensec_wrap(gensec_security,
mem_ctx,
&unwrapped, &wrapped);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
*out = data_blob_talloc(mem_ctx, NULL, 4);
if (!out->data) {
return NT_STATUS_NO_MEMORY;
}
RSIVAL(out->data, 0, wrapped.length);
nt_status = data_blob_append(mem_ctx, out, wrapped.data, wrapped.length);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
*len_processed = unwrapped.length;
return nt_status;
}
return gensec_security->ops->wrap_packets(gensec_security, mem_ctx, in, out,
len_processed);
}
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed)
{
if (!gensec_security->ops->unwrap_packets) {
DATA_BLOB wrapped;
NTSTATUS nt_status;
size_t packet_size;
if (in->length < 4) {
/* Missing the header we already had! */
DEBUG(0, ("Asked to unwrap packet of bogus length! How did we get the short packet?!\n"));
return NT_STATUS_INVALID_PARAMETER;
}
packet_size = RIVAL(in->data, 0);
wrapped = data_blob_const(in->data + 4, packet_size);
if (wrapped.length > (in->length - 4)) {
DEBUG(0, ("Asked to unwrap packed of bogus length %d > %d! How did we get this?!\n",
(int)wrapped.length, (int)(in->length - 4)));
return NT_STATUS_INTERNAL_ERROR;
}
nt_status = gensec_unwrap(gensec_security,
mem_ctx,
&wrapped, out);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
*len_processed = packet_size + 4;
return nt_status;
}
return gensec_security->ops->unwrap_packets(gensec_security, mem_ctx, in, out,
len_processed);
}
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
DATA_BLOB blob, size_t *size)
{
if (gensec_security->ops->packet_full_request) {
return gensec_security->ops->packet_full_request(gensec_security,
blob, size);
}
if (gensec_security->ops->unwrap_packets) {
if (blob.length) {
*size = blob.length;
return NT_STATUS_OK;
}
return STATUS_MORE_ENTRIES;
}
return packet_full_request_u32(NULL, blob, size);
}
static NTSTATUS gensec_socket_full_request(void *private, DATA_BLOB blob, size_t *size)
{
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
struct gensec_security *gensec_security = gensec_socket->gensec_security;
return gensec_packet_full_request(gensec_security, blob, size);
}
/* Try to figure out how much data is waiting to be read */
static NTSTATUS gensec_socket_pending(struct socket_context *sock, size_t *npending)
{
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
if (!gensec_socket->wrap) {
return socket_pending(gensec_socket->socket, npending);
}
if (gensec_socket->read_buffer.length > 0) {
*npending = gensec_socket->read_buffer.length;
return NT_STATUS_OK;
}
/* This is a lie. We hope the decrypted data will always be
* less than this value, so the application just gets a short
* read. Without reading and decrypting it, we can't tell.
* If the SASL mech does compression, then we just need to
* manually trigger read events */
return socket_pending(gensec_socket->socket, npending);
}
/* Note if an error occours, so we can return it up the stack */
static void gensec_socket_error_handler(void *private, NTSTATUS status)
{
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
gensec_socket->eof = True;
} else {
gensec_socket->error = status;
}
}
static void gensec_socket_trigger_read(struct event_context *ev,
struct timed_event *te,
struct timeval t, void *private)
{
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
gensec_socket->in_extra_read++;
gensec_socket->recv_handler(gensec_socket->recv_private, EVENT_FD_READ);
gensec_socket->in_extra_read--;
/* It may well be that, having run the recv handler, we still
* have even more data waiting for us!
*/
if (gensec_socket->read_buffer.length && gensec_socket->recv_handler) {
/* Schedule this funcion to run again */
event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
gensec_socket_trigger_read, gensec_socket);
}
}
/* These two routines could be changed to use a circular buffer of
* some kind, or linked lists, or ... */
static NTSTATUS gensec_socket_recv(struct socket_context *sock, void *buf,
size_t wantlen, size_t *nread)
{
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
if (!gensec_socket->wrap) {
return socket_recv(gensec_socket->socket, buf, wantlen, nread);
}
gensec_socket->error = NT_STATUS_OK;
if (gensec_socket->read_buffer.length == 0) {
/* Process any data on the socket, into the read buffer. At
* this point, the socket is not available for read any
* longer */
packet_recv(gensec_socket->packet);
if (gensec_socket->eof) {
*nread = 0;
return NT_STATUS_OK;
}
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
return gensec_socket->error;
}
if (gensec_socket->read_buffer.length == 0) {
/* Clearly we don't have the entire SASL packet yet,
* so it has not been written into the buffer */
*nread = 0;
return STATUS_MORE_ENTRIES;
}
}
*nread = MIN(wantlen, gensec_socket->read_buffer.length);
memcpy(buf, gensec_socket->read_buffer.data, *nread);
if (gensec_socket->read_buffer.length > *nread) {
memmove(gensec_socket->read_buffer.data,
gensec_socket->read_buffer.data + *nread,
gensec_socket->read_buffer.length - *nread);
}
gensec_socket->read_buffer.length -= *nread;
gensec_socket->read_buffer.data = talloc_realloc(gensec_socket,
gensec_socket->read_buffer.data,
uint8_t,
gensec_socket->read_buffer.length);
if (gensec_socket->read_buffer.length &&
gensec_socket->in_extra_read == 0 &&
gensec_socket->recv_handler) {
/* Manually call a read event, to get this moving
* again (as the socket should be dry, so the normal
* event handler won't trigger) */
event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(),
gensec_socket_trigger_read, gensec_socket);
}
return NT_STATUS_OK;
}
/* Completed SASL packet callback. When we have a 'whole' SASL
* packet, decrypt it, and add it to the read buffer
*
* This function (and anything under it) MUST NOT call the event system
*/
static NTSTATUS gensec_socket_unwrap(void *private, DATA_BLOB blob)
{
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
DATA_BLOB unwrapped;
NTSTATUS nt_status;
TALLOC_CTX *mem_ctx;
size_t packet_size;
mem_ctx = talloc_new(gensec_socket);
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
nt_status = gensec_unwrap_packets(gensec_socket->gensec_security,
mem_ctx,
&blob, &unwrapped,
&packet_size);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
if (packet_size != blob.length) {
DEBUG(0, ("gensec_socket_unwrap: Did not consume entire packet!\n"));
return NT_STATUS_INTERNAL_ERROR;
}
/* We could change this into a linked list, and have
* gensec_socket_recv() and gensec_socket_pending() walk the
* linked list */
nt_status = data_blob_append(gensec_socket, &gensec_socket->read_buffer,
unwrapped.data, unwrapped.length);
talloc_free(mem_ctx);
return nt_status;
}
/* when the data is sent, we know we have not been interrupted */
static void send_callback(void *private)
{
struct gensec_socket *gensec_socket = talloc_get_type(private, struct gensec_socket);
gensec_socket->interrupted = False;
}
/*
send data, but only as much as we allow in one packet.
If this returns STATUS_MORE_ENTRIES, the caller must retry with
exactly the same data, or a NULL blob.
*/
static NTSTATUS gensec_socket_send(struct socket_context *sock,
const DATA_BLOB *blob, size_t *sendlen)
{
NTSTATUS nt_status;
struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
DATA_BLOB wrapped;
TALLOC_CTX *mem_ctx;
if (!gensec_socket->wrap) {
return socket_send(gensec_socket->socket, blob, sendlen);
}
*sendlen = 0;
/* We have have been interupted, so the caller should be
* giving us the same data again. */
if (gensec_socket->interrupted) {
packet_queue_run(gensec_socket->packet);
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
return gensec_socket->error;
} else if (gensec_socket->interrupted) {
return STATUS_MORE_ENTRIES;
} else {
*sendlen = gensec_socket->orig_send_len;
gensec_socket->orig_send_len = 0;
return NT_STATUS_OK;
}
}
mem_ctx = talloc_new(gensec_socket);
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
nt_status = gensec_wrap_packets(gensec_socket->gensec_security,
mem_ctx,
blob, &wrapped,
&gensec_socket->orig_send_len);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(mem_ctx);
return nt_status;
}
gensec_socket->interrupted = True;
gensec_socket->error = NT_STATUS_OK;
nt_status = packet_send_callback(gensec_socket->packet,
wrapped,
send_callback, gensec_socket);
talloc_free(mem_ctx);
packet_queue_run(gensec_socket->packet);
if (!NT_STATUS_IS_OK(gensec_socket->error)) {
return gensec_socket->error;
} else if (gensec_socket->interrupted) {
return STATUS_MORE_ENTRIES;
} else {
*sendlen = gensec_socket->orig_send_len;
gensec_socket->orig_send_len = 0;
return NT_STATUS_OK;
}
}
/* Turn a normal socket into a potentially GENSEC wrapped socket */
NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
struct socket_context *current_socket,
struct event_context *ev,
void (*recv_handler)(void *, uint16_t),
void *recv_private,
struct socket_context **new_socket)
{
struct gensec_socket *gensec_socket;
struct socket_context *new_sock;
NTSTATUS nt_status;
nt_status = socket_create_with_ops(current_socket, &gensec_socket_ops, &new_sock,
SOCKET_TYPE_STREAM, current_socket->flags | SOCKET_FLAG_ENCRYPT);
if (!NT_STATUS_IS_OK(nt_status)) {
*new_socket = NULL;
return nt_status;
}
new_sock->state = current_socket->state;
gensec_socket = talloc(new_sock, struct gensec_socket);
if (gensec_socket == NULL) {
*new_socket = NULL;
return NT_STATUS_NO_MEMORY;
}
new_sock->private_data = gensec_socket;
gensec_socket->socket = current_socket;
if (talloc_reference(gensec_socket, current_socket) == NULL) {
*new_socket = NULL;
return NT_STATUS_NO_MEMORY;
}
/* Nothing to do here, if we are not actually wrapping on this socket */
if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) &&
!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
gensec_socket->wrap = False;
*new_socket = new_sock;
return NT_STATUS_OK;
}
gensec_socket->gensec_security = gensec_security;
gensec_socket->wrap = True;
gensec_socket->eof = False;
gensec_socket->error = NT_STATUS_OK;
gensec_socket->interrupted = False;
gensec_socket->in_extra_read = 0;
gensec_socket->read_buffer = data_blob(NULL, 0);
gensec_socket->recv_handler = recv_handler;
gensec_socket->recv_private = recv_private;
gensec_socket->ev = ev;
gensec_socket->packet = packet_init(gensec_socket);
if (gensec_socket->packet == NULL) {
*new_socket = NULL;
return NT_STATUS_NO_MEMORY;
}
packet_set_private(gensec_socket->packet, gensec_socket);
packet_set_socket(gensec_socket->packet, gensec_socket->socket);
packet_set_callback(gensec_socket->packet, gensec_socket_unwrap);
packet_set_full_request(gensec_socket->packet, gensec_socket_full_request);
packet_set_error_handler(gensec_socket->packet, gensec_socket_error_handler);
packet_set_serialise(gensec_socket->packet);
/* TODO: full-request that knows about maximum packet size */
*new_socket = new_sock;
return NT_STATUS_OK;
}
static NTSTATUS gensec_socket_set_option(struct socket_context *sock, const char *option, const char *val)
{
set_socket_options(socket_get_fd(sock), option);
return NT_STATUS_OK;
}
static char *gensec_socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
{
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
return socket_get_peer_name(gensec->socket, mem_ctx);
}
static struct socket_address *gensec_socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
{
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
return socket_get_peer_addr(gensec->socket, mem_ctx);
}
static struct socket_address *gensec_socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
{
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
return socket_get_my_addr(gensec->socket, mem_ctx);
}
static int gensec_socket_get_fd(struct socket_context *sock)
{
struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
return socket_get_fd(gensec->socket);
}
static const struct socket_ops gensec_socket_ops = {
.name = "gensec",
.fn_init = gensec_socket_init_fn,
.fn_recv = gensec_socket_recv,
.fn_send = gensec_socket_send,
.fn_pending = gensec_socket_pending,
.fn_set_option = gensec_socket_set_option,
.fn_get_peer_name = gensec_socket_get_peer_name,
.fn_get_peer_addr = gensec_socket_get_peer_addr,
.fn_get_my_addr = gensec_socket_get_my_addr,
.fn_get_fd = gensec_socket_get_fd
};
+51
View File
@@ -0,0 +1,51 @@
/*
Unix SMB/CIFS implementation.
Generic Authentication Interface (socket wrapper)
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
struct gensec_security;
struct socket_context;
NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
struct socket_context *current_socket,
struct event_context *ev,
void (*recv_handler)(void *, uint16_t),
void *recv_private,
struct socket_context **new_socket);
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed);
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out,
size_t *len_processed);
/* These functions are for use here only (public because SPNEGO must
* use them for recursion) */
NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
DATA_BLOB blob, size_t *size);
File diff suppressed because it is too large Load Diff
+66
View File
@@ -0,0 +1,66 @@
/*
Unix SMB/CIFS implementation.
RFC2478 Compliant SPNEGO implementation
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define SPNEGO_DELEG_FLAG 0x01
#define SPNEGO_MUTUAL_FLAG 0x02
#define SPNEGO_REPLAY_FLAG 0x04
#define SPNEGO_SEQUENCE_FLAG 0x08
#define SPNEGO_ANON_FLAG 0x10
#define SPNEGO_CONF_FLAG 0x20
#define SPNEGO_INTEG_FLAG 0x40
#define SPNEGO_REQ_FLAG 0x80
enum spnego_negResult {
SPNEGO_ACCEPT_COMPLETED = 0,
SPNEGO_ACCEPT_INCOMPLETE = 1,
SPNEGO_REJECT = 2,
SPNEGO_NONE_RESULT = 3
};
struct spnego_negTokenInit {
const char **mechTypes;
int reqFlags;
DATA_BLOB mechToken;
DATA_BLOB mechListMIC;
char *targetPrincipal;
};
struct spnego_negTokenTarg {
uint8_t negResult;
const char *supportedMech;
DATA_BLOB responseToken;
DATA_BLOB mechListMIC;
};
struct spnego_data {
int type;
struct spnego_negTokenInit negTokenInit;
struct spnego_negTokenTarg negTokenTarg;
};
enum spnego_message_type {
SPNEGO_NEG_TOKEN_INIT = 0,
SPNEGO_NEG_TOKEN_TARG = 1,
};
#include "auth/gensec/spnego_proto.h"
+373
View File
@@ -0,0 +1,373 @@
/*
Unix SMB/CIFS implementation.
RFC2478 Compliant SPNEGO implementation
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/gensec/spnego.h"
#include "auth/gensec/gensec.h"
#include "libcli/util/asn_1.h"
static BOOL read_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
{
ZERO_STRUCTP(token);
asn1_start_tag(asn1, ASN1_CONTEXT(0));
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
int i;
uint8_t context;
if (!asn1_peek_uint8(asn1, &context)) {
asn1->has_error = True;
break;
}
switch (context) {
/* Read mechTypes */
case ASN1_CONTEXT(0):
asn1_start_tag(asn1, ASN1_CONTEXT(0));
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
token->mechTypes = talloc(NULL, const char *);
for (i = 0; !asn1->has_error &&
0 < asn1_tag_remaining(asn1); i++) {
token->mechTypes = talloc_realloc(NULL,
token->mechTypes,
const char *, i+2);
asn1_read_OID(asn1, token->mechTypes + i);
if (token->mechTypes[i]) {
talloc_steal(token->mechTypes,
token->mechTypes[i]);
}
}
token->mechTypes[i] = NULL;
asn1_end_tag(asn1);
asn1_end_tag(asn1);
break;
/* Read reqFlags */
case ASN1_CONTEXT(1):
asn1_start_tag(asn1, ASN1_CONTEXT(1));
asn1_read_Integer(asn1, &token->reqFlags);
token->reqFlags |= SPNEGO_REQ_FLAG;
asn1_end_tag(asn1);
break;
/* Read mechToken */
case ASN1_CONTEXT(2):
asn1_start_tag(asn1, ASN1_CONTEXT(2));
asn1_read_OctetString(asn1, &token->mechToken);
asn1_end_tag(asn1);
break;
/* Read mecListMIC */
case ASN1_CONTEXT(3):
{
uint8_t type_peek;
asn1_start_tag(asn1, ASN1_CONTEXT(3));
if (!asn1_peek_uint8(asn1, &type_peek)) {
asn1->has_error = True;
break;
}
if (type_peek == ASN1_OCTET_STRING) {
asn1_read_OctetString(asn1,
&token->mechListMIC);
} else {
/* RFC 2478 says we have an Octet String here,
but W2k sends something different... */
char *mechListMIC;
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_read_GeneralString(asn1, &mechListMIC);
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
token->targetPrincipal = mechListMIC;
}
asn1_end_tag(asn1);
break;
}
default:
asn1->has_error = True;
break;
}
}
asn1_end_tag(asn1);
asn1_end_tag(asn1);
return !asn1->has_error;
}
static BOOL write_negTokenInit(struct asn1_data *asn1, struct spnego_negTokenInit *token)
{
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
/* Write mechTypes */
if (token->mechTypes && *token->mechTypes) {
int i;
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
for (i = 0; token->mechTypes[i]; i++) {
asn1_write_OID(asn1, token->mechTypes[i]);
}
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
}
/* write reqFlags */
if (token->reqFlags & SPNEGO_REQ_FLAG) {
int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
asn1_push_tag(asn1, ASN1_CONTEXT(1));
asn1_write_Integer(asn1, flags);
asn1_pop_tag(asn1);
}
/* write mechToken */
if (token->mechToken.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(2));
asn1_write_OctetString(asn1, token->mechToken.data,
token->mechToken.length);
asn1_pop_tag(asn1);
}
/* write mechListMIC */
if (token->mechListMIC.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(3));
#if 0
/* This is what RFC 2478 says ... */
asn1_write_OctetString(asn1, token->mechListMIC.data,
token->mechListMIC.length);
#else
/* ... but unfortunately this is what Windows
sends/expects */
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_push_tag(asn1, ASN1_GENERAL_STRING);
asn1_write(asn1, token->mechListMIC.data,
token->mechListMIC.length);
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
#endif
asn1_pop_tag(asn1);
}
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
return !asn1->has_error;
}
static BOOL read_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
{
ZERO_STRUCTP(token);
asn1_start_tag(asn1, ASN1_CONTEXT(1));
asn1_start_tag(asn1, ASN1_SEQUENCE(0));
while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
uint8_t context;
if (!asn1_peek_uint8(asn1, &context)) {
asn1->has_error = True;
break;
}
switch (context) {
case ASN1_CONTEXT(0):
asn1_start_tag(asn1, ASN1_CONTEXT(0));
asn1_start_tag(asn1, ASN1_ENUMERATED);
asn1_read_uint8(asn1, &token->negResult);
asn1_end_tag(asn1);
asn1_end_tag(asn1);
break;
case ASN1_CONTEXT(1):
asn1_start_tag(asn1, ASN1_CONTEXT(1));
asn1_read_OID(asn1, &token->supportedMech);
asn1_end_tag(asn1);
break;
case ASN1_CONTEXT(2):
asn1_start_tag(asn1, ASN1_CONTEXT(2));
asn1_read_OctetString(asn1, &token->responseToken);
asn1_end_tag(asn1);
break;
case ASN1_CONTEXT(3):
asn1_start_tag(asn1, ASN1_CONTEXT(3));
asn1_read_OctetString(asn1, &token->mechListMIC);
asn1_end_tag(asn1);
break;
default:
asn1->has_error = True;
break;
}
}
asn1_end_tag(asn1);
asn1_end_tag(asn1);
return !asn1->has_error;
}
static BOOL write_negTokenTarg(struct asn1_data *asn1, struct spnego_negTokenTarg *token)
{
asn1_push_tag(asn1, ASN1_CONTEXT(1));
asn1_push_tag(asn1, ASN1_SEQUENCE(0));
if (token->negResult != SPNEGO_NONE_RESULT) {
asn1_push_tag(asn1, ASN1_CONTEXT(0));
asn1_write_enumerated(asn1, token->negResult);
asn1_pop_tag(asn1);
}
if (token->supportedMech) {
asn1_push_tag(asn1, ASN1_CONTEXT(1));
asn1_write_OID(asn1, token->supportedMech);
asn1_pop_tag(asn1);
}
if (token->responseToken.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(2));
asn1_write_OctetString(asn1, token->responseToken.data,
token->responseToken.length);
asn1_pop_tag(asn1);
}
if (token->mechListMIC.data) {
asn1_push_tag(asn1, ASN1_CONTEXT(3));
asn1_write_OctetString(asn1, token->mechListMIC.data,
token->mechListMIC.length);
asn1_pop_tag(asn1);
}
asn1_pop_tag(asn1);
asn1_pop_tag(asn1);
return !asn1->has_error;
}
ssize_t spnego_read_data(DATA_BLOB data, struct spnego_data *token)
{
struct asn1_data asn1;
ssize_t ret = -1;
uint8_t context;
ZERO_STRUCTP(token);
ZERO_STRUCT(asn1);
if (data.length == 0) {
return ret;
}
asn1_load(&asn1, data);
if (!asn1_peek_uint8(&asn1, &context)) {
asn1.has_error = True;
} else {
switch (context) {
case ASN1_APPLICATION(0):
asn1_start_tag(&asn1, ASN1_APPLICATION(0));
asn1_check_OID(&asn1, GENSEC_OID_SPNEGO);
if (read_negTokenInit(&asn1, &token->negTokenInit)) {
token->type = SPNEGO_NEG_TOKEN_INIT;
}
asn1_end_tag(&asn1);
break;
case ASN1_CONTEXT(1):
if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
token->type = SPNEGO_NEG_TOKEN_TARG;
}
break;
default:
asn1.has_error = True;
break;
}
}
if (!asn1.has_error) ret = asn1.ofs;
asn1_free(&asn1);
return ret;
}
ssize_t spnego_write_data(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct spnego_data *spnego)
{
struct asn1_data asn1;
ssize_t ret = -1;
ZERO_STRUCT(asn1);
switch (spnego->type) {
case SPNEGO_NEG_TOKEN_INIT:
asn1_push_tag(&asn1, ASN1_APPLICATION(0));
asn1_write_OID(&asn1, GENSEC_OID_SPNEGO);
write_negTokenInit(&asn1, &spnego->negTokenInit);
asn1_pop_tag(&asn1);
break;
case SPNEGO_NEG_TOKEN_TARG:
write_negTokenTarg(&asn1, &spnego->negTokenTarg);
break;
default:
asn1.has_error = True;
break;
}
if (!asn1.has_error) {
*blob = data_blob_talloc(mem_ctx, asn1.data, asn1.length);
ret = asn1.ofs;
}
asn1_free(&asn1);
return ret;
}
BOOL spnego_free_data(struct spnego_data *spnego)
{
BOOL ret = True;
if (!spnego) goto out;
switch(spnego->type) {
case SPNEGO_NEG_TOKEN_INIT:
if (spnego->negTokenInit.mechTypes) {
talloc_free(spnego->negTokenInit.mechTypes);
}
data_blob_free(&spnego->negTokenInit.mechToken);
data_blob_free(&spnego->negTokenInit.mechListMIC);
talloc_free(spnego->negTokenInit.targetPrincipal);
break;
case SPNEGO_NEG_TOKEN_TARG:
if (spnego->negTokenTarg.supportedMech) {
talloc_free(discard_const(spnego->negTokenTarg.supportedMech));
}
data_blob_free(&spnego->negTokenTarg.responseToken);
data_blob_free(&spnego->negTokenTarg.mechListMIC);
break;
default:
ret = False;
break;
}
ZERO_STRUCTP(spnego);
out:
return ret;
}
+210
View File
@@ -0,0 +1,210 @@
/*
Unix SMB/CIFS implementation.
simple kerberos5 routines for active directory
Copyright (C) Andrew Tridgell 2001
Copyright (C) Luke Howard 2002-2003
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "system/network.h"
#include "system/kerberos.h"
#include "system/time.h"
#include "auth/kerberos/kerberos.h"
#ifdef HAVE_KRB5
#if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
{
return krb5_set_default_in_tkt_etypes(ctx, enc);
}
#endif
#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
/* HEIMDAL */
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
{
pkaddr->addr_type = KRB5_ADDRESS_INET;
pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
}
#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
/* MIT */
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
{
pkaddr->addrtype = ADDRTYPE_INET;
pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
}
#else
#error UNKNOWN_ADDRTYPE
#endif
#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK)
int create_kerberos_key_from_string(krb5_context context,
krb5_principal host_princ,
krb5_data *password,
krb5_keyblock *key,
krb5_enctype enctype)
{
int ret;
krb5_data salt;
krb5_encrypt_block eblock;
ret = krb5_principal2salt(context, host_princ, &salt);
if (ret) {
DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
return ret;
}
krb5_use_enctype(context, &eblock, enctype);
ret = krb5_string_to_key(context, &eblock, key, password, &salt);
SAFE_FREE(salt.data);
return ret;
}
#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
int create_kerberos_key_from_string(krb5_context context,
krb5_principal host_princ,
krb5_data *password,
krb5_keyblock *key,
krb5_enctype enctype)
{
int ret;
krb5_salt salt;
ret = krb5_get_pw_salt(context, host_princ, &salt);
if (ret) {
DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
return ret;
}
ret = krb5_string_to_key_salt(context, enctype, password->data,
salt, key);
krb5_free_salt(context, salt);
return ret;
}
#else
#error UNKNOWN_CREATE_KEY_FUNCTIONS
#endif
#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
krb5_enctype **enctypes)
{
return krb5_get_permitted_enctypes(context, enctypes);
}
#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
krb5_enctype **enctypes)
{
return krb5_get_default_in_tkt_etypes(context, enctypes);
}
#else
#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
#endif
void free_kerberos_etypes(krb5_context context,
krb5_enctype *enctypes)
{
#if defined(HAVE_KRB5_FREE_KTYPES)
krb5_free_ktypes(context, enctypes);
return;
#else
SAFE_FREE(enctypes);
return;
#endif
}
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
krb5_auth_context auth_context,
krb5_keyblock *keyblock)
{
return krb5_auth_con_setkey(context, auth_context, keyblock);
}
#endif
#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
void krb5_free_unparsed_name(krb5_context context, char *val)
{
SAFE_FREE(val);
}
#endif
void kerberos_free_data_contents(krb5_context context, krb5_data *pdata)
{
#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
if (pdata->data) {
krb5_free_data_contents(context, pdata);
}
#else
SAFE_FREE(pdata->data);
#endif
}
void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype)
{
#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS)
KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype;
#elif defined(HAVE_KRB5_SESSION_IN_CREDS)
KRB5_KEY_TYPE((&pcreds->session)) = enctype;
#else
#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT
#endif
}
BOOL kerberos_compatible_enctypes(krb5_context context,
krb5_enctype enctype1,
krb5_enctype enctype2)
{
#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE)
krb5_boolean similar = 0;
krb5_c_enctype_compare(context, enctype1, enctype2, &similar);
return similar ? True : False;
#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS)
return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False;
#endif
}
krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry)
{
#if defined(HAVE_KRB5_KT_FREE_ENTRY)
return krb5_kt_free_entry(context, kt_entry);
#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
return krb5_free_keytab_entry_contents(context, kt_entry);
#else
#error UNKNOWN_KT_FREE_FUNCTION
#endif
}
char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx)
{
char *ret;
#if defined(HAVE_KRB5_GET_ERROR_STRING) && defined(HAVE_KRB5_FREE_ERROR_STRING)
char *context_error = krb5_get_error_string(context);
if (context_error) {
ret = talloc_asprintf(mem_ctx, "%s: %s", error_message(code), context_error);
krb5_free_error_string(context, context_error);
return ret;
}
#endif
ret = talloc_strdup(mem_ctx, error_message(code));
return ret;
}
#endif
+540
View File
@@ -0,0 +1,540 @@
# NOTE! this whole m4 file is disabled in configure.in for now
#################################################
# KRB5 support
KRB5_CFLAGS=""
KRB5_CPPFLAGS=""
KRB5_LDFLAGS=""
KRB5_LIBS=""
with_krb5_support=auto
krb5_withval=auto
AC_MSG_CHECKING([for KRB5 support])
# Do no harm to the values of CFLAGS and LIBS while testing for
# Kerberos support.
AC_ARG_WITH(krb5,
[ --with-krb5=base-dir Locate Kerberos 5 support (default=auto)],
[ case "$withval" in
no)
with_krb5_support=no
AC_MSG_RESULT(no)
krb5_withval=no
;;
yes)
with_krb5_support=yes
AC_MSG_RESULT(yes)
krb5_withval=yes
;;
auto)
with_krb5_support=auto
AC_MSG_RESULT(auto)
krb5_withval=auto
;;
*)
with_krb5_support=yes
AC_MSG_RESULT(yes)
krb5_withval=$withval
KRB5CONFIG="$krb5_withval/bin/krb5-config"
;;
esac ],
AC_MSG_RESULT($with_krb5_support)
)
if test x$with_krb5_support != x"no"; then
FOUND_KRB5=no
FOUND_KRB5_VIA_CONFIG=no
#################################################
# check for krb5-config from recent MIT and Heimdal kerberos 5
AC_MSG_CHECKING(for working specified location for krb5-config)
if test x$KRB5CONFIG != "x"; then
if test -x "$KRB5CONFIG"; then
ac_save_CFLAGS=$CFLAGS
CFLAGS="";export CFLAGS
ac_save_LDFLAGS=$LDFLAGS
LDFLAGS="";export LDFLAGS
KRB5_LIBS="`$KRB5CONFIG --libs gssapi`"
KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
CFLAGS=$ac_save_CFLAGS;export CFLAGS
LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
FOUND_KRB5=yes
FOUND_KRB5_VIA_CONFIG=yes
AC_MSG_RESULT(yes. Found $KRB5CONFIG)
else
AC_MSG_RESULT(no. Fallback to specified directory)
fi
else
AC_MSG_RESULT(no. Fallback to finding krb5-config in path)
#################################################
# check for krb5-config from recent MIT and Heimdal kerberos 5
AC_PATH_PROG(KRB5CONFIG, krb5-config)
AC_MSG_CHECKING(for working krb5-config in path)
if test -x "$KRB5CONFIG"; then
ac_save_CFLAGS=$CFLAGS
CFLAGS="";export CFLAGS
ac_save_LDFLAGS=$LDFLAGS
LDFLAGS="";export LDFLAGS
KRB5_LIBS="`$KRB5CONFIG --libs gssapi`"
KRB5_CFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
KRB5_CPPFLAGS="`$KRB5CONFIG --cflags | sed s/@INCLUDE_des@//`"
CFLAGS=$ac_save_CFLAGS;export CFLAGS
LDFLAGS=$ac_save_LDFLAGS;export LDFLAGS
FOUND_KRB5=yes
FOUND_KRB5_VIA_CONFIG=yes
AC_MSG_RESULT(yes. Found $KRB5CONFIG)
else
AC_MSG_RESULT(no. Fallback to previous krb5 detection strategy)
fi
fi
if test x$FOUND_KRB5 != x"yes"; then
#################################################
# check for location of Kerberos 5 install
AC_MSG_CHECKING(for kerberos 5 install path)
case "$krb5_withval" in
no)
AC_MSG_RESULT(no krb5-path given)
;;
yes)
AC_MSG_RESULT(/usr)
FOUND_KRB5=yes
;;
*)
AC_MSG_RESULT($krb5_withval)
KRB5_CFLAGS="-I$krb5_withval/include"
KRB5_CPPFLAGS="-I$krb5_withval/include"
KRB5_LDFLAGS="-L$krb5_withval/lib"
FOUND_KRB5=yes
;;
esac
fi
if test x$FOUND_KRB5 != x"yes"; then
#################################################
# see if this box has the SuSE location for the heimdal krb implementation
AC_MSG_CHECKING(for /usr/include/heimdal)
if test -d /usr/include/heimdal; then
if test -f /usr/lib/heimdal/lib/libkrb5.a; then
KRB5_CFLAGS="-I/usr/include/heimdal"
KRB5_CPPFLAGS="-I/usr/include/heimdal"
KRB5_LDFLAGS="-L/usr/lib/heimdal/lib"
AC_MSG_RESULT(yes)
else
KRB5_CFLAGS="-I/usr/include/heimdal"
KRB5_CPPFLAGS="-I/usr/include/heimdal"
AC_MSG_RESULT(yes)
fi
else
AC_MSG_RESULT(no)
fi
fi
if test x$FOUND_KRB5 != x"yes"; then
#################################################
# see if this box has the RedHat location for kerberos
AC_MSG_CHECKING(for /usr/kerberos)
if test -d /usr/kerberos -a -f /usr/kerberos/lib/libkrb5.a; then
KRB5_LDFLAGS="-L/usr/kerberos/lib"
KRB5_CFLAGS="-I/usr/kerberos/include"
KRB5_CPPFLAGS="-I/usr/kerberos/include"
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
fi
fi
ac_save_CFLAGS=$CFLAGS
ac_save_CPPFLAGS=$CPPFLAGS
ac_save_LDFLAGS=$LDFLAGS
#MIT needs this, to let us see 'internal' parts of the headers we use
KRB5_CFLAGS="${KRB5_CFLAGS} -DKRB5_PRIVATE -DKRB5_DEPRECATED"
#Heimdal needs this
#TODO: we need to parse KRB5_LIBS for -L path
# and set -Wl,-rpath -Wl,path
CFLAGS="$CFLAGS $KRB5_CFLAGS"
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
KRB5_LIBS="$KRB5_LDFLAGS $KRB5_LIBS"
# now check for krb5.h. Some systems have the libraries without the headers!
# note that this check is done here to allow for different kerberos
# include paths
AC_CHECK_HEADERS(krb5.h)
if test x"$ac_cv_header_krb5_h" = x"no"; then
# Give a warning if KRB5 support was not explicitly requested,
# i.e with_krb5_support = auto, otherwise die with an error.
if test x"$with_krb5_support" = x"yes"; then
AC_MSG_ERROR([KRB5 cannot be supported without krb5.h])
else
AC_MSG_WARN([KRB5 cannot be supported without krb5.h])
fi
# Turn off AD support and restore CFLAGS and LIBS variables
with_krb5_support="no"
fi
CFLAGS=$ac_save_CFLAGS
CPPFLAGS=$ac_save_CPPFLAGS
LDFLAGS=$ac_save_LDFLAGS
fi
# Now we have determined whether we really want KRB5 support
if test x"$with_krb5_support" != x"no"; then
ac_save_CFLAGS=$CFLAGS
ac_save_CPPFLAGS=$CPPFLAGS
ac_save_LDFLAGS=$LDFLAGS
ac_save_LIBS=$LIBS
CFLAGS="$CFLAGS $KRB5_CFLAGS"
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
# now check for gssapi headers. This is also done here to allow for
# different kerberos include paths
AC_CHECK_HEADERS(gssapi.h gssapi_krb5.h gssapi/gssapi.h gssapi/gssapi_generic.h gssapi/gssapi_krb5.h com_err.h)
# Heimdal checks.
# But only if we didn't have a krb5-config to tell us this already
if test x"$FOUND_KRB5_VIA_CONFIG" != x"yes"; then
##################################################################
# we might need the k5crypto and com_err libraries on some systems
AC_CHECK_LIB_EXT(com_err, KRB5_LIBS, _et_list)
AC_CHECK_LIB_EXT(k5crypto, KRB5_LIBS, krb5_encrypt_data)
AC_CHECK_LIB_EXT(crypto, KRB5_LIBS, des_set_key)
AC_CHECK_LIB_EXT(asn1, KRB5_LIBS, copy_Authenticator)
AC_CHECK_LIB_EXT(roken, KRB5_LIBS, roken_getaddrinfo_hostspec)
fi
# Heimdal checks. On static Heimdal gssapi must be linked before krb5.
AC_CHECK_LIB_EXT(gssapi, KRB5_LIBS, gss_display_status,[],[],
AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
########################################################
# now see if we can find the krb5 libs in standard paths
# or as specified above
AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_mk_req_extended)
AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_kt_compare)
########################################################
# now see if we can find the gssapi libs in standard paths
if test x"$ac_cv_lib_ext_gssapi_gss_display_status" != x"yes"; then
AC_CHECK_LIB_EXT(gssapi_krb5, KRB5_LIBS,gss_display_status,[],[],
AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available]))
fi
AC_CHECK_FUNC_EXT(krb5_set_real_time, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_set_default_in_tkt_etypes, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_set_default_tgs_ktypes, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_principal2salt, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_use_enctype, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_string_to_key, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_get_pw_salt, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_string_to_key_salt, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_auth_con_setkey, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_auth_con_setuseruserkey, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_locate_kdc, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_get_permitted_enctypes, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_get_default_in_tkt_etypes, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_free_ktypes, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_free_data_contents, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_principal_get_comp_string, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_free_unparsed_name, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_free_keytab_entry_contents, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_kt_free_entry, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_verify_checksum, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_c_verify_checksum, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_ticket_get_authorization_data_type, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_c_enctype_compare, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_enctypes_compatible_keys, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_get_error_string, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_free_error_string, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_initlog, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_addlog_func, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_set_warn_dest, $KRB5_LIBS)
LIBS="$LIBS $KRB5_LIBS"
AC_CACHE_CHECK([for krb5_log_facility type],
samba_cv_HAVE_KRB5_LOG_FACILITY,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_log_facility block;],
samba_cv_HAVE_KRB5_LOG_FACILITY=yes,
samba_cv_HAVE_KRB5_LOG_FACILITY=no)])
if test x"$samba_cv_HAVE_KRB5_LOG_FACILITY" = x"yes"; then
AC_DEFINE(HAVE_KRB5_LOG_FACILITY,1,
[Whether the type krb5_log_facility exists])
fi
AC_CACHE_CHECK([for krb5_encrypt_block type],
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_encrypt_block block;],
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=yes,
samba_cv_HAVE_KRB5_ENCRYPT_BLOCK=no)])
if test x"$samba_cv_HAVE_KRB5_ENCRYPT_BLOCK" = x"yes"; then
AC_DEFINE(HAVE_KRB5_ENCRYPT_BLOCK,1,
[Whether the type krb5_encrypt_block exists])
fi
AC_CACHE_CHECK([for addrtype in krb5_address],
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_address kaddr; kaddr.addrtype = ADDRTYPE_INET;],
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=yes,
samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS=no)])
if test x"$samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS" = x"yes"; then
AC_DEFINE(HAVE_ADDRTYPE_IN_KRB5_ADDRESS,1,
[Whether the krb5_address struct has a addrtype property])
fi
AC_CACHE_CHECK([for addr_type in krb5_address],
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_address kaddr; kaddr.addr_type = KRB5_ADDRESS_INET;],
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=yes,
samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS=no)])
if test x"$samba_cv_HAVE_ADDR_TYPE_IN_KRB5_ADDRESS" = x"yes"; then
AC_DEFINE(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS,1,
[Whether the krb5_address struct has a addr_type property])
fi
AC_CACHE_CHECK([for enc_part2 in krb5_ticket],
samba_cv_HAVE_KRB5_TKT_ENC_PART2,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_ticket tkt; tkt.enc_part2->authorization_data[0]->contents = NULL;],
samba_cv_HAVE_KRB5_TKT_ENC_PART2=yes,
samba_cv_HAVE_KRB5_TKT_ENC_PART2=no)])
if test x"$samba_cv_HAVE_KRB5_TKT_ENC_PART2" = x"yes"; then
AC_DEFINE(HAVE_KRB5_TKT_ENC_PART2,1,
[Whether the krb5_ticket struct has a enc_part2 property])
fi
AC_CACHE_CHECK([for keyblock in krb5_creds],
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_creds creds; krb5_keyblock kb; creds.keyblock = kb;],
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=yes,
samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS=no)])
if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_IN_CREDS" = x"yes"; then
AC_DEFINE(HAVE_KRB5_KEYBLOCK_IN_CREDS,1,
[Whether the krb5_creds struct has a keyblock property])
fi
AC_CACHE_CHECK([for session in krb5_creds],
samba_cv_HAVE_KRB5_SESSION_IN_CREDS,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_creds creds; krb5_keyblock kb; creds.session = kb;],
samba_cv_HAVE_KRB5_SESSION_IN_CREDS=yes,
samba_cv_HAVE_KRB5_SESSION_IN_CREDS=no)])
if test x"$samba_cv_HAVE_KRB5_SESSION_IN_CREDS" = x"yes"; then
AC_DEFINE(HAVE_KRB5_SESSION_IN_CREDS,1,
[Whether the krb5_creds struct has a session property])
fi
AC_CACHE_CHECK([for keyvalue in krb5_keyblock],
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_keyblock key; key.keyvalue.data = NULL;],
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=yes,
samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE=no)])
if test x"$samba_cv_HAVE_KRB5_KEYBLOCK_KEYVALUE" = x"yes"; then
AC_DEFINE(HAVE_KRB5_KEYBLOCK_KEYVALUE,1,
[Whether the krb5_keyblock struct has a keyvalue property])
fi
AC_CACHE_CHECK([for ENCTYPE_ARCFOUR_HMAC_MD5],
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;],
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes,
samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)])
AC_CACHE_CHECK([for KEYTYPE_ARCFOUR_56],
samba_cv_HAVE_KEYTYPE_ARCFOUR_56,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_keytype keytype; keytype = KEYTYPE_ARCFOUR_56;],
samba_cv_HAVE_KEYTYPE_ARCFOUR_56=yes,
samba_cv_HAVE_KEYTYPE_ARCFOUR_56=no)])
# Heimdals with KEYTYPE_ARCFOUR but not KEYTYPE_ARCFOUR_56 are broken
# w.r.t. arcfour and windows, so we must not enable it here
if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes" -a\
x"$samba_cv_HAVE_KEYTYPE_ARCFOUR_56" = x"yes"; then
AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1,
[Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available])
fi
AC_CACHE_CHECK([for AP_OPTS_USE_SUBKEY],
samba_cv_HAVE_AP_OPTS_USE_SUBKEY,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_flags ap_options; ap_options = AP_OPTS_USE_SUBKEY;],
samba_cv_HAVE_AP_OPTS_USE_SUBKEY=yes,
samba_cv_HAVE_AP_OPTS_USE_SUBKEY=no)])
if test x"$samba_cv_HAVE_AP_OPTS_USE_SUBKEY" = x"yes"; then
AC_DEFINE(HAVE_AP_OPTS_USE_SUBKEY,1,
[Whether the AP_OPTS_USE_SUBKEY ap option is available])
fi
AC_CACHE_CHECK([for KV5M_KEYTAB],
samba_cv_HAVE_KV5M_KEYTAB,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_keytab_entry entry; entry.magic = KV5M_KEYTAB;],
samba_cv_HAVE_KV5M_KEYTAB=yes,
samba_cv_HAVE_KV5M_KEYTAB=no)])
if test x"$samba_cv_HAVE_KV5M_KEYTAB" = x"yes"; then
AC_DEFINE(HAVE_KV5M_KEYTAB,1,
[Whether the KV5M_KEYTAB option is available])
fi
AC_CACHE_CHECK([for the krb5_princ_component macro],
samba_cv_HAVE_KRB5_PRINC_COMPONENT,[
AC_TRY_LINK([#include <krb5.h>],
[const krb5_data *pkdata; krb5_context context; krb5_principal principal;
pkdata = krb5_princ_component(context, principal, 0);],
samba_cv_HAVE_KRB5_PRINC_COMPONENT=yes,
samba_cv_HAVE_KRB5_PRINC_COMPONENT=no)])
if test x"$samba_cv_HAVE_KRB5_PRINC_COMPONENT" = x"yes"; then
AC_DEFINE(HAVE_KRB5_PRINC_COMPONENT,1,
[Whether krb5_princ_component is available])
fi
AC_CACHE_CHECK([for key in krb5_keytab_entry],
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_keytab_entry entry; krb5_keyblock e; entry.key = e;],
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=yes,
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY=no)])
if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEY" = x"yes"; then
AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEY,1,
[Whether krb5_keytab_entry has key member])
fi
AC_CACHE_CHECK([for keyblock in krb5_keytab_entry],
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_keytab_entry entry; entry.keyblock.keytype = 0;],
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=yes,
samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK=no)])
if test x"$samba_cv_HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK" = x"yes"; then
AC_DEFINE(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK,1,
[Whether krb5_keytab_entry has keyblock member])
fi
AC_CACHE_CHECK([for WRFILE: keytab support],
samba_cv_HAVE_WRFILE_KEYTAB,[
AC_TRY_RUN([
#include<krb5.h>
main()
{
krb5_context context;
krb5_keytab keytab;
krb5_init_context(&context);
return krb5_kt_resolve(context, "WRFILE:api", &keytab);
}],
samba_cv_HAVE_WRFILE_KEYTAB=yes,
samba_cv_HAVE_WRFILE_KEYTAB=no)])
if test x"$samba_cv_HAVE_WRFILE_KEYTAB" = x"yes"; then
AC_DEFINE(HAVE_WRFILE_KEYTAB,1,
[Whether the WRFILE:-keytab is supported])
fi
AC_CACHE_CHECK([for krb5_princ_realm returns krb5_realm or krb5_data],
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM,[
AC_TRY_COMPILE([#include <krb5.h>],
[krb5_context context;krb5_principal principal;krb5_realm realm;
realm = *krb5_princ_realm(context, principal);],
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=yes,
samba_cv_KRB5_PRINC_REALM_RETURNS_REALM=no)])
if test x"$samba_cv_KRB5_PRINC_REALM_RETURNS_REALM" = x"yes"; then
AC_DEFINE(KRB5_PRINC_REALM_RETURNS_REALM,1,
[Whether krb5_princ_realm returns krb5_realm or krb5_data])
fi
# TODO: check all gssapi headers for this
AC_CACHE_CHECK([for GSS_C_DCE_STYLE in gssapi.h],
samba_cv_GSS_C_DCE_STYLE,[
AC_TRY_COMPILE([#include <gssapi.h>],
[int flags = GSS_C_DCE_STYLE;],
samba_cv_GSS_C_DCE_STYLE=yes,
samba_cv_GSS_C_DCE_STYLE=no)])
AC_CHECK_FUNC_EXT(gsskrb5_get_initiator_subkey, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(gsskrb5_extract_authz_data_from_sec_context, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(gsskrb5_register_acceptor_identity, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(gss_krb5_ccache_name, $KRB5_LIBS)
if test x"$ac_cv_lib_ext_krb5_krb5_mk_req_extended" = x"yes"; then
AC_DEFINE(HAVE_KRB5,1,[Whether to have KRB5 support])
AC_MSG_CHECKING(whether KRB5 support is used)
SMB_ENABLE(KRB5,YES)
AC_MSG_RESULT(yes)
echo "KRB5_CFLAGS: ${KRB5_CFLAGS}"
echo "KRB5_CPPFLAGS: ${KRB5_CPPFLAGS}"
echo "KRB5_LDFLAGS: ${KRB5_LDFLAGS}"
echo "KRB5_LIBS: ${KRB5_LIBS}"
else
if test x"$with_krb5_support" = x"yes"; then
AC_MSG_ERROR(a working krb5 library is needed for KRB5 support)
else
AC_MSG_WARN(a working krb5 library is needed for KRB5 support)
fi
KRB5_CFLAGS=""
KRB5_CPPFLAGS=""
KRB5_LDFLAGS=""
KRB5_LIBS=""
with_krb5_support=no
fi
# checks if we have access to a libkdc
# and can use it for our builtin kdc server_service
KDC_CFLAGS=""
KDC_CPPFLAGS=""
KDC_DLFLAGS=""
KDC_LIBS=""
AC_CHECK_HEADERS(kdc.h)
AC_CHECK_LIB_EXT(kdc, KDC_LIBS, krb5_kdc_default_config)
AC_CHECK_LIB_EXT(hdb, KDC_LIBS, hdb_generate_key_set_password)
AC_MSG_CHECKING(whether libkdc is used)
if test x"$ac_cv_header_kdc_h" = x"yes"; then
if test x"$ac_cv_lib_ext_kdc_krb5_kdc_default_config" = x"yes"; then
if test x"$ac_cv_lib_ext_hdb_hdb_generate_key_set_password" = x"yes"; then
SMB_ENABLE(KDC,YES)
AC_MSG_RESULT(yes)
echo "KDC_LIBS: ${KDC_LIBS}"
else
AC_MSG_RESULT(no)
fi
else
AC_MSG_RESULT(no)
fi
else
AC_MSG_RESULT(no)
fi
CFLAGS=$ac_save_CFLAGS
CPPFLAGS=$ac_save_CPPFLAGS
LDFLAGS=$ac_save_LDFLAGS
LIBS="$ac_save_LIBS"
# as a nasty hack add the krb5 stuff to the global vars,
# at some point this should not be needed anymore when the build system
# can handle that alone
CFLAGS="$CFLAGS $KRB5_CFLAGS"
CPPFLAGS="$CPPFLAGS $KRB5_CPPFLAGS"
LDFLAGS="$LDFLAGS $KRB5_LDFLAGS"
fi
SMB_EXT_LIB(KRB5,[${KRB5_LIBS}],[${KRB5_CFLAGS}],[${KRB5_CPPFLAGS}],[${KRB5_LDFLAGS}])
SMB_EXT_LIB(KDC,[${KDC_LIBS}],[${KDC_CFLAGS}],[${KDC_CPPFLAGS}],[${KDC_LDFLAGS}])
+15
View File
@@ -0,0 +1,15 @@
#################################
# Start SUBSYSTEM KERBEROS
[SUBSYSTEM::KERBEROS]
PRIVATE_PROTO_HEADER = proto.h
OBJ_FILES = kerberos.o \
clikrb5.o \
kerberos_heimdal.o \
kerberos_util.o \
kerberos_pac.o \
gssapi_parse.o \
krb5_init_context.o
PUBLIC_DEPENDENCIES = HEIMDAL_KRB5 NDR_KRB5PAC samba-socket LIBCLI_RESOLVE
PRIVATE_DEPENDENCIES = ASN1_UTIL HEIMDAL_ROKEN_ADDRINFO auth_sam CREDENTIALS_KRB5
# End SUBSYSTEM KERBEROS
#################################
+114
View File
@@ -0,0 +1,114 @@
/*
Unix SMB/CIFS implementation.
simple GSSAPI wrappers
Copyright (C) Andrew Tridgell 2001
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
Copyright (C) Luke Howard 2003
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "libcli/util/asn_1.h"
#include "auth/gensec/gensec.h"
/*
generate a krb5 GSS-API wrapper packet given a ticket
*/
DATA_BLOB gensec_gssapi_gen_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *ticket, const uint8_t tok_id[2])
{
struct asn1_data data;
DATA_BLOB ret = data_blob(NULL,0);
if (!ticket->data) {
return ret;
}
ZERO_STRUCT(data);
asn1_push_tag(&data, ASN1_APPLICATION(0));
asn1_write_OID(&data, GENSEC_OID_KERBEROS5);
asn1_write(&data, tok_id, 2);
asn1_write(&data, ticket->data, ticket->length);
asn1_pop_tag(&data);
if (data.has_error) {
DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
asn1_free(&data);
}
ret = data_blob_talloc(mem_ctx, data.data, data.length);
asn1_free(&data);
return ret;
}
/*
parse a krb5 GSS-API wrapper packet giving a ticket
*/
BOOL gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, DATA_BLOB *ticket, uint8_t tok_id[2])
{
BOOL ret;
struct asn1_data data;
int data_remaining;
asn1_load(&data, *blob);
asn1_start_tag(&data, ASN1_APPLICATION(0));
asn1_check_OID(&data, GENSEC_OID_KERBEROS5);
data_remaining = asn1_tag_remaining(&data);
if (data_remaining < 3) {
data.has_error = True;
} else {
asn1_read(&data, tok_id, 2);
data_remaining -= 2;
*ticket = data_blob_talloc(mem_ctx, NULL, data_remaining);
asn1_read(&data, ticket->data, ticket->length);
}
asn1_end_tag(&data);
ret = !data.has_error;
asn1_free(&data);
return ret;
}
/*
check a GSS-API wrapper packet givin an expected OID
*/
BOOL gensec_gssapi_check_oid(const DATA_BLOB *blob, const char *oid)
{
BOOL ret;
struct asn1_data data;
asn1_load(&data, *blob);
asn1_start_tag(&data, ASN1_APPLICATION(0));
asn1_check_OID(&data, GENSEC_OID_KERBEROS5);
ret = !data.has_error;
asn1_free(&data);
return ret;
}
@@ -0,0 +1,466 @@
AllowedWorkstationNames and Krb5
--------------------------------
Microsoft uses the clientAddresses *multiple value* field in the krb5
protocol (particularly the AS_REQ) to communicate it's netbios name.
This is (my guess) to permit the userWorkstations field to work.
The KDC I imagine checks the netbios address against this value, in
the same way that the Samba server does this.
The checking of this implies a little of the next question:
Is a DAL the layer we need?
---------------------------
Looking at what we need to pass around, I start to seriously wonder if
the DAL is even the right layer - we seem to want to create an account
authorization abstraction layer - is this account permitted to login to
this computer, at this time?
This information in AD is much richer than the Heimdal HDB, and it
seems to make sense to do AD-specific access control checks in an
AD-specific layer, not in the back-end agnostic server.
Because the DAL only reads in the principalName as the key, it has
trouble performing access control decisions on things other than the
name.
I'll be very interested if the DAL really works for eDirectory too.
Perhaps all we need to do is add in the same kludges as we have in
Samba 3.0 for eDirectory. Hmm...
That said, the current layer provides us with a very good start, and
any redefinition would occour from that basis.
GSSAPI layer requirements
-------------------------
Welcome to the wonderful world of canonicalisation
The MIT GSSAPI libs do not support kinit returning a different
realm to what the client asked for, even just in case differences.
Heimdal has the same problem, and this applies to the krb5 layer, not
just gssapi.
We need to test if the canonicalisation is controlled by the KDCOption
flags, windows always sends the Canonicalize flags
Old Clients (samba3 and HPUX clients) uses 'selfmade' gssapi/krb5
for using it in the CIFS session setup. Because they use krb5_mk_req()
they get a chksum field depending on the encryption type, but that's wrong
for GSSAPI (see rfc 1964 section 1.1.1). The Cheksum type 8003
should be used in the Authenticator of the AP-REQ! That allows the channel bindings,
the GCC_C_* req_flags and optional delegation tickets to be passed from the client to the server.
Hower windows doesn't seems to care about if the checksum is of the wrong type,
for CIFS SessionSetups, it seems that the req_flags are just set to 0.
So this can't work for LDAP connections with sign or seal, or for any DCERPC
connection.
So we need to also support old clients!
Principal Names, long and short names
-------------------------------------
As far as servicePrincipalNames are concerned, these are not
canonicalised, except as regards the realm in the reply. That is, the
client gets back the principal it asked for, with the realm portion
'fixed' to uppercase, long form.
The short name of the realm seems to be accepted for at least AS_REQ
operations, but because the server performs canonicalisation, this
causes pain for current client libraries.
The canonicalisation of names matters not only for the KDC, but also
for code that has to deal with keytabs.
We also need to handle type 10 names (NT-ENTERPRISE), which are a full
principal name in the principal field, unrelated to the realm.
HOST/ Aliases
-------------
There is another post somewhere (ref lost for the moment) that details
where in active directory the list of stored aliases for HOST/ is.
This should be read, parsed and used to allow any of these requests to
use the HOST/ key.
For example, this is how HTTP/, DNS/ and CIFS/ can use HOST/ without
any explicit entry.
Jean-Baptiste.Marchand@hsc.fr reminds me:
> This is the SPNMappings attribute in Active Directory:
> http://msdn.microsoft.com/library/en-us/adschema/adschema/a_spnmappings.asp
We implement this in hdb-ldb.
Implicit names for Win2000 Accounts
-----------------------------------
Despite not having a DNS name, nor a servicePrincipalName on accounts
created by computers running win2000, it appears we are expected to
have an implicit mapping from host/computer.full.name and
host/computer to it's entry.
Returned Salt for PreAuthentication
-----------------------------------
When the server replies for pre-authentication, it returns the Salt,
which may be in the form of a principalName that is in no way
connected with the current names. (ie, even if the userPrincipalName
and samAccountName are renamed, the old salt is returned).
This is probably the kerberos standard salt, kept in the 'Key'. The
standard generation rules are found in a Mail from Luke Howard dated
10 Nov 2004:
From: Luke Howard <lukeh@padl.com>
Message-Id: <200411100231.iAA2VLUW006101@au.padl.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Organization: PADL Software Pty Ltd
To: lukeh@padl.com
Date: Wed, 10 Nov 2004 13:31:21 +1100
Versions: dmail (bsd44) 2.6d/makemail 2.10
Cc: huaraz@moeller.plus.com, samba-technical@lists.samba.org
Subject: Re: Samba-3.0.7-1.3E Active Directory Issues
X-BeenThere: samba-technical@lists.samba.org
X-Mailman-Version: 2.1.4
Precedence: list
Reply-To: lukeh@padl.com
Did some more testing, it appears the behaviour has another
explanation. It appears that the standard Kerberos password salt
algorithm is applied in Windows 2003, just that the source principal
name is different.
Here is what I've been able to deduce from creating a bunch of
different accounts:
Type of account Principal for Salting
========================================================================
Computer Account host/<SAM-Name-Without-$>.realm@REALM
User Account Without UPN <SAM-Name>@REALM
User Account With UPN <LHS-Of-UPN>@REALM
Note that if the computer account's SAM account name does not include
the trailing '$', then the entire SAM account name is used as input to
the salting principal. Setting a UPN for a computer account has no
effect.
It seems to me odd that the RHS of the UPN is not used in the salting
principal. For example, a user with UPN foo@mydomain.com in the realm
MYREALM.COM would have a salt of MYREALM.COMfoo. Perhaps this is to
allow a user's UPN suffix to be changed without changing the salt. And
perhaps using the UPN for salting signifies a move away SAM names and
their associated constraints.
For more information on how UPNs relate to the Kerberos protocol,
see:
http://www.ietf.org/proceedings/01dec/I-D/draft-ietf-krb-wg-kerberos-referrals-02.txt
-- Luke
--
Heimdal oddities
----------------
Heimdal is built such that it should be able to serve multiple realms
at the same time. This isn't relevant for Samba's use, but it shows
up in a lot of generalisations throughout the code.
Other odd things:
- Support for multiple passwords on a client account: we seem to
call hdb_next_enctype2key() in the pre-authentication routines to
allow multiple passwords per account in krb5. (I think this was
intened to allow multiple salts)
State Machine safety
--------------------
Samba is a giant state machine, and as such have very different
requirements to those traditionally expressed for kerberos and GSSAPI
libraries.
Samba requires all of the libraries it uses to be state machine safe in
their use of internal data. This does not mean thread safe, and an
application could be thread safe, but not state machine safe (if it
instead used thread-local variables).
So, what does it mean for a library to be state machine safe? This is
mostly a question of context, and how the library manages whatever
internal state machines it has. If the library uses a context
variable, passed in by the caller, which contains all the information
about the current state of the library, then it is safe. An example
of this state is the sequence number and session keys for an ongoing
encrypted session).
The other issue affecting state machines is 'blocking' (waiting for a
read on a network socket).
Heimdal has this 'state machine safety' in parts, and we have modified
the lorikeet branch to improve this behviour, when using a new,
non-standard API.
Heimdal uses a per-context variable for the 'krb5_auth_context', which
controls the ongoing encrypted connection, but does use global
variables for the ubiquitous krb5_context parameter.
The modification that has added most to 'state machine safety' of
GSSAPI is the addition of the gsskrb5_acquire_creds function. This
allows the caller to specify a keytab and ccache, for use by the
GSSAPI code. Therefore there is no need to use global variables to
communicate this information.
At a more theoritical level (simply counting static and global
variables) Heimdal is not state machine safe for the GSSAPI layer.
The Krb5 layer alone is much closer, as far as I can tell, blocking
excepted. .
To deal with blocking, we could have a fork()ed child per context,
using the 'GSSAPI export context' function to transfer
the GSSAPI state back into the main code for the wrap()/unwrap() part
of the operation. This will still hit issues of static storage (one
gss_krb5_context per process, and multiple GSSAPI encrypted sessions
at a time) but these may not matter in practice.
In the short-term, we deal with blocking by taking over the network
send() and recv() functions, therefore making them 'semi-async'. This
doens't apply to DNS yet.
GSSAPI and Kerberos extensions
------------------------------
This is a general list of the other extensions we have made to / need from
the kerberos libraries
- DCE_STYLE
- gsskrb5_get_initiator_subkey() (return the exact key that Samba3
has always asked for. gsskrb5_get_subkey() might do what we need
anyway)
- gsskrb5_acquire_creds() (takes keytab and/or ccache as input
parameters, see keytab and state machine discussion)
- gss_krb5_copy_service_keyblock() (get the key used to actually
encrypt the ticket to the server, because the same key is used for
the PAC validation).
- gsskrb5_extract_authtime_from_sec_context (get authtime from
kerberos ticket)
- gsskrb5_extract_authz_data_from_sec_context (get authdata from
ticket, ie the PAC. Must unwrap the data if in an AD-IFRELEVENT)
- gsskrb5_wrap_size (find out how big the wrapped packet will be,
given input length).
Keytab requirements
-------------------
Because windows machine account handling is very different to the
tranditional 'MIT' keytab operation. This starts when we look at the
basis of the secrets handling:
Traditional 'MIT' behaviour is to use a keytab, continaing salted key
data, extracted from the KDC. (In this modal, there is no 'service
password', instead the keys are often simply application of random
bytes). Heimdal also implements this behaviour.
The windows modal is very different - instead of sharing a keytab with
each member server, a password is stored for the whole machine. The
password is set with non-kerberos mechanisms (particularly SAMR, a
DCE-RPC service) and when interacting on a kerberos basis, the
password is salted by the client. (That is, no salt infromation
appears to be convayed from the KDC to the member).
In dealing with this modal, we leverage both the traditional file
keytab and in-MEMORY keytabs.
When dealing with a windows KDC, the behaviour regarding case
sensitivity and canonacolisation must be accomidated. This means that
an incoming request to a member server may have a wide variety of
service principal names. These include:
machine$@REALM (samba clients)
HOST/foo.bar@realm (win2k clients)
HOST/foo@realm (win2k clients, using netbios)
cifs/foo.bar@realm (winxp clients)
cifs/foo@realm (winxp clients, using netbios)
as well as all case variations on the above.
Because that all got 'too hard' to put into a keytab in the
traditional way (with the client to specify the name), we either
pre-compute the keys into a traditional keytab or make an in-MEMORY
keytab at run time. In both cases we specifiy the principal name to
GSSAPI, which avoids the need to store duplicate principals.
We use a 'private' keytab in our private dir, referenced from the
secrets.ldb by default.
Extra Heimdal functions used
----------------------------
(an attempt to list some of the Heimdal-specific functions I know we use)
krb5_free_keyblock_contents()
also a raft of prinicpal manipulation functions:
Prncipal Manipulation
---------------------
Samba makes extensive use of the principal manipulation functions in
Heimdal, including the known structure behind krb_principal and
krb5_realm (a char *).
Authz data extraction
---------------------
We use krb5_ticket_get_authorization_data_type(), and expect it to
return the correct authz data, even if wrapped in an AD-IFRELEVENT container.
KDC/hdb Extensions
--------------
We have modified Heimdal's 'hdb' interface to specify the 'type' of
Principal being requested. This allows us to correctly behave with
the different 'classes' of Principal name.
We currently define 2 classes:
- client (kinit)
- server (tgt)
I also now specify the kerberos principal as an explict parameter, not
an in/out value on the entry itself.
Inside hdb-ldb, we add krbtgt as a special class of principal, because
of particular special-case backend requirements.
Callbacks:
In addition, I have added a new interface hdb_fetch_ex(), which
returns a structure including callbacks, which provide the hook for
the PAC, as well as a callback into the main access control routines.
A new callback should be added to increment the bad password counter
on failure.
Another possability for a callback is to obtain the keys. This would
allow the plaintext password to only be hashed into the encryption
types we need. This idea from the eDirectory/MIT DAL work.
This probably should be combined with storing the hashed passwords in
the supplementalCredentials attribute. If combined with a kvno
parameter, this could also allow changing of the krbtgt password
(valuable for security).
libkdc
------
Samba4 needs to be built as a single binary (design requirement), and
this should include the KDC. Samba also (and perhaps more
importantly) needs to control the configuration environment of the
KDC.
The interface we have defined for libkdc allow for packet injection
into the post-socket layer, with a defined krb5_context and
kdb5_kdc_configuration structure. These effectively redirect the
kerberos warnings, logging and database calls as we require.
Using our socket lib
--------------------
An important detail in the use of libkdc is that we use our own socket
lib. This allows the KDC code to be as portable as the rest of samba
(this cuts both ways), but far more importantly it ensures a
consistancy in the handling of requests, binding to sockets etc.
To handle TCP, we use of our socket layer in much the same way as
we deal with TCP for CIFS. Tridge created a generic packet handling
layer for this.
For the client, we likewise must take over the socket functions, so
that our single thread smbd will not lock up talking to itself. (We
allow processing while waiting for packets in our socket routines).
Kerberos logging support
------------------------
Samba now (optionally in the main code, required for the KDC) uses the
krb5_log_facility from Heimdal. This allows us to redirect the
warnings and status from the KDC (and client/server kerberos code) to
Samba's DEBUG() system.
Similarly important is the Heimdal-specific krb5_get_error_string()
function, which does a lot to reduce the 'administrator pain' level,
by providing specific, english text-string error messages instead of
just error code translations.
Short name rules
----------------
Samba is highly likely to be misconfigured, in many weird and
interesting ways. As such, we have a patch for Heimdal that avoids
DNS lookups on names without a . in them. This should avoid some
delay and root server load.
PAC Correctness
---------------
We now put the PAC into the TGT, not just the service ticket.
Forwarded tickets
-----------------
We extract forwarded tickets from the GSSAPI layer, and put
them into the credentials. We can then use them for proxy work.
Kerberos TODO
=============
(Feel free to contribute to any of these tasks, or ask
abartlet@samba.org about them).
Lockout Control
--------------
We need to get (either if PADL publishes their patch, or write our
own) access control hooks in the Heimdal KDC. We need to lockout
accounts, and perform other controls.
Gssmonger
---------
Microsoft has released a testsuite called gssmonger, which tests
interop. We should compile it against lorikeet-heimdal, MIT and see
if we can build a 'Samba4' server for it.
Kpasswd server
--------------
I have a partial kpasswd server which needs finishing, and a we need a
client testsuite written, either via the krb5 API or directly against
GENSEC and the ASN.1 routines.
Currently it only works for Heimdal, not MIT clients. This may be due
to call ordering constraints.
Correct TCP support
-------------------
Our current TCP support does not send back 'too large' error messages
if the high bit is set. This is needed for a proposed extension
mechanism, but is likewise unsupported in both current Heimdal and MIT.
+124
View File
@@ -0,0 +1,124 @@
/*
Unix SMB/CIFS implementation.
kerberos utility library
Copyright (C) Andrew Tridgell 2001
Copyright (C) Remus Koos 2001
Copyright (C) Nalin Dahyabhai 2004.
Copyright (C) Jeremy Allison 2004.
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "system/kerberos.h"
#include "roken.h"
#ifdef HAVE_KRB5
/*
simulate a kinit, putting the tgt in the given credentials cache.
Orignally by remus@snapserver.com
This version is built to use a keyblock, rather than needing the
original password.
*/
int kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
krb5_principal principal, krb5_keyblock *keyblock,
time_t *expire_time, time_t *kdc_time)
{
krb5_error_code code = 0;
krb5_creds my_creds;
krb5_get_init_creds_opt options;
krb5_get_init_creds_opt_init(&options);
krb5_get_init_creds_opt_set_default_flags(ctx, NULL, NULL, &options);
if ((code = krb5_get_init_creds_keyblock(ctx, &my_creds, principal, keyblock,
0, NULL, &options))) {
return code;
}
if ((code = krb5_cc_initialize(ctx, cc, principal))) {
krb5_free_cred_contents(ctx, &my_creds);
return code;
}
if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
krb5_free_cred_contents(ctx, &my_creds);
return code;
}
if (expire_time) {
*expire_time = (time_t) my_creds.times.endtime;
}
if (kdc_time) {
*kdc_time = (time_t) my_creds.times.starttime;
}
krb5_free_cred_contents(ctx, &my_creds);
return 0;
}
/*
simulate a kinit, putting the tgt in the given credentials cache.
Orignally by remus@snapserver.com
*/
int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
krb5_principal principal, const char *password,
time_t *expire_time, time_t *kdc_time)
{
krb5_error_code code = 0;
krb5_creds my_creds;
krb5_get_init_creds_opt options;
krb5_get_init_creds_opt_init(&options);
krb5_get_init_creds_opt_set_default_flags(ctx, NULL, NULL, &options);
if ((code = krb5_get_init_creds_password(ctx, &my_creds, principal, password,
NULL,
NULL, 0, NULL, &options))) {
return code;
}
if ((code = krb5_cc_initialize(ctx, cc, principal))) {
krb5_free_cred_contents(ctx, &my_creds);
return code;
}
if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
krb5_free_cred_contents(ctx, &my_creds);
return code;
}
if (expire_time) {
*expire_time = (time_t) my_creds.times.endtime;
}
if (kdc_time) {
*kdc_time = (time_t) my_creds.times.starttime;
}
krb5_free_cred_contents(ctx, &my_creds);
return 0;
}
#endif
+158
View File
@@ -0,0 +1,158 @@
/*
Unix SMB/CIFS implementation.
simple kerberos5 routines for active directory
Copyright (C) Andrew Tridgell 2001
Copyright (C) Luke Howard 2002-2003
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if defined(HAVE_KRB5)
#include "auth/kerberos/krb5_init_context.h"
#include "librpc/gen_ndr/krb5pac.h"
struct auth_serversupplied_info;
struct cli_credentials;
struct ccache_container {
struct smb_krb5_context *smb_krb5_context;
krb5_ccache ccache;
};
struct keytab_container {
struct smb_krb5_context *smb_krb5_context;
krb5_keytab keytab;
};
/* not really ASN.1, but RFC 1964 */
#define TOK_ID_KRB_AP_REQ ((const uint8_t *)"\x01\x00")
#define TOK_ID_KRB_AP_REP ((const uint8_t *)"\x02\x00")
#define TOK_ID_KRB_ERROR ((const uint8_t *)"\x03\x00")
#define TOK_ID_GSS_GETMIC ((const uint8_t *)"\x01\x01")
#define TOK_ID_GSS_WRAP ((const uint8_t *)"\x02\x01")
#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
#define KRB5_KEY_TYPE(k) ((k)->keytype)
#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length)
#define KRB5_KEY_DATA(k) ((k)->keyvalue.data)
#else
#define KRB5_KEY_TYPE(k) ((k)->enctype)
#define KRB5_KEY_LENGTH(k) ((k)->length)
#define KRB5_KEY_DATA(k) ((k)->contents)
#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
#ifndef HAVE_KRB5_SET_REAL_TIME
krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds);
#endif
#ifndef HAVE_KRB5_SET_DEFAULT_TGS_KTYPES
krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc);
#endif
#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock *keyblock);
#endif
#ifndef HAVE_KRB5_FREE_UNPARSED_NAME
void krb5_free_unparsed_name(krb5_context ctx, char *val);
#endif
#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
#endif
/* Samba wrapper function for krb5 functionality. */
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt);
krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes);
void free_kerberos_etypes(krb5_context context, krb5_enctype *enctypes);
BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote);
krb5_error_code ads_krb5_mk_req(krb5_context context,
krb5_auth_context *auth_context,
const krb5_flags ap_req_options,
const char *principal,
krb5_ccache ccache,
krb5_data *outbuf);
BOOL get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt);
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
struct smb_krb5_context *smb_krb5_context,
krb5_auth_context *auth_context,
struct cli_credentials *machine_account,
const char *service,
const DATA_BLOB *enc_ticket,
krb5_ticket **tkt,
DATA_BLOB *ap_rep,
krb5_keyblock **keyblock);
int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
krb5_principal principal, const char *password,
time_t *expire_time, time_t *kdc_time);
int kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
krb5_principal principal, krb5_keyblock *keyblock,
time_t *expire_time, time_t *kdc_time);
krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
krb5_principal host_princ,
int enctype);
void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype);
BOOL kerberos_compatible_enctypes(krb5_context context, krb5_enctype enctype1, krb5_enctype enctype2);
void kerberos_free_data_contents(krb5_context context, krb5_data *pdata);
krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry);
char *smb_get_krb5_error_message(krb5_context context, krb5_error_code code, TALLOC_CTX *mem_ctx);
krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
struct cli_credentials *credentials,
struct smb_krb5_context *smb_krb5_context,
krb5_ccache ccache);
krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
struct cli_credentials *credentials,
struct smb_krb5_context *smb_krb5_context,
krb5_principal *princ);
NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
struct PAC_DATA **pac_data_out,
DATA_BLOB blob,
krb5_context context,
const krb5_keyblock *krbtgt_keyblock,
const krb5_keyblock *service_keyblock,
krb5_const_principal client_principal,
time_t tgs_authtime,
krb5_error_code *k5ret);
NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
struct PAC_LOGON_INFO **logon_info,
DATA_BLOB blob,
krb5_context context,
const krb5_keyblock *krbtgt_keyblock,
const krb5_keyblock *service_keyblock,
krb5_const_principal client_principal,
time_t tgs_authtime,
krb5_error_code *k5ret);
krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
struct PAC_DATA *pac_data,
krb5_context context,
const krb5_keyblock *krbtgt_keyblock,
const krb5_keyblock *service_keyblock,
DATA_BLOB *pac);
krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
struct auth_serversupplied_info *server_info,
krb5_context context,
const krb5_keyblock *krbtgt_keyblock,
const krb5_keyblock *service_keyblock,
krb5_principal client_principal,
time_t tgs_authtime,
DATA_BLOB *pac);
#include "auth/kerberos/proto.h"
#endif /* HAVE_KRB5 */
@@ -0,0 +1,101 @@
/*
* Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* This file for code taken from the Heimdal code, to preserve licence */
/* Modified by Andrew Bartlett <abartlet@samba.org> */
#include "includes.h"
#include "system/kerberos.h"
/* Taken from accept_sec_context.c,v 1.65 */
krb5_error_code smb_rd_req_return_stuff(krb5_context context,
krb5_auth_context *auth_context,
const krb5_data *inbuf,
krb5_keytab keytab,
krb5_principal acceptor_principal,
krb5_data *outbuf,
krb5_ticket **ticket,
krb5_keyblock **keyblock)
{
krb5_rd_req_in_ctx in = NULL;
krb5_rd_req_out_ctx out = NULL;
krb5_error_code kret;
*keyblock = NULL;
*ticket = NULL;
outbuf->length = 0;
outbuf->data = NULL;
kret = krb5_rd_req_in_ctx_alloc(context, &in);
if (kret == 0)
kret = krb5_rd_req_in_set_keytab(context, in, keytab);
if (kret) {
if (in)
krb5_rd_req_in_ctx_free(context, in);
return kret;
}
kret = krb5_rd_req_ctx(context,
auth_context,
inbuf,
acceptor_principal,
in, &out);
krb5_rd_req_in_ctx_free(context, in);
if (kret) {
return kret;
}
/*
* We need to remember some data on the context_handle.
*/
kret = krb5_rd_req_out_get_ticket(context, out,
ticket);
if (kret == 0) {
kret = krb5_rd_req_out_get_keyblock(context, out,
keyblock);
}
krb5_rd_req_out_ctx_free(context, out);
if (kret == 0) {
kret = krb5_mk_rep(context, *auth_context, outbuf);
}
if (kret) {
krb5_free_ticket(context, *ticket);
krb5_free_keyblock(context, *keyblock);
krb5_data_free(outbuf);
}
return kret;
}
+617
View File
@@ -0,0 +1,617 @@
/*
Unix SMB/CIFS implementation.
Create and parse the krb5 PAC
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
Copyright (C) Andrew Tridgell 2001
Copyright (C) Luke Howard 2002-2003
Copyright (C) Stefan Metzmacher 2004-2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "system/kerberos.h"
#include "auth/kerberos/kerberos.h"
#include "librpc/gen_ndr/ndr_krb5pac.h"
#include "lib/ldb/include/ldb.h"
#include "auth/auth_sam.h"
static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
DATA_BLOB pac_data,
struct PAC_SIGNATURE_DATA *sig,
krb5_context context,
const krb5_keyblock *keyblock)
{
krb5_error_code ret;
krb5_crypto crypto;
Checksum cksum;
cksum.cksumtype = (CKSUMTYPE)sig->type;
cksum.checksum.length = sig->signature.length;
cksum.checksum.data = sig->signature.data;
ret = krb5_crypto_init(context,
keyblock,
0,
&crypto);
if (ret) {
DEBUG(0,("krb5_crypto_init() failed: %s\n",
smb_get_krb5_error_message(context, ret, mem_ctx)));
return ret;
}
ret = krb5_verify_checksum(context,
crypto,
KRB5_KU_OTHER_CKSUM,
pac_data.data,
pac_data.length,
&cksum);
krb5_crypto_destroy(context, crypto);
return ret;
}
NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
struct PAC_DATA **pac_data_out,
DATA_BLOB blob,
krb5_context context,
const krb5_keyblock *krbtgt_keyblock,
const krb5_keyblock *service_keyblock,
krb5_const_principal client_principal,
time_t tgs_authtime,
krb5_error_code *k5ret)
{
krb5_error_code ret;
NTSTATUS status;
struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
struct PAC_LOGON_INFO *logon_info = NULL;
struct PAC_LOGON_NAME *logon_name = NULL;
struct PAC_DATA *pac_data;
struct PAC_DATA_RAW *pac_data_raw;
DATA_BLOB *srv_sig_blob = NULL;
DATA_BLOB *kdc_sig_blob = NULL;
DATA_BLOB modified_pac_blob;
NTTIME tgs_authtime_nttime;
krb5_principal client_principal_pac;
int i;
krb5_clear_error_string(context);
if (k5ret) {
*k5ret = KRB5_PARSE_MALFORMED;
}
pac_data = talloc(mem_ctx, struct PAC_DATA);
pac_data_raw = talloc(mem_ctx, struct PAC_DATA_RAW);
kdc_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
srv_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
if (k5ret) {
*k5ret = ENOMEM;
}
return NT_STATUS_NO_MEMORY;
}
status = ndr_pull_struct_blob(&blob, pac_data, pac_data,
(ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("can't parse the PAC\n"));
return status;
}
if (pac_data->num_buffers < 4) {
/* we need logon_ingo, service_key and kdc_key */
DEBUG(0,("less than 4 PAC buffers\n"));
return NT_STATUS_INVALID_PARAMETER;
}
status = ndr_pull_struct_blob(&blob, pac_data_raw, pac_data_raw,
(ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("can't parse the PAC\n"));
return status;
}
if (pac_data_raw->num_buffers < 4) {
/* we need logon_ingo, service_key and kdc_key */
DEBUG(0,("less than 4 PAC buffers\n"));
return NT_STATUS_INVALID_PARAMETER;
}
if (pac_data->num_buffers != pac_data_raw->num_buffers) {
/* we need logon_ingo, service_key and kdc_key */
DEBUG(0,("misparse! PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n",
pac_data->num_buffers, pac_data_raw->num_buffers));
return NT_STATUS_INVALID_PARAMETER;
}
for (i=0; i < pac_data->num_buffers; i++) {
if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) {
DEBUG(0,("misparse! PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n",
i, pac_data->buffers[i].type, pac_data->buffers[i].type));
return NT_STATUS_INVALID_PARAMETER;
}
switch (pac_data->buffers[i].type) {
case PAC_TYPE_LOGON_INFO:
if (!pac_data->buffers[i].info) {
break;
}
logon_info = pac_data->buffers[i].info->logon_info.info;
break;
case PAC_TYPE_SRV_CHECKSUM:
if (!pac_data->buffers[i].info) {
break;
}
srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum;
srv_sig_blob = &pac_data_raw->buffers[i].info->remaining;
break;
case PAC_TYPE_KDC_CHECKSUM:
if (!pac_data->buffers[i].info) {
break;
}
kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum;
kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining;
break;
case PAC_TYPE_LOGON_NAME:
logon_name = &pac_data->buffers[i].info->logon_name;
break;
default:
break;
}
}
if (!logon_info) {
DEBUG(0,("PAC no logon_info\n"));
return NT_STATUS_INVALID_PARAMETER;
}
if (!logon_name) {
DEBUG(0,("PAC no logon_name\n"));
return NT_STATUS_INVALID_PARAMETER;
}
if (!srv_sig_ptr || !srv_sig_blob) {
DEBUG(0,("PAC no srv_key\n"));
return NT_STATUS_INVALID_PARAMETER;
}
if (!kdc_sig_ptr || !kdc_sig_blob) {
DEBUG(0,("PAC no kdc_key\n"));
return NT_STATUS_INVALID_PARAMETER;
}
/* Find and zero out the signatures, as required by the signing algorithm */
/* We find the data blobs above, now we parse them to get at the exact portion we should zero */
status = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe, kdc_sig_wipe,
(ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("can't parse the KDC signature\n"));
return status;
}
status = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe, srv_sig_wipe,
(ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("can't parse the SRV signature\n"));
return status;
}
/* Now zero the decoded structure */
memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length);
memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length);
/* and reencode, back into the same place it came from */
status = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw, kdc_sig_wipe,
(ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("can't repack the KDC signature\n"));
return status;
}
status = ndr_push_struct_blob(srv_sig_blob, pac_data_raw, srv_sig_wipe,
(ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("can't repack the SRV signature\n"));
return status;
}
/* push out the whole structure, but now with zero'ed signatures */
status = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw, pac_data_raw,
(ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("can't repack the RAW PAC\n"));
return status;
}
/* verify by service_key */
ret = check_pac_checksum(mem_ctx,
modified_pac_blob, srv_sig_ptr,
context,
service_keyblock);
if (ret) {
DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n",
smb_get_krb5_error_message(context, ret, mem_ctx)));
if (k5ret) {
*k5ret = ret;
}
return NT_STATUS_ACCESS_DENIED;
}
if (krbtgt_keyblock) {
ret = check_pac_checksum(mem_ctx,
srv_sig_ptr->signature, kdc_sig_ptr,
context, krbtgt_keyblock);
if (ret) {
DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n",
smb_get_krb5_error_message(context, ret, mem_ctx)));
if (k5ret) {
*k5ret = ret;
}
return NT_STATUS_ACCESS_DENIED;
}
}
/* Convert to NT time, so as not to loose accuracy in comparison */
unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
if (tgs_authtime_nttime != logon_name->logon_time) {
DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n"));
DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time)));
DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime)));
return NT_STATUS_ACCESS_DENIED;
}
ret = krb5_parse_name_flags(context, logon_name->account_name, KRB5_PRINCIPAL_PARSE_NO_REALM,
&client_principal_pac);
if (ret) {
DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n",
logon_name->account_name,
smb_get_krb5_error_message(context, ret, mem_ctx)));
if (k5ret) {
*k5ret = ret;
}
return NT_STATUS_INVALID_PARAMETER;
}
if (!krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) {
DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n",
logon_name->account_name));
return NT_STATUS_ACCESS_DENIED;
}
#if 0
if (strcasecmp(logon_info->info3.base.account_name.string,
"Administrator")== 0) {
file_save("tmp_pac_data-admin.dat",blob.data,blob.length);
}
#endif
DEBUG(3,("Found account name from PAC: %s [%s]\n",
logon_info->info3.base.account_name.string,
logon_info->info3.base.full_name.string));
*pac_data_out = pac_data;
return status;
}
_PUBLIC_ NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
struct PAC_LOGON_INFO **logon_info,
DATA_BLOB blob,
krb5_context context,
const krb5_keyblock *krbtgt_keyblock,
const krb5_keyblock *service_keyblock,
krb5_const_principal client_principal,
time_t tgs_authtime,
krb5_error_code *k5ret)
{
NTSTATUS nt_status;
struct PAC_DATA *pac_data;
int i;
nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
blob,
context,
krbtgt_keyblock,
service_keyblock,
client_principal,
tgs_authtime,
k5ret);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
*logon_info = NULL;
for (i=0; i < pac_data->num_buffers; i++) {
if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
continue;
}
*logon_info = pac_data->buffers[i].info->logon_info.info;
}
if (!*logon_info) {
return NT_STATUS_INVALID_PARAMETER;
}
return NT_STATUS_OK;
}
static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
DATA_BLOB *pac_data,
struct PAC_SIGNATURE_DATA *sig,
krb5_context context,
const krb5_keyblock *keyblock)
{
krb5_error_code ret;
krb5_crypto crypto;
Checksum cksum;
ret = krb5_crypto_init(context,
keyblock,
0,
&crypto);
if (ret) {
DEBUG(0,("krb5_crypto_init() failed: %s\n",
smb_get_krb5_error_message(context, ret, mem_ctx)));
return ret;
}
ret = krb5_create_checksum(context,
crypto,
KRB5_KU_OTHER_CKSUM,
0,
pac_data->data,
pac_data->length,
&cksum);
if (ret) {
DEBUG(2, ("PAC Verification failed: %s\n",
smb_get_krb5_error_message(context, ret, mem_ctx)));
}
krb5_crypto_destroy(context, crypto);
if (ret) {
return ret;
}
sig->type = cksum.cksumtype;
sig->signature = data_blob_talloc(mem_ctx, cksum.checksum.data, cksum.checksum.length);
free_Checksum(&cksum);
return 0;
}
krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
struct PAC_DATA *pac_data,
krb5_context context,
const krb5_keyblock *krbtgt_keyblock,
const krb5_keyblock *service_keyblock,
DATA_BLOB *pac)
{
NTSTATUS nt_status;
krb5_error_code ret;
DATA_BLOB zero_blob = data_blob(NULL, 0);
DATA_BLOB tmp_blob = data_blob(NULL, 0);
struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
int i;
/* First, just get the keytypes filled in (and lengths right, eventually) */
for (i=0; i < pac_data->num_buffers; i++) {
if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
continue;
}
kdc_checksum = &pac_data->buffers[i].info->kdc_cksum,
ret = make_pac_checksum(mem_ctx, &zero_blob,
kdc_checksum,
context, krbtgt_keyblock);
if (ret) {
DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
smb_get_krb5_error_message(context, ret, mem_ctx)));
talloc_free(pac_data);
return ret;
}
}
for (i=0; i < pac_data->num_buffers; i++) {
if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
continue;
}
srv_checksum = &pac_data->buffers[i].info->srv_cksum;
ret = make_pac_checksum(mem_ctx, &zero_blob,
srv_checksum,
context, service_keyblock);
if (ret) {
DEBUG(2, ("making service PAC checksum failed: %s\n",
smb_get_krb5_error_message(context, ret, mem_ctx)));
talloc_free(pac_data);
return ret;
}
}
if (!kdc_checksum) {
DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
return EINVAL;
}
if (!srv_checksum) {
DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
return EINVAL;
}
/* But wipe out the actual signatures */
memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
(ndr_push_flags_fn_t)ndr_push_PAC_DATA);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
talloc_free(pac_data);
return EINVAL;
}
/* Then sign the result of the previous push, where the sig was zero'ed out */
ret = make_pac_checksum(mem_ctx, &tmp_blob, srv_checksum,
context, service_keyblock);
/* Then sign Server checksum */
ret = make_pac_checksum(mem_ctx, &srv_checksum->signature, kdc_checksum, context, krbtgt_keyblock);
if (ret) {
DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
smb_get_krb5_error_message(context, ret, mem_ctx)));
talloc_free(pac_data);
return ret;
}
/* And push it out again, this time to the world. This relies on determanistic pointer values */
nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
(ndr_push_flags_fn_t)ndr_push_PAC_DATA);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
talloc_free(pac_data);
return EINVAL;
}
*pac = tmp_blob;
return ret;
}
krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
struct auth_serversupplied_info *server_info,
krb5_context context,
const krb5_keyblock *krbtgt_keyblock,
const krb5_keyblock *service_keyblock,
krb5_principal client_principal,
time_t tgs_authtime,
DATA_BLOB *pac)
{
NTSTATUS nt_status;
krb5_error_code ret;
struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
struct netr_SamInfo3 *sam3;
union PAC_INFO *u_LOGON_INFO;
struct PAC_LOGON_INFO *LOGON_INFO;
union PAC_INFO *u_LOGON_NAME;
struct PAC_LOGON_NAME *LOGON_NAME;
union PAC_INFO *u_KDC_CHECKSUM;
union PAC_INFO *u_SRV_CHECKSUM;
char *name;
enum {
PAC_BUF_LOGON_INFO = 0,
PAC_BUF_LOGON_NAME = 1,
PAC_BUF_SRV_CHECKSUM = 2,
PAC_BUF_KDC_CHECKSUM = 3,
PAC_BUF_NUM_BUFFERS = 4
};
if (!pac_data) {
return ENOMEM;
}
pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
pac_data->version = 0;
pac_data->buffers = talloc_array(pac_data,
struct PAC_BUFFER,
pac_data->num_buffers);
if (!pac_data->buffers) {
talloc_free(pac_data);
return ENOMEM;
}
/* LOGON_INFO */
u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
if (!u_LOGON_INFO) {
talloc_free(pac_data);
return ENOMEM;
}
pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
/* LOGON_NAME */
u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
if (!u_LOGON_NAME) {
talloc_free(pac_data);
return ENOMEM;
}
pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
LOGON_NAME = &u_LOGON_NAME->logon_name;
/* SRV_CHECKSUM */
u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
if (!u_SRV_CHECKSUM) {
talloc_free(pac_data);
return ENOMEM;
}
pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
/* KDC_CHECKSUM */
u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
if (!u_KDC_CHECKSUM) {
talloc_free(pac_data);
return ENOMEM;
}
pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
/* now the real work begins... */
LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
if (!LOGON_INFO) {
talloc_free(pac_data);
return ENOMEM;
}
nt_status = auth_convert_server_info_saminfo3(LOGON_INFO, server_info, &sam3);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
talloc_free(pac_data);
return EINVAL;
}
u_LOGON_INFO->logon_info.info = LOGON_INFO;
LOGON_INFO->info3 = *sam3;
ret = krb5_unparse_name_flags(context, client_principal,
KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
if (ret) {
return ret;
}
LOGON_NAME->account_name = talloc_strdup(LOGON_NAME, name);
free(name);
/*
this logon_time field is absolutely critical. This is what
caused all our PAC troubles :-)
*/
unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
ret = kerberos_encode_pac(mem_ctx,
pac_data,
context,
krbtgt_keyblock,
service_keyblock,
pac);
talloc_free(pac_data);
return ret;
}
+700
View File
@@ -0,0 +1,700 @@
/*
Unix SMB/CIFS implementation.
Kerberos utility functions for GENSEC
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "system/kerberos.h"
#include "auth/kerberos/kerberos.h"
#include "auth/credentials/credentials.h"
#include "auth/credentials/credentials_krb5.h"
struct principal_container {
struct smb_krb5_context *smb_krb5_context;
krb5_principal principal;
};
static int free_principal(struct principal_container *pc)
{
/* current heimdal - 0.6.3, which we need anyway, fixes segfaults here */
krb5_free_principal(pc->smb_krb5_context->krb5_context, pc->principal);
return 0;
}
static krb5_error_code salt_principal_from_credentials(TALLOC_CTX *parent_ctx,
struct cli_credentials *machine_account,
struct smb_krb5_context *smb_krb5_context,
krb5_principal *salt_princ)
{
krb5_error_code ret;
char *machine_username;
char *salt_body;
char *lower_realm;
const char *salt_principal;
struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
if (!mem_ctx) {
return ENOMEM;
}
salt_principal = cli_credentials_get_salt_principal(machine_account);
if (salt_principal) {
ret = krb5_parse_name(smb_krb5_context->krb5_context, salt_principal, salt_princ);
} else {
machine_username = talloc_strdup(mem_ctx, cli_credentials_get_username(machine_account));
if (!machine_username) {
talloc_free(mem_ctx);
return ENOMEM;
}
if (machine_username[strlen(machine_username)-1] == '$') {
machine_username[strlen(machine_username)-1] = '\0';
}
lower_realm = strlower_talloc(mem_ctx, cli_credentials_get_realm(machine_account));
if (!lower_realm) {
talloc_free(mem_ctx);
return ENOMEM;
}
salt_body = talloc_asprintf(mem_ctx, "%s.%s", machine_username,
lower_realm);
if (!salt_body) {
talloc_free(mem_ctx);
return ENOMEM;
}
ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ,
cli_credentials_get_realm(machine_account),
"host", salt_body, NULL);
}
if (ret == 0) {
/* This song-and-dance effectivly puts the principal
* into talloc, so we can't loose it. */
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
mem_ctx->principal = *salt_princ;
talloc_set_destructor(mem_ctx, free_principal);
}
return ret;
}
/* Obtain the principal set on this context. Requires a
* smb_krb5_context because we are doing krb5 principal parsing with
* the library routines. The returned princ is placed in the talloc
* system by means of a destructor (do *not* free). */
krb5_error_code principal_from_credentials(TALLOC_CTX *parent_ctx,
struct cli_credentials *credentials,
struct smb_krb5_context *smb_krb5_context,
krb5_principal *princ)
{
krb5_error_code ret;
const char *princ_string;
struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
if (!mem_ctx) {
return ENOMEM;
}
princ_string = cli_credentials_get_principal(credentials, mem_ctx);
/* A NULL here has meaning, as the gssapi server case will
* then use the principal from the client */
if (!princ_string) {
talloc_free(mem_ctx);
princ = NULL;
return 0;
}
ret = krb5_parse_name(smb_krb5_context->krb5_context,
princ_string, princ);
if (ret == 0) {
/* This song-and-dance effectivly puts the principal
* into talloc, so we can't loose it. */
mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
mem_ctx->principal = *princ;
talloc_set_destructor(mem_ctx, free_principal);
}
return ret;
}
/**
* Return a freshly allocated ccache (destroyed by destructor on child
* of parent_ctx), for a given set of client credentials
*/
krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
struct cli_credentials *credentials,
struct smb_krb5_context *smb_krb5_context,
krb5_ccache ccache)
{
krb5_error_code ret;
const char *password;
time_t kdc_time = 0;
krb5_principal princ;
int tries;
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
if (!mem_ctx) {
return ENOMEM;
}
ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ);
if (ret) {
talloc_free(mem_ctx);
return ret;
}
password = cli_credentials_get_password(credentials);
tries = 2;
while (tries--) {
if (password) {
ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache,
princ,
password, NULL, &kdc_time);
} else {
/* No password available, try to use a keyblock instead */
krb5_keyblock keyblock;
const struct samr_Password *mach_pwd;
mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
if (!mach_pwd) {
talloc_free(mem_ctx);
DEBUG(1, ("kinit_to_ccache: No password available for kinit\n"));
return EINVAL;
}
ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
ETYPE_ARCFOUR_HMAC_MD5,
mach_pwd->hash, sizeof(mach_pwd->hash),
&keyblock);
if (ret == 0) {
ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache,
princ,
&keyblock, NULL, &kdc_time);
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
}
}
if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
/* Perhaps we have been given an invalid skew, so try again without it */
time_t t = time(NULL);
krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
} else {
/* not a skew problem */
break;
}
}
if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
DEBUG(1,("kinit for %s failed (%s)\n",
cli_credentials_get_principal(credentials, mem_ctx),
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
/* cope with ticket being in the future due to clock skew */
if ((unsigned)kdc_time > time(NULL)) {
time_t t = time(NULL);
int time_offset =(unsigned)kdc_time-t;
DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
}
if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
ret = kinit_to_ccache(parent_ctx,
credentials,
smb_krb5_context,
ccache);
}
if (ret) {
DEBUG(1,("kinit for %s failed (%s)\n",
cli_credentials_get_principal(credentials, mem_ctx),
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
return 0;
}
static int free_keytab(struct keytab_container *ktc)
{
krb5_kt_close(ktc->smb_krb5_context->krb5_context, ktc->keytab);
return 0;
}
int smb_krb5_open_keytab(TALLOC_CTX *mem_ctx,
struct smb_krb5_context *smb_krb5_context,
const char *keytab_name, struct keytab_container **ktc)
{
krb5_keytab keytab;
int ret;
ret = krb5_kt_resolve(smb_krb5_context->krb5_context, keytab_name, &keytab);
if (ret) {
DEBUG(1,("failed to open krb5 keytab: %s\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
return ret;
}
*ktc = talloc(mem_ctx, struct keytab_container);
if (!*ktc) {
return ENOMEM;
}
(*ktc)->smb_krb5_context = talloc_reference(*ktc, smb_krb5_context);
(*ktc)->keytab = keytab;
talloc_set_destructor(*ktc, free_keytab);
return 0;
}
struct enctypes_container {
struct smb_krb5_context *smb_krb5_context;
krb5_enctype *enctypes;
};
static int free_enctypes(struct enctypes_container *etc)
{
free_kerberos_etypes(etc->smb_krb5_context->krb5_context, etc->enctypes);
return 0;
}
static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
const char *princ_string,
krb5_principal princ,
krb5_principal salt_princ,
int kvno,
const char *password_s,
struct smb_krb5_context *smb_krb5_context,
krb5_keytab keytab)
{
int i;
krb5_error_code ret;
krb5_enctype *enctypes;
char *enctype_string;
struct enctypes_container *etc;
krb5_data password;
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
if (!mem_ctx) {
return ENOMEM;
}
etc = talloc(mem_ctx, struct enctypes_container);
if (!etc) {
talloc_free(mem_ctx);
return ENOMEM;
}
ret = get_kerberos_allowed_etypes(smb_krb5_context->krb5_context,
&enctypes);
if (ret != 0) {
DEBUG(1,("keytab_add_keys: getting encrption types failed (%s)\n",
error_message(ret)));
talloc_free(mem_ctx);
return ret;
}
etc->smb_krb5_context = talloc_reference(etc, smb_krb5_context);
etc->enctypes = enctypes;
talloc_set_destructor(etc, free_enctypes);
password.data = discard_const_p(char *, password_s);
password.length = strlen(password_s);
for (i=0; enctypes[i]; i++) {
krb5_keytab_entry entry;
ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context,
salt_princ, &password, &entry.keyblock, enctypes[i]);
if (ret != 0) {
talloc_free(mem_ctx);
return ret;
}
entry.principal = princ;
entry.vno = kvno;
ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
enctype_string = NULL;
krb5_enctype_to_string(smb_krb5_context->krb5_context, enctypes[i], &enctype_string);
if (ret != 0) {
DEBUG(1, ("Failed to add %s entry for %s(kvno %d) to keytab: %s\n",
enctype_string,
princ_string,
kvno,
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
free(enctype_string);
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
return ret;
}
DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
princ_string, kvno,
enctype_string));
free(enctype_string);
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
}
talloc_free(mem_ctx);
return 0;
}
static int create_keytab(TALLOC_CTX *parent_ctx,
struct cli_credentials *machine_account,
struct smb_krb5_context *smb_krb5_context,
krb5_keytab keytab,
BOOL add_old)
{
krb5_error_code ret;
const char *password_s;
char *enctype_string;
const char *old_secret;
int kvno;
krb5_principal salt_princ;
krb5_principal princ;
const char *princ_string;
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
if (!mem_ctx) {
return ENOMEM;
}
princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
/* Get the principal we will store the new keytab entries under */
ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
if (ret) {
DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
/* The salt used to generate these entries may be different however, fetch that */
ret = salt_principal_from_credentials(mem_ctx, machine_account,
smb_krb5_context,
&salt_princ);
if (ret) {
DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
/* Finally, do the dance to get the password to put in the entry */
password_s = cli_credentials_get_password(machine_account);
if (!password_s) {
/* If we don't have the plaintext password, try for
* the MD4 password hash */
krb5_keytab_entry entry;
const struct samr_Password *mach_pwd;
mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
if (!mach_pwd) {
DEBUG(1, ("create_keytab: Domain trust informaton for account %s not available\n",
cli_credentials_get_principal(machine_account, mem_ctx)));
talloc_free(mem_ctx);
return EINVAL;
}
ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
ETYPE_ARCFOUR_HMAC_MD5,
mach_pwd->hash, sizeof(mach_pwd->hash),
&entry.keyblock);
if (ret) {
DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
entry.principal = princ;
entry.vno = cli_credentials_get_kvno(machine_account);
ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
if (ret) {
DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s",
cli_credentials_get_principal(machine_account, mem_ctx),
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
return ret;
}
krb5_enctype_to_string(smb_krb5_context->krb5_context,
ETYPE_ARCFOUR_HMAC_MD5,
&enctype_string);
DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n",
cli_credentials_get_principal(machine_account, mem_ctx),
cli_credentials_get_kvno(machine_account),
enctype_string));
free(enctype_string);
krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
/* Can't go any further, we only have this one key */
talloc_free(mem_ctx);
return 0;
}
kvno = cli_credentials_get_kvno(machine_account);
/* good, we actually have the real plaintext */
ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
kvno, password_s, smb_krb5_context, keytab);
if (!ret) {
talloc_free(mem_ctx);
return ret;
}
if (!add_old || kvno == 0) {
talloc_free(mem_ctx);
return 0;
}
old_secret = cli_credentials_get_old_password(machine_account);
if (!old_secret) {
talloc_free(mem_ctx);
return 0;
}
ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ,
kvno - 1, old_secret, smb_krb5_context, keytab);
if (!ret) {
talloc_free(mem_ctx);
return ret;
}
talloc_free(mem_ctx);
return 0;
}
/*
* Walk the keytab, looking for entries of this principal name, with KVNO other than current kvno -1.
*
* These entries are now stale, we only keep the current, and previous entries around.
*
* Inspired by the code in Samba3 for 'use kerberos keytab'.
*
*/
static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
struct cli_credentials *machine_account,
struct smb_krb5_context *smb_krb5_context,
krb5_keytab keytab, BOOL *found_previous)
{
krb5_error_code ret, ret2;
krb5_kt_cursor cursor;
krb5_principal princ;
int kvno;
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
const char *princ_string;
if (!mem_ctx) {
return ENOMEM;
}
*found_previous = False;
princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
/* Get the principal we will store the new keytab entries under */
ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
if (ret) {
DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
kvno = cli_credentials_get_kvno(machine_account);
/* for each entry in the keytab */
ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
switch (ret) {
case 0:
break;
case HEIM_ERR_OPNOTSUPP:
case ENOENT:
case KRB5_KT_END:
/* no point enumerating if there isn't anything here */
talloc_free(mem_ctx);
return 0;
default:
DEBUG(1,("failed to open keytab for read of old entries: %s\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret;
}
while (!ret) {
krb5_keytab_entry entry;
ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
if (ret) {
break;
}
/* if it matches our principal */
if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
/* Free the entry, it wasn't the one we were looking for anyway */
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
continue;
}
/* delete it, if it is not kvno -1 */
if (entry.vno != (kvno - 1 )) {
/* Release the enumeration. We are going to
* have to start this from the top again,
* because deletes during enumeration may not
* always be consistant.
*
* Also, the enumeration locks a FILE: keytab
*/
krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
/* Deleted: Restart from the top */
ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
if (ret2) {
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
DEBUG(1,("failed to restart enumeration of keytab: %s\n",
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
talloc_free(mem_ctx);
return ret2;
}
if (ret) {
break;
}
} else {
*found_previous = True;
}
/* Free the entry, we don't need it any more */
krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
}
krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
switch (ret) {
case 0:
break;
case ENOENT:
case KRB5_KT_END:
ret = 0;
break;
default:
DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
princ_string,
smb_get_krb5_error_message(smb_krb5_context->krb5_context,
ret, mem_ctx)));
}
talloc_free(mem_ctx);
return ret;
}
int smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
struct cli_credentials *machine_account,
struct smb_krb5_context *smb_krb5_context,
struct keytab_container *keytab_container)
{
krb5_error_code ret;
BOOL found_previous;
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
if (!mem_ctx) {
return ENOMEM;
}
ret = remove_old_entries(mem_ctx, machine_account,
smb_krb5_context, keytab_container->keytab, &found_previous);
if (ret != 0) {
talloc_free(mem_ctx);
return ret;
}
/* Create a new keytab. If during the cleanout we found
* entires for kvno -1, then don't try and duplicate them.
* Otherwise, add kvno, and kvno -1 */
ret = create_keytab(mem_ctx, machine_account, smb_krb5_context,
keytab_container->keytab,
found_previous ? False : True);
talloc_free(mem_ctx);
return ret;
}
_PUBLIC_ int smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
struct cli_credentials *machine_account,
struct smb_krb5_context *smb_krb5_context,
struct keytab_container **keytab_container)
{
krb5_error_code ret;
TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
const char *rand_string;
const char *keytab_name;
if (!mem_ctx) {
return ENOMEM;
}
*keytab_container = talloc(mem_ctx, struct keytab_container);
rand_string = generate_random_str(mem_ctx, 16);
if (!rand_string) {
talloc_free(mem_ctx);
return ENOMEM;
}
keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s",
rand_string);
if (!keytab_name) {
talloc_free(mem_ctx);
return ENOMEM;
}
ret = smb_krb5_open_keytab(mem_ctx, smb_krb5_context, keytab_name, keytab_container);
if (ret) {
return ret;
}
ret = smb_krb5_update_keytab(mem_ctx, machine_account, smb_krb5_context, *keytab_container);
if (ret == 0) {
talloc_steal(parent_ctx, *keytab_container);
} else {
*keytab_container = NULL;
}
talloc_free(mem_ctx);
return ret;
}
@@ -0,0 +1,455 @@
/*
Unix SMB/CIFS implementation.
Wrapper for krb5_init_context
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
Copyright (C) Andrew Tridgell 2005
Copyright (C) Stefan Metzmacher 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "system/kerberos.h"
#include "heimdal/lib/krb5/krb5_locl.h"
#include "auth/kerberos/kerberos.h"
#include "lib/socket/socket.h"
#include "system/network.h"
#include "lib/events/events.h"
#include "roken.h"
/*
context structure for operations on cldap packets
*/
struct smb_krb5_socket {
struct socket_context *sock;
/* the fd event */
struct fd_event *fde;
BOOL timeout;
NTSTATUS status;
DATA_BLOB request, reply, partial;
size_t partial_read;
krb5_krbhst_info *hi;
};
static int smb_krb5_context_destroy_1(struct smb_krb5_context *ctx)
{
krb5_free_context(ctx->krb5_context);
return 0;
}
static int smb_krb5_context_destroy_2(struct smb_krb5_context *ctx)
{
/* Otherwise krb5_free_context will try and close what we have already free()ed */
krb5_set_warn_dest(ctx->krb5_context, NULL);
krb5_closelog(ctx->krb5_context, ctx->logf);
smb_krb5_context_destroy_1(ctx);
return 0;
}
/* We never close down the DEBUG system, and no need to unreference the use */
static void smb_krb5_debug_close(void *private) {
return;
}
static void smb_krb5_debug_wrapper(const char *timestr, const char *msg, void *private)
{
DEBUG(2, ("Kerberos: %s\n", msg));
}
/*
handle recv events on a smb_krb5 socket
*/
static void smb_krb5_socket_recv(struct smb_krb5_socket *smb_krb5)
{
TALLOC_CTX *tmp_ctx = talloc_new(smb_krb5);
DATA_BLOB blob;
size_t nread, dsize;
switch (smb_krb5->hi->proto) {
case KRB5_KRBHST_UDP:
smb_krb5->status = socket_pending(smb_krb5->sock, &dsize);
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
talloc_free(tmp_ctx);
return;
}
blob = data_blob_talloc(tmp_ctx, NULL, dsize);
if (blob.data == NULL && dsize != 0) {
smb_krb5->status = NT_STATUS_NO_MEMORY;
talloc_free(tmp_ctx);
return;
}
smb_krb5->status = socket_recv(smb_krb5->sock, blob.data, blob.length, &nread);
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
talloc_free(tmp_ctx);
return;
}
blob.length = nread;
if (nread == 0) {
smb_krb5->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
talloc_free(tmp_ctx);
return;
}
DEBUG(2,("Received smb_krb5 packet of length %d\n",
(int)blob.length));
talloc_steal(smb_krb5, blob.data);
smb_krb5->reply = blob;
talloc_free(tmp_ctx);
break;
case KRB5_KRBHST_TCP:
if (smb_krb5->partial.length == 0) {
smb_krb5->partial = data_blob_talloc(smb_krb5, NULL, 4);
if (!smb_krb5->partial.data) {
smb_krb5->status = NT_STATUS_NO_MEMORY;
return;
}
smb_krb5->partial_read = 0;
}
/* read in the packet length */
if (smb_krb5->partial_read < 4) {
uint32_t packet_length;
smb_krb5->status = socket_recv(smb_krb5->sock,
smb_krb5->partial.data + smb_krb5->partial_read,
4 - smb_krb5->partial_read,
&nread);
/* todo: this should be converted to the packet_*() routines */
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
return;
}
smb_krb5->partial_read += nread;
if (smb_krb5->partial_read != 4) {
return;
}
packet_length = RIVAL(smb_krb5->partial.data, 0);
smb_krb5->partial.data = talloc_realloc(smb_krb5, smb_krb5->partial.data,
uint8_t, packet_length + 4);
if (!smb_krb5->partial.data) {
smb_krb5->status = NT_STATUS_NO_MEMORY;
return;
}
smb_krb5->partial.length = packet_length + 4;
}
/* read in the body */
smb_krb5->status = socket_recv(smb_krb5->sock,
smb_krb5->partial.data + smb_krb5->partial_read,
smb_krb5->partial.length - smb_krb5->partial_read,
&nread);
if (!NT_STATUS_IS_OK(smb_krb5->status)) return;
smb_krb5->partial_read += nread;
if (smb_krb5->partial_read != smb_krb5->partial.length) return;
smb_krb5->reply = data_blob_talloc(smb_krb5, smb_krb5->partial.data + 4, smb_krb5->partial.length - 4);
break;
case KRB5_KRBHST_HTTP:
return;
}
}
/*
handle request timeouts
*/
static void smb_krb5_request_timeout(struct event_context *event_ctx,
struct timed_event *te, struct timeval t,
void *private)
{
struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
DEBUG(5,("Timed out smb_krb5 packet\n"));
smb_krb5->timeout = True;
}
/*
handle send events on a smb_krb5 socket
*/
static void smb_krb5_socket_send(struct smb_krb5_socket *smb_krb5)
{
NTSTATUS status;
size_t len;
len = smb_krb5->request.length;
status = socket_send(smb_krb5->sock, &smb_krb5->request, &len);
if (!NT_STATUS_IS_OK(status)) return;
EVENT_FD_READABLE(smb_krb5->fde);
EVENT_FD_NOT_WRITEABLE(smb_krb5->fde);
return;
}
/*
handle fd events on a smb_krb5_socket
*/
static void smb_krb5_socket_handler(struct event_context *ev, struct fd_event *fde,
uint16_t flags, void *private)
{
struct smb_krb5_socket *smb_krb5 = talloc_get_type(private, struct smb_krb5_socket);
if (flags & EVENT_FD_WRITE) {
smb_krb5_socket_send(smb_krb5);
}
if (flags & EVENT_FD_READ) {
smb_krb5_socket_recv(smb_krb5);
}
}
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
void *data,
krb5_krbhst_info *hi,
const krb5_data *send_buf,
krb5_data *recv_buf)
{
krb5_error_code ret;
NTSTATUS status;
struct socket_address *remote_addr;
const char *name;
struct addrinfo *ai, *a;
struct smb_krb5_socket *smb_krb5;
struct event_context *ev = talloc_get_type(data, struct event_context);
DATA_BLOB send_blob = data_blob_const(send_buf->data, send_buf->length);
ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
if (ret) {
return ret;
}
for (a = ai; a; a = ai->ai_next) {
smb_krb5 = talloc(NULL, struct smb_krb5_socket);
if (!smb_krb5) {
return ENOMEM;
}
smb_krb5->hi = hi;
switch (a->ai_family) {
case PF_INET:
name = "ipv4";
break;
#ifdef HAVE_SOCKET_IPV6
case PF_INET6:
name = "ipv6";
break;
#endif
default:
talloc_free(smb_krb5);
return EINVAL;
}
status = NT_STATUS_INVALID_PARAMETER;
switch (hi->proto) {
case KRB5_KRBHST_UDP:
if (lp_parm_bool(-1, "krb5", "udp", True)) {
status = socket_create(name, SOCKET_TYPE_DGRAM, &smb_krb5->sock, 0);
}
break;
case KRB5_KRBHST_TCP:
if (lp_parm_bool(-1, "krb5", "tcp", True)) {
status = socket_create(name, SOCKET_TYPE_STREAM, &smb_krb5->sock, 0);
}
break;
case KRB5_KRBHST_HTTP:
talloc_free(smb_krb5);
return EINVAL;
}
if (!NT_STATUS_IS_OK(status)) {
talloc_free(smb_krb5);
continue;
}
talloc_steal(smb_krb5, smb_krb5->sock);
remote_addr = socket_address_from_sockaddr(smb_krb5, a->ai_addr, a->ai_addrlen);
if (!remote_addr) {
talloc_free(smb_krb5);
continue;
}
status = socket_connect_ev(smb_krb5->sock, NULL, remote_addr, 0, ev);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(smb_krb5);
continue;
}
talloc_free(remote_addr);
smb_krb5->fde = event_add_fd(ev, smb_krb5,
socket_get_fd(smb_krb5->sock), 0,
smb_krb5_socket_handler, smb_krb5);
event_add_timed(ev, smb_krb5,
timeval_current_ofs(context->kdc_timeout, 0),
smb_krb5_request_timeout, smb_krb5);
EVENT_FD_WRITEABLE(smb_krb5->fde);
switch (hi->proto) {
case KRB5_KRBHST_UDP:
smb_krb5->request = send_blob;
break;
case KRB5_KRBHST_TCP:
smb_krb5->request = data_blob_talloc(smb_krb5, NULL, send_blob.length + 4);
RSIVAL(smb_krb5->request.data, 0, send_blob.length);
memcpy(smb_krb5->request.data+4, send_blob.data, send_blob.length);
break;
case KRB5_KRBHST_HTTP:
talloc_free(smb_krb5);
return EINVAL;
}
smb_krb5->timeout = False;
smb_krb5->status = NT_STATUS_OK;
smb_krb5->reply = data_blob(NULL, 0);
smb_krb5->partial = data_blob(NULL, 0);
while (!smb_krb5->timeout && (NT_STATUS_IS_OK(smb_krb5->status)) && !smb_krb5->reply.length) {
if (event_loop_once(ev) != 0) {
talloc_free(smb_krb5);
return EINVAL;
}
}
if (!NT_STATUS_IS_OK(smb_krb5->status)) {
DEBUG(2,("Error reading smb_krb5 reply packet: %s\n", nt_errstr(smb_krb5->status)));
talloc_free(smb_krb5);
continue;
}
if (smb_krb5->timeout) {
talloc_free(smb_krb5);
continue;
}
ret = krb5_data_copy(recv_buf, smb_krb5->reply.data, smb_krb5->reply.length);
if (ret) {
talloc_free(smb_krb5);
return ret;
}
talloc_free(smb_krb5);
break;
}
if (a) {
return 0;
}
return KRB5_KDC_UNREACH;
}
krb5_error_code smb_krb5_init_context(void *parent_ctx,
struct smb_krb5_context **smb_krb5_context)
{
krb5_error_code ret;
TALLOC_CTX *tmp_ctx;
struct event_context *ev;
initialize_krb5_error_table();
tmp_ctx = talloc_new(parent_ctx);
*smb_krb5_context = talloc(tmp_ctx, struct smb_krb5_context);
if (!*smb_krb5_context || !tmp_ctx) {
talloc_free(*smb_krb5_context);
talloc_free(tmp_ctx);
return ENOMEM;
}
ret = krb5_init_context(&(*smb_krb5_context)->krb5_context);
if (ret) {
DEBUG(1,("krb5_init_context failed (%s)\n",
error_message(ret)));
return ret;
}
talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_1);
if (lp_realm() && *lp_realm()) {
char *upper_realm = strupper_talloc(tmp_ctx, lp_realm());
if (!upper_realm) {
DEBUG(1,("gensec_krb5_start: could not uppercase realm: %s\n", lp_realm()));
talloc_free(tmp_ctx);
return ENOMEM;
}
ret = krb5_set_default_realm((*smb_krb5_context)->krb5_context, upper_realm);
if (ret) {
DEBUG(1,("krb5_set_default_realm failed (%s)\n",
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
talloc_free(tmp_ctx);
return ret;
}
}
/* TODO: Should we have a different name here? */
ret = krb5_initlog((*smb_krb5_context)->krb5_context, "Samba", &(*smb_krb5_context)->logf);
if (ret) {
DEBUG(1,("krb5_initlog failed (%s)\n",
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
talloc_free(tmp_ctx);
return ret;
}
talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy_2);
ret = krb5_addlog_func((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf, 0 /* min */, -1 /* max */,
smb_krb5_debug_wrapper, smb_krb5_debug_close, NULL);
if (ret) {
DEBUG(1,("krb5_addlog_func failed (%s)\n",
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
talloc_free(tmp_ctx);
return ret;
}
krb5_set_warn_dest((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf);
ev = event_context_find(*smb_krb5_context);
/* Set use of our socket lib */
ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
smb_krb5_send_and_recv_func,
ev);
if (ret) {
DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
talloc_free(tmp_ctx);
return ret;
}
(*smb_krb5_context)->krb5_context->mem_ctx = *smb_krb5_context;
talloc_steal(parent_ctx, *smb_krb5_context);
talloc_free(tmp_ctx);
/* Set options in kerberos */
krb5_set_dns_canonicalize_hostname((*smb_krb5_context)->krb5_context, FALSE);
return 0;
}
@@ -0,0 +1,34 @@
/*
Unix SMB/CIFS implementation.
simple kerberos5 routines for active directory
Copyright (C) Andrew Bartlett 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
struct smb_krb5_context {
struct krb5_context_data *krb5_context;
krb5_log_facility *logf;
};
krb5_error_code smb_krb5_init_context(void *parent_ctx,
struct smb_krb5_context **smb_krb5_context);
void smb_krb5_free_context(struct smb_krb5_context *smb_krb5_context);
krb5_error_code smb_krb5_send_and_recv_func(krb5_context context,
void *data,
krb5_krbhst_info *hi,
const krb5_data *send_buf,
krb5_data *recv_buf);
+599
View File
@@ -0,0 +1,599 @@
/*
Unix SMB/CIFS implementation.
Password and authentication handling
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
Copyright (C) Gerald Carter 2003
Copyright (C) Luke Kenneth Casson Leighton 1996-2000
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "lib/crypto/crypto.h"
#include "librpc/gen_ndr/netlogon.h"
#include "libcli/auth/libcli_auth.h"
/****************************************************************************
Core of smb password checking routine.
****************************************************************************/
static BOOL smb_pwd_check_ntlmv1(TALLOC_CTX *mem_ctx,
const DATA_BLOB *nt_response,
const uint8_t *part_passwd,
const DATA_BLOB *sec_blob,
DATA_BLOB *user_sess_key)
{
/* Finish the encryption of part_passwd. */
uint8_t p24[24];
if (part_passwd == NULL) {
DEBUG(10,("No password set - DISALLOWING access\n"));
/* No password set - always false ! */
return False;
}
if (sec_blob->length != 8) {
DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect challenge size (%lu)\n",
(unsigned long)sec_blob->length));
return False;
}
if (nt_response->length != 24) {
DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%lu)\n",
(unsigned long)nt_response->length));
return False;
}
SMBOWFencrypt(part_passwd, sec_blob->data, p24);
#if DEBUG_PASSWORD
DEBUG(100,("Part password (P16) was |\n"));
dump_data(100, part_passwd, 16);
DEBUGADD(100,("Password from client was |\n"));
dump_data(100, nt_response->data, nt_response->length);
DEBUGADD(100,("Given challenge was |\n"));
dump_data(100, sec_blob->data, sec_blob->length);
DEBUGADD(100,("Value from encryption was |\n"));
dump_data(100, p24, 24);
#endif
if (memcmp(p24, nt_response->data, 24) == 0) {
if (user_sess_key != NULL) {
*user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
SMBsesskeygen_ntv1(part_passwd, user_sess_key->data);
}
return True;
}
return False;
}
/****************************************************************************
Core of smb password checking routine. (NTLMv2, LMv2)
Note: The same code works with both NTLMv2 and LMv2.
****************************************************************************/
static BOOL smb_pwd_check_ntlmv2(TALLOC_CTX *mem_ctx,
const DATA_BLOB *ntv2_response,
const uint8_t *part_passwd,
const DATA_BLOB *sec_blob,
const char *user, const char *domain,
BOOL upper_case_domain, /* should the domain be transformed into upper case? */
DATA_BLOB *user_sess_key)
{
/* Finish the encryption of part_passwd. */
uint8_t kr[16];
uint8_t value_from_encryption[16];
DATA_BLOB client_key_data;
if (part_passwd == NULL) {
DEBUG(10,("No password set - DISALLOWING access\n"));
/* No password set - always False */
return False;
}
if (sec_blob->length != 8) {
DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect challenge size (%lu)\n",
(unsigned long)sec_blob->length));
return False;
}
if (ntv2_response->length < 24) {
/* We MUST have more than 16 bytes, or the stuff below will go
crazy. No known implementation sends less than the 24 bytes
for LMv2, let alone NTLMv2. */
DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%lu)\n",
(unsigned long)ntv2_response->length));
return False;
}
client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
/*
todo: should we be checking this for anything? We can't for LMv2,
but for NTLMv2 it is meant to contain the current time etc.
*/
if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
return False;
}
SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
#if DEBUG_PASSWORD
DEBUG(100,("Part password (P16) was |\n"));
dump_data(100, part_passwd, 16);
DEBUGADD(100,("Password from client was |\n"));
dump_data(100, ntv2_response->data, ntv2_response->length);
DEBUGADD(100,("Variable data from client was |\n"));
dump_data(100, client_key_data.data, client_key_data.length);
DEBUGADD(100,("Given challenge was |\n"));
dump_data(100, sec_blob->data, sec_blob->length);
DEBUGADD(100,("Value from encryption was |\n"));
dump_data(100, value_from_encryption, 16);
#endif
data_blob_clear_free(&client_key_data);
if (memcmp(value_from_encryption, ntv2_response->data, 16) == 0) {
if (user_sess_key != NULL) {
*user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
}
return True;
}
return False;
}
/****************************************************************************
Core of smb password checking routine. (NTLMv2, LMv2)
Note: The same code works with both NTLMv2 and LMv2.
****************************************************************************/
static BOOL smb_sess_key_ntlmv2(TALLOC_CTX *mem_ctx,
const DATA_BLOB *ntv2_response,
const uint8_t *part_passwd,
const DATA_BLOB *sec_blob,
const char *user, const char *domain,
BOOL upper_case_domain, /* should the domain be transformed into upper case? */
DATA_BLOB *user_sess_key)
{
/* Finish the encryption of part_passwd. */
uint8_t kr[16];
uint8_t value_from_encryption[16];
DATA_BLOB client_key_data;
if (part_passwd == NULL) {
DEBUG(10,("No password set - DISALLOWING access\n"));
/* No password set - always False */
return False;
}
if (sec_blob->length != 8) {
DEBUG(0, ("smb_sess_key_ntlmv2: incorrect challenge size (%lu)\n",
(unsigned long)sec_blob->length));
return False;
}
if (ntv2_response->length < 24) {
/* We MUST have more than 16 bytes, or the stuff below will go
crazy. No known implementation sends less than the 24 bytes
for LMv2, let alone NTLMv2. */
DEBUG(0, ("smb_sess_key_ntlmv2: incorrect password length (%lu)\n",
(unsigned long)ntv2_response->length));
return False;
}
client_key_data = data_blob_talloc(mem_ctx, ntv2_response->data+16, ntv2_response->length-16);
if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
return False;
}
SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
*user_sess_key = data_blob_talloc(mem_ctx, NULL, 16);
SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
return True;
}
/**
* Compare password hashes against those from the SAM
*
* @param mem_ctx talloc context
* @param client_lanman LANMAN password hash, as supplied by the client
* @param client_nt NT (MD4) password hash, as supplied by the client
* @param username internal Samba username, for log messages
* @param client_username username the client used
* @param client_domain domain name the client used (may be mapped)
* @param stored_lanman LANMAN password hash, as stored on the SAM
* @param stored_nt NT (MD4) password hash, as stored on the SAM
* @param user_sess_key User session key
* @param lm_sess_key LM session key (first 8 bytes of the LM hash)
*/
NTSTATUS hash_password_check(TALLOC_CTX *mem_ctx,
const struct samr_Password *client_lanman,
const struct samr_Password *client_nt,
const char *username,
const struct samr_Password *stored_lanman,
const struct samr_Password *stored_nt)
{
if (stored_nt == NULL) {
DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
username));
}
if (client_nt && stored_nt) {
if (memcmp(client_nt->hash, stored_nt->hash, sizeof(stored_nt->hash)) == 0) {
return NT_STATUS_OK;
} else {
DEBUG(3,("ntlm_password_check: Interactive logon: NT password check failed for user %s\n",
username));
return NT_STATUS_WRONG_PASSWORD;
}
} else if (client_lanman && stored_lanman) {
if (!lp_lanman_auth()) {
DEBUG(3,("ntlm_password_check: Interactive logon: only LANMAN password supplied for user %s, and LM passwords are disabled!\n",
username));
return NT_STATUS_WRONG_PASSWORD;
}
if (strchr_m(username, '@')) {
return NT_STATUS_NOT_FOUND;
}
if (memcmp(client_lanman->hash, stored_lanman->hash, sizeof(stored_lanman->hash)) == 0) {
return NT_STATUS_OK;
} else {
DEBUG(3,("ntlm_password_check: Interactive logon: LANMAN password check failed for user %s\n",
username));
return NT_STATUS_WRONG_PASSWORD;
}
}
if (strchr_m(username, '@')) {
return NT_STATUS_NOT_FOUND;
}
return NT_STATUS_WRONG_PASSWORD;
}
/**
* Check a challenge-response password against the value of the NT or
* LM password hash.
*
* @param mem_ctx talloc context
* @param challenge 8-byte challenge. If all zero, forces plaintext comparison
* @param nt_response 'unicode' NT response to the challenge, or unicode password
* @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
* @param username internal Samba username, for log messages
* @param client_username username the client used
* @param client_domain domain name the client used (may be mapped)
* @param stored_lanman LANMAN ASCII password from our passdb or similar
* @param stored_nt MD4 unicode password from our passdb or similar
* @param user_sess_key User session key
* @param lm_sess_key LM session key (first 8 bytes of the LM hash)
*/
NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
uint32_t logon_parameters,
const DATA_BLOB *challenge,
const DATA_BLOB *lm_response,
const DATA_BLOB *nt_response,
const char *username,
const char *client_username,
const char *client_domain,
const struct samr_Password *stored_lanman,
const struct samr_Password *stored_nt,
DATA_BLOB *user_sess_key,
DATA_BLOB *lm_sess_key)
{
static const uint8_t zeros[8];
DATA_BLOB tmp_sess_key;
if (stored_nt == NULL) {
DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
username));
}
*lm_sess_key = data_blob(NULL, 0);
*user_sess_key = data_blob(NULL, 0);
/* Check for cleartext netlogon. Used by Exchange 5.5. */
if ((logon_parameters & MSV1_0_CLEARTEXT_PASSWORD_ALLOWED)
&& challenge->length == sizeof(zeros)
&& (memcmp(challenge->data, zeros, challenge->length) == 0 )) {
struct samr_Password client_nt;
struct samr_Password client_lm;
char *unix_pw = NULL;
BOOL lm_ok;
DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n",
username));
mdfour(client_nt.hash, nt_response->data, nt_response->length);
if (lm_response->length &&
(convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX,
lm_response->data, lm_response->length,
(void **)&unix_pw) != -1)) {
if (E_deshash(unix_pw, client_lm.hash)) {
lm_ok = True;
} else {
lm_ok = False;
}
} else {
lm_ok = False;
}
return hash_password_check(mem_ctx,
lm_ok ? &client_lm : NULL,
nt_response->length ? &client_nt : NULL,
username,
stored_lanman, stored_nt);
}
if (nt_response->length != 0 && nt_response->length < 24) {
DEBUG(2,("ntlm_password_check: invalid NT password length (%lu) for user %s\n",
(unsigned long)nt_response->length, username));
}
if (nt_response->length > 24 && stored_nt) {
/* We have the NT MD4 hash challenge available - see if we can
use it
*/
DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with domain [%s]\n", client_domain));
if (smb_pwd_check_ntlmv2(mem_ctx,
nt_response,
stored_nt->hash, challenge,
client_username,
client_domain,
False,
user_sess_key)) {
*lm_sess_key = *user_sess_key;
if (user_sess_key->length) {
lm_sess_key->length = 8;
}
return NT_STATUS_OK;
}
DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with uppercased version of domain [%s]\n", client_domain));
if (smb_pwd_check_ntlmv2(mem_ctx,
nt_response,
stored_nt->hash, challenge,
client_username,
client_domain,
True,
user_sess_key)) {
*lm_sess_key = *user_sess_key;
if (user_sess_key->length) {
lm_sess_key->length = 8;
}
return NT_STATUS_OK;
}
DEBUG(4,("ntlm_password_check: Checking NTLMv2 password without a domain\n"));
if (smb_pwd_check_ntlmv2(mem_ctx,
nt_response,
stored_nt->hash, challenge,
client_username,
"",
False,
user_sess_key)) {
*lm_sess_key = *user_sess_key;
if (user_sess_key->length) {
lm_sess_key->length = 8;
}
return NT_STATUS_OK;
} else {
DEBUG(3,("ntlm_password_check: NTLMv2 password check failed\n"));
}
} else if (nt_response->length == 24 && stored_nt) {
if (lp_ntlm_auth()) {
/* We have the NT MD4 hash challenge available - see if we can
use it (ie. does it exist in the smbpasswd file).
*/
DEBUG(4,("ntlm_password_check: Checking NT MD4 password\n"));
if (smb_pwd_check_ntlmv1(mem_ctx,
nt_response,
stored_nt->hash, challenge,
user_sess_key)) {
/* The LM session key for this response is not very secure,
so use it only if we otherwise allow LM authentication */
if (lp_lanman_auth() && stored_lanman) {
*lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
}
return NT_STATUS_OK;
} else {
DEBUG(3,("ntlm_password_check: NT MD4 password check failed for user %s\n",
username));
return NT_STATUS_WRONG_PASSWORD;
}
} else {
DEBUG(2,("ntlm_password_check: NTLMv1 passwords NOT PERMITTED for user %s\n",
username));
/* no return, becouse we might pick up LMv2 in the LM field */
}
}
if (lm_response->length == 0) {
DEBUG(3,("ntlm_password_check: NEITHER LanMan nor NT password supplied for user %s\n",
username));
return NT_STATUS_WRONG_PASSWORD;
}
if (lm_response->length < 24) {
DEBUG(2,("ntlm_password_check: invalid LanMan password length (%lu) for user %s\n",
(unsigned long)nt_response->length, username));
return NT_STATUS_WRONG_PASSWORD;
}
if (!lp_lanman_auth()) {
DEBUG(3,("ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n",
username));
} else if (!stored_lanman) {
DEBUG(3,("ntlm_password_check: NO LanMan password set for user %s (and no NT password supplied)\n",
username));
} else if (strchr_m(username, '@')) {
DEBUG(3,("ntlm_password_check: NO LanMan password allowed for username@realm logins (user: %s)\n",
username));
} else {
DEBUG(4,("ntlm_password_check: Checking LM password\n"));
if (smb_pwd_check_ntlmv1(mem_ctx,
lm_response,
stored_lanman->hash, challenge,
NULL)) {
/* The session key for this response is still very odd.
It not very secure, so use it only if we otherwise
allow LM authentication */
if (lp_lanman_auth() && stored_lanman) {
uint8_t first_8_lm_hash[16];
memcpy(first_8_lm_hash, stored_lanman->hash, 8);
memset(first_8_lm_hash + 8, '\0', 8);
*user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
*lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
}
return NT_STATUS_OK;
}
}
if (!stored_nt) {
DEBUG(4,("ntlm_password_check: LM password check failed for user, no NT password %s\n",username));
return NT_STATUS_WRONG_PASSWORD;
}
/* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes.
- related to Win9X, legacy NAS pass-though authentication
*/
DEBUG(4,("ntlm_password_check: Checking LMv2 password with domain %s\n", client_domain));
if (smb_pwd_check_ntlmv2(mem_ctx,
lm_response,
stored_nt->hash, challenge,
client_username,
client_domain,
False,
&tmp_sess_key)) {
if (nt_response->length > 24) {
/* If NTLMv2 authentication has preceeded us
* (even if it failed), then use the session
* key from that. See the RPC-SAMLOGON
* torture test */
smb_sess_key_ntlmv2(mem_ctx,
nt_response,
stored_nt->hash, challenge,
client_username,
client_domain,
False,
user_sess_key);
} else {
/* Otherwise, use the LMv2 session key */
*user_sess_key = tmp_sess_key;
}
*lm_sess_key = *user_sess_key;
if (user_sess_key->length) {
lm_sess_key->length = 8;
}
return NT_STATUS_OK;
}
DEBUG(4,("ntlm_password_check: Checking LMv2 password with upper-cased version of domain %s\n", client_domain));
if (smb_pwd_check_ntlmv2(mem_ctx,
lm_response,
stored_nt->hash, challenge,
client_username,
client_domain,
True,
&tmp_sess_key)) {
if (nt_response->length > 24) {
/* If NTLMv2 authentication has preceeded us
* (even if it failed), then use the session
* key from that. See the RPC-SAMLOGON
* torture test */
smb_sess_key_ntlmv2(mem_ctx,
nt_response,
stored_nt->hash, challenge,
client_username,
client_domain,
True,
user_sess_key);
} else {
/* Otherwise, use the LMv2 session key */
*user_sess_key = tmp_sess_key;
}
*lm_sess_key = *user_sess_key;
if (user_sess_key->length) {
lm_sess_key->length = 8;
}
return NT_STATUS_OK;
}
DEBUG(4,("ntlm_password_check: Checking LMv2 password without a domain\n"));
if (smb_pwd_check_ntlmv2(mem_ctx,
lm_response,
stored_nt->hash, challenge,
client_username,
"",
False,
&tmp_sess_key)) {
if (nt_response->length > 24) {
/* If NTLMv2 authentication has preceeded us
* (even if it failed), then use the session
* key from that. See the RPC-SAMLOGON
* torture test */
smb_sess_key_ntlmv2(mem_ctx,
nt_response,
stored_nt->hash, challenge,
client_username,
"",
False,
user_sess_key);
} else {
/* Otherwise, use the LMv2 session key */
*user_sess_key = tmp_sess_key;
}
*lm_sess_key = *user_sess_key;
if (user_sess_key->length) {
lm_sess_key->length = 8;
}
return NT_STATUS_OK;
}
/* Apparently NT accepts NT responses in the LM field
- I think this is related to Win9X pass-though authentication
*/
DEBUG(4,("ntlm_password_check: Checking NT MD4 password in LM field\n"));
if (lp_ntlm_auth()) {
if (smb_pwd_check_ntlmv1(mem_ctx,
lm_response,
stored_nt->hash, challenge,
NULL)) {
/* The session key for this response is still very odd.
It not very secure, so use it only if we otherwise
allow LM authentication */
if (lp_lanman_auth() && stored_lanman) {
uint8_t first_8_lm_hash[16];
memcpy(first_8_lm_hash, stored_lanman->hash, 8);
memset(first_8_lm_hash + 8, '\0', 8);
*user_sess_key = data_blob_talloc(mem_ctx, first_8_lm_hash, 16);
*lm_sess_key = data_blob_talloc(mem_ctx, stored_lanman->hash, 8);
}
return NT_STATUS_OK;
}
DEBUG(3,("ntlm_password_check: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",username));
} else {
DEBUG(3,("ntlm_password_check: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",username));
}
/* Try and match error codes */
if (strchr_m(username, '@')) {
return NT_STATUS_NOT_FOUND;
}
return NT_STATUS_WRONG_PASSWORD;
}
+18
View File
@@ -0,0 +1,18 @@
[SUBSYSTEM::MSRPC_PARSE]
PRIVATE_PROTO_HEADER = msrpc_parse.h
OBJ_FILES = ntlmssp_parse.o
################################################
# Start MODULE gensec_ntlmssp
[MODULE::gensec_ntlmssp]
SUBSYSTEM = gensec
INIT_FUNCTION = gensec_ntlmssp_init
PRIVATE_PROTO_HEADER = proto.h
OBJ_FILES = ntlmssp.o \
ntlmssp_sign.o \
ntlmssp_client.o \
ntlmssp_server.o
PUBLIC_DEPENDENCIES = auth MSRPC_PARSE
OUTPUT_TYPE = INTEGRATED
# End MODULE gensec_ntlmssp
################################################
+441
View File
@@ -0,0 +1,441 @@
/*
Unix SMB/Netbios implementation.
Version 3.0
handle NLTMSSP, client server side parsing
Copyright (C) Andrew Tridgell 2001
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/ntlmssp/ntlmssp.h"
#include "auth/ntlmssp/msrpc_parse.h"
#include "librpc/gen_ndr/ndr_dcerpc.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
#include "auth/auth.h"
/**
* Callbacks for NTLMSSP - for both client and server operating modes
*
*/
static const struct ntlmssp_callbacks {
enum ntlmssp_role role;
enum ntlmssp_message_type command;
NTSTATUS (*sync_fn)(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
DATA_BLOB in, DATA_BLOB *out);
} ntlmssp_callbacks[] = {
{
.role = NTLMSSP_CLIENT,
.command = NTLMSSP_INITIAL,
.sync_fn = ntlmssp_client_initial,
},{
.role = NTLMSSP_SERVER,
.command = NTLMSSP_NEGOTIATE,
.sync_fn = ntlmssp_server_negotiate,
},{
.role = NTLMSSP_CLIENT,
.command = NTLMSSP_CHALLENGE,
.sync_fn = ntlmssp_client_challenge,
},{
.role = NTLMSSP_SERVER,
.command = NTLMSSP_AUTH,
.sync_fn = ntlmssp_server_auth,
}
};
/**
* Print out the NTLMSSP flags for debugging
* @param neg_flags The flags from the packet
*/
void debug_ntlmssp_flags(uint32_t neg_flags)
{
DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags));
if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_OEM)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n"));
if (neg_flags & NTLMSSP_REQUEST_TARGET)
DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_SIGN)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_SEAL)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM_STYLE)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DATAGRAM_STYLE\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_NTLM)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n"));
if (neg_flags & NTLMSSP_CHAL_ACCEPT_RESPONSE)
DEBUGADD(4, (" NTLMSSP_CHAL_ACCEPT_RESPONSE\n"));
if (neg_flags & NTLMSSP_CHAL_NON_NT_SESSION_KEY)
DEBUGADD(4, (" NTLMSSP_CHAL_NON_NT_SESSION_KEY\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n"));
if (neg_flags & NTLMSSP_CHAL_TARGET_INFO)
DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_128)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
if (neg_flags & NTLMSSP_NEGOTIATE_56)
DEBUGADD(4, (" NTLMSSP_NEGOTIATE_56\n"));
}
static NTSTATUS gensec_ntlmssp_magic(struct gensec_security *gensec_security,
const DATA_BLOB *first_packet)
{
if (first_packet->length > 8 && memcmp("NTLMSSP\0", first_packet->data, 8) == 0) {
return NT_STATUS_OK;
} else {
return NT_STATUS_INVALID_PARAMETER;
}
}
static NTSTATUS gensec_ntlmssp_update_find(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
const DATA_BLOB input, uint32_t *idx)
{
struct gensec_security *gensec_security = gensec_ntlmssp_state->gensec_security;
uint32_t ntlmssp_command;
uint32_t i;
if (gensec_ntlmssp_state->expected_state == NTLMSSP_DONE) {
/* We are strict here because other modules, which we
* don't fully control (such as GSSAPI) are also
* strict, but are tested less often */
DEBUG(1, ("Called NTLMSSP after state machine was 'done'\n"));
return NT_STATUS_INVALID_PARAMETER;
}
if (!input.length) {
switch (gensec_ntlmssp_state->role) {
case NTLMSSP_CLIENT:
ntlmssp_command = NTLMSSP_INITIAL;
break;
case NTLMSSP_SERVER:
if (gensec_security->want_features & GENSEC_FEATURE_DATAGRAM_MODE) {
/* 'datagram' mode - no neg packet */
ntlmssp_command = NTLMSSP_NEGOTIATE;
} else {
/* This is normal in SPNEGO mech negotiation fallback */
DEBUG(2, ("Failed to parse NTLMSSP packet: zero length\n"));
return NT_STATUS_INVALID_PARAMETER;
}
break;
}
} else {
if (!msrpc_parse(gensec_ntlmssp_state,
&input, "Cd",
"NTLMSSP",
&ntlmssp_command)) {
DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n"));
dump_data(2, input.data, input.length);
return NT_STATUS_INVALID_PARAMETER;
}
}
if (ntlmssp_command != gensec_ntlmssp_state->expected_state) {
DEBUG(2, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, gensec_ntlmssp_state->expected_state));
return NT_STATUS_INVALID_PARAMETER;
}
for (i=0; i < ARRAY_SIZE(ntlmssp_callbacks); i++) {
if (ntlmssp_callbacks[i].role == gensec_ntlmssp_state->role &&
ntlmssp_callbacks[i].command == ntlmssp_command) {
*idx = i;
return NT_STATUS_OK;
}
}
DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n",
gensec_ntlmssp_state->role, ntlmssp_command));
return NT_STATUS_INVALID_PARAMETER;
}
/**
* Next state function for the wrapped NTLMSSP state machine
*
* @param gensec_security GENSEC state, initialised to NTLMSSP
* @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
* @param in The request, as a DATA_BLOB
* @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
* @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
* or NT_STATUS_OK if the user is authenticated.
*/
static NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB input, DATA_BLOB *out)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
NTSTATUS status;
uint32_t i;
*out = data_blob(NULL, 0);
if (!out_mem_ctx) {
/* if the caller doesn't want to manage/own the memory,
we can put it on our context */
out_mem_ctx = gensec_ntlmssp_state;
}
status = gensec_ntlmssp_update_find(gensec_ntlmssp_state, input, &i);
NT_STATUS_NOT_OK_RETURN(status);
status = ntlmssp_callbacks[i].sync_fn(gensec_security, out_mem_ctx, input, out);
NT_STATUS_NOT_OK_RETURN(status);
return NT_STATUS_OK;
}
/**
* Return the NTLMSSP master session key
*
* @param gensec_ntlmssp_state NTLMSSP State
*/
NTSTATUS gensec_ntlmssp_session_key(struct gensec_security *gensec_security,
DATA_BLOB *session_key)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
if (!gensec_ntlmssp_state->session_key.data) {
return NT_STATUS_NO_USER_SESSION_KEY;
}
*session_key = gensec_ntlmssp_state->session_key;
return NT_STATUS_OK;
}
void ntlmssp_handle_neg_flags(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
uint32_t neg_flags, BOOL allow_lm)
{
if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
gensec_ntlmssp_state->unicode = True;
} else {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE;
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
gensec_ntlmssp_state->unicode = False;
}
if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm && !gensec_ntlmssp_state->use_ntlmv2) {
/* other end forcing us to use LM */
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
} else {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
}
if (!(neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
}
if (!(neg_flags & NTLMSSP_NEGOTIATE_SIGN)) {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
}
if (!(neg_flags & NTLMSSP_NEGOTIATE_SEAL)) {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SEAL;
}
if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
}
if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128;
}
if (!(neg_flags & NTLMSSP_NEGOTIATE_56)) {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_56;
}
if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH;
}
/* Woop Woop - unknown flag for Windows compatibility...
What does this really do ? JRA. */
if (!(neg_flags & NTLMSSP_UNKNOWN_02000000)) {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_UNKNOWN_02000000;
}
if ((neg_flags & NTLMSSP_REQUEST_TARGET)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET;
}
}
/**
Weaken NTLMSSP keys to cope with down-level clients and servers.
We probably should have some parameters to control this, but as
it only occours for LM_KEY connections, and this is controlled
by the client lanman auth/lanman auth parameters, it isn't too bad.
*/
DATA_BLOB ntlmssp_weakend_key(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
TALLOC_CTX *mem_ctx)
{
DATA_BLOB weakened_key = data_blob_talloc(mem_ctx,
gensec_ntlmssp_state->session_key.data,
gensec_ntlmssp_state->session_key.length);
/* Nothing to weaken. We certainly don't want to 'extend' the length... */
if (weakened_key.length < 16) {
/* perhaps there was no key? */
return weakened_key;
}
/* Key weakening not performed on the master key for NTLM2
and does not occour for NTLM1. Therefore we only need
to do this for the LM_KEY.
*/
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
/* LM key doesn't support 128 bit crypto, so this is
* the best we can do. If you negotiate 128 bit, but
* not 56, you end up with 40 bit... */
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
weakened_key.data[7] = 0xa0;
weakened_key.length = 8;
} else { /* forty bits */
weakened_key.data[5] = 0xe5;
weakened_key.data[6] = 0x38;
weakened_key.data[7] = 0xb0;
weakened_key.length = 8;
}
}
return weakened_key;
}
static BOOL gensec_ntlmssp_have_feature(struct gensec_security *gensec_security,
uint32_t feature)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
if (feature & GENSEC_FEATURE_SIGN) {
if (!gensec_ntlmssp_state->session_key.length) {
return False;
}
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN) {
return True;
}
}
if (feature & GENSEC_FEATURE_SEAL) {
if (!gensec_ntlmssp_state->session_key.length) {
return False;
}
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
return True;
}
}
if (feature & GENSEC_FEATURE_SESSION_KEY) {
if (gensec_ntlmssp_state->session_key.length) {
return True;
}
}
if (feature & GENSEC_FEATURE_DCE_STYLE) {
return True;
}
if (feature & GENSEC_FEATURE_ASYNC_REPLIES) {
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
return True;
}
}
return False;
}
NTSTATUS gensec_ntlmssp_start(struct gensec_security *gensec_security)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state;
gensec_ntlmssp_state = talloc_zero(gensec_security, struct gensec_ntlmssp_state);
if (!gensec_ntlmssp_state) {
return NT_STATUS_NO_MEMORY;
}
gensec_ntlmssp_state->gensec_security = gensec_security;
gensec_ntlmssp_state->auth_context = NULL;
gensec_ntlmssp_state->server_info = NULL;
gensec_security->private_data = gensec_ntlmssp_state;
return NT_STATUS_OK;
}
static const char *gensec_ntlmssp_oids[] = {
GENSEC_OID_NTLMSSP,
NULL
};
static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
.name = "ntlmssp",
.sasl_name = "NTLM",
.auth_type = DCERPC_AUTH_TYPE_NTLMSSP,
.oid = gensec_ntlmssp_oids,
.client_start = gensec_ntlmssp_client_start,
.server_start = gensec_ntlmssp_server_start,
.magic = gensec_ntlmssp_magic,
.update = gensec_ntlmssp_update,
.sig_size = gensec_ntlmssp_sig_size,
.sign_packet = gensec_ntlmssp_sign_packet,
.check_packet = gensec_ntlmssp_check_packet,
.seal_packet = gensec_ntlmssp_seal_packet,
.unseal_packet = gensec_ntlmssp_unseal_packet,
.wrap = gensec_ntlmssp_wrap,
.unwrap = gensec_ntlmssp_unwrap,
.session_key = gensec_ntlmssp_session_key,
.session_info = gensec_ntlmssp_session_info,
.have_feature = gensec_ntlmssp_have_feature,
.enabled = True,
.priority = GENSEC_NTLMSSP
};
NTSTATUS gensec_ntlmssp_init(void)
{
NTSTATUS ret;
auth_init();
ret = gensec_register(&gensec_ntlmssp_security_ops);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register '%s' gensec backend!\n",
gensec_ntlmssp_security_ops.name));
return ret;
}
return ret;
}
+190
View File
@@ -0,0 +1,190 @@
/*
Unix SMB/CIFS implementation.
SMB parameters and setup
Copyright (C) Andrew Tridgell 1992-1997
Copyright (C) Luke Kenneth Casson Leighton 1996-1997
Copyright (C) Paul Ashton 1997
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "librpc/gen_ndr/samr.h"
/* NTLMSSP mode */
enum ntlmssp_role
{
NTLMSSP_SERVER,
NTLMSSP_CLIENT
};
/* NTLMSSP message types */
enum ntlmssp_message_type
{
NTLMSSP_INITIAL = 0 /* samba internal state */,
NTLMSSP_NEGOTIATE = 1,
NTLMSSP_CHALLENGE = 2,
NTLMSSP_AUTH = 3,
NTLMSSP_UNKNOWN = 4,
NTLMSSP_DONE = 5 /* samba final state */
};
/* NTLMSSP negotiation flags */
#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
#define NTLMSSP_NEGOTIATE_OEM 0x00000002
#define NTLMSSP_REQUEST_TARGET 0x00000004
#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* Message integrity */
#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* Message confidentiality */
#define NTLMSSP_NEGOTIATE_DATAGRAM_STYLE 0x00000040
#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
#define NTLMSSP_NEGOTIATE_NETWARE 0x00000100
#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000
#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000
#define NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL 0x00004000
#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000
#define NTLMSSP_TARGET_TYPE_SERVER 0x20000
#define NTLMSSP_CHAL_INIT_RESPONSE 0x00010000
#define NTLMSSP_CHAL_ACCEPT_RESPONSE 0x00020000
#define NTLMSSP_CHAL_NON_NT_SESSION_KEY 0x00040000
#define NTLMSSP_NEGOTIATE_NTLM2 0x00080000
#define NTLMSSP_CHAL_TARGET_INFO 0x00800000
#define NTLMSSP_UNKNOWN_02000000 0x02000000
#define NTLMSSP_NEGOTIATE_128 0x20000000 /* 128-bit encryption */
#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000
#define NTLMSSP_NEGOTIATE_56 0x80000000
#define NTLMSSP_NAME_TYPE_SERVER 0x01
#define NTLMSSP_NAME_TYPE_DOMAIN 0x02
#define NTLMSSP_NAME_TYPE_SERVER_DNS 0x03
#define NTLMSSP_NAME_TYPE_DOMAIN_DNS 0x04
#define NTLMSSP_SIGN_VERSION 1
#define NTLMSSP_SIG_SIZE 16
struct gensec_ntlmssp_state
{
struct gensec_security *gensec_security;
enum ntlmssp_role role;
enum samr_Role server_role;
uint32_t expected_state;
BOOL unicode;
BOOL use_ntlmv2;
BOOL use_nt_response; /* Set to 'False' to debug what happens when the NT response is omited */
BOOL allow_lm_key; /* The LM_KEY code is not functional at this point, and it's not
very secure anyway */
BOOL server_multiple_authentications; /* Set to 'True' to allow squid 2.5
style 'challenge caching' */
char *user;
char *domain;
const char *workstation;
char *server_domain;
DATA_BLOB internal_chal; /* Random challenge as supplied to the client for NTLM authentication */
DATA_BLOB chal; /* Random challenge as input into the actual NTLM (or NTLM2) authentication */
DATA_BLOB lm_resp;
DATA_BLOB nt_resp;
DATA_BLOB session_key;
uint32_t neg_flags; /* the current state of negotiation with the NTLMSSP partner */
/* internal variables used by KEY_EXCH (client-supplied user session key */
DATA_BLOB encrypted_session_key;
/**
* Callback to get the 'challenge' used for NTLM authentication.
*
* @param ntlmssp_state This structure
* @return 8 bytes of challenge data, determined by the server to be the challenge for NTLM authentication
*
*/
const uint8_t *(*get_challenge)(const struct gensec_ntlmssp_state *);
/**
* Callback to find if the challenge used by NTLM authentication may be modified
*
* The NTLM2 authentication scheme modifies the effective challenge, but this is not compatiable with the
* current 'security=server' implementation..
*
* @param ntlmssp_state This structure
* @return Can the challenge be set to arbitary values?
*
*/
BOOL (*may_set_challenge)(const struct gensec_ntlmssp_state *);
/**
* Callback to set the 'challenge' used for NTLM authentication.
*
* The callback may use the void *auth_context to store state information, but the same value is always available
* from the DATA_BLOB chal on this structure.
*
* @param ntlmssp_state This structure
* @param challenge 8 bytes of data, agreed by the client and server to be the effective challenge for NTLM2 authentication
*
*/
NTSTATUS (*set_challenge)(struct gensec_ntlmssp_state *, DATA_BLOB *challenge);
/**
* Callback to check the user's password.
*
* The callback must reads the feilds of this structure for the information it needs on the user
* @param ntlmssp_state This structure
* @param nt_session_key If an NT session key is returned by the authentication process, return it here
* @param lm_session_key If an LM session key is returned by the authentication process, return it here
*
*/
NTSTATUS (*check_password)(struct gensec_ntlmssp_state *,
TALLOC_CTX *mem_ctx,
DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key);
const char *server_name;
const char *(*get_domain)(void);
BOOL doing_ntlm2;
union {
/* NTLM */
struct {
uint32_t seq_num;
struct arcfour_state *arcfour_state;
} ntlm;
/* NTLM2 */
struct {
uint32_t send_seq_num;
uint32_t recv_seq_num;
DATA_BLOB send_sign_key;
DATA_BLOB recv_sign_key;
struct arcfour_state *send_seal_arcfour_state;
struct arcfour_state *recv_seal_arcfour_state;
/* internal variables used by NTLM2 */
uint8_t session_nonce[16];
} ntlm2;
} crypt;
struct auth_context *auth_context;
struct auth_serversupplied_info *server_info;
};
struct auth_session_info;
#include "auth/ntlmssp/proto.h"
+368
View File
@@ -0,0 +1,368 @@
/*
Unix SMB/Netbios implementation.
Version 3.0
handle NLTMSSP, client server side parsing
Copyright (C) Andrew Tridgell 2001
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/ntlmssp/ntlmssp.h"
#include "auth/ntlmssp/msrpc_parse.h"
#include "lib/crypto/crypto.h"
#include "libcli/auth/libcli_auth.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
/*********************************************************************
Client side NTLMSSP
*********************************************************************/
/**
* Next state function for the Initial packet
*
* @param ntlmssp_state NTLMSSP State
* @param out_mem_ctx The DATA_BLOB *out will be allocated on this context
* @param in A NULL data blob (input ignored)
* @param out The initial negotiate request to the server, as an talloc()ed DATA_BLOB, on out_mem_ctx
* @return Errors or NT_STATUS_OK.
*/
NTSTATUS ntlmssp_client_initial(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
DATA_BLOB in, DATA_BLOB *out)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
if (gensec_ntlmssp_state->unicode) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
} else {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
}
if (gensec_ntlmssp_state->use_ntlmv2) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
}
/* generate the ntlmssp negotiate packet */
msrpc_gen(out_mem_ctx,
out, "CddAA",
"NTLMSSP",
NTLMSSP_NEGOTIATE,
gensec_ntlmssp_state->neg_flags,
gensec_ntlmssp_state->get_domain(),
cli_credentials_get_workstation(gensec_security->credentials));
gensec_ntlmssp_state->expected_state = NTLMSSP_CHALLENGE;
return NT_STATUS_MORE_PROCESSING_REQUIRED;
}
/**
* Next state function for the Challenge Packet. Generate an auth packet.
*
* @param gensec_security GENSEC state
* @param out_mem_ctx Memory context for *out
* @param in The server challnege, as a DATA_BLOB. reply.data must be NULL
* @param out The next request (auth packet) to the server, as an allocated DATA_BLOB, on the out_mem_ctx context
* @return Errors or NT_STATUS_OK.
*/
NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
uint32_t chal_flags, ntlmssp_command, unkn1, unkn2;
DATA_BLOB server_domain_blob;
DATA_BLOB challenge_blob;
DATA_BLOB target_info = data_blob(NULL, 0);
char *server_domain;
const char *chal_parse_string;
const char *auth_gen_string;
DATA_BLOB lm_response = data_blob(NULL, 0);
DATA_BLOB nt_response = data_blob(NULL, 0);
DATA_BLOB session_key = data_blob(NULL, 0);
DATA_BLOB lm_session_key = data_blob(NULL, 0);
DATA_BLOB encrypted_session_key = data_blob(NULL, 0);
NTSTATUS nt_status;
int flags = 0;
const char *user, *domain;
TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
if (!msrpc_parse(mem_ctx,
&in, "CdBd",
"NTLMSSP",
&ntlmssp_command,
&server_domain_blob,
&chal_flags)) {
DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
dump_data(2, in.data, in.length);
talloc_free(mem_ctx);
return NT_STATUS_INVALID_PARAMETER;
}
data_blob_free(&server_domain_blob);
DEBUG(3, ("Got challenge flags:\n"));
debug_ntlmssp_flags(chal_flags);
ntlmssp_handle_neg_flags(gensec_ntlmssp_state, chal_flags, gensec_ntlmssp_state->allow_lm_key);
if (gensec_ntlmssp_state->unicode) {
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
chal_parse_string = "CdUdbddB";
} else {
chal_parse_string = "CdUdbdd";
}
auth_gen_string = "CdBBUUUBd";
} else {
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
chal_parse_string = "CdAdbddB";
} else {
chal_parse_string = "CdAdbdd";
}
auth_gen_string = "CdBBAAABd";
}
if (!msrpc_parse(mem_ctx,
&in, chal_parse_string,
"NTLMSSP",
&ntlmssp_command,
&server_domain,
&chal_flags,
&challenge_blob, 8,
&unkn1, &unkn2,
&target_info)) {
DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));
dump_data(2, in.data, in.length);
talloc_free(mem_ctx);
return NT_STATUS_INVALID_PARAMETER;
}
gensec_ntlmssp_state->server_domain = server_domain;
if (challenge_blob.length != 8) {
talloc_free(mem_ctx);
return NT_STATUS_INVALID_PARAMETER;
}
cli_credentials_get_ntlm_username_domain(gensec_security->credentials, mem_ctx,
&user, &domain);
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
flags |= CLI_CRED_NTLM2;
}
if (gensec_ntlmssp_state->use_ntlmv2) {
flags |= CLI_CRED_NTLMv2_AUTH;
}
if (gensec_ntlmssp_state->use_nt_response) {
flags |= CLI_CRED_NTLM_AUTH;
}
if (lp_client_lanman_auth()) {
flags |= CLI_CRED_LANMAN_AUTH;
}
nt_status = cli_credentials_get_ntlm_response(gensec_security->credentials, mem_ctx,
&flags, challenge_blob, target_info,
&lm_response, &nt_response,
&lm_session_key, &session_key);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
if (!(flags & CLI_CRED_LANMAN_AUTH)) {
/* LM Key is still possible, just silly. Fortunetly
* we require command line options to end up here */
/* gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY; */
}
if (!(flags & CLI_CRED_NTLM2)) {
/* NTLM2 is incompatible... */
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
}
if ((gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
&& lp_client_lanman_auth() && lm_session_key.length == 16) {
DATA_BLOB new_session_key = data_blob_talloc(mem_ctx, NULL, 16);
if (lm_response.length == 24) {
SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data,
new_session_key.data);
} else {
static const uint8_t zeros[24];
SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros,
new_session_key.data);
}
session_key = new_session_key;
dump_data_pw("LM session key\n", session_key.data, session_key.length);
}
/* Key exchange encryptes a new client-generated session key with
the password-derived key */
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
/* Make up a new session key */
uint8_t client_session_key[16];
generate_random_buffer(client_session_key, sizeof(client_session_key));
/* Encrypt the new session key with the old one */
encrypted_session_key = data_blob_talloc(gensec_ntlmssp_state,
client_session_key, sizeof(client_session_key));
dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length);
arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length);
dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);
/* Mark the new session key as the 'real' session key */
session_key = data_blob_talloc(mem_ctx, client_session_key, sizeof(client_session_key));
}
DEBUG(3, ("NTLMSSP: Set final flags:\n"));
debug_ntlmssp_flags(gensec_ntlmssp_state->neg_flags);
/* this generates the actual auth packet */
if (!msrpc_gen(mem_ctx,
out, auth_gen_string,
"NTLMSSP",
NTLMSSP_AUTH,
lm_response.data, lm_response.length,
nt_response.data, nt_response.length,
domain,
user,
cli_credentials_get_workstation(gensec_security->credentials),
encrypted_session_key.data, encrypted_session_key.length,
gensec_ntlmssp_state->neg_flags)) {
talloc_free(mem_ctx);
return NT_STATUS_NO_MEMORY;
}
gensec_ntlmssp_state->session_key = session_key;
talloc_steal(gensec_ntlmssp_state, session_key.data);
talloc_steal(out_mem_ctx, out->data);
gensec_ntlmssp_state->chal = challenge_blob;
gensec_ntlmssp_state->lm_resp = lm_response;
talloc_steal(gensec_ntlmssp_state->lm_resp.data, lm_response.data);
gensec_ntlmssp_state->nt_resp = nt_response;
talloc_steal(gensec_ntlmssp_state->nt_resp.data, nt_response.data);
gensec_ntlmssp_state->expected_state = NTLMSSP_DONE;
if (gensec_security->want_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) {
nt_status = ntlmssp_sign_init(gensec_ntlmssp_state);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n",
nt_errstr(nt_status)));
talloc_free(mem_ctx);
return nt_status;
}
}
talloc_free(mem_ctx);
return NT_STATUS_OK;
}
NTSTATUS gensec_ntlmssp_client_start(struct gensec_security *gensec_security)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state;
NTSTATUS nt_status;
nt_status = gensec_ntlmssp_start(gensec_security);
NT_STATUS_NOT_OK_RETURN(nt_status);
gensec_ntlmssp_state = gensec_security->private_data;
gensec_ntlmssp_state->role = NTLMSSP_CLIENT;
gensec_ntlmssp_state->get_domain = lp_workgroup;
gensec_ntlmssp_state->unicode = lp_parm_bool(-1, "ntlmssp_client", "unicode", True);
gensec_ntlmssp_state->use_nt_response = lp_parm_bool(-1, "ntlmssp_client", "send_nt_reponse", True);
gensec_ntlmssp_state->allow_lm_key = (lp_client_lanman_auth()
&& (lp_parm_bool(-1, "ntlmssp_client", "allow_lm_key", False)
|| lp_parm_bool(-1, "ntlmssp_client", "lm_key", False)));
gensec_ntlmssp_state->use_ntlmv2 = lp_client_ntlmv2_auth();
gensec_ntlmssp_state->expected_state = NTLMSSP_INITIAL;
gensec_ntlmssp_state->neg_flags =
NTLMSSP_NEGOTIATE_NTLM |
NTLMSSP_REQUEST_TARGET;
if (lp_parm_bool(-1, "ntlmssp_client", "128bit", True)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_128;
}
if (lp_parm_bool(-1, "ntlmssp_client", "56bit", False)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56;
}
if (lp_parm_bool(-1, "ntlmssp_client", "lm_key", False)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
}
if (lp_parm_bool(-1, "ntlmssp_client", "keyexchange", True)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
}
if (lp_parm_bool(-1, "ntlmssp_client", "alwayssign", True)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
}
if (lp_parm_bool(-1, "ntlmssp_client", "ntlm2", True)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
} else {
/* apparently we can't do ntlmv2 if we don't do ntlm2 */
gensec_ntlmssp_state->use_ntlmv2 = False;
}
if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
/*
* We need to set this to allow a later SetPassword
* via the SAMR pipe to succeed. Strange.... We could
* also add NTLMSSP_NEGOTIATE_SEAL here. JRA.
*
* Without this, Windows will not create the master key
* that it thinks is only used for NTLMSSP signing and
* sealing. (It is actually pulled out and used directly)
*/
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
}
if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
}
if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
}
gensec_security->private_data = gensec_ntlmssp_state;
return NT_STATUS_OK;
}
+336
View File
@@ -0,0 +1,336 @@
/*
Unix SMB/CIFS implementation.
simple kerberos5/SPNEGO routines
Copyright (C) Andrew Tridgell 2001
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
Copyright (C) Andrew Bartlett 2002-2003
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "pstring.h"
/*
this is a tiny msrpc packet generator. I am only using this to
avoid tying this code to a particular varient of our rpc code. This
generator is not general enough for all our rpc needs, its just
enough for the spnego/ntlmssp code
format specifiers are:
U = unicode string (input is unix string)
a = address (input is char *unix_string)
(1 byte type, 1 byte length, unicode/ASCII string, all inline)
A = ASCII string (input is unix string)
B = data blob (pointer + length)
b = data blob in header (pointer + length)
D
d = word (4 bytes)
C = constant ascii string
*/
BOOL msrpc_gen(TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
const char *format, ...)
{
int i;
ssize_t n;
va_list ap;
char *s;
uint8_t *b;
int head_size=0, data_size=0;
int head_ofs, data_ofs;
int *intargs;
DATA_BLOB *pointers;
pointers = talloc_array(mem_ctx, DATA_BLOB, strlen(format));
intargs = talloc_array(pointers, int, strlen(format));
/* first scan the format to work out the header and body size */
va_start(ap, format);
for (i=0; format[i]; i++) {
switch (format[i]) {
case 'U':
s = va_arg(ap, char *);
head_size += 8;
n = push_ucs2_talloc(pointers, (void **)&pointers[i].data, s);
if (n == -1) {
return False;
}
pointers[i].length = n;
pointers[i].length -= 2;
data_size += pointers[i].length;
break;
case 'A':
s = va_arg(ap, char *);
head_size += 8;
n = push_ascii_talloc(pointers, (char **)&pointers[i].data, s);
if (n == -1) {
return False;
}
pointers[i].length = n;
pointers[i].length -= 1;
data_size += pointers[i].length;
break;
case 'a':
n = va_arg(ap, int);
intargs[i] = n;
s = va_arg(ap, char *);
n = push_ucs2_talloc(pointers, (void **)&pointers[i].data, s);
if (n == -1) {
return False;
}
pointers[i].length = n;
pointers[i].length -= 2;
data_size += pointers[i].length + 4;
break;
case 'B':
b = va_arg(ap, uint8_t *);
head_size += 8;
pointers[i].data = b;
pointers[i].length = va_arg(ap, int);
data_size += pointers[i].length;
break;
case 'b':
b = va_arg(ap, uint8_t *);
pointers[i].data = b;
pointers[i].length = va_arg(ap, int);
head_size += pointers[i].length;
break;
case 'd':
n = va_arg(ap, int);
intargs[i] = n;
head_size += 4;
break;
case 'C':
s = va_arg(ap, char *);
pointers[i].data = (uint8_t *)s;
pointers[i].length = strlen(s)+1;
head_size += pointers[i].length;
break;
}
}
va_end(ap);
/* allocate the space, then scan the format again to fill in the values */
*blob = data_blob_talloc(mem_ctx, NULL, head_size + data_size);
head_ofs = 0;
data_ofs = head_size;
va_start(ap, format);
for (i=0; format[i]; i++) {
switch (format[i]) {
case 'U':
case 'A':
case 'B':
n = pointers[i].length;
SSVAL(blob->data, head_ofs, n); head_ofs += 2;
SSVAL(blob->data, head_ofs, n); head_ofs += 2;
SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
if (pointers[i].data && n) /* don't follow null pointers... */
memcpy(blob->data+data_ofs, pointers[i].data, n);
data_ofs += n;
break;
case 'a':
n = intargs[i];
SSVAL(blob->data, data_ofs, n); data_ofs += 2;
n = pointers[i].length;
SSVAL(blob->data, data_ofs, n); data_ofs += 2;
if (n >= 0) {
memcpy(blob->data+data_ofs, pointers[i].data, n);
}
data_ofs += n;
break;
case 'd':
n = intargs[i];
SIVAL(blob->data, head_ofs, n);
head_ofs += 4;
break;
case 'b':
n = pointers[i].length;
memcpy(blob->data + head_ofs, pointers[i].data, n);
head_ofs += n;
break;
case 'C':
n = pointers[i].length;
memcpy(blob->data + head_ofs, pointers[i].data, n);
head_ofs += n;
break;
}
}
va_end(ap);
talloc_free(pointers);
return True;
}
/* a helpful macro to avoid running over the end of our blob */
#define NEED_DATA(amount) \
if ((head_ofs + amount) > blob->length) { \
return False; \
}
/*
this is a tiny msrpc packet parser. This the the partner of msrpc_gen
format specifiers are:
U = unicode string (output is unix string)
A = ascii string
B = data blob
b = data blob in header
d = word (4 bytes)
C = constant ascii string
*/
BOOL msrpc_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob,
const char *format, ...)
{
int i;
va_list ap;
const char **ps, *s;
DATA_BLOB *b;
size_t head_ofs = 0;
uint16_t len1, len2;
uint32_t ptr;
uint32_t *v;
pstring p;
va_start(ap, format);
for (i=0; format[i]; i++) {
switch (format[i]) {
case 'U':
NEED_DATA(8);
len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
ps = (const char **)va_arg(ap, char **);
if (len1 == 0 && len2 == 0) {
*ps = "";
} else {
/* make sure its in the right format - be strict */
if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
return False;
}
if (len1 & 1) {
/* if odd length and unicode */
return False;
}
if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data)
return False;
if (0 < len1) {
pull_string(p, blob->data + ptr, sizeof(p),
len1, STR_UNICODE|STR_NOALIGN);
(*ps) = talloc_strdup(mem_ctx, p);
if (!(*ps)) {
return False;
}
} else {
(*ps) = "";
}
}
break;
case 'A':
NEED_DATA(8);
len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
ps = (const char **)va_arg(ap, char **);
/* make sure its in the right format - be strict */
if (len1 == 0 && len2 == 0) {
*ps = "";
} else {
if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
return False;
}
if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data)
return False;
if (0 < len1) {
pull_string(p, blob->data + ptr, sizeof(p),
len1, STR_ASCII|STR_NOALIGN);
(*ps) = talloc_strdup(mem_ctx, p);
if (!(*ps)) {
return False;
}
} else {
(*ps) = "";
}
}
break;
case 'B':
NEED_DATA(8);
len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
b = (DATA_BLOB *)va_arg(ap, void *);
if (len1 == 0 && len2 == 0) {
*b = data_blob_talloc(mem_ctx, NULL, 0);
} else {
/* make sure its in the right format - be strict */
if ((len1 != len2) || (ptr + len1 < ptr) || (ptr + len1 < len1) || (ptr + len1 > blob->length)) {
return False;
}
if (blob->data + ptr < (uint8_t *)ptr || blob->data + ptr < blob->data)
return False;
*b = data_blob_talloc(mem_ctx, blob->data + ptr, len1);
}
break;
case 'b':
b = (DATA_BLOB *)va_arg(ap, void *);
len1 = va_arg(ap, uint_t);
/* make sure its in the right format - be strict */
NEED_DATA(len1);
if (blob->data + head_ofs < (uint8_t *)head_ofs || blob->data + head_ofs < blob->data)
return False;
*b = data_blob_talloc(mem_ctx, blob->data + head_ofs, len1);
head_ofs += len1;
break;
case 'd':
v = va_arg(ap, uint32_t *);
NEED_DATA(4);
*v = IVAL(blob->data, head_ofs); head_ofs += 4;
break;
case 'C':
s = va_arg(ap, char *);
if (blob->data + head_ofs < (uint8_t *)head_ofs || blob->data + head_ofs < blob->data)
return False;
head_ofs += pull_string(p, blob->data+head_ofs, sizeof(p),
blob->length - head_ofs,
STR_ASCII|STR_TERMINATE);
if (strcmp(s, p) != 0) {
return False;
}
break;
}
}
va_end(ap);
return True;
}
+852
View File
@@ -0,0 +1,852 @@
/*
Unix SMB/Netbios implementation.
Version 3.0
handle NLTMSSP, client server side parsing
Copyright (C) Andrew Tridgell 2001
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2005
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "auth/ntlmssp/ntlmssp.h"
#include "auth/ntlmssp/msrpc_parse.h"
#include "lib/crypto/crypto.h"
#include "pstring.h"
#include "system/filesys.h"
#include "libcli/auth/libcli_auth.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
#include "auth/auth.h"
/**
* Set a username on an NTLMSSP context - ensures it is talloc()ed
*
*/
static NTSTATUS ntlmssp_set_username(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *user)
{
if (!user) {
/* it should be at least "" */
DEBUG(1, ("NTLMSSP failed to set username - cannot accept NULL username\n"));
return NT_STATUS_INVALID_PARAMETER;
}
gensec_ntlmssp_state->user = talloc_strdup(gensec_ntlmssp_state, user);
if (!gensec_ntlmssp_state->user) {
return NT_STATUS_NO_MEMORY;
}
return NT_STATUS_OK;
}
/**
* Set a domain on an NTLMSSP context - ensures it is talloc()ed
*
*/
static NTSTATUS ntlmssp_set_domain(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *domain)
{
gensec_ntlmssp_state->domain = talloc_strdup(gensec_ntlmssp_state, domain);
if (!gensec_ntlmssp_state->domain) {
return NT_STATUS_NO_MEMORY;
}
return NT_STATUS_OK;
}
/**
* Set a workstation on an NTLMSSP context - ensures it is talloc()ed
*
*/
static NTSTATUS ntlmssp_set_workstation(struct gensec_ntlmssp_state *gensec_ntlmssp_state, const char *workstation)
{
gensec_ntlmssp_state->workstation = talloc_strdup(gensec_ntlmssp_state, workstation);
if (!gensec_ntlmssp_state->workstation) {
return NT_STATUS_NO_MEMORY;
}
return NT_STATUS_OK;
}
/**
* Determine correct target name flags for reply, given server role
* and negotiated flags
*
* @param gensec_ntlmssp_state NTLMSSP State
* @param neg_flags The flags from the packet
* @param chal_flags The flags to be set in the reply packet
* @return The 'target name' string.
*/
static const char *ntlmssp_target_name(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
uint32_t neg_flags, uint32_t *chal_flags)
{
if (neg_flags & NTLMSSP_REQUEST_TARGET) {
*chal_flags |= NTLMSSP_CHAL_TARGET_INFO;
*chal_flags |= NTLMSSP_REQUEST_TARGET;
if (gensec_ntlmssp_state->server_role == ROLE_STANDALONE) {
*chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
return gensec_ntlmssp_state->server_name;
} else {
*chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
return gensec_ntlmssp_state->get_domain();
};
} else {
return "";
}
}
/*
Andrew, please remove these totally bogus calls when you get time
*/
static BOOL get_myfullname(char *my_name)
{
pstring hostname;
*hostname = 0;
/* get my host name */
if (gethostname(hostname, sizeof(hostname)) == -1) {
DEBUG(0,("gethostname failed\n"));
return False;
}
/* Ensure null termination. */
hostname[sizeof(hostname)-1] = '\0';
if (my_name)
fstrcpy(my_name, hostname);
return True;
}
static BOOL get_mydomname(char *my_domname)
{
pstring hostname;
char *p;
/* arrgh! relies on full name in system */
*hostname = 0;
/* get my host name */
if (gethostname(hostname, sizeof(hostname)) == -1) {
DEBUG(0,("gethostname failed\n"));
return False;
}
/* Ensure null termination. */
hostname[sizeof(hostname)-1] = '\0';
p = strchr_m(hostname, '.');
if (!p)
return False;
p++;
if (my_domname)
fstrcpy(my_domname, p);
return True;
}
/**
* Next state function for the Negotiate packet
*
* @param gensec_security GENSEC state
* @param out_mem_ctx Memory context for *out
* @param in The request, as a DATA_BLOB. reply.data must be NULL
* @param out The reply, as an allocated DATA_BLOB, caller to free.
* @return Errors or MORE_PROCESSING_REQUIRED if (normal) a reply is required.
*/
NTSTATUS ntlmssp_server_negotiate(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
DATA_BLOB struct_blob;
fstring dnsname, dnsdomname;
uint32_t neg_flags = 0;
uint32_t ntlmssp_command, chal_flags;
const uint8_t *cryptkey;
const char *target_name;
/* parse the NTLMSSP packet */
#if 0
file_save("ntlmssp_negotiate.dat", request.data, request.length);
#endif
if (in.length) {
if ((in.length < 16) || !msrpc_parse(out_mem_ctx, &in, "Cdd",
"NTLMSSP",
&ntlmssp_command,
&neg_flags)) {
DEBUG(1, ("ntlmssp_server_negotiate: failed to parse "
"NTLMSSP Negotiate of length %u:\n",
(unsigned int)in.length ));
dump_data(2, in.data, in.length);
return NT_STATUS_INVALID_PARAMETER;
}
debug_ntlmssp_flags(neg_flags);
}
ntlmssp_handle_neg_flags(gensec_ntlmssp_state, neg_flags, gensec_ntlmssp_state->allow_lm_key);
/* Ask our caller what challenge they would like in the packet */
cryptkey = gensec_ntlmssp_state->get_challenge(gensec_ntlmssp_state);
/* Check if we may set the challenge */
if (!gensec_ntlmssp_state->may_set_challenge(gensec_ntlmssp_state)) {
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
}
/* The flags we send back are not just the negotiated flags,
* they are also 'what is in this packet'. Therfore, we
* operate on 'chal_flags' from here on
*/
chal_flags = gensec_ntlmssp_state->neg_flags;
/* get the right name to fill in as 'target' */
target_name = ntlmssp_target_name(gensec_ntlmssp_state,
neg_flags, &chal_flags);
if (target_name == NULL)
return NT_STATUS_INVALID_PARAMETER;
gensec_ntlmssp_state->chal = data_blob_talloc(gensec_ntlmssp_state, cryptkey, 8);
gensec_ntlmssp_state->internal_chal = data_blob_talloc(gensec_ntlmssp_state, cryptkey, 8);
/* This should be a 'netbios domain -> DNS domain' mapping */
dnsdomname[0] = '\0';
get_mydomname(dnsdomname);
strlower_m(dnsdomname);
dnsname[0] = '\0';
get_myfullname(dnsname);
/* This creates the 'blob' of names that appears at the end of the packet */
if (chal_flags & NTLMSSP_CHAL_TARGET_INFO)
{
const char *target_name_dns = "";
if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) {
target_name_dns = dnsdomname;
} else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) {
target_name_dns = dnsname;
}
msrpc_gen(out_mem_ctx,
&struct_blob, "aaaaa",
NTLMSSP_NAME_TYPE_DOMAIN, target_name,
NTLMSSP_NAME_TYPE_SERVER, gensec_ntlmssp_state->server_name,
NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname,
NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname,
0, "");
} else {
struct_blob = data_blob(NULL, 0);
}
{
/* Marshel the packet in the right format, be it unicode or ASCII */
const char *gen_string;
if (gensec_ntlmssp_state->unicode) {
gen_string = "CdUdbddB";
} else {
gen_string = "CdAdbddB";
}
msrpc_gen(out_mem_ctx,
out, gen_string,
"NTLMSSP",
NTLMSSP_CHALLENGE,
target_name,
chal_flags,
cryptkey, 8,
0, 0,
struct_blob.data, struct_blob.length);
}
gensec_ntlmssp_state->expected_state = NTLMSSP_AUTH;
return NT_STATUS_MORE_PROCESSING_REQUIRED;
}
/**
* Next state function for the Authenticate packet
*
* @param gensec_ntlmssp_state NTLMSSP State
* @param request The request, as a DATA_BLOB
* @return Errors or NT_STATUS_OK.
*/
static NTSTATUS ntlmssp_server_preauth(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
const DATA_BLOB request)
{
uint32_t ntlmssp_command, auth_flags;
NTSTATUS nt_status;
uint8_t session_nonce_hash[16];
const char *parse_string;
char *domain = NULL;
char *user = NULL;
char *workstation = NULL;
#if 0
file_save("ntlmssp_auth.dat", request.data, request.length);
#endif
if (gensec_ntlmssp_state->unicode) {
parse_string = "CdBBUUUBd";
} else {
parse_string = "CdBBAAABd";
}
/* zero these out */
data_blob_free(&gensec_ntlmssp_state->lm_resp);
data_blob_free(&gensec_ntlmssp_state->nt_resp);
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
gensec_ntlmssp_state->user = NULL;
gensec_ntlmssp_state->domain = NULL;
gensec_ntlmssp_state->workstation = NULL;
/* now the NTLMSSP encoded auth hashes */
if (!msrpc_parse(gensec_ntlmssp_state,
&request, parse_string,
"NTLMSSP",
&ntlmssp_command,
&gensec_ntlmssp_state->lm_resp,
&gensec_ntlmssp_state->nt_resp,
&domain,
&user,
&workstation,
&gensec_ntlmssp_state->encrypted_session_key,
&auth_flags)) {
DEBUG(10, ("ntlmssp_server_auth: failed to parse NTLMSSP (nonfatal):\n"));
dump_data(10, request.data, request.length);
/* zero this out */
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
auth_flags = 0;
/* Try again with a shorter string (Win9X truncates this packet) */
if (gensec_ntlmssp_state->unicode) {
parse_string = "CdBBUUU";
} else {
parse_string = "CdBBAAA";
}
/* now the NTLMSSP encoded auth hashes */
if (!msrpc_parse(gensec_ntlmssp_state,
&request, parse_string,
"NTLMSSP",
&ntlmssp_command,
&gensec_ntlmssp_state->lm_resp,
&gensec_ntlmssp_state->nt_resp,
&domain,
&user,
&workstation)) {
DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
dump_data(2, request.data, request.length);
return NT_STATUS_INVALID_PARAMETER;
}
}
if (auth_flags)
ntlmssp_handle_neg_flags(gensec_ntlmssp_state, auth_flags, gensec_ntlmssp_state->allow_lm_key);
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(gensec_ntlmssp_state, domain))) {
/* zero this out */
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
return nt_status;
}
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(gensec_ntlmssp_state, user))) {
/* zero this out */
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
return nt_status;
}
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(gensec_ntlmssp_state, workstation))) {
/* zero this out */
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
return nt_status;
}
DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n",
gensec_ntlmssp_state->user, gensec_ntlmssp_state->domain, gensec_ntlmssp_state->workstation, (unsigned long)gensec_ntlmssp_state->lm_resp.length, (unsigned long)gensec_ntlmssp_state->nt_resp.length));
#if 0
file_save("nthash1.dat", &gensec_ntlmssp_state->nt_resp.data, &gensec_ntlmssp_state->nt_resp.length);
file_save("lmhash1.dat", &gensec_ntlmssp_state->lm_resp.data, &gensec_ntlmssp_state->lm_resp.length);
#endif
/* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a
client challenge
However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful.
*/
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
if (gensec_ntlmssp_state->nt_resp.length == 24 && gensec_ntlmssp_state->lm_resp.length == 24) {
struct MD5Context md5_session_nonce_ctx;
SMB_ASSERT(gensec_ntlmssp_state->internal_chal.data
&& gensec_ntlmssp_state->internal_chal.length == 8);
gensec_ntlmssp_state->doing_ntlm2 = True;
memcpy(gensec_ntlmssp_state->crypt.ntlm2.session_nonce, gensec_ntlmssp_state->internal_chal.data, 8);
memcpy(&gensec_ntlmssp_state->crypt.ntlm2.session_nonce[8], gensec_ntlmssp_state->lm_resp.data, 8);
MD5Init(&md5_session_nonce_ctx);
MD5Update(&md5_session_nonce_ctx, gensec_ntlmssp_state->crypt.ntlm2.session_nonce, 16);
MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
gensec_ntlmssp_state->chal = data_blob_talloc(gensec_ntlmssp_state,
session_nonce_hash, 8);
/* LM response is no longer useful, zero it out */
data_blob_free(&gensec_ntlmssp_state->lm_resp);
/* We changed the effective challenge - set it */
if (!NT_STATUS_IS_OK(nt_status =
gensec_ntlmssp_state->set_challenge(gensec_ntlmssp_state,
&gensec_ntlmssp_state->chal))) {
/* zero this out */
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
return nt_status;
}
/* LM Key is incompatible... */
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
}
}
return NT_STATUS_OK;
}
/**
* Next state function for the Authenticate packet
* (after authentication - figures out the session keys etc)
*
* @param gensec_ntlmssp_state NTLMSSP State
* @return Errors or NT_STATUS_OK.
*/
static NTSTATUS ntlmssp_server_postauth(struct gensec_security *gensec_security,
DATA_BLOB *user_session_key,
DATA_BLOB *lm_session_key)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
NTSTATUS nt_status;
DATA_BLOB session_key = data_blob(NULL, 0);
if (user_session_key)
dump_data_pw("USER session key:\n", user_session_key->data, user_session_key->length);
if (lm_session_key)
dump_data_pw("LM first-8:\n", lm_session_key->data, lm_session_key->length);
/* Handle the different session key derivation for NTLM2 */
if (gensec_ntlmssp_state->doing_ntlm2) {
if (user_session_key && user_session_key->data && user_session_key->length == 16) {
session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
hmac_md5(user_session_key->data, gensec_ntlmssp_state->crypt.ntlm2.session_nonce,
sizeof(gensec_ntlmssp_state->crypt.ntlm2.session_nonce), session_key.data);
DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n"));
dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
} else {
DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n"));
session_key = data_blob(NULL, 0);
}
} else if ((gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
/* Ensure we can never get here on NTLMv2 */
&& (gensec_ntlmssp_state->nt_resp.length == 0 || gensec_ntlmssp_state->nt_resp.length == 24)) {
if (lm_session_key && lm_session_key->data && lm_session_key->length >= 8) {
if (gensec_ntlmssp_state->lm_resp.data && gensec_ntlmssp_state->lm_resp.length == 24) {
session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
SMBsesskeygen_lm_sess_key(lm_session_key->data, gensec_ntlmssp_state->lm_resp.data,
session_key.data);
DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
dump_data_pw("LM session key:\n", session_key.data, session_key.length);
} else {
/* When there is no LM response, just use zeros */
static const uint8_t zeros[24];
session_key = data_blob_talloc(gensec_ntlmssp_state, NULL, 16);
SMBsesskeygen_lm_sess_key(zeros, zeros,
session_key.data);
DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
dump_data_pw("LM session key:\n", session_key.data, session_key.length);
}
} else {
/* LM Key not selected */
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n"));
session_key = data_blob(NULL, 0);
}
} else if (user_session_key && user_session_key->data) {
session_key = *user_session_key;
DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n"));
dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
/* LM Key not selected */
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
} else if (lm_session_key && lm_session_key->data) {
/* Very weird to have LM key, but no user session key, but anyway.. */
session_key = *lm_session_key;
DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n"));
dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
/* LM Key not selected */
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
} else {
DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n"));
session_key = data_blob(NULL, 0);
/* LM Key not selected */
gensec_ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
}
/* With KEY_EXCH, the client supplies the proposed session key,
but encrypts it with the long-term key */
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
if (!gensec_ntlmssp_state->encrypted_session_key.data
|| gensec_ntlmssp_state->encrypted_session_key.length != 16) {
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n",
(unsigned)gensec_ntlmssp_state->encrypted_session_key.length));
return NT_STATUS_INVALID_PARAMETER;
} else if (!session_key.data || session_key.length != 16) {
DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n",
(unsigned)session_key.length));
gensec_ntlmssp_state->session_key = session_key;
} else {
dump_data_pw("KEY_EXCH session key (enc):\n",
gensec_ntlmssp_state->encrypted_session_key.data,
gensec_ntlmssp_state->encrypted_session_key.length);
arcfour_crypt(gensec_ntlmssp_state->encrypted_session_key.data,
session_key.data,
gensec_ntlmssp_state->encrypted_session_key.length);
gensec_ntlmssp_state->session_key = data_blob_talloc(gensec_ntlmssp_state,
gensec_ntlmssp_state->encrypted_session_key.data,
gensec_ntlmssp_state->encrypted_session_key.length);
dump_data_pw("KEY_EXCH session key:\n", gensec_ntlmssp_state->encrypted_session_key.data,
gensec_ntlmssp_state->encrypted_session_key.length);
}
} else {
gensec_ntlmssp_state->session_key = session_key;
}
/* keep the session key around on the new context */
talloc_steal(gensec_ntlmssp_state, session_key.data);
if ((gensec_security->want_features & GENSEC_FEATURE_SIGN)
|| (gensec_security->want_features & GENSEC_FEATURE_SEAL)) {
nt_status = ntlmssp_sign_init(gensec_ntlmssp_state);
} else {
nt_status = NT_STATUS_OK;
}
data_blob_free(&gensec_ntlmssp_state->encrypted_session_key);
/* allow arbitarily many authentications, but watch that this will cause a
memory leak, until the gensec_ntlmssp_state is shutdown
*/
if (gensec_ntlmssp_state->server_multiple_authentications) {
gensec_ntlmssp_state->expected_state = NTLMSSP_AUTH;
} else {
gensec_ntlmssp_state->expected_state = NTLMSSP_DONE;
}
return nt_status;
}
/**
* Next state function for the Authenticate packet
*
* @param gensec_security GENSEC state
* @param out_mem_ctx Memory context for *out
* @param in The request, as a DATA_BLOB. reply.data must be NULL
* @param out The reply, as an allocated DATA_BLOB, caller to free.
* @return Errors or NT_STATUS_OK if authentication sucessful
*/
NTSTATUS ntlmssp_server_auth(struct gensec_security *gensec_security,
TALLOC_CTX *out_mem_ctx,
const DATA_BLOB in, DATA_BLOB *out)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
DATA_BLOB user_session_key = data_blob(NULL, 0);
DATA_BLOB lm_session_key = data_blob(NULL, 0);
NTSTATUS nt_status;
TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
/* zero the outbound NTLMSSP packet */
*out = data_blob_talloc(out_mem_ctx, NULL, 0);
if (!NT_STATUS_IS_OK(nt_status = ntlmssp_server_preauth(gensec_ntlmssp_state, in))) {
talloc_free(mem_ctx);
return nt_status;
}
/*
* Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth
* is required (by "ntlm auth = no" and "lm auth = no" being set in the
* smb.conf file) and no NTLMv2 response was sent then the password check
* will fail here. JRA.
*/
/* Finally, actually ask if the password is OK */
if (!NT_STATUS_IS_OK(nt_status = gensec_ntlmssp_state->check_password(gensec_ntlmssp_state, mem_ctx,
&user_session_key, &lm_session_key))) {
talloc_free(mem_ctx);
return nt_status;
}
if (gensec_security->want_features
& (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL|GENSEC_FEATURE_SESSION_KEY)) {
nt_status = ntlmssp_server_postauth(gensec_security, &user_session_key, &lm_session_key);
talloc_free(mem_ctx);
return nt_status;
} else {
gensec_ntlmssp_state->session_key = data_blob(NULL, 0);
talloc_free(mem_ctx);
return NT_STATUS_OK;
}
}
/**
* Return the challenge as determined by the authentication subsystem
* @return an 8 byte random challenge
*/
static const uint8_t *auth_ntlmssp_get_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
{
NTSTATUS status;
const uint8_t *chal;
status = auth_get_challenge(gensec_ntlmssp_state->auth_context, &chal);
if (!NT_STATUS_IS_OK(status)) {
return NULL;
}
return chal;
}
/**
* Some authentication methods 'fix' the challenge, so we may not be able to set it
*
* @return If the effective challenge used by the auth subsystem may be modified
*/
static BOOL auth_ntlmssp_may_set_challenge(const struct gensec_ntlmssp_state *gensec_ntlmssp_state)
{
return auth_challenge_may_be_modified(gensec_ntlmssp_state->auth_context);
}
/**
* NTLM2 authentication modifies the effective challenge,
* @param challenge The new challenge value
*/
static NTSTATUS auth_ntlmssp_set_challenge(struct gensec_ntlmssp_state *gensec_ntlmssp_state, DATA_BLOB *challenge)
{
NTSTATUS nt_status;
struct auth_context *auth_context = gensec_ntlmssp_state->auth_context;
const uint8_t *chal;
if (challenge->length != 8) {
return NT_STATUS_INVALID_PARAMETER;
}
chal = challenge->data;
nt_status = auth_context_set_challenge(auth_context, chal, "NTLMSSP callback (NTLM2)");
return nt_status;
}
/**
* Check the password on an NTLMSSP login.
*
* Return the session keys used on the connection.
*/
static NTSTATUS auth_ntlmssp_check_password(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
TALLOC_CTX *mem_ctx,
DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
{
NTSTATUS nt_status;
struct auth_usersupplied_info *user_info = talloc(mem_ctx, struct auth_usersupplied_info);
if (!user_info) {
return NT_STATUS_NO_MEMORY;
}
user_info->logon_parameters = MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
user_info->flags = 0;
user_info->mapped_state = False;
user_info->client.account_name = gensec_ntlmssp_state->user;
user_info->client.domain_name = gensec_ntlmssp_state->domain;
user_info->workstation_name = gensec_ntlmssp_state->workstation;
user_info->remote_host = gensec_get_peer_addr(gensec_ntlmssp_state->gensec_security);
user_info->password_state = AUTH_PASSWORD_RESPONSE;
user_info->password.response.lanman = gensec_ntlmssp_state->lm_resp;
user_info->password.response.lanman.data = talloc_steal(user_info, gensec_ntlmssp_state->lm_resp.data);
user_info->password.response.nt = gensec_ntlmssp_state->nt_resp;
user_info->password.response.nt.data = talloc_steal(user_info, gensec_ntlmssp_state->nt_resp.data);
nt_status = auth_check_password(gensec_ntlmssp_state->auth_context, mem_ctx,
user_info, &gensec_ntlmssp_state->server_info);
talloc_free(user_info);
NT_STATUS_NOT_OK_RETURN(nt_status);
talloc_steal(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info);
if (gensec_ntlmssp_state->server_info->user_session_key.length) {
DEBUG(10, ("Got NT session key of length %u\n",
(unsigned)gensec_ntlmssp_state->server_info->user_session_key.length));
if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->user_session_key.data)) {
return NT_STATUS_NO_MEMORY;
}
*user_session_key = gensec_ntlmssp_state->server_info->user_session_key;
}
if (gensec_ntlmssp_state->server_info->lm_session_key.length) {
DEBUG(10, ("Got LM session key of length %u\n",
(unsigned)gensec_ntlmssp_state->server_info->lm_session_key.length));
if (!talloc_reference(mem_ctx, gensec_ntlmssp_state->server_info->lm_session_key.data)) {
return NT_STATUS_NO_MEMORY;
}
*lm_session_key = gensec_ntlmssp_state->server_info->lm_session_key;
}
return nt_status;
}
/**
* Return the credentials of a logged on user, including session keys
* etc.
*
* Only valid after a successful authentication
*
* May only be called once per authentication.
*
*/
NTSTATUS gensec_ntlmssp_session_info(struct gensec_security *gensec_security,
struct auth_session_info **session_info)
{
NTSTATUS nt_status;
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
nt_status = auth_generate_session_info(gensec_ntlmssp_state, gensec_ntlmssp_state->server_info, session_info);
NT_STATUS_NOT_OK_RETURN(nt_status);
(*session_info)->session_key = data_blob_talloc(*session_info,
gensec_ntlmssp_state->session_key.data,
gensec_ntlmssp_state->session_key.length);
return NT_STATUS_OK;
}
/**
* Start NTLMSSP on the server side
*
*/
NTSTATUS gensec_ntlmssp_server_start(struct gensec_security *gensec_security)
{
NTSTATUS nt_status;
struct gensec_ntlmssp_state *gensec_ntlmssp_state;
nt_status = gensec_ntlmssp_start(gensec_security);
NT_STATUS_NOT_OK_RETURN(nt_status);
gensec_ntlmssp_state = gensec_security->private_data;
gensec_ntlmssp_state->role = NTLMSSP_SERVER;
gensec_ntlmssp_state->workstation = NULL;
gensec_ntlmssp_state->server_name = lp_netbios_name();
gensec_ntlmssp_state->get_domain = lp_workgroup;
gensec_ntlmssp_state->expected_state = NTLMSSP_NEGOTIATE;
gensec_ntlmssp_state->allow_lm_key = (lp_lanman_auth()
&& lp_parm_bool(-1, "ntlmssp_server", "allow_lm_key", False));
gensec_ntlmssp_state->server_multiple_authentications = False;
gensec_ntlmssp_state->neg_flags =
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_UNKNOWN_02000000;
gensec_ntlmssp_state->lm_resp = data_blob(NULL, 0);
gensec_ntlmssp_state->nt_resp = data_blob(NULL, 0);
gensec_ntlmssp_state->encrypted_session_key = data_blob(NULL, 0);
if (lp_parm_bool(-1, "ntlmssp_server", "128bit", True)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_128;
}
if (lp_parm_bool(-1, "ntlmssp_server", "56bit", True)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_56;
}
if (lp_parm_bool(-1, "ntlmssp_server", "keyexchange", True)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
}
if (lp_parm_bool(-1, "ntlmssp_server", "alwayssign", True)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
}
if (lp_parm_bool(-1, "ntlmssp_server", "ntlm2", True)) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
}
if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
}
if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
gensec_ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
}
nt_status = auth_context_create(gensec_ntlmssp_state, lp_auth_methods(),
gensec_security->event_ctx,
gensec_security->msg_ctx,
&gensec_ntlmssp_state->auth_context);
NT_STATUS_NOT_OK_RETURN(nt_status);
gensec_ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge;
gensec_ntlmssp_state->may_set_challenge = auth_ntlmssp_may_set_challenge;
gensec_ntlmssp_state->set_challenge = auth_ntlmssp_set_challenge;
gensec_ntlmssp_state->check_password = auth_ntlmssp_check_password;
gensec_ntlmssp_state->server_role = lp_server_role();
return NT_STATUS_OK;
}
+549
View File
@@ -0,0 +1,549 @@
/*
* Unix SMB/CIFS implementation.
* Version 3.0
* NTLMSSP Signing routines
* Copyright (C) Luke Kenneth Casson Leighton 1996-2001
* Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2005
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "includes.h"
#include "auth/auth.h"
#include "auth/ntlmssp/ntlmssp.h"
#include "auth/ntlmssp/msrpc_parse.h"
#include "lib/crypto/crypto.h"
#include "auth/credentials/credentials.h"
#include "auth/gensec/gensec.h"
#define CLI_SIGN "session key to client-to-server signing key magic constant"
#define CLI_SEAL "session key to client-to-server sealing key magic constant"
#define SRV_SIGN "session key to server-to-client signing key magic constant"
#define SRV_SEAL "session key to server-to-client sealing key magic constant"
/**
* Some notes on the NTLM2 code:
*
* NTLM2 is a AEAD system. This means that the data encrypted is not
* all the data that is signed. In DCE-RPC case, the headers of the
* DCE-RPC packets are also signed. This prevents some of the
* fun-and-games one might have by changing them.
*
*/
static void calc_ntlmv2_key(TALLOC_CTX *mem_ctx,
DATA_BLOB *subkey,
DATA_BLOB session_key,
const char *constant)
{
struct MD5Context ctx3;
*subkey = data_blob_talloc(mem_ctx, NULL, 16);
MD5Init(&ctx3);
MD5Update(&ctx3, session_key.data, session_key.length);
MD5Update(&ctx3, (const uint8_t *)constant, strlen(constant)+1);
MD5Final(subkey->data, &ctx3);
}
enum ntlmssp_direction {
NTLMSSP_SEND,
NTLMSSP_RECEIVE
};
static NTSTATUS ntlmssp_make_packet_signature(struct gensec_ntlmssp_state *gensec_ntlmssp_state,
TALLOC_CTX *sig_mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
enum ntlmssp_direction direction,
DATA_BLOB *sig, BOOL encrypt_sig)
{
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
HMACMD5Context ctx;
uint8_t digest[16];
uint8_t seq_num[4];
*sig = data_blob_talloc(sig_mem_ctx, NULL, NTLMSSP_SIG_SIZE);
if (!sig->data) {
return NT_STATUS_NO_MEMORY;
}
switch (direction) {
case NTLMSSP_SEND:
SIVAL(seq_num, 0, gensec_ntlmssp_state->crypt.ntlm2.send_seq_num);
gensec_ntlmssp_state->crypt.ntlm2.send_seq_num++;
hmac_md5_init_limK_to_64(gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.data,
gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.length, &ctx);
break;
case NTLMSSP_RECEIVE:
SIVAL(seq_num, 0, gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num);
gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num++;
hmac_md5_init_limK_to_64(gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.data,
gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.length, &ctx);
break;
}
hmac_md5_update(seq_num, sizeof(seq_num), &ctx);
hmac_md5_update(whole_pdu, pdu_length, &ctx);
hmac_md5_final(digest, &ctx);
if (encrypt_sig && gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
switch (direction) {
case NTLMSSP_SEND:
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, digest, 8);
break;
case NTLMSSP_RECEIVE:
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state, digest, 8);
break;
}
}
SIVAL(sig->data, 0, NTLMSSP_SIGN_VERSION);
memcpy(sig->data + 4, digest, 8);
memcpy(sig->data + 12, seq_num, 4);
DEBUG(10, ("NTLM2: created signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
dump_data(11, sig->data, sig->length);
} else {
uint32_t crc;
crc = crc32_calc_buffer(data, length);
if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
return NT_STATUS_NO_MEMORY;
}
gensec_ntlmssp_state->crypt.ntlm.seq_num++;
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, sig->data+4, sig->length-4);
DEBUG(10, ("NTLM1: created signature over %llu bytes of input:\n", (unsigned long long)length));
dump_data(11, sig->data, sig->length);
}
return NT_STATUS_OK;
}
/* TODO: make this non-public */
_PUBLIC_ NTSTATUS gensec_ntlmssp_sign_packet(struct gensec_security *gensec_security,
TALLOC_CTX *sig_mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
return ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
data, length,
whole_pdu, pdu_length,
NTLMSSP_SEND, sig, True);
}
/**
* Check the signature of an incoming packet
*
*/
NTSTATUS gensec_ntlmssp_check_packet(struct gensec_security *gensec_security,
TALLOC_CTX *sig_mem_ctx,
const uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
DATA_BLOB local_sig;
NTSTATUS nt_status;
if (!gensec_ntlmssp_state->session_key.length) {
DEBUG(3, ("NO session key, cannot check packet signature\n"));
return NT_STATUS_NO_USER_SESSION_KEY;
}
if (sig->length < 8) {
DEBUG(0, ("NTLMSSP packet check failed due to short signature (%lu bytes)!\n",
(unsigned long)sig->length));
}
nt_status = ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
data, length,
whole_pdu, pdu_length,
NTLMSSP_RECEIVE, &local_sig, True);
if (!NT_STATUS_IS_OK(nt_status)) {
DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
return nt_status;
}
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
if (local_sig.length != sig->length ||
memcmp(local_sig.data,
sig->data, sig->length) != 0) {
DEBUG(5, ("BAD SIG NTLM2: wanted signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
dump_data(5, local_sig.data, local_sig.length);
DEBUG(5, ("BAD SIG: got signature over %llu bytes of input:\n", (unsigned long long)pdu_length));
dump_data(5, sig->data, sig->length);
DEBUG(0, ("NTLMSSP NTLM2 packet check failed due to invalid signature on %llu bytes of input!\n", (unsigned long long)pdu_length));
return NT_STATUS_ACCESS_DENIED;
}
} else {
if (local_sig.length != sig->length ||
memcmp(local_sig.data + 8,
sig->data + 8, sig->length - 8) != 0) {
DEBUG(5, ("BAD SIG NTLM1: wanted signature of %llu bytes of input:\n", (unsigned long long)length));
dump_data(5, local_sig.data, local_sig.length);
DEBUG(5, ("BAD SIG: got signature of %llu bytes of input:\n", (unsigned long long)length));
dump_data(5, sig->data, sig->length);
DEBUG(0, ("NTLMSSP NTLM1 packet check failed due to invalid signature on %llu bytes of input:\n", (unsigned long long)length));
return NT_STATUS_ACCESS_DENIED;
}
}
dump_data_pw("checked ntlmssp signature\n", sig->data, sig->length);
return NT_STATUS_OK;
}
/**
* Seal data with the NTLMSSP algorithm
*
*/
NTSTATUS gensec_ntlmssp_seal_packet(struct gensec_security *gensec_security,
TALLOC_CTX *sig_mem_ctx,
uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
NTSTATUS nt_status;
if (!gensec_ntlmssp_state->session_key.length) {
DEBUG(3, ("NO session key, cannot seal packet\n"));
return NT_STATUS_NO_USER_SESSION_KEY;
}
DEBUG(10,("ntlmssp_seal_data: seal\n"));
dump_data_pw("ntlmssp clear data\n", data, length);
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
/* The order of these two operations matters - we must first seal the packet,
then seal the sequence number - this is becouse the send_seal_hash is not
constant, but is is rather updated with each iteration */
nt_status = ntlmssp_make_packet_signature(gensec_ntlmssp_state, sig_mem_ctx,
data, length,
whole_pdu, pdu_length,
NTLMSSP_SEND, sig, False);
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, data, length);
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state, sig->data+4, 8);
}
} else {
uint32_t crc;
crc = crc32_calc_buffer(data, length);
if (!msrpc_gen(sig_mem_ctx, sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, gensec_ntlmssp_state->crypt.ntlm.seq_num)) {
return NT_STATUS_NO_MEMORY;
}
/* The order of these two operations matters - we must
first seal the packet, then seal the sequence
number - this is becouse the ntlmssp_hash is not
constant, but is is rather updated with each
iteration */
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, data, length);
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, sig->data+4, sig->length-4);
/* increment counter on send */
gensec_ntlmssp_state->crypt.ntlm.seq_num++;
nt_status = NT_STATUS_OK;
}
dump_data_pw("ntlmssp signature\n", sig->data, sig->length);
dump_data_pw("ntlmssp sealed data\n", data, length);
return nt_status;
}
/**
* Unseal data with the NTLMSSP algorithm
*
*/
/*
wrappers for the ntlmssp_*() functions
*/
NTSTATUS gensec_ntlmssp_unseal_packet(struct gensec_security *gensec_security,
TALLOC_CTX *sig_mem_ctx,
uint8_t *data, size_t length,
const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig)
{
struct gensec_ntlmssp_state *gensec_ntlmssp_state = gensec_security->private_data;
if (!gensec_ntlmssp_state->session_key.length) {
DEBUG(3, ("NO session key, cannot unseal packet\n"));
return NT_STATUS_NO_USER_SESSION_KEY;
}
dump_data_pw("ntlmssp sealed data\n", data, length);
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state, data, length);
} else {
arcfour_crypt_sbox(gensec_ntlmssp_state->crypt.ntlm.arcfour_state, data, length);
}
dump_data_pw("ntlmssp clear data\n", data, length);
return gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx, data, length, whole_pdu, pdu_length, sig);
}
/**
Initialise the state for NTLMSSP signing.
*/
/* TODO: make this non-public */
_PUBLIC_ NTSTATUS ntlmssp_sign_init(struct gensec_ntlmssp_state *gensec_ntlmssp_state)
{
TALLOC_CTX *mem_ctx = talloc_new(gensec_ntlmssp_state);
if (!mem_ctx) {
return NT_STATUS_NO_MEMORY;
}
DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n"));
debug_ntlmssp_flags(gensec_ntlmssp_state->neg_flags);
if (gensec_ntlmssp_state->session_key.length < 8) {
talloc_free(mem_ctx);
DEBUG(3, ("NO session key, cannot intialise signing\n"));
return NT_STATUS_NO_USER_SESSION_KEY;
}
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
{
DATA_BLOB weak_session_key = gensec_ntlmssp_state->session_key;
const char *send_sign_const;
const char *send_seal_const;
const char *recv_sign_const;
const char *recv_seal_const;
DATA_BLOB send_seal_key;
DATA_BLOB recv_seal_key;
switch (gensec_ntlmssp_state->role) {
case NTLMSSP_CLIENT:
send_sign_const = CLI_SIGN;
send_seal_const = CLI_SEAL;
recv_sign_const = SRV_SIGN;
recv_seal_const = SRV_SEAL;
break;
case NTLMSSP_SERVER:
send_sign_const = SRV_SIGN;
send_seal_const = SRV_SEAL;
recv_sign_const = CLI_SIGN;
recv_seal_const = CLI_SEAL;
break;
default:
talloc_free(mem_ctx);
return NT_STATUS_INTERNAL_ERROR;
}
gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state);
gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state);
/**
Weaken NTLMSSP keys to cope with down-level
clients, servers and export restrictions.
We probably should have some parameters to control
this, once we get NTLM2 working.
*/
/* Key weakening was not performed on the master key
* for NTLM2 (in ntlmssp_weaken_keys()), but must be
* done on the encryption subkeys only. That is why
* we don't have this code for the ntlmv1 case.
*/
if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) {
} else if (gensec_ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
weak_session_key.length = 7;
} else { /* forty bits */
weak_session_key.length = 5;
}
dump_data_pw("NTLMSSP weakend master key:\n",
weak_session_key.data,
weak_session_key.length);
/* SEND: sign key */
calc_ntlmv2_key(gensec_ntlmssp_state,
&gensec_ntlmssp_state->crypt.ntlm2.send_sign_key,
gensec_ntlmssp_state->session_key, send_sign_const);
dump_data_pw("NTLMSSP send sign key:\n",
gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.data,
gensec_ntlmssp_state->crypt.ntlm2.send_sign_key.length);
/* SEND: seal ARCFOUR pad */
calc_ntlmv2_key(mem_ctx,
&send_seal_key,
weak_session_key, send_seal_const);
dump_data_pw("NTLMSSP send seal key:\n",
send_seal_key.data,
send_seal_key.length);
arcfour_init(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state,
&send_seal_key);
dump_data_pw("NTLMSSP send sesl hash:\n",
gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state->sbox,
sizeof(gensec_ntlmssp_state->crypt.ntlm2.send_seal_arcfour_state->sbox));
/* RECV: sign key */
calc_ntlmv2_key(gensec_ntlmssp_state,
&gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key,
gensec_ntlmssp_state->session_key, recv_sign_const);
dump_data_pw("NTLMSSP recv sign key:\n",
gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.data,
gensec_ntlmssp_state->crypt.ntlm2.recv_sign_key.length);
/* RECV: seal ARCFOUR pad */
calc_ntlmv2_key(mem_ctx,
&recv_seal_key,
weak_session_key, recv_seal_const);
dump_data_pw("NTLMSSP recv seal key:\n",
recv_seal_key.data,
recv_seal_key.length);
arcfour_init(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state,
&recv_seal_key);
dump_data_pw("NTLMSSP receive seal hash:\n",
gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state->sbox,
sizeof(gensec_ntlmssp_state->crypt.ntlm2.recv_seal_arcfour_state->sbox));
gensec_ntlmssp_state->crypt.ntlm2.send_seq_num = 0;
gensec_ntlmssp_state->crypt.ntlm2.recv_seq_num = 0;
} else {
DATA_BLOB weak_session_key = ntlmssp_weakend_key(gensec_ntlmssp_state, mem_ctx);
DEBUG(5, ("NTLMSSP Sign/Seal - using NTLM1\n"));
gensec_ntlmssp_state->crypt.ntlm.arcfour_state = talloc(gensec_ntlmssp_state, struct arcfour_state);
NT_STATUS_HAVE_NO_MEMORY(gensec_ntlmssp_state->crypt.ntlm.arcfour_state);
arcfour_init(gensec_ntlmssp_state->crypt.ntlm.arcfour_state,
&weak_session_key);
dump_data_pw("NTLMSSP hash:\n", gensec_ntlmssp_state->crypt.ntlm.arcfour_state->sbox,
sizeof(gensec_ntlmssp_state->crypt.ntlm.arcfour_state->sbox));
gensec_ntlmssp_state->crypt.ntlm.seq_num = 0;
}
talloc_free(mem_ctx);
return NT_STATUS_OK;
}
size_t gensec_ntlmssp_sig_size(struct gensec_security *gensec_security, size_t data_size)
{
return NTLMSSP_SIG_SIZE;
}
NTSTATUS gensec_ntlmssp_wrap(struct gensec_security *gensec_security,
TALLOC_CTX *sig_mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out)
{
DATA_BLOB sig;
NTSTATUS nt_status;
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
*out = data_blob_talloc(sig_mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE);
if (!out->data) {
return NT_STATUS_NO_MEMORY;
}
memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length);
nt_status = gensec_ntlmssp_seal_packet(gensec_security, sig_mem_ctx,
out->data + NTLMSSP_SIG_SIZE,
out->length - NTLMSSP_SIG_SIZE,
out->data + NTLMSSP_SIG_SIZE,
out->length - NTLMSSP_SIG_SIZE,
&sig);
if (NT_STATUS_IS_OK(nt_status)) {
memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE);
}
return nt_status;
} else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
*out = data_blob_talloc(sig_mem_ctx, NULL, in->length + NTLMSSP_SIG_SIZE);
if (!out->data) {
return NT_STATUS_NO_MEMORY;
}
memcpy(out->data + NTLMSSP_SIG_SIZE, in->data, in->length);
nt_status = gensec_ntlmssp_sign_packet(gensec_security, sig_mem_ctx,
out->data + NTLMSSP_SIG_SIZE,
out->length - NTLMSSP_SIG_SIZE,
out->data + NTLMSSP_SIG_SIZE,
out->length - NTLMSSP_SIG_SIZE,
&sig);
if (NT_STATUS_IS_OK(nt_status)) {
memcpy(out->data, sig.data, NTLMSSP_SIG_SIZE);
}
return nt_status;
} else {
*out = *in;
return NT_STATUS_OK;
}
}
NTSTATUS gensec_ntlmssp_unwrap(struct gensec_security *gensec_security,
TALLOC_CTX *sig_mem_ctx,
const DATA_BLOB *in,
DATA_BLOB *out)
{
DATA_BLOB sig;
if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
if (in->length < NTLMSSP_SIG_SIZE) {
return NT_STATUS_INVALID_PARAMETER;
}
sig.data = in->data;
sig.length = NTLMSSP_SIG_SIZE;
*out = data_blob_talloc(sig_mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE);
return gensec_ntlmssp_unseal_packet(gensec_security, sig_mem_ctx,
out->data, out->length,
out->data, out->length,
&sig);
} else if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
if (in->length < NTLMSSP_SIG_SIZE) {
return NT_STATUS_INVALID_PARAMETER;
}
sig.data = in->data;
sig.length = NTLMSSP_SIG_SIZE;
*out = data_blob_talloc(sig_mem_ctx, in->data + NTLMSSP_SIG_SIZE, in->length - NTLMSSP_SIG_SIZE);
return gensec_ntlmssp_check_packet(gensec_security, sig_mem_ctx,
out->data, out->length,
out->data, out->length,
&sig);
} else {
*out = *in;
return NT_STATUS_OK;
}
}
+126
View File
@@ -0,0 +1,126 @@
/*
* Unix SMB/CIFS implementation.
* PAM error mapping functions
* Copyright (C) Andrew Bartlett 2002
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#ifdef WITH_HAVE_SECURITY_PAM_APPL_H
#include <security/pam_appl.h>
#if defined(PAM_AUTHTOK_RECOVERY_ERR) && !defined(PAM_AUTHTOK_RECOVER_ERR)
#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR
#endif
/* PAM -> NT_STATUS map */
static const struct {
int pam_code;
NTSTATUS ntstatus;
} pam_to_nt_status_map[] = {
{PAM_OPEN_ERR, NT_STATUS_UNSUCCESSFUL},
{PAM_SYMBOL_ERR, NT_STATUS_UNSUCCESSFUL},
{PAM_SERVICE_ERR, NT_STATUS_UNSUCCESSFUL},
{PAM_SYSTEM_ERR, NT_STATUS_UNSUCCESSFUL},
{PAM_BUF_ERR, NT_STATUS_UNSUCCESSFUL},
{PAM_PERM_DENIED, NT_STATUS_ACCESS_DENIED},
{PAM_AUTH_ERR, NT_STATUS_WRONG_PASSWORD},
{PAM_CRED_INSUFFICIENT, NT_STATUS_INSUFFICIENT_LOGON_INFO}, /* FIXME: Is this correct? */
{PAM_AUTHINFO_UNAVAIL, NT_STATUS_LOGON_FAILURE},
{PAM_USER_UNKNOWN, NT_STATUS_NO_SUCH_USER},
{PAM_MAXTRIES, NT_STATUS_REMOTE_SESSION_LIMIT}, /* FIXME: Is this correct? */
{PAM_NEW_AUTHTOK_REQD, NT_STATUS_PASSWORD_MUST_CHANGE},
{PAM_ACCT_EXPIRED, NT_STATUS_ACCOUNT_EXPIRED},
{PAM_SESSION_ERR, NT_STATUS_INSUFFICIENT_RESOURCES},
{PAM_CRED_UNAVAIL, NT_STATUS_NO_TOKEN}, /* FIXME: Is this correct? */
{PAM_CRED_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, /* FIXME: Is this correct? */
{PAM_CRED_ERR, NT_STATUS_UNSUCCESSFUL},
{PAM_AUTHTOK_ERR, NT_STATUS_UNSUCCESSFUL},
#ifdef PAM_AUTHTOK_RECOVER_ERR
{PAM_AUTHTOK_RECOVER_ERR, NT_STATUS_UNSUCCESSFUL},
#endif
{PAM_AUTHTOK_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
{PAM_SUCCESS, NT_STATUS_OK}
};
/* NT_STATUS -> PAM map */
static const struct {
NTSTATUS ntstatus;
int pam_code;
} nt_status_to_pam_map[] = {
{NT_STATUS_UNSUCCESSFUL, PAM_SYSTEM_ERR},
{NT_STATUS_NO_SUCH_USER, PAM_USER_UNKNOWN},
{NT_STATUS_WRONG_PASSWORD, PAM_AUTH_ERR},
{NT_STATUS_LOGON_FAILURE, PAM_AUTH_ERR},
{NT_STATUS_ACCOUNT_EXPIRED, PAM_ACCT_EXPIRED},
{NT_STATUS_PASSWORD_EXPIRED, PAM_AUTHTOK_EXPIRED},
{NT_STATUS_PASSWORD_MUST_CHANGE, PAM_NEW_AUTHTOK_REQD},
{NT_STATUS_OK, PAM_SUCCESS}
};
/*****************************************************************************
convert a PAM error to a NT status32 code
*****************************************************************************/
NTSTATUS pam_to_nt_status(int pam_error)
{
int i;
if (pam_error == 0) return NT_STATUS_OK;
for (i=0; NT_STATUS_V(pam_to_nt_status_map[i].ntstatus); i++) {
if (pam_error == pam_to_nt_status_map[i].pam_code)
return pam_to_nt_status_map[i].ntstatus;
}
return NT_STATUS_UNSUCCESSFUL;
}
/*****************************************************************************
convert an NT status32 code to a PAM error
*****************************************************************************/
int nt_status_to_pam(NTSTATUS nt_status)
{
int i;
if NT_STATUS_IS_OK(nt_status) return PAM_SUCCESS;
for (i=0; NT_STATUS_V(nt_status_to_pam_map[i].ntstatus); i++) {
if (NT_STATUS_EQUAL(nt_status,nt_status_to_pam_map[i].ntstatus))
return nt_status_to_pam_map[i].pam_code;
}
return PAM_SYSTEM_ERR;
}
#else
/*****************************************************************************
convert a PAM error to a NT status32 code
*****************************************************************************/
NTSTATUS pam_to_nt_status(int pam_error)
{
if (pam_error == 0) return NT_STATUS_OK;
return NT_STATUS_UNSUCCESSFUL;
}
/*****************************************************************************
convert an NT status32 code to a PAM error
*****************************************************************************/
int nt_status_to_pam(NTSTATUS nt_status)
{
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OK)) return 0;
return 4; /* PAM_SYSTEM_ERR */
}
#endif
+396
View File
@@ -0,0 +1,396 @@
/*
Unix SMB/CIFS implementation.
Password and authentication handling
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2004
Copyright (C) Gerald Carter 2003
Copyright (C) Stefan Metzmacher 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "system/time.h"
#include "auth/auth.h"
#include "db_wrap.h"
#include "dsdb/samdb/samdb.h"
#include "libcli/security/security.h"
#include "libcli/ldap/ldap.h"
#include "librpc/gen_ndr/ndr_netlogon.h"
const char *user_attrs[] = {
/* required for the krb5 kdc */
"objectClass",
"sAMAccountName",
"userPrincipalName",
"servicePrincipalName",
"msDS-KeyVersionNumber",
"krb5Key",
/* passwords */
"lmPwdHash",
"ntPwdHash",
"userAccountControl",
"pwdLastSet",
"accountExpires",
"objectSid",
/* check 'allowed workstations' */
"userWorkstations",
/* required for server_info, not access control: */
"displayName",
"scriptPath",
"profilePath",
"homeDirectory",
"homeDrive",
"lastLogon",
"lastLogoff",
"accountExpires",
"badPwdCount",
"logonCount",
"primaryGroupID",
NULL,
};
const char *domain_ref_attrs[] = {"nETBIOSName", "nCName",
"dnsRoot", "objectClass", NULL};
/****************************************************************************
Do a specific test for a SAM_ACCOUNT being vaild for this connection
(ie not disabled, expired and the like).
****************************************************************************/
_PUBLIC_ NTSTATUS authsam_account_ok(TALLOC_CTX *mem_ctx,
struct ldb_context *sam_ctx,
uint32_t logon_parameters,
struct ldb_message *msg,
struct ldb_message *msg_domain_ref,
const char *logon_workstation,
const char *name_for_logs)
{
uint16_t acct_flags;
const char *workstation_list;
NTTIME acct_expiry;
NTTIME must_change_time;
NTTIME last_set_time;
struct ldb_dn *domain_dn = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", ldb_dn_new(mem_ctx, sam_ctx, NULL));
NTTIME now;
DEBUG(4,("authsam_account_ok: Checking SMB password for user %s\n", name_for_logs));
acct_flags = samdb_result_acct_flags(msg, "userAccountControl");
acct_expiry = samdb_result_nttime(msg, "accountExpires", 0);
must_change_time = samdb_result_force_password_change(sam_ctx, mem_ctx,
domain_dn, msg);
last_set_time = samdb_result_nttime(msg, "pwdLastSet", 0);
workstation_list = samdb_result_string(msg, "userWorkstations", NULL);
/* Quit if the account was disabled. */
if (acct_flags & ACB_DISABLED) {
DEBUG(1,("authsam_account_ok: Account for user '%s' was disabled.\n", name_for_logs));
return NT_STATUS_ACCOUNT_DISABLED;
}
/* Quit if the account was locked out. */
if (acct_flags & ACB_AUTOLOCK) {
DEBUG(1,("authsam_account_ok: Account for user %s was locked out.\n", name_for_logs));
return NT_STATUS_ACCOUNT_LOCKED_OUT;
}
/* Test account expire time */
unix_to_nt_time(&now, time(NULL));
if (now > acct_expiry) {
DEBUG(1,("authsam_account_ok: Account for user '%s' has expired.\n", name_for_logs));
DEBUG(3,("authsam_account_ok: Account expired at '%s'.\n",
nt_time_string(mem_ctx, acct_expiry)));
return NT_STATUS_ACCOUNT_EXPIRED;
}
if (!(acct_flags & ACB_PWNOEXP)) {
/* check for immediate expiry "must change at next logon" */
if (must_change_time == 0 && last_set_time != 0) {
DEBUG(1,("sam_account_ok: Account for user '%s' password must change!.\n",
name_for_logs));
return NT_STATUS_PASSWORD_MUST_CHANGE;
}
/* check for expired password */
if ((must_change_time != 0) && (must_change_time < now)) {
DEBUG(1,("sam_account_ok: Account for user '%s' password expired!.\n",
name_for_logs));
DEBUG(1,("sam_account_ok: Password expired at '%s' unix time.\n",
nt_time_string(mem_ctx, must_change_time)));
return NT_STATUS_PASSWORD_EXPIRED;
}
}
/* Test workstation. Workstation list is comma separated. */
if (logon_workstation && workstation_list && *workstation_list) {
BOOL invalid_ws = True;
int i;
const char **workstations = str_list_make(mem_ctx, workstation_list, ",");
for (i = 0; workstations && workstations[i]; i++) {
DEBUG(10,("sam_account_ok: checking for workstation match '%s' and '%s'\n",
workstations[i], logon_workstation));
if (strequal(workstations[i], logon_workstation) == 0) {
invalid_ws = False;
break;
}
}
talloc_free(workstations);
if (invalid_ws) {
return NT_STATUS_INVALID_WORKSTATION;
}
}
if (acct_flags & ACB_DOMTRUST) {
DEBUG(2,("sam_account_ok: Domain trust account %s denied by server\n", name_for_logs));
return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
}
if (!(logon_parameters & MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT)) {
if (acct_flags & ACB_SVRTRUST) {
DEBUG(2,("sam_account_ok: Server trust account %s denied by server\n", name_for_logs));
return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
}
}
if (!(logon_parameters & MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT)) {
if (acct_flags & ACB_WSTRUST) {
DEBUG(4,("sam_account_ok: Wksta trust account %s denied by server\n", name_for_logs));
return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
}
}
return NT_STATUS_OK;
}
_PUBLIC_ NTSTATUS authsam_make_server_info(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx,
struct ldb_message *msg,
struct ldb_message *msg_domain_ref,
DATA_BLOB user_sess_key, DATA_BLOB lm_sess_key,
struct auth_serversupplied_info **_server_info)
{
struct auth_serversupplied_info *server_info;
struct ldb_message **group_msgs;
int group_ret;
const char *group_attrs[3] = { "sAMAccountType", "objectSid", NULL };
/* find list of sids */
struct dom_sid **groupSIDs = NULL;
struct dom_sid *account_sid;
struct dom_sid *primary_group_sid;
const char *str;
struct ldb_dn *ncname;
int i;
uint_t rid;
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
group_ret = gendb_search(sam_ctx,
tmp_ctx, NULL, &group_msgs, group_attrs,
"(&(member=%s)(sAMAccountType=*))",
ldb_dn_get_linearized(msg->dn));
if (group_ret == -1) {
talloc_free(tmp_ctx);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
server_info = talloc(mem_ctx, struct auth_serversupplied_info);
NT_STATUS_HAVE_NO_MEMORY(server_info);
if (group_ret > 0) {
groupSIDs = talloc_array(server_info, struct dom_sid *, group_ret);
NT_STATUS_HAVE_NO_MEMORY(groupSIDs);
}
/* Need to unroll some nested groups, but not aliases */
for (i = 0; i < group_ret; i++) {
groupSIDs[i] = samdb_result_dom_sid(groupSIDs,
group_msgs[i], "objectSid");
NT_STATUS_HAVE_NO_MEMORY(groupSIDs[i]);
}
talloc_free(tmp_ctx);
account_sid = samdb_result_dom_sid(server_info, msg, "objectSid");
NT_STATUS_HAVE_NO_MEMORY(account_sid);
primary_group_sid = dom_sid_dup(server_info, account_sid);
NT_STATUS_HAVE_NO_MEMORY(primary_group_sid);
rid = samdb_result_uint(msg, "primaryGroupID", ~0);
if (rid == ~0) {
if (group_ret > 0) {
primary_group_sid = groupSIDs[0];
} else {
primary_group_sid = NULL;
}
} else {
primary_group_sid->sub_auths[primary_group_sid->num_auths-1] = rid;
}
server_info->account_sid = account_sid;
server_info->primary_group_sid = primary_group_sid;
server_info->n_domain_groups = group_ret;
server_info->domain_groups = groupSIDs;
server_info->account_name = talloc_steal(server_info, samdb_result_string(msg, "sAMAccountName", NULL));
server_info->domain_name = talloc_steal(server_info, samdb_result_string(msg_domain_ref, "nETBIOSName", NULL));
str = samdb_result_string(msg, "displayName", "");
server_info->full_name = talloc_strdup(server_info, str);
NT_STATUS_HAVE_NO_MEMORY(server_info->full_name);
str = samdb_result_string(msg, "scriptPath", "");
server_info->logon_script = talloc_strdup(server_info, str);
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_script);
str = samdb_result_string(msg, "profilePath", "");
server_info->profile_path = talloc_strdup(server_info, str);
NT_STATUS_HAVE_NO_MEMORY(server_info->profile_path);
str = samdb_result_string(msg, "homeDirectory", "");
server_info->home_directory = talloc_strdup(server_info, str);
NT_STATUS_HAVE_NO_MEMORY(server_info->home_directory);
str = samdb_result_string(msg, "homeDrive", "");
server_info->home_drive = talloc_strdup(server_info, str);
NT_STATUS_HAVE_NO_MEMORY(server_info->home_drive);
server_info->logon_server = talloc_strdup(server_info, lp_netbios_name());
NT_STATUS_HAVE_NO_MEMORY(server_info->logon_server);
server_info->last_logon = samdb_result_nttime(msg, "lastLogon", 0);
server_info->last_logoff = samdb_result_nttime(msg, "lastLogoff", 0);
server_info->acct_expiry = samdb_result_nttime(msg, "accountExpires", 0);
server_info->last_password_change = samdb_result_nttime(msg, "pwdLastSet", 0);
ncname = samdb_result_dn(sam_ctx, mem_ctx, msg_domain_ref, "nCName", NULL);
if (!ncname) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
server_info->allow_password_change
= samdb_result_allow_password_change(sam_ctx, mem_ctx,
ncname, msg, "pwdLastSet");
server_info->force_password_change
= samdb_result_force_password_change(sam_ctx, mem_ctx,
ncname, msg);
server_info->logon_count = samdb_result_uint(msg, "logonCount", 0);
server_info->bad_password_count = samdb_result_uint(msg, "badPwdCount", 0);
server_info->acct_flags = samdb_result_acct_flags(msg, "userAccountControl");
server_info->user_session_key = user_sess_key;
server_info->lm_session_key = lm_sess_key;
server_info->authenticated = True;
*_server_info = server_info;
return NT_STATUS_OK;
}
_PUBLIC_ NTSTATUS sam_get_results_principal(struct ldb_context *sam_ctx,
TALLOC_CTX *mem_ctx, const char *principal,
struct ldb_message ***msgs,
struct ldb_message ***msgs_domain_ref)
{
struct ldb_dn *user_dn, *domain_dn;
NTSTATUS nt_status;
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
int ret;
struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
if (!tmp_ctx) {
return NT_STATUS_NO_MEMORY;
}
nt_status = crack_user_principal_name(sam_ctx, tmp_ctx, principal, &user_dn, &domain_dn);
if (!NT_STATUS_IS_OK(nt_status)) {
talloc_free(tmp_ctx);
return nt_status;
}
/* grab domain info from the reference */
ret = gendb_search(sam_ctx, tmp_ctx, partitions_basedn, msgs_domain_ref, domain_ref_attrs,
"(ncName=%s)", ldb_dn_get_linearized(domain_dn));
if (ret != 1) {
talloc_free(tmp_ctx);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
/* pull the user attributes */
ret = gendb_search_dn(sam_ctx, tmp_ctx, user_dn, msgs, user_attrs);
if (ret != 1) {
talloc_free(tmp_ctx);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
talloc_steal(mem_ctx, *msgs);
talloc_steal(mem_ctx, *msgs_domain_ref);
talloc_free(tmp_ctx);
return NT_STATUS_OK;
}
/* Used in the gensec_gssapi and gensec_krb5 server-side code, where the PAC isn't available */
NTSTATUS sam_get_server_info_principal(TALLOC_CTX *mem_ctx, const char *principal,
struct auth_serversupplied_info **server_info)
{
NTSTATUS nt_status;
DATA_BLOB user_sess_key = data_blob(NULL, 0);
DATA_BLOB lm_sess_key = data_blob(NULL, 0);
struct ldb_message **msgs;
struct ldb_message **msgs_domain_ref;
struct ldb_context *sam_ctx;
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
if (!tmp_ctx) {
return NT_STATUS_NO_MEMORY;
}
sam_ctx = samdb_connect(tmp_ctx, system_session(tmp_ctx));
if (sam_ctx == NULL) {
talloc_free(tmp_ctx);
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
nt_status = sam_get_results_principal(sam_ctx, tmp_ctx, principal,
&msgs, &msgs_domain_ref);
if (!NT_STATUS_IS_OK(nt_status)) {
return nt_status;
}
nt_status = authsam_make_server_info(tmp_ctx, sam_ctx, msgs[0], msgs_domain_ref[0],
user_sess_key, lm_sess_key,
server_info);
if (NT_STATUS_IS_OK(nt_status)) {
talloc_steal(mem_ctx, *server_info);
}
talloc_free(tmp_ctx);
return nt_status;
}
+68
View File
@@ -0,0 +1,68 @@
#!/bin/sh
# Run this script to build samba from SVN.
## insert all possible names (only works with
## autoconf 2.x
TESTAUTOHEADER="autoheader autoheader-2.53 autoheader2.50 autoheader259 autoheader253"
TESTAUTOCONF="autoconf autoconf-2.53 autoconf2.50 autoconf259 autoconf253"
AUTOHEADERFOUND="0"
AUTOCONFFOUND="0"
##
## Look for autoheader
##
for i in $TESTAUTOHEADER; do
if which $i > /dev/null 2>&1; then
if test `$i --version | head -n 1 | cut -d. -f 2 | tr -d [:alpha:]` -ge 53; then
AUTOHEADER=$i
AUTOHEADERFOUND="1"
break
fi
fi
done
##
## Look for autoconf
##
for i in $TESTAUTOCONF; do
if which $i > /dev/null 2>&1; then
if test `$i --version | head -n 1 | cut -d. -f 2 | tr -d [:alpha:]` -ge 53; then
AUTOCONF=$i
AUTOCONFFOUND="1"
break
fi
fi
done
##
## do we have it?
##
if test "$AUTOCONFFOUND" = "0" -o "$AUTOHEADERFOUND" = "0"; then
echo "$0: need autoconf 2.53 or later to build samba from SVN" >&2
exit 1
fi
echo "$0: running script/mkversion.sh"
./script/mkversion.sh || exit 1
rm -rf autom4te*.cache
rm -f configure include/config_tmp.h*
IPATHS="-I. -Ilib/replace"
echo "$0: running $AUTOHEADER $IPATHS"
$AUTOHEADER $IPATHS || exit 1
echo "$0: running $AUTOCONF $IPATHS"
$AUTOCONF $IPATHS || exit 1
rm -rf autom4te*.cache
echo "Now run ./configure and then make."
exit 0
@@ -0,0 +1,109 @@
dnl @synopsis AX_CFLAGS_GCC_OPTION (optionflag [,[shellvar][,[A][,[NA]]])
dnl
dnl AX_CFLAGS_GCC_OPTION(-fvomit-frame) would show a message as like
dnl "checking CFLAGS for gcc -fvomit-frame ... yes" and adds
dnl the optionflag to CFLAGS if it is understood. You can override
dnl the shellvar-default of CFLAGS of course. The order of arguments
dnl stems from the explicit macros like AX_CFLAGS_WARN_ALL.
dnl
dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or
dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options.
dnl However, if you use this macro in a few places, it would be great
dnl if you would make up a new function-macro and submit it to the
dnl ac-archive.
dnl
dnl - $1 option-to-check-for : required ("-option" as non-value)
dnl - $2 shell-variable-to-add-to : CFLAGS
dnl - $3 action-if-found : add value to shellvariable
dnl - $4 action-if-not-found : nothing
dnl
dnl note: in earlier versions, $1-$2 were swapped. We try to detect the
dnl situation and accept a $2=~/-/ as being the old option-to-check-for.
dnl
dnl also: there are other variants that emerged from the original macro
dnl variant which did just test an option to be possibly added. However,
dnl some compilers accept an option silently, or possibly for just
dnl another option that was not intended. Therefore, we have to do a
dnl generic test for a compiler family. For gcc we check "-pedantic"
dnl being accepted which is also understood by compilers who just want
dnl to be compatible with gcc even when not being made from gcc sources.
dnl
dnl see also:
dnl AX_CFLAGS_SUN_OPTION AX_CFLAGS_HPUX_OPTION
dnl AX_CFLAGS_AIX_OPTION AX_CFLAGS_IRIX_OPTION
dnl
dnl @, tested, experimental
dnl @version $Id: ax_cflags_gcc_option.m4,v 1.5 2003/11/29 08:13:25 guidod Exp $
dnl @author Guido Draheim <guidod@gmx.de>
dnl http://ac-archive.sourceforge.net/C_Support/ax_cflags_gcc_option.m4
dnl
AC_DEFUN([AX_CFLAGS_GCC_OPTION_OLD], [dnl
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$2])dnl
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for gcc m4_ifval($2,$2,-option)],
VAR,[VAR="no, unknown"
AC_LANG_SAVE
AC_LANG_C
ac_save_[]FLAGS="$[]FLAGS"
for ac_arg dnl
in "-pedantic % m4_ifval($2,$2,-option)" dnl GCC
#
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
AC_TRY_COMPILE([],[return 0;],
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
done
FLAGS="$ac_save_[]FLAGS"
AC_LANG_RESTORE
])
case ".$VAR" in
.ok|.ok,*) m4_ifvaln($3,$3) ;;
.|.no|.no,*) m4_ifvaln($4,$4) ;;
*) m4_ifvaln($3,$3,[
if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
fi ]) ;;
esac
AS_VAR_POPDEF([VAR])dnl
AS_VAR_POPDEF([FLAGS])dnl
])
dnl -------------------------------------------------------------------------
AC_DEFUN([AX_CFLAGS_GCC_OPTION_NEW], [dnl
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_gcc_option_$1])dnl
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for gcc m4_ifval($1,$1,-option)],
VAR,[VAR="no, unknown"
AC_LANG_SAVE
AC_LANG_C
ac_save_[]FLAGS="$[]FLAGS"
for ac_arg dnl
in "-pedantic % m4_ifval($1,$1,-option)" dnl GCC
#
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
AC_TRY_COMPILE([],[return 0;],
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
done
FLAGS="$ac_save_[]FLAGS"
AC_LANG_RESTORE
])
case ".$VAR" in
.ok|.ok,*) m4_ifvaln($3,$3) ;;
.|.no|.no,*) m4_ifvaln($4,$4) ;;
*) m4_ifvaln($3,$3,[
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
fi ]) ;;
esac
AS_VAR_POPDEF([VAR])dnl
AS_VAR_POPDEF([FLAGS])dnl
])
AC_DEFUN([AX_CFLAGS_GCC_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
[AX_CFLAGS_GCC_OPTION_NEW($@)],[AX_CFLAGS_GCC_OPTION_OLD($@)])])
@@ -0,0 +1,174 @@
dnl @synopsis AX_CFLAGS_IRIX_OPTION (optionflag [,[shellvar][,[A][,[NA]]])
dnl
dnl AX_CFLAGS_IRIX_OPTION(-go_for_it) would show a message as like
dnl "checking CFLAGS for irix/cc -go_for_it ... yes" and adds the
dnl optionflag to CFLAGS if it is understood. You can override the
dnl shellvar-default of CFLAGS of course. The order of arguments stems
dnl from the explicit macros like AX_CFLAGS_WARN_ALL.
dnl
dnl The cousin AX_CXXFLAGS_IRIX_OPTION would check for an option to add
dnl to CXXFLAGS - and it uses the autoconf setup for C++ instead of C
dnl (since it is possible to use different compilers for C and C++).
dnl
dnl The macro is a lot simpler than any special AX_CFLAGS_* macro (or
dnl ac_cxx_rtti.m4 macro) but allows to check for arbitrary options.
dnl However, if you use this macro in a few places, it would be great
dnl if you would make up a new function-macro and submit it to the
dnl ac-archive.
dnl
dnl - $1 option-to-check-for : required ("-option" as non-value)
dnl - $2 shell-variable-to-add-to : CFLAGS (or CXXFLAGS in the other case)
dnl - $3 action-if-found : add value to shellvariable
dnl - $4 action-if-not-found : nothing
dnl
dnl note: in earlier versions, $1-$2 were swapped. We try to detect the
dnl situation and accept a $2=~/-/ as being the old
dnl option-to-check-for.
dnl
dnl see also: AX_CFLAGS_GCC_OPTION for the widely used original
dnl variant.
dnl
dnl @category C
dnl @author Guido Draheim <guidod@gmx.de>
dnl @version 2005-01-21
dnl @license GPLWithACException
AC_DEFUN([AX_CFLAGS_IRIX_OPTION_OLD], [dnl
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_irix_option_$2])dnl
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for irix/cc m4_ifval($2,$2,-option)],
VAR,[VAR="no, unknown"
AC_LANG_SAVE
AC_LANG_C
ac_save_[]FLAGS="$[]FLAGS"
for ac_arg dnl
in "-fullwarn -use_readonly_const % m4_ifval($2,$2,-option)" dnl IRIX C
#
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
AC_TRY_COMPILE([],[return 0;],
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
done
FLAGS="$ac_save_[]FLAGS"
AC_LANG_RESTORE
])
case ".$VAR" in
.ok|.ok,*) m4_ifvaln($3,$3) ;;
.|.no|.no,*) m4_ifvaln($4,$4) ;;
*) m4_ifvaln($3,$3,[
if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
fi ]) ;;
esac
AS_VAR_POPDEF([VAR])dnl
AS_VAR_POPDEF([FLAGS])dnl
])
dnl the only difference - the LANG selection... and the default FLAGS
AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION_OLD], [dnl
AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_irix_option_$2])dnl
AC_CACHE_CHECK([m4_ifval($1,$1,FLAGS) for irix/cc m4_ifval($2,$2,-option)],
VAR,[VAR="no, unknown"
AC_LANG_SAVE
AC_LANG_CXX
ac_save_[]FLAGS="$[]FLAGS"
for ac_arg dnl
in "-fullwarn -use_readonly_const % m4_ifval($2,$2,-option)" dnl IRIX C
#
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
AC_TRY_COMPILE([],[return 0;],
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
done
FLAGS="$ac_save_[]FLAGS"
AC_LANG_RESTORE
])
case ".$VAR" in
.ok|.ok,*) m4_ifvaln($3,$3) ;;
.|.no|.no,*) m4_ifvaln($4,$4) ;;
*) m4_ifvaln($3,$3,[
if echo " $[]m4_ifval($1,$1,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
then AC_RUN_LOG([: m4_ifval($1,$1,FLAGS) does contain $VAR])
else AC_RUN_LOG([: m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"])
m4_ifval($1,$1,FLAGS)="$m4_ifval($1,$1,FLAGS) $VAR"
fi ]) ;;
esac
AS_VAR_POPDEF([VAR])dnl
AS_VAR_POPDEF([FLAGS])dnl
])
dnl --------------------------------------------------------------------------
AC_DEFUN([AX_CFLAGS_IRIX_OPTION_NEW], [dnl
AS_VAR_PUSHDEF([FLAGS],[CFLAGS])dnl
AS_VAR_PUSHDEF([VAR],[ac_cv_cflags_irix_option_$1])dnl
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for irix/cc m4_ifval($1,$1,-option)],
VAR,[VAR="no, unknown"
AC_LANG_SAVE
AC_LANG_C
ac_save_[]FLAGS="$[]FLAGS"
for ac_arg dnl
in "-fullwarn -use_readonly_const % m4_ifval($1,$1,-option)" dnl IRIX C
#
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
AC_TRY_COMPILE([],[return 0;],
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
done
FLAGS="$ac_save_[]FLAGS"
AC_LANG_RESTORE
])
case ".$VAR" in
.ok|.ok,*) m4_ifvaln($3,$3) ;;
.|.no|.no,*) m4_ifvaln($4,$4) ;;
*) m4_ifvaln($3,$3,[
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
fi ]) ;;
esac
AS_VAR_POPDEF([VAR])dnl
AS_VAR_POPDEF([FLAGS])dnl
])
dnl the only difference - the LANG selection... and the default FLAGS
AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION_NEW], [dnl
AS_VAR_PUSHDEF([FLAGS],[CXXFLAGS])dnl
AS_VAR_PUSHDEF([VAR],[ac_cv_cxxflags_irix_option_$1])dnl
AC_CACHE_CHECK([m4_ifval($2,$2,FLAGS) for irix/cc m4_ifval($1,$1,-option)],
VAR,[VAR="no, unknown"
AC_LANG_SAVE
AC_LANG_CXX
ac_save_[]FLAGS="$[]FLAGS"
for ac_arg dnl
in "-fullwarn -use_readonly_const % m4_ifval($1,$1,-option)" dnl IRIX C
#
do FLAGS="$ac_save_[]FLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'`
AC_TRY_COMPILE([],[return 0;],
[VAR=`echo $ac_arg | sed -e 's,.*% *,,'` ; break])
done
FLAGS="$ac_save_[]FLAGS"
AC_LANG_RESTORE
])
case ".$VAR" in
.ok|.ok,*) m4_ifvaln($3,$3) ;;
.|.no|.no,*) m4_ifvaln($4,$4) ;;
*) m4_ifvaln($3,$3,[
if echo " $[]m4_ifval($2,$2,FLAGS) " | grep " $VAR " 2>&1 >/dev/null
then AC_RUN_LOG([: m4_ifval($2,$2,FLAGS) does contain $VAR])
else AC_RUN_LOG([: m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"])
m4_ifval($2,$2,FLAGS)="$m4_ifval($2,$2,FLAGS) $VAR"
fi ]) ;;
esac
AS_VAR_POPDEF([VAR])dnl
AS_VAR_POPDEF([FLAGS])dnl
])
AC_DEFUN([AX_CFLAGS_IRIX_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
[AX_CFLAGS_IRIX_OPTION_NEW($@)],[AX_CFLAGS_IRIX_OPTION_OLD($@)])])
AC_DEFUN([AX_CXXFLAGS_IRIX_OPTION],[ifelse(m4_bregexp([$2],[-]),-1,
[AX_CXXFLAGS_IRIX_OPTION_NEW($@)],[AX_CXXFLAGS_IRIX_OPTION_OLD($@)])])
+154
View File
@@ -0,0 +1,154 @@
dnl SMB Build Environment CC Checks
dnl -------------------------------------------------------
dnl Copyright (C) Stefan (metze) Metzmacher 2004
dnl Released under the GNU GPL
dnl -------------------------------------------------------
dnl
AC_LIBREPLACE_CC_CHECKS
#
# Set the debug symbol option if we have
# --enable-*developer or --enable-debug
# and the compiler supports it
#
if test x$ac_cv_prog_cc_g = xyes -a x$debug = xyes; then
CFLAGS="${CFLAGS} -g"
fi
############################################
# check if the compiler handles c99 struct initialization
LIBREPLACE_C99_STRUCT_INIT(samba_cv_c99_struct_initialization=yes,
samba_cv_c99_struct_initialization=no)
if test x"$samba_cv_c99_struct_initialization" != x"yes"; then
AC_MSG_WARN([C compiler does not support c99 struct initialization!])
AC_MSG_ERROR([Please Install gcc from http://gcc.gnu.org/])
fi
############################################
# check if the compiler can handle negative enum values
# and don't truncate the values to INT_MAX
# a runtime test is needed here
AC_CACHE_CHECK([that the C compiler understands negative enum values],SMB_BUILD_CC_NEGATIVE_ENUM_VALUES, [
AC_TRY_RUN(
[
#include <stdio.h>
enum negative_values { NEGATIVE_VALUE = 0xFFFFFFFF };
int main(void) {
enum negative_values v1 = NEGATIVE_VALUE;
unsigned v2 = 0xFFFFFFFF;
if (v1 != v2) {
printf("v1=0x%08x v2=0x%08x\n", v1, v2);
return 1;
}
return 0;
}
],
SMB_BUILD_CC_NEGATIVE_ENUM_VALUES=yes,SMB_BUILD_CC_NEGATIVE_ENUM_VALUES=no)])
if test x"$SMB_BUILD_CC_NEGATIVE_ENUM_VALUES" != x"yes"; then
AC_MSG_WARN([using --unit-enums for pidl])
PIDL_ARGS="$PIDL_ARGS --uint-enums"
fi
AC_MSG_CHECKING([for test routines])
AC_TRY_RUN([#include "${srcdir-.}/build/tests/trivial.c"],
AC_MSG_RESULT(yes),
AC_MSG_ERROR([cant find test code. Aborting config]),
AC_MSG_WARN([cannot run when cross-compiling]))
#
# Check if the compiler support ELF visibility for symbols
#
visibility_attribute=no
VISIBILITY_CFLAGS=""
if test x"$GCC" = x"yes" ; then
AX_CFLAGS_GCC_OPTION([-fvisibility=hidden], VISIBILITY_CFLAGS)
fi
if test -n "$VISIBILITY_CFLAGS"; then
AC_MSG_CHECKING([whether the C compiler supports the visibility attribute])
OLD_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $VISIBILITY_CFLAGS"
AC_TRY_RUN([
void vis_foo1(void) {}
__attribute__((visibility("default"))) void vis_foo2(void) {}
#include "${srcdir-.}/build/tests/trivial.c"
],[
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_VISIBILITY_ATTR,1,[Whether the C compiler supports the visibility attribute])
visibility_attribute=yes
],[
AC_MSG_RESULT(no)
])
CFLAGS="$OLD_CFLAGS"
fi
AC_SUBST(visibility_attribute)
#
# Check if the compiler can handle the options we selected by
# --enable-*developer
#
DEVELOPER_CFLAGS=""
if test x$developer = xyes; then
OLD_CFLAGS="${CFLAGS}"
CFLAGS="${CFLAGS} -D_SAMBA_DEVELOPER_DONNOT_USE_O2_"
DEVELOPER_CFLAGS="-DDEBUG_PASSWORD -DDEVELOPER"
if test x"$GCC" = x"yes" ; then
#
# warnings we want...
#
AX_CFLAGS_GCC_OPTION(-Wall, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wshadow, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Werror-implicit-function-declaration, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wstrict-prototypes, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wpointer-arith, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wcast-qual, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wcast-align, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wwrite-strings, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wmissing-format-attribute, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wformat=2, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wdeclaration-after-statement, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wunused-macros, DEVELOPER_CFLAGS)
# AX_CFLAGS_GCC_OPTION(-Wextra, DEVELOPER_CFLAGS)
# AX_CFLAGS_GCC_OPTION(-Wc++-compat, DEVELOPER_CFLAGS)
# AX_CFLAGS_GCC_OPTION(-Wmissing-prototypes, DEVELOPER_CFLAGS)
# AX_CFLAGS_GCC_OPTION(-Wmissing-declarations, DEVELOPER_CFLAGS)
# AX_CFLAGS_GCC_OPTION(-Wmissing-field-initializers, DEVELOPER_CFLAGS)
#
# warnings we don't want...
#
AX_CFLAGS_GCC_OPTION(-Wno-format-y2k, DEVELOPER_CFLAGS)
AX_CFLAGS_GCC_OPTION(-Wno-unused-parameter, DEVELOPER_CFLAGS)
else
AX_CFLAGS_IRIX_OPTION(-fullwarn, DEVELOPER_CFLAGS)
fi
CFLAGS="${OLD_CFLAGS}"
fi
if test -n "$DEVELOPER_CFLAGS"; then
OLD_CFLAGS="${CFLAGS}"
CFLAGS="${CFLAGS} ${DEVELOPER_CFLAGS}"
AC_MSG_CHECKING([that the C compiler can use the DEVELOPER_CFLAGS])
AC_TRY_COMPILE([],[],
AC_MSG_RESULT(yes),
DEVELOPER_CFLAGS=""; AC_MSG_RESULT(no))
CFLAGS="${OLD_CFLAGS}"
fi
# allow for --with-hostcc=gcc
AC_ARG_WITH(hostcc,[ --with-hostcc=compiler choose host compiler],
[HOSTCC=$withval],
[
if test z"$cross_compiling" = "yes"; then
HOSTCC=cc
else
HOSTCC=$CC
fi
])
AC_SUBST(HOSTCC)
AC_PATH_PROG(GCOV,gcov)
+1
View File
@@ -0,0 +1 @@
AC_PATH_PROG(XSLTPROC, xsltproc)
+208
View File
@@ -0,0 +1,208 @@
dnl SMB Build Environment LD Checks
dnl -------------------------------------------------------
dnl Copyright (C) Stefan (metze) Metzmacher 2004
dnl Copyright (C) Jelmer Vernooij 2006
dnl Released under the GNU GPL
dnl -------------------------------------------------------
dnl
AC_PATH_PROG(PROG_LD,ld)
LD=${PROG_LD}
AC_PROG_LD_GNU
LD=""
AC_PATH_PROG(PROG_AR, ar)
AC_SUBST(STLD)
AC_SUBST(STLD_FLAGS)
AC_SUBST(BLDSHARED)
AC_SUBST(LD)
AC_SUBST(LDFLAGS)
AC_SUBST(SHLD)
AC_SUBST(SHLD_FLAGS)
AC_SUBST(SHLD_UNDEF_FLAGS)
AC_SUBST(SHLIBEXT)
AC_SUBST(SONAMEFLAG)
AC_SUBST(PICFLAG)
# Assume non-shared by default and override below
# these are the defaults, good for lots of systems
STLD=${PROG_AR}
STLD_FLAGS="-rcs"
BLDSHARED="false"
LD="${CC}"
LDFLAGS=""
SHLD="${CC}"
SHLD_FLAGS="-shared"
SHLIBEXT="so"
SONAMEFLAG=""
PICFLAG=""
AC_MSG_CHECKING([ability to build shared libraries])
# and these are for particular systems
case "$host_os" in
*linux*)
BLDSHARED="true"
SHLD_FLAGS="-shared -Wl,-Bsymbolic"
SHLD_UNDEF_FLAGS="-Wl,--allow-shlib-undefined"
LDFLAGS="-Wl,--export-dynamic"
PICFLAG="-fPIC"
SONAMEFLAG="-Wl,-soname="
;;
*solaris*)
BLDSHARED="true"
SHLD_FLAGS="-G"
SONAMEFLAG="-h "
if test "${GCC}" = "yes"; then
PICFLAG="-fPIC"
SONAMEFLAG="-Wl,-soname="
if test "${ac_cv_prog_gnu_ld}" = "yes"; then
LDFLAGS="-Wl,-E"
fi
else
PICFLAG="-KPIC"
## ${CFLAGS} added for building 64-bit shared
## libs using Sun's Compiler
SHLD_FLAGS="-G \${CFLAGS}"
fi
;;
*sunos*)
BLDSHARED="true"
SHLD_FLAGS="-G"
SONAMEFLAG="-Wl,-h,"
PICFLAG="-KPIC" # Is this correct for SunOS
;;
*netbsd* | *freebsd* | *dragonfly* )
BLDSHARED="true"
LDFLAGS="-Wl,--export-dynamic"
SONAMEFLAG="-Wl,-soname,"
PICFLAG="-fPIC -DPIC"
;;
*openbsd*)
BLDSHARED="true"
LDFLAGS="-Wl,-Bdynamic"
SONAMEFLAG="-Wl,-soname,"
PICFLAG="-fPIC"
;;
*irix*)
BLDSHARED="true"
SHLD_FLAGS="-set_version sgi1.0 -shared"
SONAMEFLAG="-soname "
SHLD="${PROG_LD}"
if test "${GCC}" = "yes"; then
PICFLAG="-fPIC"
else
PICFLAG="-KPIC"
fi
;;
*aix*)
BLDSHARED="true"
SHLD_FLAGS="-Wl,-G,-bexpall,-bbigtoc"
LDFLAGS="-Wl,-brtl,-bexpall,-bbigtoc"
# as AIX code is always position independent...
PICFLAG="-O2"
;;
*hpux*)
# Use special PIC flags for the native HP-UX compiler.
if test $ac_cv_prog_cc_Ae = yes; then
BLDSHARED="true"
SHLD_FLAGS="-b -Wl,-B,symbolic,-b,-z"
SONAMEFLAG="-Wl,+h "
PICFLAG="+z"
elif test "${GCC}" = "yes"; then
BLDSHARED="true" # I hope this is correct
PICFLAG="-fPIC"
fi
if test "$host_cpu" = "ia64"; then
SHLIBEXT="so"
LDFLAGS="-Wl,-E,+b/usr/local/lib/hpux32:/usr/lib/hpux32"
else
SHLIBEXT="sl"
LDFLAGS="-Wl,-E,+b/usr/local/lib:/usr/lib"
fi
;;
*osf*)
BLDSHARED="true"
SONAMEFLAG="-Wl,-soname,"
PICFLAG="-fPIC"
;;
*unixware*)
BLDSHARED="true"
SONAMEFLAG="-Wl,-soname,"
PICFLAG="-KPIC"
;;
*darwin*)
BLDSHARED="true"
SHLD_FLAGS="-bundle -flat_namespace -undefined suppress"
SHLIBEXT="dylib"
;;
esac
AC_MSG_RESULT($BLDSHARED)
AC_MSG_CHECKING([LD])
AC_MSG_RESULT([$LD])
AC_MSG_CHECKING([LDFLAGS])
AC_MSG_RESULT([$LDFLAGS])
AC_MSG_CHECKING([STLD])
AC_MSG_RESULT([$STLD])
AC_MSG_CHECKING([STLD_FLAGS])
AC_MSG_RESULT([$STLD_FLAGS])
#######################################################
# test whether building a shared library actually works
if test $BLDSHARED = true; then
AC_MSG_CHECKING([SHLD])
AC_MSG_RESULT([$SHLD])
AC_MSG_CHECKING([SHLD_FLAGS])
AC_MSG_RESULT([$SHLD_FLAGS])
AC_DEFINE_UNQUOTED(SHLIBEXT, "$SHLIBEXT", [Shared library extension])
AC_MSG_CHECKING([SHLIBEXT])
AC_MSG_RESULT([$SHLIBEXT])
AC_MSG_CHECKING([SONAMEFLAG])
AC_MSG_RESULT([$SONAMEFLAG])
AC_MSG_CHECKING([PICFLAG])
AC_MSG_RESULT([$PICFLAG])
AC_CACHE_CHECK([whether building shared libraries actually works],
[ac_cv_shlib_works],[
ac_cv_shlib_works=no
# try building a trivial shared library
${CC} ${CFLAGS} ${PICFLAG} -c ${srcdir-.}/build/tests/shlib.c -o shlib.o &&
${SHLD} `eval echo ${SHLD_FLAGS} ` -o shlib.${SHLIBEXT} shlib.o &&
ac_cv_shlib_works=yes
rm -f shlib.${SHLIBEXT} shlib.o
])
if test $ac_cv_shlib_works = no; then
BLDSHARED=false
fi
fi
AC_MSG_CHECKING([if we can link using the selected flags])
AC_TRY_RUN([#include "${srcdir-.}/build/tests/trivial.c"],
AC_MSG_RESULT(yes),
AC_MSG_ERROR([we cannot link with the selected cc and ld flags. Aborting configure]),
AC_MSG_WARN([cannot run when cross-compiling]))
USESHARED=false
AC_SUBST(USESHARED)
AC_ARG_ENABLE(dso,
[ --enable-dso Enable using shared libraries internally (experimental)],
[],[enable_dso=no])
if test x"$enable_dso" = x"yes" -a x"$BLDSHARED" != x"true"; then
AC_MSG_ERROR([--enable-dso: no support for shared libraries])
fi
if test x"$enable_dso" != x"no"; then
USESHARED=$BLDSHARED
fi
AC_MSG_CHECKING([if binaries will use shared libraries])
AC_MSG_RESULT([$USESHARED])
+48
View File
@@ -0,0 +1,48 @@
dnl SMB Build Environment make Checks
dnl -------------------------------------------------------
dnl Copyright (C) Stefan (metze) Metzmacher 2004
dnl Copyright (C) Jelmer Vernooij 2005
dnl Released under the GNU GPL
dnl -------------------------------------------------------
dnl
AC_PATH_PROG(MAKE,make)
AC_CACHE_CHECK([whether we have GNU make], samba_cv_gnu_make, [
if $ac_cv_path_MAKE --version | head -1 | grep GNU 2>/dev/null >/dev/null
then
samba_cv_gnu_make=yes
else
samba_cv_gnu_make=no
fi
])
GNU_MAKE=$samba_cv_gnu_make
AC_SUBST(GNU_MAKE)
if test "x$GNU_MAKE" = x"yes"; then
AC_CACHE_CHECK([GNU make version], samba_cv_gnu_make_version,[
samba_cv_gnu_make_version=`$ac_cv_path_MAKE --version | head -1 | cut -d " " -f 3 2>/dev/null`
])
GNU_MAKE_VERSION=$samba_cv_gnu_make_version
AC_SUBST(GNU_MAKE_VERSION)
fi
new_make=no
AC_MSG_CHECKING([for GNU make >= 3.81])
if test x$GNU_MAKE = x"yes"; then
if $PERL -e " \$_ = '$GNU_MAKE_VERSION'; s/@<:@^\d\.@:>@.*//g; exit (\$_ < 3.81);"; then
new_make=yes
fi
fi
AC_MSG_RESULT($new_make)
automatic_dependencies=no
AX_CFLAGS_GCC_OPTION([-M -MT conftest.d -MF conftest.o], [], [ automatic_dependencies=$new_make ], [])
AC_MSG_CHECKING([Whether to use automatic dependencies])
AC_ARG_ENABLE(automatic-dependencies,
[ --enable-automatic-dependencies Enable automatic dependencies],
[ automatic_dependencies=$enableval ],
[ automatic_dependencies=no ])
AC_MSG_RESULT($automatic_dependencies)
AC_SUBST(automatic_dependencies)
+169
View File
@@ -0,0 +1,169 @@
dnl SMB Build Environment Path Checks
dnl -------------------------------------------------------
dnl Copyright (C) Stefan (metze) Metzmacher 2004
dnl Released under the GNU GPL
dnl -------------------------------------------------------
dnl
AC_LIBREPLACE_LOCATION_CHECKS
#################################################
# Directory handling stuff to support both the
# legacy SAMBA directories and FHS compliant
# ones...
AC_PREFIX_DEFAULT(/usr/local/samba)
# Defaults and --without-fhs
logfilebase="${localstatedir}"
lockdir="${localstatedir}/locks"
piddir="${localstatedir}/run"
privatedir="\${prefix}/private"
modulesdir="\${prefix}/modules"
winbindd_socket_dir="${localstatedir}/run/winbind_pipe"
AC_ARG_WITH(fhs,
[ --with-fhs Use FHS-compliant paths (default=no)],
lockdir="${localstatedir}/lib/samba"
piddir="${localstatedir}/run/samba"
logfilebase="${localstatedir}/log/samba"
privatedir="${localstatedir}/lib/samba/private"
sysconfdir="${sysconfdir}/samba"
modulesdir="${libdir}/samba"
datadir="${datadir}/samba"
includedir="${includedir}/samba-4.0"
winbindd_socket_dir="${localstatedir}/run/samba/winbind_pipe"
)
#################################################
# set private directory location
AC_ARG_WITH(privatedir,
[ --with-privatedir=DIR Where to put sam.ldb and other private files containing key material ($ac_default_prefix/private)],
[ case "$withval" in
yes|no)
#
# Just in case anybody calls it without argument
#
AC_MSG_WARN([--with-privatedir called without argument - will use default])
;;
* )
privatedir="$withval"
;;
esac])
#################################################
# set where the winbindd socket should be put
AC_ARG_WITH(winbindd-socket-dir,
[ --with-winbindd-socket-dir=DIR Where to put the winbindd socket ($ac_default_prefix/run/winbind_pipe)],
[ case "$withval" in
yes|no)
#
# Just in case anybody calls it without argument
#
AC_MSG_WARN([--with-winbind-socketdir called without argument - will use default])
;;
* )
winbindd_socket_dir="$withval"
;;
esac])
#################################################
# set lock directory location
AC_ARG_WITH(lockdir,
[ --with-lockdir=DIR Where to put lock files ($ac_default_prefix/var/locks)],
[ case "$withval" in
yes|no)
#
# Just in case anybody calls it without argument
#
AC_MSG_WARN([--with-lockdir called without argument - will use default])
;;
* )
lockdir="$withval"
;;
esac])
#################################################
# set pid directory location
AC_ARG_WITH(piddir,
[ --with-piddir=DIR Where to put pid files ($ac_default_prefix/var/locks)],
[ case "$withval" in
yes|no)
#
# Just in case anybody calls it without argument
#
AC_MSG_WARN([--with-piddir called without argument - will use default])
;;
* )
piddir="$withval"
;;
esac])
#################################################
# set log directory location
AC_ARG_WITH(logfilebase,
[ --with-logfilebase=DIR Where to put log files (\$(VARDIR))],
[ case "$withval" in
yes|no)
#
# Just in case anybody does it
#
AC_MSG_WARN([--with-logfilebase called without argument - will use default])
;;
* )
logfilebase="$withval"
;;
esac])
AC_SUBST(lockdir)
AC_SUBST(piddir)
AC_SUBST(logfilebase)
AC_SUBST(privatedir)
AC_SUBST(bindir)
AC_SUBST(sbindir)
AC_SUBST(winbindd_socket_dir)
AC_SUBST(modulesdir)
#################################################
# set prefix for 'make test'
# this is needed to workarround the 108 char
# unix socket path limitation!
#
selftest_prefix="./st"
AC_SUBST(selftest_prefix)
AC_ARG_WITH(selftest-prefix,
[ --with-selftest-prefix=DIR The prefix where make test will be runned ($selftest_prefix)],
[ case "$withval" in
yes|no)
AC_MSG_WARN([--with-selftest-prefix called without argument - will use default])
;;
* )
selftest_prefix="$withval"
;;
esac])
debug=no
AC_ARG_ENABLE(debug,
[ --enable-debug Turn on compiler debugging information (default=no)],
[if test x$enable_debug = xyes; then
debug=yes
fi])
developer=no
AC_SUBST(developer)
AC_ARG_ENABLE(developer,
[ --enable-developer Turn on developer warnings and debugging (default=no)],
[if test x$enable_developer = xyes; then
debug=yes
developer=yes
fi])
dnl disable these external libs
AC_ARG_WITH(disable-ext-lib,
[ --with-disable-ext-lib=LIB Comma-seperated list of external libraries],
[ if test $withval; then
for i in `echo $withval | sed -e's/,/ /g'`
do
eval SMB_$i=NO
done
fi ])
+31
View File
@@ -0,0 +1,31 @@
dnl SMB Build Environment Perl Checks
dnl -------------------------------------------------------
dnl Copyright (C) Stefan (metze) Metzmacher 2004
dnl Released under the GNU GPL
dnl -------------------------------------------------------
dnl
case "$host_os" in
*irix*)
# On IRIX, we prefer Freeware or Nekoware Perl, because the
# system perl is so ancient.
AC_PATH_PROG(PERL, perl, "", "/usr/freeware/bin:/usr/nekoware/bin:$PATH")
;;
*)
AC_PATH_PROG(PERL, perl)
;;
esac
if test x"$PERL" = x""; then
AC_MSG_WARN([No version of perl was found!])
AC_MSG_ERROR([Please install perl from http://www.perl.com/])
fi
if test x"$debug" = x"yes";then
PERL="$PERL -W"
fi
export PERL
AC_PATH_PROG(YAPP, yapp, false)
PIDL_ARGS=""
AC_SUBST(PIDL_ARGS)
+32
View File
@@ -0,0 +1,32 @@
dnl SMB Build Environment Checks
dnl -------------------------------------------------------
dnl Copyright (C) Stefan (metze) Metzmacher 2004
dnl Released under the GNU GPL
dnl -------------------------------------------------------
dnl
AC_SUBST(srcdir)
export srcdir;
# we always set builddir to "." as that's nicer than
# having the absolute path of the current work directory
builddir=.
AC_SUBST(builddir)
export builddir;
AC_SUBST(datarootdir)
SMB_VERSION_STRING=`cat ${srcdir}/version.h | grep 'SAMBA_VERSION_OFFICIAL_STRING' | cut -d '"' -f2`
echo "SAMBA VERSION: ${SMB_VERSION_STRING}"
SAMBA_VERSION_SVN_REVISION=`cat ${srcdir}/version.h | grep 'SAMBA_VERSION_SVN_REVISION' | cut -d ' ' -f3-`
if test -n "${SAMBA_VERSION_SVN_REVISION}";then
echo "BUILD REVISION: ${SAMBA_VERSION_SVN_REVISION}"
fi
m4_include(build/m4/check_path.m4)
m4_include(build/m4/check_perl.m4)
m4_include(build/m4/check_cc.m4)
m4_include(build/m4/check_ld.m4)
m4_include(build/m4/check_make.m4)
m4_include(build/m4/check_doc.m4)
+119
View File
@@ -0,0 +1,119 @@
dnl SMB Build System
dnl ----------------
dnl Copyright (C) 2004 Stefan Metzmacher
dnl Copyright (C) 2004-2005 Jelmer Vernooij
dnl Published under the GPL
dnl
dnl SMB_SUBSYSTEM(name,obj_files,required_subsystems)
dnl
dnl SMB_EXT_LIB_FROM_PKGCONFIG(name,pkg-config name)
dnl
dnl SMB_EXT_LIB(name,libs,cflags,cppflags,ldflags)
dnl
dnl SMB_ENABLE(name,default_build)
dnl
dnl #######################################################
dnl ### And now the implementation ###
dnl #######################################################
dnl SMB_SUBSYSTEM(name,obj_files,required_subsystems,cflags)
AC_DEFUN([SMB_SUBSYSTEM],
[
SMB_INFO_SUBSYSTEMS="$SMB_INFO_SUBSYSTEMS
###################################
# Start Subsystem $1
@<:@SUBSYSTEM::$1@:>@
OBJ_FILES = $2
PRIVATE_DEPENDENCIES = $3
CFLAGS = $4
ENABLE = YES
# End Subsystem $1
###################################
"
])
dnl SMB_EXT_LIB_FROM_PKGCONFIG(name,pkg-config name)
AC_DEFUN([SMB_EXT_LIB_FROM_PKGCONFIG],
[
dnl Figure out the correct variables and call SMB_EXT_LIB()
if test -z "$PKG_CONFIG"; then
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
fi
if test "$PKG_CONFIG" = "no" ; then
echo "*** The pkg-config script could not be found. Make sure it is"
echo "*** in your path, or set the PKG_CONFIG environment variable"
echo "*** to the full path to pkg-config."
echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config."
SMB_EXT_LIB($1)
SMB_ENABLE($1, NO)
else
if $PKG_CONFIG --atleast-pkgconfig-version 0.9.0; then
AC_MSG_CHECKING(for $2)
if test "$SMB_$1"x = "NO"x ; then
SMB_ENABLE($1, NO)
AC_MSG_RESULT(disabled)
elif $PKG_CONFIG --exists '$2' ; then
AC_MSG_RESULT(yes)
$1_CFLAGS="`$PKG_CONFIG --cflags '$2'`"
OLD_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $$1_CFLAGS"
AC_MSG_CHECKING([that the C compiler can use the $1_CFLAGS])
AC_TRY_RUN([#include "${srcdir-.}/build/tests/trivial.c"],
SMB_ENABLE($1, YES)
AC_MSG_RESULT(yes),
AC_MSG_RESULT(no),
AC_MSG_WARN([cannot run when cross-compiling]))
CFLAGS="$OLD_CFLAGS"
SMB_EXT_LIB($1,
[`$PKG_CONFIG --libs-only-l '$2'`],
[`$PKG_CONFIG --cflags-only-other '$2'`],
[`$PKG_CONFIG --cflags-only-I '$2'`],
[`$PKG_CONFIG --libs-only-other '$2'` `$PKG_CONFIG --libs-only-L '$2'`])
else
SMB_EXT_LIB($1)
SMB_ENABLE($1, NO)
AC_MSG_RESULT(no)
$PKG_CONFIG --errors-to-stdout --print-errors '$2'
fi
else
echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer."
echo "*** See http://www.freedesktop.org/software/pkgconfig"
SMB_EXT_LIB($1)
SMB_ENABLE($1, NO)
fi
fi
])
dnl SMB_EXT_LIB(name,libs,cflags,cppflags,ldflags)
AC_DEFUN([SMB_EXT_LIB],
[
SMB_INFO_EXT_LIBS="$SMB_INFO_EXT_LIBS
###################################
# Start Ext Lib $1
@<:@EXT_LIB::$1@:>@
LIBS = $2
CFLAGS = $3
CPPFLAGS = $4
LDFLAGS = $5
# End Ext Lib $1
###################################
"
])
dnl SMB_ENABLE(name,default_build)
AC_DEFUN([SMB_ENABLE],
[
[SMB_ENABLE_][$1]="$2";
SMB_INFO_ENABLES="$SMB_INFO_ENABLES
\$enabled{$1} = \"$2\";"
])
+5
View File
@@ -0,0 +1,5 @@
asn1.pm: asn1.yp
yapp -s asn1.yp
clean:
rm -f asn1.pm
+306
View File
@@ -0,0 +1,306 @@
########################
# ASN.1 Parse::Yapp parser
# Copyright (C) Stefan (metze) Metzmacher <metze@samba.org>
# released under the GNU GPL version 2 or later
# the precedence actually doesn't matter at all for this grammer, but
# by providing a precedence we reduce the number of conflicts
# enormously
%left '-' '+' '&' '|' '*' '>' '.' '/' '(' ')' '[' ']' ':' ',' ';'
################
# grammer
%%
asn1:
identifier asn1_definitions asn1_delimitter asn1_begin asn1_decls asn1_end
{{
"OBJECT" => "ASN1_DEFINITION",
"IDENTIFIER" => $_[1],
"DATA" => $_[5]
}}
;
asn1_delimitter:
delimitter
;
asn1_definitions:
'DEFINITIONS'
;
asn1_begin:
'BEGIN'
;
asn1_end:
'END'
;
asn1_decls:
asn1_def
{ [ $_[1] ] }
| asn1_decls asn1_def
{ push(@{$_[1]}, $_[2]); $_[1] }
;
asn1_def:
asn1_target asn1_delimitter asn1_application asn1_type
{{
"OBJECT" => "ASN1_DEF",
"IDENTIFIER" => $_[1],
"APPLICATION" => $_[3],
"STRUCTURE" => $_[4]
}}
;
asn1_target:
identifier
;
asn1_application:
#empty
| '[' 'APPLICATION' constant ']'
{ $_[3] }
;
asn1_type:
asn1_boolean
| asn1_integer
| asn1_bit_string
| asn1_octet_string
| asn1_null
| asn1_object_identifier
| asn1_real
| asn1_enumerated
| asn1_sequence
| identifier
;
asn1_boolean:
'BOOLEAN'
{{
"TYPE" => "BOOLEAN",
"TAG" => 1
}}
;
asn1_integer:
'INTEGER'
{{
"TYPE" => "INTEGER",
"TAG" => 2
}}
| 'INTEGER' '(' constant '.' '.' constant ')'
{{
"TYPE" => "INTEGER",
"TAG" => 2,
"RANGE_LOW" => $_[3],
"RENAGE_HIGH" => $_[6]
}}
;
asn1_bit_string:
'BIT' 'STRING'
{{
"TYPE" => "BIT STRING",
"TAG" => 3
}}
;
asn1_octet_string:
'OCTET' 'STRING'
{{
"TYPE" => "OCTET STRING",
"TAG" => 4
}}
;
asn1_null:
'NULL'
{{
"TYPE" => "NULL",
"TAG" => 5
}}
;
asn1_object_identifier:
'OBJECT' 'IDENTIFIER'
{{
"TYPE" => "OBJECT IDENTIFIER",
"TAG" => 6
}}
;
asn1_real:
'REAL'
{{
"TYPE" => "REAL",
"TAG" => 9
}}
;
asn1_enumerated:
'ENUMERATED'
{{
"TYPE" => "ENUMERATED",
"TAG" => 10
}}
;
asn1_sequence:
'SEQUENCE' '{' asn1_var_dec_list '}'
{{
"TYPE" => "SEQUENCE",
"TAG" => 16,
"STRUCTURE" => $_[3]
}}
;
asn1_var_dec_list:
asn1_var_dec
{ [ $_[1] ] }
| asn1_var_dec_list ',' asn1_var_dec
{ push(@{$_[1]}, $_[3]); $_[1] }
;
asn1_var_dec:
identifier asn1_type
{{
"NAME" => $_[1],
"TYPE" => $_[2]
}}
;
anytext: #empty { "" }
| identifier | constant | text
| anytext '-' anytext { "$_[1]$_[2]$_[3]" }
| anytext '.' anytext { "$_[1]$_[2]$_[3]" }
| anytext '*' anytext { "$_[1]$_[2]$_[3]" }
| anytext '>' anytext { "$_[1]$_[2]$_[3]" }
| anytext '|' anytext { "$_[1]$_[2]$_[3]" }
| anytext '&' anytext { "$_[1]$_[2]$_[3]" }
| anytext '/' anytext { "$_[1]$_[2]$_[3]" }
| anytext '+' anytext { "$_[1]$_[2]$_[3]" }
| anytext '(' anytext ')' anytext { "$_[1]$_[2]$_[3]$_[4]$_[5]" }
;
delimitter: DELIMITTER
;
identifier: IDENTIFIER
;
constant: CONSTANT
;
text: TEXT { "\"$_[1]\"" }
;
#####################################
# start code
%%
use util;
sub _ASN1_Error {
if (exists $_[0]->YYData->{ERRMSG}) {
print $_[0]->YYData->{ERRMSG};
delete $_[0]->YYData->{ERRMSG};
return;
};
my $line = $_[0]->YYData->{LINE};
my $last_token = $_[0]->YYData->{LAST_TOKEN};
my $file = $_[0]->YYData->{INPUT_FILENAME};
print "$file:$line: Syntax error near '$last_token'\n";
}
sub _ASN1_Lexer($)
{
my($parser)=shift;
$parser->YYData->{INPUT}
or return('',undef);
again:
$parser->YYData->{INPUT} =~ s/^[ \t]*//;
for ($parser->YYData->{INPUT}) {
if (/^\#/) {
if (s/^\# (\d+) \"(.*?)\"( \d+|)//) {
$parser->YYData->{LINE} = $1-1;
$parser->YYData->{INPUT_FILENAME} = $2;
goto again;
}
if (s/^\#line (\d+) \"(.*?)\"( \d+|)//) {
$parser->YYData->{LINE} = $1-1;
$parser->YYData->{INPUT_FILENAME} = $2;
goto again;
}
if (s/^(\#.*)$//m) {
goto again;
}
}
if (s/^(\n)//) {
$parser->YYData->{LINE}++;
goto again;
}
if (s/^(--.*\n)//) {
$parser->YYData->{LINE}++;
goto again;
}
if (s/^(::=)//) {
$parser->YYData->{LAST_TOKEN} = $1;
return('DELIMITTER',$1);
}
if (s/^\"(.*?)\"//) {
$parser->YYData->{LAST_TOKEN} = $1;
return('TEXT',$1);
}
if (s/^(\d+)(\W|$)/$2/) {
$parser->YYData->{LAST_TOKEN} = $1;
return('CONSTANT',$1);
}
if (s/^([\w_-]+)//) {
$parser->YYData->{LAST_TOKEN} = $1;
if ($1 =~
/^(SEQUENCE|INTEGER|OCTET|STRING|
APPLICATION|OPTIONAL|NULL|COMPONENTS|OF|
BOOLEAN|ENUMERATED|CHOISE|REAL|BIT|OBJECT|IDENTIFIER|
DEFAULT|FALSE|TRUE|SET|DEFINITIONS|BEGIN|END)$/x) {
return $1;
}
return('IDENTIFIER',$1);
}
if (s/^(.)//s) {
$parser->YYData->{LAST_TOKEN} = $1;
return($1,$1);
}
}
}
sub parse_asn1($$)
{
my $self = shift;
my $filename = shift;
my $saved_delim = $/;
undef $/;
my $cpp = $ENV{CPP};
if (! defined $cpp) {
$cpp = "cpp"
}
my $data = `$cpp -xc $filename`;
$/ = $saved_delim;
$self->YYData->{INPUT} = $data;
$self->YYData->{LINE} = 0;
$self->YYData->{LAST_TOKEN} = "NONE";
return $self->YYParse( yylex => \&_ASN1_Lexer, yyerror => \&_ASN1_Error );
}
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/perl -w
###################################################
# package to parse ASN.1 files and generate code for
# LDAP functions in Samba
# Copyright tridge@samba.org 2002-2003
# Copyright metze@samba.org 2004
# released under the GNU GPL
use strict;
use FindBin qw($RealBin);
use lib "$RealBin";
use lib "$RealBin/lib";
use Getopt::Long;
use File::Basename;
use asn1;
use util;
my($opt_help) = 0;
my($opt_output);
my $asn1_parser = new asn1;
#####################################################################
# parse an ASN.1 file returning a structure containing all the data
sub ASN1Parse($)
{
my $filename = shift;
my $asn1 = $asn1_parser->parse_asn1($filename);
util::CleanData($asn1);
return $asn1;
}
#########################################
# display help text
sub ShowHelp()
{
print "
perl ASN.1 parser and code generator
Copyright (C) tridge\@samba.org
Copyright (C) metze\@samba.org
Usage: pasn1.pl [options] <asn1file>
Options:
--help this help page
--output OUTNAME put output in OUTNAME
\n";
exit(0);
}
# main program
GetOptions (
'help|h|?' => \$opt_help,
'output|o=s' => \$opt_output,
);
if ($opt_help) {
ShowHelp();
exit(0);
}
sub process_file($)
{
my $input_file = shift;
my $output_file;
my $pasn1;
my $basename = basename($input_file, ".asn1");
if (!defined($opt_output)) {
$output_file = util::ChangeExtension($input_file, ".pasn1");
} else {
$output_file = $opt_output;
}
# if (file is .pasn1) {
# $pasn1 = util::LoadStructure($pasn1_file);
# defined $pasn1 || die "Failed to load $pasn1_file - maybe you need --parse\n";
# } else {
$pasn1 = ASN1Parse($input_file);
defined $pasn1 || die "Failed to parse $input_file";
util::SaveStructure($output_file, $pasn1) ||
die "Failed to save $output_file\n";
#}
}
foreach my $filename (@ARGV) {
process_file($filename);
}
+379
View File
@@ -0,0 +1,379 @@
###################################################
# utility functions to support pidl
# Copyright tridge@samba.org 2000
# released under the GNU GPL
package util;
#####################################################################
# load a data structure from a file (as saved with SaveStructure)
sub LoadStructure($)
{
my $f = shift;
my $contents = FileLoad($f);
defined $contents || return undef;
return eval "$contents";
}
use strict;
#####################################################################
# flatten an array of arrays into a single array
sub FlattenArray2($)
{
my $a = shift;
my @b;
for my $d (@{$a}) {
for my $d1 (@{$d}) {
push(@b, $d1);
}
}
return \@b;
}
#####################################################################
# flatten an array of arrays into a single array
sub FlattenArray($)
{
my $a = shift;
my @b;
for my $d (@{$a}) {
for my $d1 (@{$d}) {
push(@b, $d1);
}
}
return \@b;
}
#####################################################################
# flatten an array of hashes into a single hash
sub FlattenHash($)
{
my $a = shift;
my %b;
for my $d (@{$a}) {
for my $k (keys %{$d}) {
$b{$k} = $d->{$k};
}
}
return \%b;
}
#####################################################################
# traverse a perl data structure removing any empty arrays or
# hashes and any hash elements that map to undef
sub CleanData($)
{
sub CleanData($);
my($v) = shift;
if (ref($v) eq "ARRAY") {
foreach my $i (0 .. $#{$v}) {
CleanData($v->[$i]);
if (ref($v->[$i]) eq "ARRAY" && $#{$v->[$i]}==-1) {
$v->[$i] = undef;
next;
}
}
# this removes any undefined elements from the array
@{$v} = grep { defined $_ } @{$v};
} elsif (ref($v) eq "HASH") {
foreach my $x (keys %{$v}) {
CleanData($v->{$x});
if (!defined $v->{$x}) { delete($v->{$x}); next; }
if (ref($v->{$x}) eq "ARRAY" && $#{$v->{$x}}==-1) { delete($v->{$x}); next; }
}
}
}
#####################################################################
# return the modification time of a file
sub FileModtime($)
{
my($filename) = shift;
return (stat($filename))[9];
}
#####################################################################
# read a file into a string
sub FileLoad($)
{
my($filename) = shift;
local(*INPUTFILE);
open(INPUTFILE, $filename) || return undef;
my($saved_delim) = $/;
undef $/;
my($data) = <INPUTFILE>;
close(INPUTFILE);
$/ = $saved_delim;
return $data;
}
#####################################################################
# write a string into a file
sub FileSave($$)
{
my($filename) = shift;
my($v) = shift;
local(*FILE);
open(FILE, ">$filename") || die "can't open $filename";
print FILE $v;
close(FILE);
}
#####################################################################
# return a filename with a changed extension
sub ChangeExtension($$)
{
my($fname) = shift;
my($ext) = shift;
if ($fname =~ /^(.*)\.(.*?)$/) {
return "$1$ext";
}
return "$fname$ext";
}
#####################################################################
# a dumper wrapper to prevent dependence on the Data::Dumper module
# unless we actually need it
sub MyDumper($)
{
require Data::Dumper;
my $s = shift;
return Data::Dumper::Dumper($s);
}
#####################################################################
# save a data structure into a file
sub SaveStructure($$)
{
my($filename) = shift;
my($v) = shift;
FileSave($filename, MyDumper($v));
}
#####################################################################
# see if a pidl property list contains a give property
sub has_property($$)
{
my($e) = shift;
my($p) = shift;
if (!defined $e->{PROPERTIES}) {
return undef;
}
return $e->{PROPERTIES}->{$p};
}
sub is_scalar_type($)
{
my($type) = shift;
if ($type =~ /^u?int\d+/) {
return 1;
}
if ($type =~ /char|short|long|NTTIME|
time_t|error_status_t|boolean32|unsigned32|
HYPER_T|wchar_t|DATA_BLOB/x) {
return 1;
}
return 0;
}
# return the NDR alignment for a type
sub type_align($)
{
my($e) = shift;
my $type = $e->{TYPE};
if (need_wire_pointer($e)) {
return 4;
}
return 4, if ($type eq "uint32");
return 4, if ($type eq "long");
return 2, if ($type eq "short");
return 1, if ($type eq "char");
return 1, if ($type eq "uint8");
return 2, if ($type eq "uint16");
return 4, if ($type eq "NTTIME");
return 4, if ($type eq "time_t");
return 8, if ($type eq "HYPER_T");
return 2, if ($type eq "wchar_t");
return 4, if ($type eq "DATA_BLOB");
# it must be an external type - all we can do is guess
return 4;
}
# this is used to determine if the ndr push/pull functions will need
# a ndr_flags field to split by buffers/scalars
sub is_builtin_type($)
{
my($type) = shift;
return 1, if (is_scalar_type($type));
return 0;
}
# determine if an element needs a reference pointer on the wire
# in its NDR representation
sub need_wire_pointer($)
{
my $e = shift;
if ($e->{POINTERS} &&
!has_property($e, "ref")) {
return $e->{POINTERS};
}
return undef;
}
# determine if an element is a pass-by-reference structure
sub is_ref_struct($)
{
my $e = shift;
if (!is_scalar_type($e->{TYPE}) &&
has_property($e, "ref")) {
return 1;
}
return 0;
}
# determine if an element is a pure scalar. pure scalars do not
# have a "buffers" section in NDR
sub is_pure_scalar($)
{
my $e = shift;
if (has_property($e, "ref")) {
return 1;
}
if (is_scalar_type($e->{TYPE}) &&
!$e->{POINTERS} &&
!array_size($e)) {
return 1;
}
return 0;
}
# determine the array size (size_is() or ARRAY_LEN)
sub array_size($)
{
my $e = shift;
my $size = has_property($e, "size_is");
if ($size) {
return $size;
}
$size = $e->{ARRAY_LEN};
if ($size) {
return $size;
}
return undef;
}
# see if a variable needs to be allocated by the NDR subsystem on pull
sub need_alloc($)
{
my $e = shift;
if (has_property($e, "ref")) {
return 0;
}
if ($e->{POINTERS} || array_size($e)) {
return 1;
}
return 0;
}
# determine the C prefix used to refer to a variable when passing to a push
# function. This will be '*' for pointers to scalar types, '' for scalar
# types and normal pointers and '&' for pass-by-reference structures
sub c_push_prefix($)
{
my $e = shift;
if ($e->{TYPE} =~ "string") {
return "";
}
if (is_scalar_type($e->{TYPE}) &&
$e->{POINTERS}) {
return "*";
}
if (!is_scalar_type($e->{TYPE}) &&
!$e->{POINTERS} &&
!array_size($e)) {
return "&";
}
return "";
}
# determine the C prefix used to refer to a variable when passing to a pull
# return '&' or ''
sub c_pull_prefix($)
{
my $e = shift;
if (!$e->{POINTERS} && !array_size($e)) {
return "&";
}
if ($e->{TYPE} =~ "string") {
return "&";
}
return "";
}
# determine if an element has a direct buffers component
sub has_direct_buffers($)
{
my $e = shift;
if ($e->{POINTERS} || array_size($e)) {
return 1;
}
return 0;
}
# return 1 if the string is a C constant
sub is_constant($)
{
my $s = shift;
if ($s =~ /^\d/) {
return 1;
}
return 0;
}
# return 1 if this is a fixed array
sub is_fixed_array($)
{
my $e = shift;
my $len = $e->{"ARRAY_LEN"};
if (defined $len && is_constant($len)) {
return 1;
}
return 0;
}
# return 1 if this is a inline array
sub is_inline_array($)
{
my $e = shift;
my $len = $e->{"ARRAY_LEN"};
if (is_fixed_array($e) ||
defined $len && $len ne "*") {
return 1;
}
return 0;
}
1;
+86
View File
@@ -0,0 +1,86 @@
The Samba Build System
----------------------
----------------------
The build system basically has two main parts: the autoconf-generated
shell scripts which check for availability of functions and libraries
which is stored in the .m4 files and the information about the various
subsystems which is stored in the .mk files.
Object Types
------------
the build system knows about the following object types
SUBSYSTEM:
a SUBSYSTEM is basicly a collection of functions, which provide an
an generic API for a specific problem (e.g. libldb provides an api
for gneric ldb databases. libldb_plugin provides a generic api
for calling ldb plugins, so 'libldb' and 'libldb_plugin' are subsystems)
MODULE:
a MODULE is a specify implementation of a API provided by a SUBSYSTEM.
(e.g. 'libldb_tdb' and 'libldb_ldap' are implementations of the subsystem 'libldb' API,
and 'libldb_plugin_timestamp' is a module of the 'libldb_plugin' subsystem)
EXT_LIB:
an EXT_LIB is an external library which is needed by a SUBSYSTEM, MODULE, BINARY or LIBRARY.
(e.g. 'gtk' or 'KRB5')
BINARY:
a BINARY means a executable binary file.
(e.g. 'smbtorture' or 'ldbedit')
a BINARY typicly has only commandline handling and basic
functionality code in it and depends on the functions of
SUBSYSTEM's (REQUIRED_SUBSYSTEMS).
LIBRARY:
a LIBRARY means a static and/or shared library,
which depends on the used OS.
(e.g. for libldb 'libldb.so', 'libldb.so.0' 'libldb.so.0.0.1'
and libldb.a are created on linux)
a LIBRARY typicly has only glue code in it and depends on
SUBSYSTEM's (REQUIRED_SUBSYSTEMS).
File summary:
-------------
public.m4 - public M4 macros of the build system
config_mk.pm - Support for reading .mk files
dot.pm - Support for generating .dot files for analysis of dependencies
input.pm - Input validation
main.pm - Main
makefile.pm - Makefile generation
output.pm - Dependency calculation
header.pm - build.h generation
cflags.pm - Generates cflags.txt for file-specific cflags
Layout
-------
Toplevel file: configure.in
- included by autogen.sh: aclocal.m4
which includes the SMB_YXZ*() macros
- default tests of the build system
are in build/smb_build/check_*.m4
(mostly compiler and basic C type and function
checks)
- subsystem specific stuff should be included by 'SMB_INCLUDE_M4()'
Generating the configure file
-------------------------
you need to rerun ./autogen.sh when 'configure.in' or any
'.m4' file was modified, then you need to rerun configure.
Generating config.status
-----------------------------
you need to run ./config.status (or 'configure') after a '.mk'
file was changed.
Examples
--------
for now please take a look at the .m4 and .mk files
you find in the source tree, they should be a good reference to start.
+25
View File
@@ -0,0 +1,25 @@
- use pkg-config files in the buildsystem?
- let the build system implement some make functions($(patsubst),$(wildcard),...) and use our own implementations where `make' does not support them
- include extra_flags.txt using Makefile construction if
supported by current make
- fix shared module loading for selftest during builds without install
- remove recursive dependency between LIBSOCKET, LIBCLI_NBT and LIBCLI_RESOLVE
- clearer distinction between dcerpc and ndr. seperate interface tables? Maybe get rid of
NDR's table altogether and use dlopen/dlsym ?
- saner names for:
libcli.so.0.0.1 (rename to libsmb?)
libcli_cldap.so.0.0.1 (rename to libcldap?)
libcli_nbt.so.0.0.1 (rename to libnbt?)
libcli_wrepl.so.0.0.1 (rename to libwrepl?)
- generate headermap.txt
set of test scripts that check the code:
- configure_check_unused.pl
- find_unused_macros.pl
- find_unused_makefilevars.pl
- find_unused_options.sh
- findstatic.pl
- minimal_includes.pl
- check dependencies based on #include lines ?
- check whether private headers are not used outside their own subsystem
- undocumented (no manpage) installed binaries
+35
View File
@@ -0,0 +1,35 @@
# SMB Build System
#
# Copyright (C) Jelmer Vernooij 2006
# Released under the GNU GPL
package cflags;
use strict;
sub create_cflags($$)
{
my ($CTX, $file) = @_;
open(CFLAGS_TXT,">$file") || die ("Can't open `$file'\n");
foreach my $key (values %{$CTX}) {
next unless defined ($key->{OBJ_LIST});
next unless defined ($key->{FINAL_CFLAGS});
next unless ($#{$key->{FINAL_CFLAGS}} >= 0);
my $cflags = join(' ', @{$key->{FINAL_CFLAGS}});
foreach (@{$key->{OBJ_LIST}}) {
my $ofile = $_;
my $dfile = $_;
$dfile =~ s/\.o$/.d/;
$dfile =~ s/\.ho$/.d/;
print CFLAGS_TXT "$ofile $dfile: CFLAGS+= $cflags\n";
}
}
close(CFLAGS_TXT);
print __FILE__.": creating $file\n";
}
1;
+263
View File
@@ -0,0 +1,263 @@
# Samba Build System
# - config.mk parsing functions
#
# Copyright (C) Stefan (metze) Metzmacher 2004
# Copyright (C) Jelmer Vernooij 2005
# Released under the GNU GPL
#
package smb_build::config_mk;
use smb_build::input;
use File::Basename;
use strict;
my $section_types = {
"EXT_LIB" => {
"LIBS" => "list",
"CFLAGS" => "list",
"CPPFLAGS" => "list",
"LDFLAGS" => "list",
},
"SUBSYSTEM" => {
"OBJ_FILES" => "list",
"PRIVATE_DEPENDENCIES" => "list",
"PUBLIC_DEPENDENCIES" => "list",
"ENABLE" => "bool",
"MANPAGE" => "string",
"PUBLIC_PROTO_HEADER" => "string",
"PRIVATE_PROTO_HEADER" => "string",
"PUBLIC_HEADERS" => "list",
"CFLAGS" => "list",
"LDFLAGS" => "list",
"STANDARD_VISIBILITY" => "string"
},
"MODULE" => {
"SUBSYSTEM" => "string",
"INIT_FUNCTION" => "string",
"OBJ_FILES" => "list",
"PUBLIC_DEPENDENCIES" => "list",
"PRIVATE_DEPENDENCIES" => "list",
"ALIASES" => "list",
"ENABLE" => "bool",
"OUTPUT_TYPE" => "list",
"MANPAGE" => "string",
"PRIVATE_PROTO_HEADER" => "string",
"PUBLIC_PROTO_HEADER" => "string",
"PUBLIC_HEADERS" => "list",
"CFLAGS" => "list"
},
"BINARY" => {
"OBJ_FILES" => "list",
"PRIVATE_DEPENDENCIES" => "list",
"ENABLE" => "bool",
"MANPAGE" => "string",
"INSTALLDIR" => "string",
"PRIVATE_PROTO_HEADER" => "string",
"PUBLIC_PROTO_HEADER" => "string",
"PUBLIC_HEADERS" => "list",
"CFLAGS" => "list",
"LDFLAGS" => "list",
"STANDARD_VISIBILITY" => "string",
"USE_HOSTCC" => "bool"
},
"LIBRARY" => {
"VERSION" => "string",
"SO_VERSION" => "string",
"LIBRARY_REALNAME" => "string",
"INIT_FUNCTION_TYPE" => "string",
"OBJ_FILES" => "list",
"DESCRIPTION" => "string",
"PRIVATE_DEPENDENCIES" => "list",
"PUBLIC_DEPENDENCIES" => "list",
"ENABLE" => "bool",
"MANPAGE" => "string",
"PUBLIC_HEADERS" => "list",
"PUBLIC_PROTO_HEADER" => "string",
"PRIVATE_PROTO_HEADER" => "string",
"CFLAGS" => "list",
"LDFLAGS" => "list",
"STANDARD_VISIBILITY" => "string"
}
};
use vars qw(@parsed_files);
@parsed_files = ();
###########################################################
# The parsing function which parses the file
#
# $result = _parse_config_mk($filename)
#
# $filename - the path of the config.mk file
# which should be parsed
sub run_config_mk($$$$)
{
sub run_config_mk($$$$);
my ($input, $srcdir, $builddir, $filename) = @_;
my $result;
my $linenum = -1;
my $infragment = 0;
my $section = "GLOBAL";
my $makefile = "";
my $parsing_file = $filename;
my $retry_parsing_file = undef;
$ENV{samba_builddir} = $builddir;
$ENV{samba_srcdir} = $srcdir;
if (($srcdir ne ".") or ($builddir ne ".")) {
$parsing_file = $builddir."/".$filename;
$retry_parsing_file = $srcdir."/".$filename;
}
if (open(CONFIG_MK, $parsing_file)) {
$retry_parsing_file = undef;
} else {
die("Can't open $parsing_file") unless defined($retry_parsing_file);
}
if (defined($retry_parsing_file)) {
if (open(CONFIG_MK, $parsing_file)) {
$parsing_file = $retry_parsing_file;
$retry_parsing_file = undef;
} else {
die("Can't open neither '$parsing_file' nor '$retry_parsing_file'\n");
}
}
push (@parsed_files, $parsing_file);
my @lines = <CONFIG_MK>;
close(CONFIG_MK);
my $line = "";
my $prev = "";
foreach (@lines) {
$linenum++;
# lines beginning with '#' are ignored
next if (/^\#.*$/);
if (/^(.*)\\$/) {
$prev .= $1;
next;
} else {
$line = "$prev$_";
$prev = "";
}
if ($line =~ /^\[([-a-zA-Z0-9_:]+)\][\t ]*$/)
{
$section = $1;
$infragment = 0;
next;
}
# include
if ($line =~ /^include (.*)$/) {
my $subfile= $1;
my $subdir = dirname($filename);
$subdir =~ s/^\.$//g;
$subdir =~ s/^\.\///g;
$subdir .= "/" if ($subdir ne "");
$makefile .= run_config_mk($input, $srcdir, $builddir, $subdir.$subfile);
next;
}
# empty line
if ($line =~ /^[ \t]*$/) {
$section = "GLOBAL";
if ($infragment) { $makefile.="\n"; }
next;
}
# global stuff is considered part of the makefile
if ($section eq "GLOBAL") {
if (!$infragment) { $makefile.="\n"; }
$makefile .= $line;
$infragment = 1;
next;
}
# Assignment
if ($line =~ /^([a-zA-Z0-9_]+)[\t ]*=(.*)$/) {
$result->{$section}{$1}{VAL} = $2;
$result->{$section}{$1}{KEY} = $1;
next;
}
die("$parsing_file:$linenum: Bad line while parsing $parsing_file");
}
foreach my $section (keys %{$result}) {
my ($type, $name) = split(/::/, $section, 2);
my $sectype = $section_types->{$type};
if (not defined($sectype)) {
die($parsing_file.":[".$section."] unknown section type \"".$type."\"!");
}
$input->{$name}{NAME} = $name;
$input->{$name}{TYPE} = $type;
$input->{$name}{MK_FILE} = $parsing_file;
$input->{$name}{BASEDIR} = dirname($filename);
foreach my $key (values %{$result->{$section}}) {
$key->{VAL} = smb_build::input::strtrim($key->{VAL});
my $vartype = $sectype->{$key->{KEY}};
if (not defined($vartype)) {
die($parsing_file.":[".$section."]: unknown attribute type \"$key->{KEY}\"!");
}
if ($vartype eq "string") {
$input->{$name}{$key->{KEY}} = $key->{VAL};
} elsif ($vartype eq "list") {
$input->{$name}{$key->{KEY}} = [smb_build::input::str2array($key->{VAL})];
} elsif ($vartype eq "bool") {
if (($key->{VAL} ne "YES") and ($key->{VAL} ne "NO")) {
die("Invalid value for bool attribute $key->{KEY}: $key->{VAL} in section $section");
}
$input->{$name}{$key->{KEY}} = $key->{VAL};
}
}
}
return $makefile;
}
1;
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/perl
# Samba4 Dependency Graph Generator
# (C) 2004-2005 Jelmer Vernooij <jelmer@samba.org>
# Published under the GNU GPL
use strict;
use lib 'build';
use smb_build::config_mk;
my $subsys = shift @ARGV;
sub contains($$)
{
my ($haystack,$needle) = @_;
foreach (@$haystack) {
return 1 if ($_ eq $needle);
}
return 0;
}
sub generate($$$)
{
my ($depend,$only,$name) = @_;
my $res = "digraph $name {\n";
foreach my $part (values %{$depend}) {
next if (defined($only) and not contains($only,$part->{NAME}));
foreach my $elem (@{$part->{PUBLIC_DEPENDENCIES}},
@{$part->{PRIVATE_DEPENDENCIES}}) {
$res .= "\t\"$part->{NAME}\" -> \"$elem\";\n";
}
}
return $res . "}\n";
}
my $INPUT = {};
smb_build::config_mk::run_config_mk($INPUT, '.', '.', "main.mk");
my $name = "samba4";
my $only;
if (defined($subsys)) {
my $DEPEND = smb_build::input::check($INPUT, \%config::enabled,
"STATIC_LIBRARY", "SHARED_LIBRARY", "SHARED_LIBRARY");
die("No such subsystem $subsys") unless (defined($DEPEND->{$subsys}));
$only = $DEPEND->{$subsys}->{UNIQUE_DEPENDENCIES_ALL};
push (@$only, "$subsys");
$name = $subsys;
}
my $fname = "$name-deps.dot";
print __FILE__.": creating $fname\n";
open DOTTY, ">$fname";
print DOTTY generate($INPUT, $only, $name);
close DOTTY;
1;

Some files were not shown because too many files have changed in this diff Show More